Git怎么撤销提交的代码?

文章导读
撤销 Git 提交要先分清两件事:这条提交是不是已经推送到远程、被别人拉走过;以及你是想连代码改动一起丢掉,还是只撤掉“提交”这个动作、把改动留在工作区。前一件事决定用 reset 还是 revert,后一件事决定参数用 --soft、--mixed 还是 --hard。没推过的本地提交,通常可以直接改本地历史;已经推送、并且有他人在用的分支,建议用 revert 生成一条反向提交,避免改写别人手
📋 目录
  1. Ⅰ Git:先把能核对的事实摆清楚
  2. Ⅱ Git最容易搞错的一点
  3. Ⅲ Git:一个照着就能做完的做法
  4. Ⅳ Git:怎么验证结果对不对
A A

撤销 Git 提交要先分清两件事:这条提交是不是已经推送到远程、被别人拉走过;以及你是想连代码改动一起丢掉,还是只撤掉“提交”这个动作、把改动留在工作区。前一件事决定用 reset 还是 revert,后一件事决定参数用 --soft、--mixed 还是 --hard。没推过的本地提交,通常可以直接改本地历史;已经推送、并且有他人在用的分支,建议用 revert 生成一条反向提交,避免改写别人手里的历史。

判断路径:先用 git log --oneline origin/<分支>..HEAD 看提交是否已在远程,没有输出说明还没推送。未推送时 git reset --soft HEAD~1 撤销提交并保留改动,确认内容无误再决定是否用 --hard;已推送的共享分支用 git revert <commit> 生成反向提交后正常 push。验证看 git status 与 git log 是否和预期一致、git log origin/<分支>..HEAD 是否为空。风险边界:--hard 和 push --force 都可能丢改动或覆盖他人提交,执行前先拉一个备份分支。

Git:先把能核对的事实摆清楚

“撤销提交”涉及四个不同的位置:工作区里的文件、暂存区(index)、本地分支的提交记录、远程分支的提交记录。reset 只动本地,revert 是新增一条提交,push 才会影响远程。动手前先把这几个位置的状态读出来,再决定用哪条命令。

先确认自己在哪个分支、上游是谁,这条命令的输出来自本地配置,可以直接核对:

git branch -vv
git status

git branch -vv 会输出当前分支名、跟踪的远程分支(如 origin/main)以及领先/落后几个提交;git status 会告诉你有没有未提交的改动、有没有正在进行的 merge 或 rebase。这两项是前提条件:如果工作区是脏的,先 git stash 或先提交,否则 --hard 类操作会把未提交的改动一起带走。

接下来核对要撤销的那条提交本身:

git log --oneline -5
git log --oneline origin/main..HEAD
git reflog -10

git log --oneline -5 给出最近的提交短哈希和说明,撤销时要用到这个哈希;git log --oneline origin/main..HEAD 列出“本地有、远程没有”的提交,输出为空通常说明你的提交已经推上去了;git reflog -10 记录本地 HEAD 的移动历史,是后面误操作时的兜底线索。远程侧可以在 GitHub、GitLab 的分支 commits 页面核对同一个提交哈希是否存在,IDE 的 Git 面板里也能看到 Local 与 Remote 两组分支。

Git最容易搞错的一点

最常见的误解是把“撤销提交”等同于 git reset --hard HEAD~1,以为它只是把提交记录退回去。实际上 --hard 会同时重置暂存区和工作区,那条提交里的代码改动会一起消失;如果这些改动没有别的分支或 reflog 兜着,恢复起来很麻烦。另一种反向误解是以为 git revert 会“删掉”历史,其实 revert 是新增一条内容相反的提交,原提交仍然留在历史里。

区分这两种情况的分界线只有一个:这条提交有没有离开本机、被别人或远程分支引用过。一个能把两种情况分开的检查动作是:

git branch -r --contains <commit>

有输出,说明远程分支已经包含这条提交,按共享历史处理,走 revert;没有任何输出,说明远程还没有它,可以按本地历史处理,走 reset。注意这条命令依赖本地已有的远程跟踪分支,执行前先 git fetch 一次,否则看到的可能是过期状态。

Git:一个照着就能做完的做法

第一步先留退路,建一个备份分支,后面任何一步出问题都能切回来:

git branch backup/before-undo
git log --oneline -5

执行完 git branch 不会有输出,用 git branch --list 'backup/*' 能看到新分支即成功;git log 用来记下要撤销的提交哈希。

场景一,提交还没推送,只想撤掉提交、改动保留:

git reset --soft HEAD~1
git status

--soft 只移动分支指针,改动全部留在暂存区。执行后 git status 应显示这些文件在“Changes to be committed”一节,说明提交没了、内容还在。

Git怎么撤销提交的代码?

场景二,提交还没推送,改动保留但不想暂存:

git reset HEAD~1
git status

不带参数的 reset 等价于 --mixed,改动会退回工作区,git status 里这些文件应出现在“Changes not staged for commit”一节。

场景三,提交还没推送,改动也不想留:

git reset --hard HEAD~1
git status

执行后 git status 应显示“working tree clean”,git log --oneline -5 里那条提交已经不见了。这一步之前务必确认备份分支已经建好。

场景四,提交已经推送,且是多人共用的分支:

git revert --no-edit <commit>
git push

git revert 会生成一条新的反向提交,--no-edit 表示沿用默认提交说明。执行后 git log --oneline -3 应该能看到一条说明类似“Revert ...”的新提交,push 成功后远程 commits 页面也会出现它,原提交仍在历史中。

场景五,提交已经推送,但确认只有自己在用的个人分支:

git reset --hard HEAD~1
git push --force-with-lease

--force-with-lease 比 --force 保守:远端如果已经被别人更新过,这次推送会被拒绝而不是直接覆盖。如果被拒绝,说明分支不是只有你在用,退回去用场景四的 revert 更稳妥。

Git:怎么验证结果对不对

撤销动作做完,用下面几条逐项核对,而不是只看命令有没有报错:

git status
git log --oneline -5
git log --oneline origin/main..HEAD
git show <commit>
git reflog -5

判断标准可以对着看:git status 应只剩你预期保留的改动,或显示 working tree clean;git log --oneline -5 的顶端应是预期的提交,reset 场景下被撤销的哈希不再出现,revert 场景下顶端是一条 Revert 提交;git log --oneline origin/main..HEAD 为空表示本地与远程已对齐,如果有内容说明还差一次 push;git show 后面跟 revert 生成的哈希,应能看到它把原提交的改动逐行还原;git reflog -5 里应保留你刚才每一步的 HEAD 移动记录,这也是万一撤销错了的回退依据。

如果改动过的是远程分支,还要到 GitHub、GitLab 的分支 commits 页面或文件浏览页确认一次:提交列表和关键文件内容应与本地一致。团队协作场景下,撤销共享分支的历史后,建议同步通知相关同事执行 git pull,避免他们基于旧提交继续提交,产生不必要的合并冲突。