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
,避免引发不必要的冲突和混乱。