Harry Yu

Git Rebase 与 Merge 完全指南:图解、对比与冲突解决

Git
目录

1. 引言

在 Git 版本控制中,整合分支的常用方式有两种:mergerebase。它们都能将另一个分支的更改合并到当前分支,但实现方式和工作原理截然不同。理解两者的区别,能帮助你根据场景选择合适的方法,保持代码历史的清晰与协作的顺畅。

本文将深入讲解:

  • rebase 的概念、工作原理与操作
  • merge 的概念、工作原理与操作
  • 两者的详细对比(含 ASCII 图解)
  • 冲突产生的原因及标准解决方法
  • 如何在实际开发中做出选择

2. Rebase 详解

2.1 什么是 Rebase?

git rebase 的字面意思是”变换分支的根基”。它的作用是将当前分支的提交**“移植”到另一个分支的最新提交之上**,从而让提交历史变成一条直线。

2.2 工作原理

假设当前分支为 feature,执行 git rebase main 时,Git 会:

  1. 找到 featuremain 的最近共同祖先提交。
  2. feature 分支上从共同祖先之后的所有提交临时保存为补丁(patch)。
  3. feature 分支的指针直接指向 main 的最新提交。
  4. 将保存的补丁依次应用到新的位置,生成一系列全新的提交(哈希值改变)。

2.3 常用命令

git rebase <target-branch>

git rebase -i HEAD~n

git add <resolved-file>
git rebase --continue

git rebase --skip

git rebase --abort

2.4 注意事项

  • 永远不要对已经推送到远程、且多人协作的分支执行 rebase,因为这会重写历史,导致他人本地仓库混乱。
  • Rebase 会改变提交的哈希值,即使内容相同。

3. Merge 详解

3.1 什么是 Merge?

git merge 用于将两个或多个分支的开发历史合并在一起。它会将目标分支的更改整合到当前分支,并生成一个特殊的合并提交(merge commit)来记录合并的来源。

3.2 工作原理

执行 git merge main(在 feature 分支上)时:

  1. Git 会找到两个分支的最近共同祖先。
  2. main 分支上的所有更改(自共同祖先以来)应用到 feature 分支。
  3. 如果有冲突,需要手动解决;如果没有冲突,Git 会自动创建一个新的合并提交,其父提交为 featuremain 的最新提交。

3.3 常用命令

git merge <target-branch>

git add <resolved-file>
git commit -m "Merge message"

git merge --continue

git merge --abort

3.4 注意事项

  • Merge 是非破坏性操作,不会修改已有提交的历史。
  • 会产生额外的合并提交,历史可能呈现分叉状。

4. Rebase 与 Merge 图解对比

假设初始状态如下:

A---B---C  (main)
     \
      D---E  (feature)

4.1 使用 git merge main(在 feature 分支)

执行后:

A---B---C  (main)
     \   \
      D---E---F  (feature)
  • F 是合并提交,有两个父提交:EC
  • 历史保留了分叉,真实反映了并行开发过程。

4.2 使用 git rebase main(在 feature 分支)

执行后:

A---B---C  (main)
         \
          D'---E'  (feature)
  • D'E' 是原提交 DE 的”复制品”,但哈希值改变。
  • 历史呈线性,像从最新的 main 上直接开发一样。

5. 冲突的产生与解决方法

无论是 merge 还是 rebase,当两个分支修改了同一文件的同一部分时,Git 无法自动决定保留哪个版本,就会产生冲突。

5.1 冲突标识

冲突文件中会包含类似这样的标记:

<<<<<<< HEAD (当前分支的内容)
当前分支的代码
=======
被合并分支的代码
>>>>>>> <commit-hash> (其他分支的提交)

你需要手动编辑文件,决定最终内容,并删除这些标记。

5.2 解决冲突的通用步骤

  1. 查看冲突文件git status 列出所有冲突文件。
  2. 手动编辑:打开每个冲突文件,分析并整合代码。
  3. 标记已解决git add <resolved-file> 将文件标记为已解决。
  4. 完成操作
    • 如果是 merge:执行 git commit 生成合并提交(或 git merge --continue)。
    • 如果是 rebase:执行 git rebase --continue 继续应用下一个提交。

5.3 使用图形化合并工具

可以运行 git mergetool 启动配置好的可视化合并工具(如 VSCode、Meld、Beyond Compare 等),更直观地解决冲突。

5.4 最佳实践

  • 理解意图:不要盲目选择,要分析两个分支的改动目的。
  • 及时沟通:复杂冲突最好与相关开发者讨论。
  • 测试:解决后编译/运行测试,确保功能正常。
  • 保持提交原子性:每个提交只做一件事,减少冲突概率。

5.5 遇到多个冲突

  • rebase 过程中,如果多个提交都产生冲突,你需要依次为每个冲突提交执行”解决 → git addgit rebase --continue”的循环。
  • 如果想放弃整个 rebase,可用 git rebase --abort

6. 如何选择:rebase 还是 merge?

因素推荐用 rebase推荐用 merge
分支类型个人功能分支、本地开发公共分支(如 main)、多人协作分支
历史整洁度希望历史线性、干净可以接受分叉,但需保留真实合并记录
安全性不担心重写历史绝对不能修改已推送的提交
冲突处理需逐个提交解决,可能多次冲突一次性解决所有冲突
查看分支来源难以追踪分支起始点合并提交清晰记录分支来源

黄金法则

  • 推送之前,可以用 rebase 整理本地提交。
  • 已推送的公共分支,永远用 merge

7. 总结

  • git rebase 通过重演提交来保持线性历史,但会修改提交哈希,适合个人分支。
  • git merge 保留分支分叉,生成合并提交,适合公共分支的协作。
  • 冲突不可避免,但掌握标准的解决流程能让你从容应对。
  • 根据场景选择合适的方法,是 Git 高效协作的关键。