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 checkout
和 git 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
这个示例中:
- 创建测试文件并提交初始内容
- 添加新内容并再次提交
- 通过
git log
确定要撤销的提交哈希 - 执行
git revert
撤销最新提交 - 通过查看文件内容验证撤销结果
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
**!必须假设其他开发者依赖这些已发布的提交。