首页
学习
活动
专区
圈层
工具
发布

你合并代码用 merge 还是用 rebase?

上周在公司群里,产品在催版本上线,我们在合并分支,结果 Git 一通冲突,整个人都傻了。小李一边 merge,一边骂:“我靠谁又 force push 啦!” 我在旁边看着 log,一堆奇奇怪怪的提交历史,一条线分岔成八条蛇。那一刻我就想说一句:merge 和 rebase 真是孪生兄弟,一个乱写历史,一个改历史。

merge 到底干了啥?

咱先不整术语,用最直白的比喻:merge 就是“结婚登记”,两个人原来的生活都保留,结婚之后加个共同签名。

比如你有一个feature/login分支,别人有一个dev主干。你写完登录功能后,想合并回主干。

git checkout dev

git merge feature/login

Git 会生成一个新的“合并提交”,这玩意是一个特殊节点,有两个父节点。提交历史像这样:

A---B---C---D (dev)

       \

        E---F (feature/login)

执行 merge 后变成:

A---B---C---D------G (merge commit)

       \        /

        E------F

你看,原来的历史都还在,Git 没改你过去干过的事,只是额外加了一个“把他们俩合起来”的节点。

好处是啥?历史完整、可追溯。 坏处也明显:日志一旦多了,看 commit log 简直像走迷宫。

rebase 是改历史的狠人

rebase跟 merge 最大的区别就是:它不加节点,它直接让你“假装历史是线性的”。

举个栗子。 你写了feature/login,然后别人也在dev改了点别的。你不想看见那种“分叉”线条,就 rebase 一下:

git checkout feature/login

git rebase dev

Git 会“把你的提交从原来的历史上摘下来”,然后挨个重新应用到dev的末尾。结果是这样的:

A---B---C---D---E'---F' (feature/login)

注意那个',说明这是“新的”提交。 Git 实际上是重新造了一批 commit,把旧的扔了。你原来的 E、F 已经不是原来的 E、F 了。

那到底该用哪个?

这个问题就像“洗碗机和手洗哪个干净”,得看场景。

用 merge 的时候

团队协作开发,一个分支多人同时改。

需要保留完整历史,比如生产版本要追溯问题。

代码合并频繁,不想冒改历史的风险。

比如我们每次发版前,主干都会 merge 各个 feature 分支,确保线上可查:

git checkout main

git merge --no-ff release/v1.3.0

这行命令会强制生成一个 merge commit,方便以后回看是哪一版上线的。

用 rebase 的时候

你在自己本地写功能,还没推到远程。

想让提交历史更干净,像一条直线一样。

需要整理 commit 顺序、合并无意义提交。

比如你写功能写得太碎了,commit 一堆:

fix bug1

fix bug2

change color

try fix

final fix

你想上线时干净点,可以这么整:

git rebase -i HEAD~5

在弹出的编辑界面里,把没用的改成squash:

pick e1a23 fix bug1

squash e2b33 fix bug2

squash e3a12 change color

squash e4f98 try fix

squash e5c12 final fix

保存后,Git 会把五个 commit 合成一个新的,干净整齐。

那个“冲突地狱”又是怎么来的?

merge 和 rebase 都可能冲突,只不过 rebase 冲突的地方更“阴险”一点。 因为 rebase 是逐个 commit 应用的,一旦某个 commit 改动了别人已经改过的行,Git 就得停下来让你手动解决。

有一次我在本地 rebase 了半天,改了十几个文件,结果到第 8 个 commit 冲突了,我解决完之后才发现——前 7 个 commit 早就推不上去了,因为远程已经有新的分支。 最后的解决办法是这样的:

git rebase --abort

git pull --rebase origin dev

意思是:算了别改了,重新跟远程同步,再 rebase 一次。 结果还是冲突,不过这次我心态平和了。

小贴士:合并后保持整洁的日志

有些团队会这样配置:

git pull --rebase

这意味着每次拉代码的时候,不是直接 merge,而是先把本地提交摘下来,放到远程更新之后重新贴上去。 这样就不会出现那种“Merge branch 'dev' of origin/dev”的垃圾 commit。

最后的建议

如果你是新人,建议先学会 merge,再学 rebase。 merge 容错高,不会破坏历史; rebase 要心细,否则分分钟历史错乱。

我个人的习惯是:

在本地开发时用 rebase 整理;

推远程、多人协作时只用 merge。

最后给你看个我常用的命令组合,用来“优雅合并”:

# 整理提交

git fetch origin

git rebase origin/dev

# 确认无误后合并

git checkout dev

git merge --no-ff feature/login

# 删除分支

git branch -d feature/login

历史干净、逻辑清晰、回滚方便。 不过话说回来,只要团队约定一致,无论你是 merge 派还是 rebase 党,都没错。怕的就是——一个人 merge,一个人 rebase,那才是真正的修罗场。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OMjO9-isIzxLvb3V9yXG2cNA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券