Git后悔药大全吐血整理 建议收藏

1,561 阅读8分钟

no-logo.png 先上个完整的思维导图,你也可以先点个赞😊

一、撤销文件修改

  • 撤销单个文件的修改
git reset HEAD XXX.js
  • 撤销所有未提交的修改
git reset --hard

因为此时的代码还没有提交到仓库,所以这种撤销是不可恢复的,请谨慎操作。撤销之后代码版本将会回回退到最近一次拉取/提交的状态。

二、暂存区添加撤销

1. 暂存区添加

  • 首先查看项目文件状态(有哪些没有被添加到暂存区的文件 Untracked files)
    • Changes to be committed 新创建未提交的文件(暂存区中)
    • Changes not staged for commit 已修改未提交的文件(暂存区中)
    • Untracked files 未添加到暂存区的文件(不在暂存区中)
git status

上一步的结果中 Untracked files 对应的就是没有被添加到暂存区的文件,直接使用 git add + <Untracked files中的文件路径> 即可添加到暂存区(添加到暂存区的文件才会在 commit 时参与提交),举个栗子:

git add leetcode/977.js

2. 暂存区移除

  • 第一步还是git status查看暂存区状态,在 Changes to be committed 中找到对应你要从暂存区删除的文件列表
  • 第二步通过 git reset HEAD <要移除的文件路径> 命令将无需提交的文件从暂存区移除
git reset HEAD <要移除的文件路径>
  • 第三步 本来移除到这里就结束了,这里建议再使用 git status 检查下删除结果

三、编辑最近一次提交

高能警告,以下操作可以支持三种维度的commit修改

  • 重新编辑 commit message
  • 添加漏提交的文件
  • 修改已提交的文件内容

其实这三个问题使用一个命令就能搞定,方便理解,所以就不分开写了。至于怎么提交,各位看官有不会的吗?…… 很好大家都会,那我们就从提交完成开始讲起

1. 提交完成,先查看提交状态

git status

g01.png

如果你突然发现 Untracked files 中存在你漏提交的文件,以截图中勾出来的test.vue文件为例

2. 发现漏提交的文件,添加到缓存区

git add src/components/fileEdit/test.vue

根据 Unix 设计哲学,没有消息就是最好的消息。添加成功你不会有任何提示。

当然,如果你只想要修改 commit message,那就忽略第二条。

3. 存在要修改的文件,添加到缓存区

注意,当你之前的提交已经包含如 index.vue 文件,你在提交之后发现你提交有误,但是要又不想重新来一次commit,这时需要将你修改的文件手动加到缓存区。很简单,只需以下三步:

  • 修改完文件
  • git status 查看改动文件
  • git add <上一步查看到的文件名对应路径>

注意,index.vue 文件是你已经提交过的文件,但是要想合并到上一次 commit 中也需要 git add 添加一次。因为git add 添加的是具体的文件改动内容,而不是文件名。

4. 撤销

amend 是修正的意思。

当在 commit 命令后边加上 --amend 命令,Git 不会在当前 commit 上增加一条 commit 记录,而是会把当前 commit 里的内容和暂存区(stageing area)里的内容合并起来后创建一个新的 commit,用这个新的 commit 把当前 commit 替换掉。所以撤销后,你在 git 提交历史中只会看到一条 commit 记录,之前的那一条被覆盖了。

git commit --amend

4.png

输入以上命令你应该是进入了如下界面图所示的界面,根据操作系统的不同它可能是 nano 或者 vi,这里介绍一下它的最基本用法。

  • 默认进入是在命令模式,按一次小写i,进入编辑模式
  • 输入完成,按一次ESC回到命令模式
  • 输入两次大写Z(支持组合键)即可保存退出

了解了以上用法,要想修改 commit message 信息就简单,先按一次小写 i,配合键盘上下左右按键修改完message信息后按一次 ESC,然后再按两次大写 Z,大功告成。

看图,提交信息已经变了 5.png

再看看提交记录,有彩蛋哦 7.png

现在已经是是下午四点多了,git commit --amend修改过的commit记录在提交历史中显示的时间还是你首次提交的时间。也就是说你可以偷偷的把提交过的文件修正,而git提交记录中只显示一条。

四、编辑任意一次提交

