找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2762

积分

0

好友

372

主题
发表于 3 小时前 | 查看: 3| 回复: 0

在实际团队开发中,我们通常会在自己的功能分支上进行开发,完成后需要将代码合并回主分支。Git提供了两种主流的合并方式:mergerebase。它们都能达到合并代码的目的,但背后的原理和最终产生的提交历史却截然不同。本文将通过一个具体的开发场景,带你直观地理解两者的核心区别。

场景搭建:一个简单的项目

首先,我们在Git上新建一个项目(例如 rebaseStudy),默认会有一个 master 分支。将项目克隆到本地,准备工作就完成了。

GitHub仓库初始状态截图,显示默认的master分支

同学A:在主分支提交

同学A在本地执行 git log,可以看到初始的提交记录。

lianjia@DESKTOP-B7ANVOH MINGW64 /d/wilson/rebaseStudy (master) $ git log
commit 71dbd8a5c376d574ad00b32cc8ebb7c7f87f16e (HEAD -> master, origin/master, origin/HEAD)
Author: shuyuan1992 <42328986+shuyuan1992@users.noreply.github.com>
Date:   Sun Nov 11 10:07:08 2018 +0800

    Initial commit

接着,他新增一个文件 a.txt,提交后再次查看提交记录,现在有两条了。

lianjia@DESKTOP-B7ANVOH MINGW64 /d/wilson/rebaseStudy (master)$ git log
commit zae743b22fe94c636c7cfae192b9ff34ceed138 (HEAD -> master)
Author: wilson <weishuyuan001>
Date:   Sun Nov 11 10:24:25 2018 +0800

    A first commit a.txt

commit 71dbd8a5c376d574ad00b32cc88ebb7c7f87f16e (origin/master, origin/HEAD)
Author: shuyuan1992 <42328986+shuyuan1992@users.noreply.github.com>
Date:   Sun Nov 11 10:07:07 2018 +0800

    Initial commit

将本次新的提交推送到远程仓库后,在GitHub上就能看到这两次提交记录了。

GitHub提交记录页面,显示两次提交

同学B:创建功能分支并开发

此时,同学B基于最新的 master 分支(提交点 C2)检出一个新的功能分支 dev,并推送到远程。

Tianjia@DESKTOP-B7ANV0H MINGW64 /d/wilson/rebaseStudy (master) $ git checkout -b dev
Switched to a new branch ‘dev’

Tianjia@DESKTOP-B7ANV0H MINGW64 /d/wilson/rebaseStudy (dev) $ git push origin dev
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for ‘dev’ on GitHub by visiting:
remote:      https://github.com/shuyuan1992/rebaseStudy/pull/new/dev
To https://github.com/shuyuan1992/rebaseStudy.git
 * [new branch]      dev -> dev

Tianjia@DESKTOP-B7ANV0H MINGW64 /d/wilson/rebaseStudy (dev) $ git branch
  dev
* master

远程仓库随之多出了一个 dev 分支。

GitHub仓库分支列表,显示master和dev分支

此时,Git 的分支状态如下图所示,devmaster 指向同一个最新的提交 C2。

初始分支状态图:master和dev都指向提交C2

随后,B同学在 dev 分支上进行了三次功能开发提交。查看日志如下:

Tianji@DESKTOP-B7ANV0H MINGW64 /d/wilson/rebaseStudy (dev) $ git log
commit 5c0adc3677504f8db8cc94dc89a415356aa2e97 (HEAD -> dev)
author: wilson <weishuyuan001>
Date:   Sun Nov 11 10:57:54 2018 +0800

    A 4 commit a.txt

commit 6d860217c834da6a9ebc2fc7e30dfdc788fb763e
author: wilson <weishuyuan001>
Date:   Sun Nov 11 10:57:15 2018 +0800

    A 3 commit a.txt

commit 89f2cda3eda29ead5b244de96d61e12467741d7
author: wilson <weishuyuan001>
Date:   Sun Nov 11 10:56:42 2018 +0800

    A 2 commit a.txt

commit 2ae743b22fe94c636c7cfaee192b9ff34ceed138 (origin/master, origin/dev, origin/HEAD, master)
Author: wilson <weishuyuan001>
Date:   Sun Nov 11 10:24:25 2018 +0800

    A first commit a.txt

