Git 版本回退

Git 版本管理的基本结构

  • Working Tree :当前的工作区域
  • Index/Stage :暂存区域,使用 git add xx,就可以将 xx 添加近 Stage 里面。(注意区分 stage 和 stash; git stash 命令暂存的地方不是 stage/index)
  • Repository :版本库,提交的历史,即使用 git commit 提交后的结果
  • Remote Repository:远程版本库,上面没有画,就是利用 github、Gitee 等将 repository 同步到云端

版本管理的基本命令

git revert

git revert -n <commit_id> 是用于“反做”某一个版本,以达到间接撤销该版本的修改的目的,它不会删除 repository 中的原有版本,相反会增加一个新版本。比如,我们 commit 了三个版本(版本一、版本二、 版本三),突然发现版本二不行(如:有 bug),想要撤销版本二,但又不想影响撤销版本三的提交,就可以用 git revert 命令来反做版本二,生成新的版本四,这个版本四里会保留版本三的东西,但撤销了版本二的东西。

revert 如果之前(版本三)和之后(版本二)版本有改到同一个地方可能会触发冲突,需要手动解决冲突。

git rebase

git rebase 的功能是完成变基的操作,常常用来进行对 commit 进行整理,如合并多个 commit。常用的命令是 git rebase -i [startpoint] [endpoint] 用来交互式整理指定范围的 commit。运行之后会出现类似下面的界面,需要我们根据需求对其内容进行编辑:

上面未被注释的部分列出的是我们本次 rebase 操作包含的所有提交,下面注释部分是 git 为我们提供的命令说明。每一个 commit id 前面的 pick 表示指令类型,git 为我们提供了以下几个命令:

  • pick:保留该 commit(缩写:p)
  • reword:保留该 commit,但我需要修改该 commit 的注释(缩写:r)
  • edit:保留该 commit, 但我要停下来修改该提交 (不仅仅修改注释)(缩写:e)
  • squash:将该 commit 和前一个 commit 合并(缩写:s)
  • fixup:将该 commit 和前一个 commit 合并,但我不要保留该提交的注释信息(缩写:f)
  • exec:执行 shell 命令(缩写:x)
  • drop:我要丢弃该 commit(缩写:d)

编辑完成后,保存退出。然后根据选择的设置,可能会继续提示我们修改确定注释内容,编辑完注释后再次保存退出,然后 commit 即可。

git reset

git reset 有三种模式,即 --soft--mixed(默认),--hard

  • git reset --hard:删除被回退了的 commits 版本,重置 stage 暂存区和 working tree 工作目录
    • 此模式会重置 repository 的 HEAD 到指定版本,回退区间内的 commits 版本会直接清空,同时也会重置清空 stage 区和工作目录里的内容为该版本对应的内容。换句话说,就是你的“被回退了的 commits” 和“没有 commit 的修改(包括没有 add 的修改)”会被全部擦掉,此时用 git status 查看状态就是 nothing to commit, working tree clean,类似于指定版本刚刚 commit 时的状态。
    • 注意:此模式会清空被回退了的 commits 和当前没有 commit 的内容,谨慎操作
  • git reset --soft:保留 stage 暂存区和 working tree 工作目录,并把重置 HEAD 所带来的新的差异放进暂存区
    • 此模式会重置 repository 的 HEAD 到指定版本,但是会保留当前工作区和暂存区的内容。此外由于版本回退而可能导致的 repository 和当前工作区的差异,也会被放置在暂存区。
    • 当我们想合并「当前节点」与「reset 目标节点」之间不具太大意义的 commit 记录 (可能是阶段性地频繁提交) 时,可以考虑使用 soft 模式来让 commit 演进线图较为清晰点。此时 git reset --soft 相当于把「当前节点」与「reset 目标节点」之间的提交都重新放回暂存区,直接 git commit 就可以把它们全部提交到一个新的 commit 里面。
  • git reset --mixed 或者 git reset:保留 working tree 工作目录,并把 stage 暂存区的内容退回给工作目录,此外重置 HEAD 所带来的新的差异也会被放入工作目录
    • 此模式会重置 repository 的 HEAD 到指定版本,但是会保留当前工作区的内容。暂存区内容(已经 git add 了的)会被退回给工作区(相当于 git add 的逆操作)。此外由于版本回退而可能导致的 repository 和当前工作区的差异,也会被放置在工作区(git add 之前的状态)。简而言之,就是「把所有差异都混合(mixed)放在工作目录中」。

