1. 简介

在使用 Git 的过程中,我们常常需要撤销或回滚某次提交,无论是为了回退到某个历史状态,还是为了修复一次错误的提交。

在本篇文章中,我们将介绍 Git 中最常用的几种撤销和回滚提交的方法,并演示它们之间细微但重要的差异。

2. 使用 git checkout 查看历史提交

我们可以使用 git checkout 命令来查看某个特定提交时项目的状态。通过 git log 可以查看 Git 仓库的历史提交记录,每条提交都有一个唯一的 SHA-1 标识哈希值。我们可以用这个哈希值配合 git checkout 来查看任意一次提交的状态。

例如,我们想查看哈希值为 e0390cd8d75dc0f1115ca9f350ac1a27fddba67d 的提交:

git checkout e0390cd8d75dc0f1115ca9f350ac1a27fddba67d

此时我们的工作目录会精确匹配该提交的状态。我们称这种状态为 detached HEAD(分离头指针)状态。✅

在此状态下:

  • 可以查看和编辑文件;
  • 所有修改不会保存到仓库中;
  • 不用担心破坏当前项目状态。

我们也可以使用 git checkout 来恢复本地修改的文件到工作区版本。

3. 使用 git revert 回滚提交

git revert 命令用于撤销某个特定的提交。⚠️注意:这个命令并不是传统意义上的“撤销”,而是通过生成一个新的提交来反转某个提交所做的更改。

这意味着:

  • ✅ 它不会删除提交历史;
  • ❌ 也不会回退整个项目状态,只是撤销某一次提交的内容;
  • ✅ 适用于撤销已经推送到远程仓库的提交。

git revert 不会移动 HEAD 指针,而是创建一个新的提交来抵消目标提交的更改。这与 git reset 等命令的行为不同。

举个例子:

mkdir git_revert_example
cd git_revert_example/
git init .
touch test_file
echo "Test content" >> test_file 
git add test_file
git commit -m "Adding content to test file"
echo "More test content" >> test_file 
git add test_file
git commit -m "Adding more test content"
git log
git revert e0390cd8d75dc0f1115ca9f350ac1a27fddba67d
cat test_file

在这个例子中,我们创建了一个测试文件,进行了两次提交,然后使用 git revert 回滚了第一次提交。最后通过 cat test_file 查看文件内容,确认回滚生效。

4. 使用 git reset 回退到历史状态

如果我们想将整个项目回退到之前的某个状态,可以使用 git reset 命令。⚠️这个命令比 git revert 更加强大但也更危险。

git reset 有三种主要模式:

  • --soft
  • --mixed(默认)
  • --hard

理解这三种模式的区别是使用 git reset 的关键。

Git 内部有三个“树”:

  • 工作目录(Working Directory):本地文件系统的状态;
  • 暂存区(Staging Index):使用 git add 添加的变更;
  • 提交历史(Commit History):已提交的快照。

4.1. --hard

--hard 是最危险的选项。它会:

  • 移动 HEAD 指针到指定提交;
  • 将暂存区和工作目录都重置为目标提交的状态;
  • 丢失所有未提交的改动

例如:

echo "Text to be committed" >> test_file
git add test_file
touch new_test_file
git add new_test_file

如果我们执行:

git reset --hard 9d6bedfd771f73373348f8337cf60915372d7954

那么:

  • test_file 中新增的内容会消失;
  • new_test_file 会被删除;
  • 所有未提交的改动都会被清除,不可恢复。

4.2. --soft

--soft 是最温和的选项。它只会:

  • 移动 HEAD 指针;
  • 不改变暂存区和工作目录的内容 ✅

也就是说,我们之前通过 git add 添加的改动仍然保留在暂存区中,可以再次提交。

4.3. --mixed

--mixed 是默认选项。它的行为介于 --soft--hard 之间:

  • 移动 HEAD 指针;
  • 清空暂存区,使其与目标提交一致;
  • 已暂存的改动会被移到工作目录中等待重新添加 ✅

也就是说:

  • 文件内容不会被删除;
  • 但之前通过 git add 添加的改动会被取消暂存,需重新添加。

5. 总结

我们可以用一句话来总结这两个命令的核心区别:

git revert 是安全的,适合撤销已推送的提交;
git reset 是危险的,适用于撤销本地未推送的改动。

在多人协作的项目中,**永远不要对已经推送到远程仓库的提交使用 git reset**,因为其他开发者可能已经基于这些提交做了新的开发,强行重置会导致混乱。

总结对比表

命令 是否创建新提交 是否移动 HEAD 是否影响工作目录 是否影响暂存区 适用场景
git revert ✅ 是 ❌ 否 ❌ 否 ❌ 否 撤销已推送的提交
git reset --soft ❌ 否 ✅ 是 ❌ 否 ❌ 否 撤销提交但保留暂存内容
git reset --mixed ❌ 否 ✅ 是 ✅ 是 ✅ 是 撤销提交并取消暂存
git reset --hard ❌ 否 ✅ 是 ✅ 是 ✅ 是 彻底丢弃所有改动(危险)

希望这篇文章能帮助你在 Git 提交撤销时做出更明智的选择。如果你在团队协作中使用 Git,建议优先使用 git revert,避免引发不必要的冲突和混乱。


原始标题:Undo and Revert Commits in Git