提交历史也没有那些多余的合并提交,看起来更干净。
我把整个过程从后往前倒着讲一遍,先把命令列出来,然后再用大白话把每一步的来由和要注意的坑说清楚。老实说,这套流程就是为了把我自己的改动干净利落地接到团队的阶段主分支上,避免把历史搞得像蜘蛛网一样,让别人回溯时眼睛疼。
先把我用的 git 命令按顺序摆出来,和你们一样直接照着敲就行:
git fetch origin
git checkout feature/phase_2_devjjq
git rebase origin/feature/phase_2
# 发生冲突时解决冲突,git add ,然后 git rebase –continue
git checkout feature/phase_2
git merge –ff-only feature/phase_2_devjjq
git push origin feature/phase_2
# 如果不想强制快进,也可以用 git merge feature/phase_2_devjjq,然后 push
接下来用白话把每一步说清楚,顺序是从结果倒回到起点,先说为什么要这么干,再说每条命令到底干啥。
背景交代一下:主分支叫 master,开发按阶段来,像 phase_1、phase_2、phase_3。每个阶段有一个阶段主分支,名字是 feature/phase_1、feature/phase_2。每个人又有自己的开发分支,我叫江建清,我的分支是 feature/phase_2_devjjq。目标就是把我在个人分支上的改动合并到阶段主分支 feature/phase_2,但不想留一堆“合并分支”的提交记录。
我以前的做法是先切到阶段分支拉最新,然后把个人分支 merge 过去。看着简单,但会留下合并提交。时间长了日志里全是“Merge branch XXX into YYY”,追问题时很费劲。目前的做法是先把我的改动在本地用 rebase 平移到阶段分支的最新提交上,这样历史看起来像一条直线,没那么多分叉节点。再在阶段分支上做一次快进式合并,这就不会产生新的合并提交。
具体步骤和注意点:
1)git fetch origin
这步是必须的,先把远端的最新信息拉下来。别图省事直接 pull,有可能把别人设置的合并策略也带进来,影响后续的 rebase。
2)git checkout feature/phase_2_devjjq
切到自己的开发分支上,准备把分支基底换成远端的阶段主分支。
3)git rebase origin/feature/phase_2
这句的意思是把我在 feature/phase_2_devjjq 上的提交,挪到 origin/feature/phase_2 最新提交后面。过程里常见问题就是冲突,遇到冲突别慌,按常规流程解决:打开冲突的文件,看区别,决定保留哪部分,保存后 git add 那些文件,然后 git rebase –continue。如果觉得当前 rebase 太乱,想回退,可以用 git rebase –abort。处理冲突时要分清楚哪些改动是阶段主分支的修复,哪些是你自己的逻辑变更,别一刀切把别人的修复给覆盖掉。
补充一句:rebase 会改变提交的 SHA,这意味着如果你的个人分支已经推到远端且别人可能拉过,强行变基并推送会让别人头疼。所以这套方法只适合你真的只在自己分支上工作、别人不会去改你的个人分支的场景。团队里最好约定好个人分支就是你一个人管,这样 rebase 比较安全。
4)git checkout feature/phase_2
rebase 完成后,你的个人分支就像“接上”了阶段主分支的最新提交。接着切回阶段分支准备合并。
5)git merge –ff-only feature/phase_2_devjjq
–ff-only 的好处是:如果可以快进就直接快进,不会生成新的合并提交;如果不能快进(说明远端有额外提交或分歧),命令会报错,这时候你就得停下处理分歧再合并。这样能强制保证合并过程不产生多余的合并节点。
6)git push origin feature/phase_2
合并到阶段分支后把改动推到远端。万一你不想严格要求快进,也可以改成 git merge feature/phase_2_devjjq 然后 push,但那样会有合并提交。
再聊聊常见的坑和防护措施:
– rebase 前必定要 git fetch,别在本地基线过时的情况下做变基。许多冲突就是由于基线不对导致的。
– 冲突别用暴力覆盖,逐个文件看清楚再处理。尤其是别人修了 bug,你千万别把他们的修复抹掉。
– rebase 后如果要把个人分支推远端,尽量用 git push –force-with-lease 而不是简单强推。这个选项能在推送前检查远端有没有别人新的提交,比单纯强推安全一点。
– 团队里如果有 CI 或合并策略(列如必须走 PR 才能合并),把 rebase 在本地做完,再去发 PR。PR 的合并策略可以配置成 fast-forward 或由管理员合并,这样历史会更干净。
– 别对公共分支随意变基。公共分支一旦被多个人引用,改历史会搞乱别人仓库。
我也把以前的老流程贴一下,说明为什么会换成目前这套:
git checkout feature/phase_2
git pull origin feature/phase_2
git merge feature/phase_2_devjjq
git push origin feature/phase_2
这看着没问题,但每次 merge 都会生成一个合并提交。多人都这么干,日志里就会满是合并节点,追 bug 或看提交历史时得绕好几道弯。把个人改动 rebase 到阶段主分支上,再做 fast-forward,历史会直一些,回溯问题时也更容易找到缘由。
最后再补充几条实践中总结出的经验,都是踩过坑的血:
– 在 rebase 前先确认自己的分支是不是“私有”的,别把公共分支改历史。团队里要有共识谁的个人分支可以 rebase。
– 推送时用 –force-with-lease 而不是 –force。前者会在推之前检查远端是否有差异,少了点踩雷的概率。
– 如果改动很大、冲突复杂,别强行一个人解决,拉相关同事一起看。一个协作的决定比独自搞定后把别人的改动误删要强许多。
– CI 钩子、PR 审核这些流程要提前思考进去,把 rebase 放在本地解决冲突的环节,然后走团队既定的合并流程。
就这样把我的改动从 feature/phase_2_devjjq 干净利落地合并进了 feature/phase_2,历史也清爽了许多。


