skip to content
月与羽

Git 撤销操作

/ 9 min read

核心总结图

更改所在位置推荐的撤销命令主要作用
工作区 (改了但没 add)git restore <file>放弃对某个文件的修改
暂存区 (已 add 但没 commit)git restore --staged <file>将文件从暂存区撤下,但不影响工作区
本地仓库 (已 commit 但没 push)git reset / git commit --amend修改或回退本地的提交记录
远程仓库 (已 push)git revert创建一个“反向”的新提交来撤销
终极后悔药 (不小心搞砸了)git reflog查看 HEAD 的移动记录,找回丢失的提交

1:文件在【工作区】,还没 git add

你修改了一个文件,但发现改错了,想恢复到上次提交时的样子。

  • 命令: git restore <file>
  • 示例: git restore README.md (撤销对 README.md 的所有修改)
  • 什么情况下使用:
    • 代码写崩了,想放弃所有本地修改。
    • 不小心删错文件了,想从上次提交中恢复回来。
  • ⚠️ 注意事项:
    • 这个操作是危险的!它会永久丢弃你在工作区的所有修改,且无法通过 Git 找回。
    • 在旧版 Git 中,这个操作使用 git checkout -- <file>,但 checkout 功能过于复杂,现在官方推荐使用更清晰的 restore

2:文件在【暂存区】,已经 git add 但还没 commit

你用 git add 把文件放到了暂存区,但突然发现: a) 这个文件不应该被提交。 b) 文件里还有些内容需要修改,想先撤销暂存。

  • 命令: git restore --staged <file>
  • 示例: git restore --staged config.js (将 config.js 从暂存区移出)
  • 什么情况下使用:
    • git add . 时,不小心把临时文件、密码文件也加进去了。
    • 想把一个大文件拆分成几次提交,需要先把整个文件从暂存区撤出。
  • ⚠️ 注意事项:
    • 此命令只影响暂存区,你工作区里的文件修改会完整保留
    • 在旧版 Git 中,这个操作使用 git reset HEAD <file>,现在 restore 语义更明确。

3:提交在【本地仓库】,已经 commit 但还没 push

这是最复杂的情况,因为你操作的是已经生成的“历史记录”。

3.1 只想修改最后一次提交

你刚刚 commit,但发现: a) Commit message 写错了。 b) 漏掉了一个文件没 add

  • 命令: git commit --amend
  • 使用流程:
    1. 如果有漏掉的文件,先 git add <forgotten-file>
    2. 然后执行 git commit --amend
    3. Git 会打开编辑器让你修改 Commit message,保存并退出即可。
  • 什么情况下使用:
    • 修复最近一次提交的拼写错误或补充信息。
  • ⚠️ 注意事项:
    • --amend 并非“修改”了上次提交,而是创建了一个包含新内容/新信息的新 Commit,并用它替换掉旧的 Commit。旧 Commit 会被丢弃。
    • 绝对不要对已经推送到公共分支(如 main, develop)的提交使用 amend 因为这会改变历史,导致团队其他成员的历史记录和你产生冲突。

3.2 想彻底撤销最近的几次提交

你连续做了几次 commit,发现方向完全错了,想回到某个历史版本。

  • 命令: git reset [--soft | --mixed | --hard] <commit-id>
  • --soft: 最温柔。仅仅移动 HEAD 指针,你的代码改动会保留在暂存区
    • 场景: ” 我提交了 3 次,但发现这 3 次应该合并成 1 次提交 “,你可以 git reset --soft HEAD~3,然后重新 git commit
  • --mixed (默认模式): 移动 HEAD 指针,同时清空暂存区,你的代码改动会保留在工作区
    • 场景: ” 我提交了,但想重新审视所有代码再决定怎么提交 “,你可以 git reset HEAD~1,然后重新 git addgit commit
  • --hard: 最粗暴。移动 HEAD 指针,同时清空暂存区和工作区。所有代码改动都会被彻底删除
    • 场景: ” 这几次提交完全是垃圾,我不要了,彻底滚蛋!”
  • 示例:
    • git reset HEAD~1 (默认 mixed 模式,撤销最后 1 次提交,代码保留在工作区)
    • git reset --hard abc5021 (彻底回退到 abc5021 这个版本,之后的所有修改全部丢弃)
  • ⚠️ 注意事项:
    • git reset --hard 是 Git 中最危险的命令之一,会永久删除你的工作,使用前请三思。
    • amend 一样,reset 会改写历史。绝对不要对已经推送到公共分支的提交使用 reset

4:提交在【远程仓库】,已经 push

这是最严重的情况。你把一个错误的提交推送到了团队共享的分支。此时严禁使用 git resetgit commit --amend,因为这会 “force push” (强制推送),搞乱所有人的仓库。

  • 命令: git revert <commit-id>
  • 工作原理: revert 不会删除或修改历史。它会创建一个新的 Commit,这个新 Commit 的内容恰好是指定 Commit 的反向操作。比如你 revert 了一个添加了代码的 Commit,那么这个新 Commit 就会把那些代码删除。
  • 示例: git revert abc5021 (创建一个新提交,用来撤销 abc5021 这次提交所做的所有更改)
  • 什么情况下使用:
    • 线上版本出现 Bug,需要紧急撤销某次引入问题的合并或提交。
    • 撤销已经推送到公共分支的错误提交。
  • ⚠️ 注意事项:
    • 这是唯一推荐的、用于撤销公共历史的安全方式。
    • 它保留了完整的提交历史,所有人都知道“这里曾经犯过错,然后被修正了”。
    • 执行 revert 时可能会产生冲突,因为后续的修改可能依赖于你想要撤销的那个提交。如果出现冲突,需要手动解决后再次提交。

5: 终极后悔药:git reflog

如果你不小心执行了 reset --hard,或者 amend 了一个不该动的提交,感觉世界末日了,别慌!

  • 命令: git reflog
  • 作用: reflog 记录了你本地仓库 HEAD 指针的所有移动历史,包括每一次 commit, reset, amend, merge 等操作。它就像一个飞行黑匣子。
  • 使用流程:
    1. 运行 git reflog,你会看到一个操作列表,每一行都有一个 HEAD@{n} 的索引和一个 Commit ID。
    2. 找到你误操作之前的那个状态,记下它的 Commit ID (例如 abc5021)。
    3. 执行 git reset --hard abc5021,你的仓库就能奇迹般地恢复到那个状态!
  • ⚠️ 注意事项:
    • reflog 只存在于你的本地仓库,不会被推送到远程。
    • reflog 中的记录有过期时间(默认 90 天),所以这是紧急救援措施,不是常规操作。

总结

  1. 分清界限:先想清楚你的修改在哪一步(工作区、暂存区、本地库、远程库)。
  2. 本地操作要谨慎:在本地(未 push)时,resetamend 很强大,但 reset --hard 要格外小心。
  3. 远程操作要安全:一旦 push 到公共分支,永远优先考虑 git revert,它不会改写历史,对团队协作最友好。
  4. reflog 是你的救命稻草:当你搞砸了本地历史时,记得 reflog 可以帮你找回几乎一切。