Skip to main content

git restore 和 revert 的区别?

· 5 min read

git restore 和 revert 都能"撤销",但操作对象和历史影响完全不同。

  1. restore:操作文件(工作区/暂存区),不产生 commit
  2. revert:操作某次 commit必须新 commit
  3. 核心差异:restore 改文件,revert 反做 diff
  4. 进阶用法restore --source=<sha> + commit,文件 = 目标版
  5. 不能用 revert 退旧版:revert 只反补丁,无法对齐到任意 commit
  6. preserve history:替代 reset --hard免 force-push

一句话

  • git restore:操作文件(工作区 / 暂存区)。不产生 commit,历史不动。
  • git revert:操作某次 commit。生成一个新 commit,把那次 commit 的 diff 反过来。

git restore:改文件,不改历史

restore 的作用是"把文件从某个已知状态拷回来"。
# 丢掉工作区未暂存的修改,回到暂存区 / HEAD 的内容
git restore file.ts

# 取消暂存(等价于旧的 git reset HEAD -- file.ts),工作区改动保留
git restore --staged file.ts

# 暂存区和工作区都回到 HEAD
git restore --source=HEAD --staged --worktree file.ts

特点:

  • 只动 工作区 和 / 或 暂存区
  • 不产生 commit
  • 未提交的改动被覆盖后,默认 找不回来
  • 替代了旧命令里容易混淆的 git checkout -- filegit reset HEAD -- file

适合:写错了、暂存错了、想丢掉本地改动。

git revert:用新 commit "反做"

revert 的作用是"安全地撤销已经提交的某次改动"。
# 生成一个新 commit,效果等于把 abc123 的 diff 反过来
git revert abc123

# 不立刻提交,只把反向改动放到暂存区
git revert --no-commit abc123

特点:

  • 一定走提交流程(除非 --no-commit
  • 不改写已有 commit,只追加一个"反向 commit"
  • 已经 push 的提交用这个比 reset --hard 安全
  • 若目标是 merge commit,需要 -m 指定保留哪一边

适合:线上已经合进去的 bug commit,要公开撤销。

对照

restorerevert
操作对象文件(工作区/暂存区)某次 commit
是否产生新 commit
是否改写历史否(只追加)
未提交改动可以丢掉不管这个
已 push 的提交不能"撤销那次 commit"正确做法
危险点未提交内容可能永久丢可能有冲突,但历史完整

进阶用法:restore --source=<sha>

restore 还有一种日常少见的用法:"按某个 commit 拷文件"。

日常说的 git restore file.ts 是丢未提交改动。带 --source 的 restore 是从历史里把整棵树拷出来:

git restore --source=<sha> .
# 把 <sha> 那次提交的文件,覆盖进当前工作区 / 暂存区

git commit # 记成当前分支上的一次新提交
git push # 正常快进,不用 force

restore 在这里只是 从历史里取整棵树拷贝的工具。真正"回到那一版"靠的是后面那次 新 commit。历史变成:

C、D 还在历史上,只是当前文件回到了 B。这就是 "Preserve history"。

为什么不是 revert

"文件长得像那一版" 不是 "反做那一版的补丁"。

git revert abc123 的含义是:把 abc123 自己引入的那份 diff 反过来,再记一次新提交。它不管"仓库现在应不应该长得像 abc123"。

A ── B ── C ── D (HEAD)

你想回到 B 的文件状态
做法结果
git revert D树变成 C,不是 B
git revert C只去掉 C 的改动,D 的改动还在,更不是 B
git revert D C把 B 之后全部反做,绕一圈回到 B,但中间容易冲突
git restore --source=B . + commit新提交 E 的 tree 就是 B,一步到位

revert 对不上"文件树 = 任意旧版本"这个目标。

为什么不是 reset --hard

reset --hard 看起来最直接,但它改历史、且已 push 要 force-push。
git reset --hard B

分支指针直接回到 B,C、D 从当前分支消失。已经 push 过就要 force-push。在 auto-push + 禁止 force-push 的工作流下,这条路走不通。

restore --source + commit 的方案:在不改写历史、不 force-push 的前提下,让当前分支的文件内容等于目标 commit。

怎么选

三句话分清:
场景正确做法
本地改乱了,还没 commitgit restore
刚 commit 但还没 push,想当没这回事git reset(改历史)
已经 push,要公开撤销某次 commitgit revert
文件内容想回到某次 commit,且要保历史git restore --source=<sha> + commit
想用 revert 退回到任意旧版本不可能,要么 reset 要么 restore --source

一句话总结

restore 改的是文件,revert 改的是历史——前者从历史里"取树拷贝"再 commit,后者把那次 commit 的 diff 反着再写一遍。

revert 永远做不到"把当前文件树对齐到任意旧版本",要这种能力只能 restore --source + commit 或 reset --hard。

Read More