本节摘要:rebase 不"移动"提交——提交不可变,它把每个旧提交"复印"成新提交排到新基线上,最后只挪分支指针。理解这一点,变基的一切行为(哈希全变、reflog 救场、黄金法则)都不再神秘。cherry-pick 则是同一套复印术的单点应用。
沿用 2.2 的分叉现场:main 与 dev 都从公共祖先 B 前进。执行变基前先看形态:
git switch dev git rebase main # Successfully rebased and updated refs/heads/dev. git log --oneline --graph --all # * 5k6l7m8 (dev) dev edit 被复印的新提交 # * 4j9i8h7 (main) main edit # * ...公共祖先
变基内部发生的序列,用侦探的语言逐帧还原:
所以"变基"这个名字有误导性:没有任何旧提交被移动,它们原地不动,只是 dev 指针改挂到一串新复印的提交上。旧提交此刻成了悬空对象,仍在对象库待两周以上,reflog 里也有记录——这就是"变基翻车还能救"的物理保证:
git reflog # 5k6l7m8 dev@{0}: rebase (finished): returning to refs/heads/dev # 3f4g5h6 dev@{1}: rebase (started) # e5f6a7b dev@{2}: commit: dev edit <- 变基前的旧顶点 git reset --hard dev@{2} # 一句话回到变基前
"不要对已推送到公共仓库的提交变基"——这条法则常被当成教条背诵,其实它的依据完全来自对象模型:变基生成的新提交与旧提交内容可能相同但哈希不同,在协作者眼里这是两个不同的提交。当变基者 force push 之后,同事本地既有旧链又有拉下来的新链,Git 会把它们当作分叉的历史,下次合并时把同一段变更合并两遍,冲突与混乱随之而来。
那 force push 什么时候可用?仅当这条分支确认为你个人私有(比如你自己的功能分支,团队规范明确"该分支可强推")。Git 也提供了护栏:git push --force-with-lease 只在远端引用与你上次看到的一致时才允许强推,避免误删别人刚推的提交。团队规范里应当把"允许强推的分支名单"写清楚(第 5 章 5.2 节展开)。
git rebase -i <基线> 打开提交清单编辑器,是整理功能分支历史的瑞士军刀:
git rebase -i main # pick 1a2b3c4 dev-1 # pick 5e6f7a8 dev-2 # pick 9c0d1e2 dev-3
每行一个提交,改动词再保存退出即可:squash 把该提交并进前一个(合并说明)、fixup 同样并入但丢弃说明、reword 只改说明、drop 删除、调换行序重排提交。执行时 Git 依然是在做"逐个复印",只是复印的内容与顺序按你的指示重组。合并零碎提交、拆分大提交、把"fix typo"类噪音清出历史,都靠它。代价是每一步都可能冲突——机理与 2.2 完全相同,解决后 git rebase --continue,反悔 git rebase --abort。
拣选是变基机理的最小应用:把某一个提交复印到当前位置。
git switch main git cherry-pick 5e6f7a8 # [main 8h9i0j1] dev-2 的内容 哈希是新的
新提交内容与 dev-2 相同、哈希不同,author 保留原作者、committer 是你——2.1 讲过的双字段在此派上用场。典型场景:main 上发现一个只存在于 dev 分支的修复,等不起整条 dev 合并;或维护多个发布分支时把关键补丁逐个"点射"到各版本。它也支持区间(git cherry-pick A..B)与不提交模式(-n 只改工作区与 index,让你整理后再提交)。
上面是 merge 形态(真实分叉拓扑,历史保真);rebase 形态则是一条直线:C1→C2→M1→D1'→D2'(D1、D2 的复印品)。merge 保留"发生过什么",rebase 呈现"希望发生过什么"。我的实践倾向:个人功能分支合入主干前先 rebase 主干,让主线历史可线性阅读;主干本身永远只接受 merge 或 fast-forward,绝不改写。这也是后面第 5 章多数工作流的共同底座。
⚠️ 常见坑:交互式变基中途有冲突时,
git status会同时显示"rebase in progress"与冲突文件,新手常在此连环 abort。记住序列:解决冲突 → add →git rebase --continue。abort 永远是可选项,但每次 abort 都丢掉已完成的整理进度。
💡 关键直觉:判断一条命令"危不危险",看它改写的是不是已被别人引用的哈希。本地未推送的提交随便 rebase(没人引用);已推送的公共提交一根手指都别碰。黄金法则不是礼仪,是引用计数。
第 2 章结案。现在你已经掌握了 Git 引用系统的全部核心机制——第 3 章回到日常,把高频命令逐一还原成对象与指针操作。