在使用 Git 时,经常会遇到这些问题:代码改错了想撤销、已经 git add 了想取消暂存、commit 写错了想修改、提交后想回退版本,甚至 reset 误操作后想恢复提交。
这些情况都属于 Git 的“撤销与回退”,但不同场景要使用不同命令。本章会按照实际开发流程,系统讲清 restore、commit –amend、revert、reset、reflog 的用法和区别。
学习本章节前需要理解 Git 工作区、暂存区、本地仓库的概念,可以跳转至 Git 的核心概念
Git 撤销与回退常用命令#
| 场景 | 推荐命令 | 是否安全 | 是否改历史 |
|---|---|---|---|
| 撤销工作区未暂存修改 | git restore <file> | 安全 | 否 |
| 取消暂存 | git restore --staged <file> | 安全 | 否 |
| 修改最后一次提交信息 | git commit --amend | 谨慎 | 是 |
| 撤销某次已提交记录 | git revert <commit> | 安全 | 否 |
| 回退到某个提交 | git reset --soft/mixed/hard | 谨慎 | 是 |
| reset 后想恢复 | git reflog | 救命命令 | 可恢复 |
撤销工作区未暂存的修改#
# 撤销工作区指定文件 file.txt 的修改
git restore file.txt
# 撤销工作区所有未暂存修改
git restore .撤销操作会丢弃工作区未提交的修改,执行前应确认这些内容不再需要。
取消暂存#
如果不小心把不需要提交的文件加入了暂存区(staging area),可以使用下面的命令将其移出暂存区。
# 取消暂存区指定文件 file.txt 的暂存
git restore --staged file.txt
# 取消暂存区所有暂存文件
git restore --staged .git restore --staged 只会把文件从暂存区移回工作区,不会删除你的代码修改。
修改最后一次提交#
# 修改最后一次 commit message
git commit --amend
# 修改提交信息但不打开编辑器
git commit --amend -m "新的提交信息"
# 把遗漏文件补进上一次提交,--no-edit 表示保留原提交信息。
git add missed-file.txt
git commit --amend --no-edit如果这次提交已经 push 到远程仓库,不建议随意 amend,因为它会改写提交历史。
撤销已提交内容#
git revert 不会删除原来的提交,而是生成一个新的提交,用来抵消指定提交的改动。
git revert <commit-id>
# 撤销指定 Commit适合:
- 已经推送到远程的提交;
- 已经被团队共享的提交;
- 公共分支中的错误修改。
回退版本#
git reset 会移动当前分支的 HEAD 指针,可以用来回退到某个历史提交。
1. soft 模式:保留暂存区和工作区#
git reset --soft <commit-id>
# 回退到指定 Commit
git reset --soft HEAD~1
# 从当前提交往回数 1 个提交(即上一次提交)效果:
- 移动分支位置;
- 保留暂存区;
- 保留工作区修改。
适合重新组织最近一次提交。
2. mixed 模式:保留工作区,清空暂存区#
git reset --mixed <commit-id>
# 回退到指定 Commit
git reset --mixed HEAD~1
# 从当前提交往回数 1 个提交(即上一次提交)等同于:
git reset <commit-id>
# 回退到指定 Commit
git reset HEAD~1
# 从当前提交往回数 1 个提交(即上一次提交)效果:
- 移动分支位置;
- 清空相应暂存状态;
- 保留工作区修改。
3. hard 模式:彻底回退#
git reset --hard <commit-id>
# 回退到指定 Commit
git reset --hard HEAD~1
# 从当前提交往回数 1 个提交(即上一次提交)效果:
- 移动分支位置;
- 重置暂存区;
- 重置工作区。
git reset --hard 会丢弃目标提交之后的提交和工作区修改,谨慎使用。
soft、mixed、hard 对比表#
| 命令 | 移动 HEAD | 保留暂存区 | 保留工作区 | 风险 |
|---|---|---|---|---|
reset --soft | 是 | 是 | 是 | 低 |
reset --mixed | 是 | 否 | 是 | 中 |
reset --hard | 是 | 否 | 否 | 高 |
git revert 和 git reset 的区别#
git revert 是新增一个提交来撤销旧提交;git reset 是移动 HEAD 指针来回退历史。
| 对比项 | git revert | git reset |
|---|---|---|
| 是否保留原提交 | 保留 | 可能不保留 |
| 是否生成新提交 | 是 | 否 |
| 是否改写历史 | 否 | 是 |
| 是否适合已 push 的提交 | 适合 | 不建议 |
| 是否适合本地未 push 提交 | 可以 | 适合 |
什么时候用 revert:提交已经推送到远程仓库,并且别人可能已经基于这个提交开发。
什么时候用 reset:提交还在本地,没有 push,想整理提交历史或回到某个版本。
恢复误删提交#
git reflog 会记录 HEAD 的移动历史,即使 commit 被 reset 掉,也通常可以通过 reflog 找回来。
# 查看操作记录
git reflog
# 找回 reset --hard 前的提交
git reset --hard <commit-id>
# 更安全的恢复方式:新建分支
git branch recover-branch <commit-id>reflog 可以帮助恢复:
- 错误 reset 的提交;
- 误删分支中的提交;
- 意外丢失的 HEAD 位置。