Skip to content
Git
撤销与回退

撤销与回退

Git 提供了多层级的撤销能力——从撤销未暂存的修改到回退整个提交链。


撤销层级概览

场景                               命令
──────────────────────────────────────────────────
工作区文件修改(未 add)           git restore <file>
暂存区文件(已 add 未 commit)     git restore --staged <file>
修改最近一次提交                    git commit --amend
回退提交(保留修改)                git reset --soft
回退提交(保留修改在工作区)         git reset --mixed(默认)
回退提交(彻底丢弃)                git reset --hard
公开分支撤销                        git revert
找回"丢失"的提交                    git reflog

reset(重置)

git reset 移动 HEAD 和当前分支指针,有三种模式:

git reset [--soft | --mixed | --hard] <ref>

三种模式

原始状态:A ← B ← C ← D (HEAD)

git reset --soft B:
    工作区:不修改(D 的修改留在暂存区)
    暂存区:包含 D 的修改
    HEAD:  移到 B

git reset --mixed B:  (默认)
    工作区:不修改(D 的修改留在工作区,未暂存)
    暂存区:清空
    HEAD:  移到 B

git reset --hard B:
    工作区:清空(D 的修改彻底丢失⚠️)
    暂存区:清空
    HEAD:  移到 B
模式 HEAD 暂存区 工作目录 安全程度
--soft 移动 保留 保留 ✅ 安全
--mixed 移动 重置 保留 ✅ 较安全
--hard 移动 重置 重置 🚨 危险

常用 reset 场景

# 撤销最近一次提交,修改回到暂存区(可以重新 commit)
git reset --soft HEAD~1

# 撤销最近一次提交,修改回到工作区(需要重新 add)
git reset HEAD~1            # 等价于 --mixed

# 撤销最近一次提交及其所有修改(危险⚠️)
git reset --hard HEAD~1

# 取消暂存(unstage)
git reset HEAD <file>
# 等价于新语法:
git restore --staged <file>

# 撤销到某个远程状态
git reset --hard origin/main

revert(反转提交)

git revert 创建一个新的提交来撤销指定提交的修改,不破坏历史。

git revert <hash>              # 撤销单个提交
git revert <hash1>..<hash2>    # 撤销范围(需要逐个处理)
git revert -m 1 <merge_hash>   # 撤销合并提交(1 = 保留第一个 parent)

reset vs revert

reset:
  A ← B ← C ← D         →   A ← B
  (C、D 消失,就像从未存在过)

revert:
  A ← B ← C ← D ← D'    →   A ← B ← C ← D ← D'
  (D' 是反转 D 的新提交,但历史保留)
场景 用 reset 用 revert
本地的、未推送的提交 也可以
已推送到共享仓库的提交 ❌ 不要用
想彻底删除某提交
想保留撤销记录

💡 简单规则:已经 push 过的 → revert;还没 push 的 → reset


commit –amend(修改提交)

# 修改最近一次提交的信息
git commit --amend -m "new message"

# 补充遗漏的文件(不改信息)
git add forgotten_file
git commit --amend --no-edit

# 修改作者
git commit --amend --author="Name <email>"

🚨 陷阱amend 会改写 commit hash。如果已经 push 过,amend 后需要 --force-with-lease 才能推送——和 rebase 一样只能在个人分支上使用


reflog(引用日志)

🔬 核心原理:reflog 记录 HEAD 和分支指针的每一次变更。即使你用 reset --hardrebase “丢失"了提交,reflog 仍然保留它们的 hash。

# 查看 HEAD 移动记录
git reflog

# 输出示例:
# a1b2c3d HEAD@{0}: commit: Add login
# d4e5f6g HEAD@{1}: reset: moving to HEAD~1
# g7h8i9j HEAD@{2}: commit: Bad commit (这个被 reset 删掉了)
# j0k1l2m HEAD@{3}: commit: Initial commit

# 找回"丢失"的提交
git checkout g7h8i9j          # 检出到那个提交
# 或
git cherry-pick g7h8i9j       # 摘取那个提交
# 或
git branch recovered g7h8i9j  # 基于该提交创建新分支

# 查看特定分支的引用日志
git reflog show main

# 恢复到 reflog 中的某个状态
git reset --hard HEAD@{2}

💡 reflog 是 Git 的"后悔药”。基本上只要提交过,就能通过 reflog 找回。reflog 默认保留 90 天(不可达的对象 30 天后会被 gc 清理)。

reflog 恢复实战

# 场景:git reset --hard 误操作,删掉了重要提交

# 1. 查看 reflog
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# d4e5f6g HEAD@{1}: commit: Important work ← 这个丢了

# 2. 恢复
git reset --hard HEAD@{1}
# 或
git cherry-pick d4e5f6g

checkout / restore(文件级撤销)

# 丢弃工作区对某文件的修改(用暂存区覆盖)
git restore <file>
git checkout -- <file>        # 旧语法

# 丢弃所有工作区修改
git restore .

# 将文件恢复到某次提交时的状态
git restore --source=<hash> <file>

# 从暂存区移除(取消 add)
git restore --staged <file>

# 从暂存区移除所有
git restore --staged .

撤销场景速查

场景 命令
修改工作区文件,想还原 git restore <file>
已经 add 了,想取消暂存 git restore --staged <file>
已 commit,想修改信息 git commit --amend
已 commit,想补充文件 git commit --amend --no-edit
已 commit(未 push),想撤销 git reset HEAD~1
已 commit + push,想撤销 git revert <hash>
已 commit + push,想彻底抹掉敏感信息 git reset --hard + git push --force-with-lease(仅个人分支)
reset 误操作,想找回 git refloggit reset --hard HEAD@{n}
删除的 stash 想找回 git fsck --lost-found