Skip to content

Git - 修正错误

每个人都会犯错。Git 提供了多种方法来撤销或还原(undo 或 revert)修改,具体取决于这些修改是位于你的工作区(working directory)、暂存区(staging area),还是已经提交(committed)。

如果你在工作区对文件进行了修改,但尚未暂存(stage),并且想丢弃这些修改(将文件恢复到最后一次提交/HEAD 的状态),可以使用 git restore <file> 命令。

假设 Jerry 不小心修改了 string_operations.c:

[jerry@dev-machine src]$ pwd
/home/jerry/project/src
[jerry@dev-machine src]$ # ...Jerry 对 string_operations.c 做了些不想要的修改...
[jerry@dev-machine src]$ git status -s
M string_operations.c
[jerry@dev-machine src]$ git restore string_operations.c
[jerry@dev-machine src]$ git status -s
# 无输出,表示工作区是干净的

Jerry 对 string_operations.c 所做的修改现在已消失,文件已恢复到最后一次提交时的状态。在引入 git restore 之前,通常使用 git checkout -- <file> 命令来实现这一目的。

类似地,如果你不小心删除了一个文件,git restore <file> 也可以将其恢复:

[tom@dev-machine src]$ ls
Makefile string_operations.c
[tom@dev-machine src]$ rm string_operations.c
[tom@dev-machine src]$ git status -s
D string_operations.c
[tom@dev-machine src]$ git restore string_operations.c
[tom@dev-machine src]$ ls
Makefile string_operations.c
[tom@dev-machine src]$ git status -s
# 工作区干净

取消暂存文件(从暂存区移除修改)

Section titled “取消暂存文件(从暂存区移除修改)”

如果你已经使用 git add 暂存了一个文件,但想取消暂存(从暂存区移除它,但保留工作区中的修改),请使用 git restore --staged <file>。

Tom 修改了 string_operations.c 并将其暂存:

[tom@dev-machine src]$ # ...Tom 修改了 string_operations.c...
[tom@dev-machine src]$ git add string_operations.c
[tom@dev-machine src]$ git status -s
M string_operations.c

现在,Tom 决定暂时不提交这些修改,于是他取消了文件的暂存:

[tom@dev-machine src]$ git restore --staged string_operations.c
[tom@dev-machine src]$ git status -s
M string_operations.c

该文件不再处于暂存状态(注意 ‘M’ 现在位于第一列,而不是第二列),但修改仍然存在于工作区中。在引入 git restore --staged 之前,通常使用 git reset HEAD <file> 来实现此目的。

git reset 是一个功能强大的命令,用于通过将当前分支的 HEAD 指针移动到先前的提交来撤销已提交的修改,并可选择性地修改暂存区和工作区。理解其不同的模式至关重要。注意:如果使用不当,特别是与 --hard 结合使用时,git reset 可能会导致工作丢失。

假设提交历史是 C1 <- C2 <- C3 (HEAD -> main)。我们想重置到 C2。

将 HEAD 移动到 <commit-SHA>。暂存区和工作区 不 会改变。<commit-SHA> 之后提交的修改(在我们的示例中是 C3 的修改)将显示为已暂存的修改。

# 假设 HEAD 在提交 C3 (例如 5776472) 并且 C2 是 29af9d4
[jerry@dev-machine project]$ git log -1 --oneline
5776472 (HEAD -> main) Removed executable binary
[jerry@dev-machine project]$ git reset --soft 29af9d4
[jerry@dev-machine project]$ git log -1 --oneline
29af9d4 (HEAD -> main) Added compiled binary
[jerry@dev-machine project]$ git status
# 来自提交 5776472 的修改将被暂存

如果你想以不同的方式重新提交 C3 的修改,例如将它们合并成一个新的提交或拆分它们,这会很有用。

git reset --mixed <commit-SHA>(默认)

Section titled “git reset --mixed <commit-SHA>(默认)”

将 HEAD 移动到 <commit-SHA> 并更新暂存区以匹配 <commit-SHA>。工作区 不 会改变。<commit-SHA> 之后提交的修改将显示为工作区中未暂存的本地修改。

[jerry@dev-machine project]$ git reset --mixed 29af9d4
# 或者简单地使用:git reset 29af9d4(因为 --mixed 是默认模式)
[jerry@dev-machine project]$ git log -1 --oneline
29af9d4 (HEAD -> main) Added compiled binary
[jerry@dev-machine project]$ git status
# 来自提交 5776472 的修改将存在于工作区中,未暂存。

这对于完全取消提交但将修改保留在本地以便重新工作非常有用。

警告:此命令可能导致未提交的修改以及 <commit-SHA> 之后的所有提交永久丢失。请极其谨慎使用。

将 HEAD 移动到 <commit-SHA>,并更新暂存区和工作区以匹配 <commit-SHA>。<commit-SHA> 之后提交中的任何修改以及任何未提交的本地修改(无论是否已暂存)都将永久删除。

Jerry 对 string_operations.c 进行了一些本地修改,甚至将其暂存了。最新的提交是 5776472。

[jerry@dev-machine src]$ head -2 string_operations.c
// Temporary debug line
#include <stdio.h>
[jerry@dev-machine src]$ git add string_operations.c
[jerry@dev-machine src]$ git status -s
M string_operations.c
[jerry@dev-machine src]$ git reset --hard 5776472 # 重置到上一个提交
HEAD is now at 5776472 Removed executable binary
[jerry@dev-machine src]$ git status -s
# 工作区干净
[jerry@dev-machine src]$ head -2 string_operations.c
#include <stdio.h>
// 临时调试行已消失。

--hard 重置丢弃了所有已暂存和未暂存的本地修改,将 string_operations.c 恢复到提交 5776472 中的状态。如果他重置到更早的提交,则该点之后的所有提交也将从分支历史中消失。

如果你想撤销一个已经推送到共享仓库的提交,git reset 是危险的,因为它会重写历史。在这种情况下,应该使用 git revert <commit-SHA>。这个命令会创建一个 新的 提交,该提交应用了指定提交所引入修改的反向操作。这保留了历史记录,对于共享分支来说是安全的。

[jerry@dev-machine project]$ git revert HEAD # 还原最新的提交
# 这将打开一个编辑器,用于输入新的还原提交消息。
# 保存后,将创建一个新的提交,该提交撤销了 HEAD 提交的修改。

git revert 是在公共/共享分支上撤销修改的推荐方法。