commit 71bdb5c376d574ad00b32cc88ebb7c7f87f16e
Author: shuyuan1992 <42328986+shuyuan1992@users.noreply.github.com>
Date:   Sun Nov 11 10:07:07 2018 +0800

    Initial commit

此时的分支状态图变为一条“分叉”的形态,dev 分支领先于 master 分支三个提交(C3, C4, C5)。

分叉分支状态图:dev分支在C2之后有了C3,C4,C5三个提交

关键问题:主分支更新了

现实开发中常见的情况是:当B同学在 dev 分支开发时,其他同学(比如A同学)向 master 分支合并了新的功能。也就是说,master 分支已经向前推进了,产生了新的提交 C6。

此时的分支状态图如下,dev 分支是基于旧的 master(C2)开发的,而 master 已经更新到了 C6。

主分支更新后的分叉图:master指向C6,dev基于C2开发出C3,C4,C5

现在,B同学完成了 dev 分支的开发,需要将其功能合并到 master 分支。他面临着两种选择:git mergegit rebase

方案一:使用 git merge 合并

如果B同学选择直接执行 git merge,Git 会进行以下操作:

  1. 找到 masterdev 两个分支的最近共同祖先,即提交 C2。
  2. dev 分支的最新提交 C5 和 master 分支的最新提交 C6 进行合并,如果存在冲突需要解决。这个操作会产生一个全新的合并提交 C7。
  3. 最终,将 C2 之后 devmaster 的所有提交点,按照提交时间顺序整合到 master 分支的历史中。

合并后的分支状态图如下,产生了一个新的合并提交 C7,历史轨迹呈现出非线性的“分叉-合并”结构

merge合并后的分支图:产生一个新的合并提交C7,连接C5和C6

方案二:使用 git rebase 变基

如果B同学希望得到一条更整洁的线性历史,他可以选择 rebase(变基)。

  1. 首先,切换到需要变基的分支,即 dev 分支。
  2. 执行 git rebase master。这条命令的语义是:“将当前 dev 分支的修改,在最新的 master 分支的基础上重新‘播放’一遍”。
  3. Git 会暂时保存 dev 分支上 C2 之后的所有提交(C3, C4, C5),然后找到 master 分支的最新提交 C6,并以此为新的基础。
  4. 接着,Git 会将保存的那些提交依次应用到 C6 之后。这个过程可能会产生冲突,需要解决。解决后使用 git add .git rebase --continue 继续。
  5. 最终,dev 分支的起点从 C2 变为了 C6,原来的提交 C3, C4, C5 会被复制成新的提交 C3', C4', C5'(哈希值改变),从而形成一条线性的历史。

变基后的分支状态图如下所示。可以看到,整个 master 分支并没有多出一个合并提交,历史是一条干净的直线。原来 dev 分支上的几次提交内容被保留,但它们的哈希值已经改变。

rebase变基后的分支图:dev的提交C3,C4,C5被复制并应用到C6之后,形成线性历史

核心总结与选择建议

  • git merge:保留真实的合并历史,会创建一个新的合并提交(Merge Commit)。最终的分支树呈现非线性的结构,清晰地记录了分支何时合并以及为何合并。这是保留完整项目历史的推荐方式。
  • git rebase:重写提交历史,将当前分支的提交“移植”到目标分支的最新提交之后。结果是得到一条线性的分支历史,更加整洁。但这也意味着改写了公共历史,适用于整理本地、尚未推送的提交。

如何选择?
一个被广泛接受的准则是:只对尚未推送的本地提交执行变基(rebase),永远不要对已推送至远程仓库的提交进行变基。 对于需要整合到公共分支(如 master)的更改,使用 merge 更为安全,因为它不会改变其他开发者所依赖的公共历史。

理解 mergerebase 的差异,能帮助你在不同的团队协作场景下做出合适的选择,从而维护一个清晰且实用的项目提交历史。如果你想与更多开发者交流此类版本控制的最佳实践,欢迎在技术社区中参与讨论。




上一篇:AI浪潮下,前后端开发者如何构建不可替代的核心竞争力?
下一篇:职场遇打压?技术人的“以柔克刚”生存法则
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-29 06:29 , Processed in 0.526932 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表