撤销 Git 修改

内容纲要

查看【Git】专题可浏览更多内容

在使用 Git 时在做出一个操作后后悔了怎么办?以下列出一些场景:

修改最后一次提交

有的时候提交完了才发现少提交了某个文件,或是提交信息写错了,这时候再进行一次类似的提交就会让提交信息变得很难看。

这时候可以使用 --amend 选项,就相当于用一个新的提交来替换掉之前的最后一次。

⚠️ 注意,使用这个技巧的时候需要小心,因为修正会改变提交的 SHA-1 校验和。 它类似于一个小的「变基」,要注意两点:

  1. 只能用它修正上一次提交;
  2. 不要对一个已经在远程仓库上的提交进行操作,因为团队的其他成员可能已经获得了你的这次提交并且在此基础上进行了改动;

漏提交了文件

# 添加漏提交的文件
git add <文件名>

# --no-edit 表示不需要编辑提交信息
git commit --amend --no-edit

如果提交信息并没有写错,只是少提交了文件,那么可以使用 --no-edit 选项跳过修改提交信息。

提交信息写错了

如果是提交信息写错了:

$ git commit --amend

这时候会进入到另一个界面,主要是修改提交信息,如果你从未使用过,简单来说是这样:

  1. I 键进入「插入模式」,以修改提交信息;
  2. 修改完后按 Ctrl + C 键然后输入::wq 保存并退出即可。

删除

删除文件并保留在工作区

使用 git rm 命令可以将文件移除。

git rm <文件名>

# 如果想保留在工作区
git rm --cached <文件名>

如果有一个文件想在工作区中保留,但从暂存区和版本库中移除,就可以使用 --cached 选项,这样该文件状态就会变成「未跟踪」。

删除自上次提交后的新文件

如果自上次提交后增加了很多新文件,这些文件还是「未跟踪」状态,现在全都不想要了可以使用命令:

# 为了以防万一,可以先使用 git clean -n 看看会删除什么
git clean -df

恢复

在当改动还没有被提交之前,使用 git restore 恢复命令用于撤销本地改动。

⚠️ 注意:但也正因为没有被提交,撤销本地改动的操作是无法挽回的,因为这些改动没有提交成为历史版本,所以三思而后行。

将工作区的文件恢复至指定历史版本

该操作不会影响暂存区

# 恢复当前目录中的所有文件
git restore .
# git restore <文件名>,在老版本 Git 中通常会使用 git checkout -- <文件名>
git restore README.md

将添加到暂存区的文件恢复到工作区

# 在老版本 Git 中通常会使用 git reset HEAD [文件名]
git restore --staged <文件名>

将工作区及暂存区的文件恢复到指定历史版本

# --source=<commit id>
# --staged 可缩写为 -S --worktree 可缩写为 -W
git restore --source=HEAD --staged --worktree <文件名>

回滚

使用 git revert 命令可用于撤销已提交的改动。这个操作实际上会自动产生一个新的提交,在提交中包括了想要撤销的那个提交的所有反向改动。

⚠️ git revert 需要工作区是干净的。

# git revert <commmit id>
git revert HEAD

这也许有点绕,举个例子,假设当前有一个 Markdown 文件是这样的:

## 这是一个标题

该项目现在有两次提交,第一次是空的 Markdown 文件,第二次就如上添加了一个标题,此时使用 git revert HEAD 命令会将文件内容恢复成空内容也就是第一次提交,并且会有一个新的提交(也就是第三次提交)。

当你想撤销某次提交的改动时就可以使用 git revert 命令,这样非常方便,就不需要回溯老版本然后再复制粘贴。

重置

使用 git reset 命令也可用于撤销已提交的改动。这个操作不会自动产生一个新的提交,或是删除你要撤销的提交,它会重置你当前的 HEAD 分支到一个特定旧的版本。

举个例子:

git log --pretty=oneline
9a201ca2be395f656318d47a3c1f9573041d4e14 (HEAD -> master) 添加正文内容
a02c2108c97e8ea09f623404360940c528415ffb 添加文章标题
de4d93a0f98c0657188222bbc0053f6fdc63b4a7 添加说明文件
8cc6573cd56084378cd48dad4951cbdcd0e61b74 项目成立

假设这个说明文件的改动很不满意,想回到刚开始都第一版,而之后的两个版本都不要了,就可以使用 git reset 命令。

另外请注意,与使用 git commit --amend 命令时提到的一样:最好不要在你与他人共同协作的「公共分支」上使用 git reset

因为当把提交推送到公共分支后,其他人很可能就已经拉取并使用,而此时如果你强制推送 reset 后的提交到远程仓库后,其他人拉取这个公共分支时他们的历史会消失一部分。

总而言之其实就是要注意一点:你的操作不能影响到他人

git reset 有三个选项:

  • --mixed
  • --soft
  • --hard

重置版本库至指定历史版本

使用 --soft 回退版本时不会操作工作区和暂存区的内容:

# git reset --soft <commit-id>
git reset --soft de4d93a

另外除了 Commit ID 还可以有别的表达方式:

  • HEAD:最后一次提交;
  • HEAD^:上一次提交;
  • HEAD~n:上 n 次提交,如 git reset --soft HEAD~2 回到前一次提交;

重置暂存区与版本库至指定历史版本

--mixed 就是默认不带任何选项时的 git reset 命令的效果:

# git reset <commit-id>
git reset de4d93a # 等同 git reset --mixed de4d93a

这样工作区中的修改不变,但是暂存区和历史版本会回到指定的版本。

重置工作区、暂存区及版本库至指定历史版本

如果工作区和暂存区的东西都不想要了,只想与版本库一样回退到指定版本,就可以使用 --hard 选项,这也是最直接最危险以及最常用的选项。

# git reset --hard <commit-id>
git reset --hard de4d93a

重置至未来版本

如果回到过去的版本后后悔了,这时使用 git log 已经看不到被删除的版本了怎么办?

可以使用 git reflog

git reflog
ec1420c (HEAD -> master) HEAD@{0}: reset: moving to ec1420c
c0b3823 HEAD@{1}: commit: 修改正文内容
40f6729 HEAD@{2}: commit: 添加正文内容
ec1420c (HEAD -> master) HEAD@{3}: commit: 添加标题
6cdb131 HEAD@{4}: commit (initial): 添加说明文件

然后选择要回退到 ID

git reset --hard 40f6729