发现某一次提交的代码有问题,想修改回退?

回顾一下刚刚学会的第三条,你已经已经学会了直接修改上一条提交有误的commit,所以我们只需要考虑你要修改或回退的提交不是最新提交的情况。

其实学会了git commit --amend,这一次的操作也很简单。我们只需要把要修改的焦点移动到你要修改的那次commit即可,这里就要用到 rebase -i 命令(i是 interactive 的缩写,意为交互式,rebase -i 可以理解为交互式变基)。

交互式变基?是不是有点蒙圈?
举个最简单的例子,你在电脑上写文章时光标一直指向的是最后一个字符,你可以直接按back键实现删除;但是你发现之前的字有误又不想把最后写的几个字全部删掉怎么办?当然是将光标移动到错误字符处,你可以认为交互式变基和移动光标的操作差不多。 不过这里的焦点不是指HEAD

再来张动图理解一下。

7.gif

继续说rebase -i这个命令,如果发现倒数第三次commit有误,也就是说当前光标要向前移动两次在考虑编辑修改,这里需要了解一下Git支持的两种偏移符号

  • ^ 一个^代表往回偏移一次。
    • 偏移三次对应commit语法应该是 git rebase -i HEAD^^^
  • ~ ~加一个数字n代表偏移往回偏移n次。
    • 偏移三次对应commit语法应该是 git rebase -i HEAD~3

接下来的操作和第三条的流程一样了,我大概罗列一下

  • 修改文件
  • 添加文件 git add <修改了要提交的文件>
  • git commit --amend

等等,还有最后一步,把你的光标移到最后,依旧是 rebase 语法。

git rebase --continue

到这要恭喜你,这次交互式 rebase 的过程就完美结束了。

rebase 的中文解释是变基/重定基底,在你理解了以上流程后你会发现这个解释还是比较准确的,可以留意品味一下。

8.gif

五、回退某一次提交

首先要变基到你想撤销的那一次提交。(刚已经讲过rebase具体用法,没看明白的可以再看一遍)

git rebase -i HEAD~2

回退的原理其实就是生成一个新的提交,把之前提交的内容抵消掉。

git revert HEAD^

如果你本地存在没提交的文件你可能会遇到如下警告阻止你回退。你可以先提交或将这些修改暂存到某处再回退来解决。

error: your local changes would be overwritten by revert.
hint: commit your changes or stash them to proceed.

完成后你会发现本地仓库多了一次commit,这时直接push到对应的远程仓库即可。

六、撤销分支删除

有的 task 或 bug 分支,用完就删是个好习惯,但是一不小心把一个有用的分支删除了怎么办?

这里介绍一下 reflog 命令,全称 reference log,它可以查看 Git 仓库中的引用的移动记录。如果不指定引用,它会显示 HEAD 的移动记录,(关于HEAD和引用的深入介绍之后应该会单独写一篇讲解)。

我在本地建了一个 dev-reflog 且 push 到了远程仓库,然后使用如下命令删除了这个分支(注意我用的 -D 是强制删除,实际项目中请慎用)。

git branch -D dev-reflog

好了,到这里 dev-reflog 分支就被删除了,要想恢复首先通过 git reflog 查看操作记录。

git reflog

reflog.png

上图中是倒数第二条我框出来的就是被删掉的 dev-reflog 分支对应的引用code,接下来就可以进行找回操作了

git checkout 715b2cb
git checkout -b dev-reflog

因为Git的回收机制,一定时间被删除的分支会被Git彻底回收导致无法找回,所以使用 reflog 一定要及时。

七、无法找回的情景

大多数情况下git操作都是有迹可循可回退的,但也有三种特殊情况是无法恢复的。

1. 本地文件reset

本地文件reset后因为没有提交,在git中还没有留下记录所以是无法找回的。(当然ctrl/command+z这种非git操作不在我们的讨论范畴)

2. amend 修改后的文件

amend 命令修改文件实际是用当前文件修改覆盖之前的 commit 提交,因为是覆盖所以在git历史中只会存在最新一次的commit记录,所以 amend 修改前的状态是不可回退的。

3. 被 Git 回收的分支

在删除一个分之后,git 会自动回收长时间被删除的分支。

感谢