git restore 和 revert 的区别?
· 5 min read
git restore 和 revert 都能"撤销",但操作对象和历史影响完全不同。
- restore:操作文件(工作区/暂存区),不产生 commit
- revert:操作某次 commit,必须新 commit
- 核心差异:restore 改文件,revert 反做 diff
- 进阶用法:
restore --source=<sha>+ commit,文件 = 目标版 - 不能用 revert 退旧版:revert 只反补丁,无法对齐到任意 commit
- 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 -- file和git 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,要公开撤销。
对照
restore | revert | |
|---|---|---|
| 操作对象 | 文件(工作区/暂存区) | 某次 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。怎么选
三句话分清:| 场景 | 正确做法 |
|---|---|
| 本地改乱了,还没 commit | git restore |
| 刚 commit 但还没 push,想当没这回事 | git reset(改历史) |
| 已经 push,要公开撤销某次 commit | git revert |
| 文件内容想回到某次 commit,且要保历史 | git restore --source=<sha> + commit |
| 想用 revert 退回到任意旧版本 | 不可能,要么 reset 要么 restore --source |
一句话总结
restore 改的是文件,revert 改的是历史——前者从历史里"取树拷贝"再 commit,后者把那次 commit 的 diff 反着再写一遍。
revert 永远做不到"把当前文件树对齐到任意旧版本",要这种能力只能 restore --source + commit 或 reset --hard。