最近在帮团队做代码评审和分支策略梳理,发现很多人对 Git 的认知停留在 commit、push、pull 三板斧。平时用着没什么大问题,可一旦遇到 rebase 冲突、提交写错、分支搞乱、或者想找回一个被删掉的 commit,就开始慌了,甚至有人直接选择重新 clone 仓库。
所以这篇东西,我想专门聊聊 Git 里那些“会了就能救命”的高级操作。这篇文章面向的读者是:已经能熟练使用 git add、git commit、git push 等基础命令,但希望进一步掌握历史改写、问题定位、工作区管理和分支策略的开发者。
我不会事无巨细地罗列所有参数,而是挑真正高频、真正能解决实际问题的玩法来拆,每个操作都带上前因后果,讲讲我实际用下来踩过的坑。
1. 交互式 Rebase:不只是合并提交,还能批量改写历史
1.1 为什么说 rebase -i 是“后悔药”制作机
很多人第一次听说 git rebase -i(交互式变基)是因为“把多个 commit 合并成一个”。但它的能力远不止 squash(合并)。
先明确一个核心概念:rebase 的本质是把一系列提交“摘下来”,然后重新按顺序“栽”到另一个基准点之上。中间每个提交都会以补丁形式重新应用。-i 参数则允许你在重放之前,对这批提交做一个“翻牌处理”——你可以对每一个提交选择:保留(pick)、改写说明(reword)、合并进上一个(squash/fixup)、删除(drop)、拆分(edit)等。
换句话说,只要这批提交还没有被推到远端共享分支,你几乎可以对历史做任意调整。这就是我们常说的“提交还没出门之前,随便改”。
1.2 实战:整理一段乱糟糟的本地提交
假设你在本地开发了一个功能,产生了 5 个提交,看着很不舒服:
bash复制git log --oneline
# d1f2e3a fix typo
# 8a9b1c0 adjust style
# c3d4e5f implement feature
# 7a6b5c4 temp commit
# 2b1a0c9 init
你想把 temp commit 删掉,把 fix typo 和 adjust style 合并进 implement feature,并且把提交说明写得更规范。操作如下:
bash复制git rebase -i HEAD~5
Git 会打开一个编辑器,内容大致是:
bash复制pick 2b1a0c9 init
pick 7a6b5c4 temp commit
pick c3d4e5f implement feature
pick 8a9b1c0 adjust style
pick d1f2e3a fix typo
按你的意图改写成:
bash复制pick 2b1a0c9 init
drop 7a6b5c4 temp commit
pick c3d4e5f implement feature
fixup 8a9b1c0 adjust style
fixup d1f2e3a fix typo
保存退出,Git 就会按新方案重放提交。注意我在这里用的是 fixup 而不是 squash,两者的区别是:squash 会保留被合并提交的提交说明,让编辑界面再次出现,让你重写合并后的 message;而 fixup 默认直接丢弃被合并提交的说明,用上一行的 message。日常整理时我更喜欢 fixup,省去一步交互。
如果你想把某个提交拆开,可以用 edit。当 rebase 停在该提交时,执行 git reset HEAD~(软回退),然后重新按你的逻辑分次 git add 和 git commit,最后 git rebase --continue 继续完成剩余提交的重放。
注意:
git rebase -i操作的对象是提交历史,一旦推送到远程并被他人在使用,就绝对不要再对这批提交做改写。否则别人的本地历史会和你重写后的历史分叉,下次 pull 会冲突得怀疑人生。
1.3 自动化整理:autosquash 与 fixup 的配合
还有一个很提升体验的组合技。当你在 code review 之后需要修改某个旧提交,与其重新提交一个新的“修改意见”commit,再用 rebase 去合并,不如直接:
bash复制git commit --fixup=目标提交的SHA
这条命令会生成一个 commit message 以 fixup! 原提交说明 开头的提交。之后执行:
bash复制git rebase -i --autosquash 目标提交之前的某个节点
Git 会自动把这个 fixup! 提交拖到目标提交旁边,并设置成 fixup,你基本只需要在编辑器里确认保存,历史就变得干净整洁。
我个人的习惯是:任何不准备保留在主干历史里的临时提交,都先用 --fixup 标记,等一个功能完整了再统一 autosquash 整理。这样既保证了开发过程可以随心所欲地提交,又不污染最终合入主线时的历史。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Reflog:Git 的后悔药仓库,一切误删的底牌
2.1 Reflog 到底是什么
很多人在 git reset 或 git rebase 之后发现丢了代码,第一反应是“完了”。其实 Git 对所有本地引用(HEAD、分支、tag)的变更记录都保存在 .git/logs 目录里,这就是 Reflog。
可以理解为:Reflog 是 Git 版本的“浏览器历史记录”,记录着你每一次让 HEAD 移动的操作。分支被删了、commit 丢失了、reset 过头了,都能通过 Reflog 找回来。
2.2 经典误操作恢复流程
我半年前就干过一次这种事:在 git reset --hard 时把分支退回到了较早的提交,结果那个提交里有一个已经写好的大文件,退出后才发现没提交过其它机器。
恢复流程很简单:
bash复制git reflog
屏幕上会列出类似这个的提交记录:
bash复制8a9b1c0 HEAD@{0}: reset: moving to 8a9b1c0
c3d4e5f HEAD@{1}: commit: add important file
7a6b5c4 HEAD@{2}: commit: temp stuff
你只要找到那条 add important file 的提交 SHA,然后把它捡回
