Git 平时用就那么几个:add、commit、push、pull。但总有那么些时刻——提交错了、文件加错了、改动没了、分支删了、代码"丢了"——你会突然需要一些平时根本不碰的命令。而那个时刻你通常正手忙脚乱、没心情现搜文档。
这篇把那些"迟早会用到"的救命操作系统整理出来,12 个高频场景 + 2 个进阶技巧,每个都给命令、讲清楚原理、标好坑。建议收藏,真出事的时候照着做就行。
1. 改错了最后一次提交
提交信息打错字了,或者提交完发现漏了个文件。只要这次提交还没 push,都能干净地补救:
git commit --amend # 打开编辑器改提交信息
如果只是想把漏掉的文件补进去、信息不用改:
git add 漏掉的文件 git commit --amend --no-edit # 把文件补进上次提交,信息不变
坑:--amend 的本质是"用一次新提交替换掉最后一次提交"。已经 push 到共享分支的提交别 amend——你本地的提交和远程对不上了,再推就得强推,会影响别人。amend 只对"还没推出去的"提交安全。
2. 撤销提交,但改动要保留
不小心把东西 commit 了,想把"提交"这个动作撤销、但代码改动一点不能丢:
git reset --soft HEAD~1 # 撤销提交,改动留在暂存区,随时重新提交 git reset HEAD <file> # 把某个文件移出暂存区(改动还在)
--soft 是关键:它只回退"提交"这个动作,你的改动原封不动留在暂存区,重新整理一下再提交即可。--mixed(默认值)则会把改动退到工作区。记住:soft 退到暂存区,mixed 退到工作区,改动都还在。
3. 彻底丢弃提交和改动
这次提交和它带的改动你都确定不要了:
git reset --hard HEAD~1 # 撤销提交 + 丢弃改动(危险!不可逆)
--hard 会把提交和工作区改动一起抹掉。这是最危险的一条,按下去之前务必想清楚。不过别太怕——即使误用了,只要那些提交曾经存在过,第 5 条的 reflog 还能把它们捞回来。
4. 丢弃工作区的改动,回到没动过的样子
git restore <file> # 丢弃工作区某个文件的改动 git restore . # 丢弃所有未暂存的改动(危险,想清楚再按回车)
git restore . 会把所有未暂存的改动一笔勾销,而且这些改动不进任何记录、不可恢复。拿不准的话,先 git stash(见第 6 条)留个后路,确认不要了再 drop。
5. 代码"丢了"——真正的救命稻草
这是整篇里最值得记死的一条:只要你 commit 过,在 Git 里就几乎没有真正丢失的代码。
误 reset --hard 了、误删了分支、rebase 搞砸了、amend 覆盖了……只要那些提交曾经存在过,git reflog 都能帮你找回来。它记录了 HEAD 的每一次移动:
git reflog # 列出 HEAD 的每一次移动,带 hash git reset --hard <出事前那条记录的hash> # 时光机:回到任意历史状态
在 reflog 列表里找到"出事之前"的那个状态,把它的 hash 拿去 reset --hard,就回去了。很多人用 Git 多年都不知道 reflog 的存在,直到第一次靠它捞回半天的工作量——然后这辈子都忘不掉。
6. 临时存放改动,干净地切分支
正在 A 分支改东西改到一半,突然要切去 B 分支处理急事,但手里的改动还没到能提交的程度:
git stash # 把当前改动打包收起,工作区瞬间变干净 git stash pop # 切回来后,把改动倒出来接着干 git stash list # 查看存了哪些
git stash 把当前改动打包收起来,工作区瞬间变干净,你可以随便切分支。事情处理完切回来,git stash pop 把改动倒出来接着干。它能存多个,git stash list 查看。这也是做任何"危险补救"之前给自己留后路的标准动作。
7. 只想要别人分支上的某一个提交
另一个分支上有一次提交,你只想要那一个 commit,不想把整条分支合过来:
git cherry-pick <commit-hash> # 把指定的某次提交"摘"到当前分支
cherry-pick 会把指定的那次提交"摘"到你当前分支上。修了个 bug 想同步到多个分支、或者把某个功能从实验分支单独捞出来,都靠它。
8. 撤销一个已经 push 出去的提交
提交已经推到共享分支了,这时候千万别用 reset(那会改写历史,逼别人强行同步)。正确做法是用 revert:
git revert <commit-hash> # 生成一个「反向提交」来抵消,历史不被改写,可安全推送
revert 不删除历史,而是新增一个"反向提交"来抵消目标提交的改动。历史是往前走的,所有人正常 pull 就行,不会有任何冲突灾难。记住这条铁律:没 push 的用 reset,已 push 的用 revert。
9. 提交到错的分支上了
本该提到 feature,结果手一抖提到了 main。把它搬回去:
# 假设刚才在 main 上误提交了,本该提到 feature git log --oneline -1 # 记下这次提交的 hash git reset --hard HEAD~1 # main 退回去 git checkout feature git cherry-pick <刚才记下的hash> # 把提交搬到 feature
思路就是"在错的分支上撤销、到对的分支上摘过来"——reset 和 cherry-pick 的组合拳。同样,前提是这次误提交还没 push。
10. 误删了分支
手滑 git branch -D 把一个还有用的分支删了。别慌,分支只是一个指向某次提交的指针,提交本身还在:
git branch # 先看分支没了 git reflog # 找到被删分支最后那次提交的 hash git branch <分支名> <那个hash> # 用 hash 把分支重新建出来
又是 reflog 救场。它能列出那个分支最后指向的提交 hash,你拿这个 hash 重新 git branch 一下,分支就回来了。
11. 已经提交的文件,想加进 .gitignore
很常见的失误:某个本地配置文件、日志文件、node_modules 已经被 commit 进仓库了,这时候光往 .gitignore 里加一行是没用的——Git 对已追踪的文件不看 .gitignore。得先让 Git "忘掉"它:
echo "config.local.js" >> .gitignore git rm --cached config.local.js # 从 Git 里移除追踪,但保留本地文件 git commit -m "stop tracking config.local.js"
git rm --cached 的 --cached 很关键:它只把文件从 Git 的追踪里移除,本地文件本身不删。提交之后,这个文件就既留在你本地、又不再被 Git 管了。
12. 用二分法揪出引入 bug 的提交
"上周还好好的,现在坏了,中间几十个提交,到底是哪个搞坏的?"——靠肉眼一个个翻能翻到天黑。git bisect 用二分查找帮你定位:
git bisect start git bisect bad # 当前版本是坏的 git bisect good <一个已知正常的旧commit> # Git 自动二分检出,你测一次答一次 good / bad # 几轮之后它会告诉你:是哪个 commit 引入了 bug git bisect reset # 结束,回到原来的状态
它会自动在"已知正常"和"已知出错"之间二分,每次检出一个版本让你测、你回答 good 或 bad,几轮(log₂ 次)之后就能精确指出是哪个 commit 引入的问题。几十个提交,通常五六次就定位到了,堪称神器。
附:用交互式 rebase 整理提交历史
开发过程中难免留下一堆"修个错别字""再改改""哦又漏了"这种零碎提交。push 之前用交互式 rebase 把它们整理干净,提交历史会清爽很多:
git rebase -i HEAD~3 # 整理最近 3 次提交 # 编辑器里把某行的 pick 改成: # squash / s —— 合并到上一条 # reword / r —— 改提交信息 # drop / d —— 删掉这次提交 # edit / e —— 停下来让你修改
同样的铁律:交互式 rebase 只对还没 push 的提交用,它会改写历史。整理自己本地的提交很好用,别拿去动共享分支。
写在最后:三条万能心法
这些命令的共同点是:平时用不上,用上时往往在救火。值得花十分钟把它们各跑一遍、心里有个底——真出事的时候,"知道有办法"和"完全懵了"是两种完全不同的体验。
最后给三条贯穿全篇的心法:
- 动手补救前,先
git stash或记下当前状态。给自己留后路,永远不亏。 - 没 push 的随便折腾,已 push 的用 revert。这条线划清楚,就不会把队友拖下水。
- 记住
git reflog的存在。它是 Git 给你的后悔药,只要 commit 过,基本没有真的回不去的状态。
—— 别看了 · 2026