版本撤销及常见场景

根据上述结构,Git 版本回退可以分为 4 种,即

  1. 工作区的代码想撤销
  2. add 到暂存区的代码想撤销
  3. 提交到本地仓库的代码想撤销
  4. 远程仓库的代码想撤销

工作区的代码想撤销

git checkout -- <file> 撤销当前工作目录中指定文件的修改

add 到暂存区的代码想撤销

git reset HEAD 将已经 add 到暂存区的代码撤销到工作区,即 add 操作的逆操作

提交到本地仓库的代码想撤销

可以利用 git reset --hard <版本号> 命令来实现版本回退,该命令中的版本号有几种不同的写法:

  • 可以使用 HEAD^ 来描述版本,一个 ^ 表示前一个版本,两个 ^^ 表示前两个版本,以此类推;
  • 也可以使用数字来代替 ^,比如说前 100 个版本可以写作 HEAD~100
  • 也可以直接写版本号,表示跳转到某一个版本处。每次提交后,都会生成一个哈希码作为版本号,所以这里可以直接填版本号,哈希码很长,但是不用全部输入,只需要输入前面几个字符即可。

注意:此操作会直接将工作区,暂存区的当前内容清空,并撤回到指定回退版本的状态

远程仓库的代码想撤销

本地撤销后,push 到远程即可。可能会报错远程分支比当前本地分支超前,可以通过该 -f 参数强制 push 到远程。

版本回退误操作的补救

有时候脑抽或者手欠,总是可能发生一些意外,特别是 git reset --hard 这个死亡操作,更是可能产生严重后果。如果发现 git reset --hard 误操作了,在某些情况下是可以还原的:(1)想还原被回退了的那些 commits 的内容,比较简单;(2)想还原被 add 但是没有 commit 的内容,比较复杂;(3)想还原没有 add 的内容,别做梦了,基本不可能。

首先的首先,切记,还原操作之前,先把当前的内容做好备份!还原操作本身也是有“破坏性”的,别捡回西瓜,又丢了芝麻。

还原被回退了的那些 commits

git reset --hard 虽然会导致回退区间内的 commits 内容被删除,但是其实这些记录还是在的。如果想取消这次回退,可以先用 git reflog 查看所有 commit 历史(也包括被我们误回退了的那些 commits),大概结果如下:

9565525 (HEAD -> main, origin/main, origin/HEAD) HEAD@{0}: reset: moving to HEAD^
5f117a5 HEAD@{1}: commit: test2
9565525 (HEAD -> main, origin/main, origin/HEAD) HEAD@{2}: commit: test1
726293d HEAD@{3}: init

可以看到有两个 HEAD->main 开头的 commits,其实就是我们名义上的当前版本和回退操作所回退到的版本(这俩其实是同一个版本),而这俩版本中间的 commits 就是被我们回退操作删除了的内容。如果我们希望撤销误回退操作,只需要找到这个操作之前的 commit id(这里是 5f117a5),执行 git reset --hard 5f117a5 就行了(注意:撤销回退也是通过 git reset --hard 命令实现的,所以执行之前务必先保存好当前还需要的内容,别又被撤销回退的操作给冲掉了,无限套娃)。

还原 add 但没有 commit 的内容

这个复原比较麻烦,但是是有可能的。主要用到的命令是 git fsck --lost-found,运行后,可以在本地项目文件中路径为 .git/lost-found/other 的地方,找到所有的没有 add 但没有 commit 的文件,甚至可能还包括你每次 git add 的版本。

但是!它们的文件名不再是原来的名字,而是变成了一长串 id 号码,连扩展名都没了,需要自己分辨到底谁是谁。分辨时有一些技巧,比如用 find .git/objects -type f | xargs ls -lt | sed 60q 查看最近修改的记录是哪些(参数 60 代表最近的 60 个 add 过的文件)。然后把这些记录复制出来(别再搞丢了,那就没了),然后用 txt 打开,如果是文本类型的文件(代码之类的),内容会正常显示;如果是图片,docx 等内容,显示的是二进制内容,这时候尝试把文件扩展名改成 pngdocx 之类的,用对应软件打开,如果能打开就对了,具体名字叫啥自己慢慢回忆吧。

参考

posted @ 2023-08-16 16:12  凌晗  阅读(83)  评论(0编辑  收藏  举报