1. 引言

在日常开发中,我们经常需要撤销或回滚 Git 提交——无论是回退到特定时间点,还是撤销某个问题提交。

本文将系统介绍 Git 中最常用的撤销和回滚命令,并深入剖析这些命令之间的微妙差异。

2. 使用 git checkout 回溯历史提交

通过 git checkout 命令,我们可以查看项目在特定提交时的状态。首先使用 git log 查看提交历史,每个提交都有唯一的 SHA-1 标识哈希值,我们可以用这个哈希值回溯任意历史提交。

例如,要回溯到哈希值为 e0390cd8d75dc0f1115ca9f350ac1a27fddba67d 的提交:

git checkout e0390cd8d75dc0f1115ca9f350ac1a27fddba67d

此时工作目录将完全匹配指定提交的状态。这意味着我们可以安全地查看项目历史状态并编辑文件,而不会丢失当前项目状态。此处的所有修改都不会保存到仓库,这种状态被称为分离头指针(detached HEAD)

小技巧:git checkout 也可用于恢复本地修改的文件到工作区版本。

3. 使用 git revert 安全撤销提交

git revert 命令用于撤销提交,但需注意它并非传统意义上的撤销操作。该命令会反转目标提交的变更,并生成一个包含反向内容的新提交

关键点:**只有当我们需要应用某个提交的反向操作时,才应使用 git revert**。它不会删除后续提交来恢复项目状态,而是仅撤销单个提交。

git checkoutgit reset 不同,git revert 不会移动引用指针到目标提交,而是通过新提交实现撤销。

下面通过示例演示撤销操作:

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

这个示例中:

  1. 创建测试文件并提交初始内容
  2. 添加新内容并再次提交
  3. 通过 git log 确定要撤销的提交哈希
  4. 执行 git revert 撤销最新提交
  5. 通过查看文件内容验证撤销结果

4. 使用 git reset 回退项目状态

git reset 是 Git 中更强大的回退工具,它通过三种模式与 Git 的内部状态管理系统(即三棵树)交互:--hard--soft--mixed。理解这些模式的差异是掌握 git reset 的关键。

踩坑预警:git reset 会移动 HEAD 引用指针,而 git checkout 仅操作指针但不移动它

4.1. --hard 模式(最危险)

这是最常用也最危险的选项:

  • 更新提交历史,引用指针指向指定提交
  • 暂存区和工作区完全重置为指定提交状态
  • 所有未提交的更改将永久丢失

继续前面的示例,假设我们提交了新内容并添加了新文件:

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

现在决定回退到仓库的第一个提交:

git reset --hard 9d6bedfd771f73373348f8337cf60915372d7954

执行后:

  • Git 提示 HEAD 已指向指定提交
  • test_file 中的最新内容消失
  • new_test_file 被彻底删除

警告:此操作不可逆!务必理解 --hard 对 Git 三棵树的影响。

4.2. --soft 模式(安全回退)

使用 --soft 时:

  • 仅更新引用指针
  • 暂存区和工作区保持不变
  • 已暂存的更改仍可提交

在前面的示例中,如果使用 --soft

  • 暂存区的更改不会被删除
  • 仍可正常提交这些更改

4.3. --mixed 模式(默认模式)

不指定参数时的默认模式,介于 --soft--hard 之间:

  • 更新引用指针
  • 暂存区重置为指定提交状态
  • 从暂存区撤销的更改会移回工作区

在前面的示例中使用 --mixed

  • 本地文件修改不会被删除
  • 更改会从暂存区撤销,需重新处理

5. 结论

简单总结两种核心方法:

  • git revert 安全:通过创建新提交实现撤销,适合公共分支
  • git reset 危险:直接修改提交历史,可能导致数据丢失

关键差异:

  • git reset 移动 HEAD 引用指针
  • git revert 通过新提交实现撤销,不移动指针

血泪教训:**当后续提交已被推送到共享仓库时,绝对不要使用 git reset**!必须假设其他开发者依赖这些已发布的提交。


原始标题:Undo and Revert Commits in Git