核心概念:git reset 用来移动 HEAD 分支指针,同时控制版本库、暂存区、工作区三个区域的状态;已经推送到远程公共分支,不要用 reset –hard,优先 revert。
三种模式对比:
| 参数 | 版本库(HEAD) | 暂存区(index) | 工作区(working) | 场景 |
|---|---|---|---|---|
| –soft | ✅ 回退 | ❌ 不变 | ❌ 不变 | 撤销commit,代码保留在暂存区 |
| –mixed(默认) | ✅ 回退 | ✅ 回退 | ❌ 不变 | 撤销commit + 撤销add,代码保留在工作区 |
| –hard | ✅ 回退 | ✅ 回退 | ✅ 回退 | 彻底丢弃后面所有改动,危险 |
提交简写:
HEAD当前最新提交HEAD^/HEAD~1上1个提交HEAD~3往前3个提交commit-hash完整/短commit哈希
1. –soft (只回退commit,保留暂存)
# 撤销最近一次commit,代码还在暂存区,直接可以重新commit
git reset --soft HEAD^
# 回退到指定commit,中间所有提交都撤销,改动全部留在暂存
git reset --soft a62bc3
示例流程:
echo "v1" > test.txt
git add test.txt
git commit -m "commit 1"
echo "v2" >> test.txt
git add test.txt
git commit -m "commit 2"
# 撤销 commit2,文件还在暂存,git status 显示待提交
git reset --soft HEAD^
2. –mixed(默认,不加参数就是mixed)
撤销 commit + 撤销 git add,代码留在工作区,未add状态
# 等价 git reset HEAD^
git reset --mixed HEAD^
# 只对单个文件取消暂存(最常用!取消add,不回退commit)
git reset HEAD test.txt
示例流程:
echo "111" > a.txt
git add a.txt
git commit -m "add a"
echo "222" >> a.txt
git add a.txt
git commit -m "modify a"
# 回退上一次commit,改动放到工作区,不在暂存
git reset HEAD^
3. –hard(危险!丢弃所有改动)
版本库、暂存、工作区全部同步到目标commit,后面的修改直接删掉,无法恢复未提交代码
# 回退到上一个提交,所有改动直接清空
git reset --hard HEAD^
# 回退到指定哈希版本
git reset --hard 78ac21
# 回退3个提交
git reset --hard HEAD~3
⚠️ 重要提醒:已经push到远程的分支,禁止直接 reset –hard 然后强制推送,会破坏别人的提交历史。
4. 指定文件路径 reset(只操作暂存区,不移动HEAD)
这种用法不会改变提交历史,只把指定文件从暂存区撤回到工作区,等价取消add:
# 把 readme.md 从暂存区撤回来,回到工作区
git reset HEAD readme.md
# 把 readme.md 恢复到某个历史版本的状态(覆盖暂存区)
git reset a62bc3 -- readme.md
5. 恢复被 reset 丢掉的提交(reflog)
reset –hard 后如果后悔,提交还在 reflog 里(一段时间内)
# 查看HEAD操作日志
git reflog
# 找到之前的commit hash,再reset回去
git reset --hard 前面的hash值
6. 公共分支回退正确做法(不用reset)
多人协作、已经推远程,想撤销某次提交,用 git revert(新增反向提交,历史保留)
git revert <commit-id>
git push
7. 踩坑总结
reset --soft:改提交备注、合并多次commit时很好用reset(默认mixed):日常取消add、撤销最近commit最安全reset --hard:只在本地未推送分支使用,会丢代码- reset 带文件路径:只操作暂存,不会回退commit,和不带路径是两回事
- 远程公共分支不要
reset --hard + git push -f
快速记忆口诀
- soft:退commit,代码还在暂存
- mixed:退commit+退add,代码留在工作区(默认)
- hard:全部清空,回到目标版本