Git merge 合并分支
合并是 Git 将分叉历史重新组合在一起的方式。 git merge 命令可以让我们使用 git branch 创建的独立开发线,并将它们合并到单个分支中。
请注意,下面显示的所有命令都合并到当前分支中。将更新当前分支以反映合并,但目标分支将完全不受影响。同样,这也就是意味着git merge
通常与git checkout一起用于选择当前分支,而git branch -d用于删除过时的目标分支。
git merge 如何工作
git merge 将多个提交序列合并为一个统一的历史记录。 在最常见的用例中, git merge 用于合并两个分支。 本文档中的以下示例将重点介绍此分支合并模式。 在这些场景中, git merge 需要两个提交指针,通常是分支提示,并会在它们之间找到一个共同的基础提交。 一旦 Git 找到一个共同的基本提交,它就会创建一个新的“合并提交”,它结合了每个排队的合并提交序列的更改。
假设我们有一个基于 main 分支的新分支功能。 我们现在想将此功能分支合并到 main 分支中。
调用此命令会将指定的分支功能合并到当前分支中,我们假设是 main。 Git 将自动确定合并算法(下面讨论)。
合并提交与其他提交相比是独一无二的,因为它们有两个父提交。 创建合并提交时,Git 将尝试自动为我们合并单独的历史记录。 如果 Git 遇到在两个历史记录中都发生更改的数据,它将无法自动组合它们。 这种情况是版本控制冲突
,Git 需要用户干预才能继续。
在执行合并之前,需要采取几个准备步骤以确保合并顺利进行。
确认接收分支
执行 git status 命令 以确保 HEAD 指向正确的合并接收分支。 如果需要,执行 git checkout 命令 切换到接收分支。 在我们的例子中,我们将执行 git checkout main
。
获取最新的远程提交
确保接收分支和合并分支与最新的远程更改保持同步。 执行 git fetch 命令 拉取最新的远程提交。 获取完成后,通过执行 git pull 确保 main 分支具有最新更新。
合并
一旦执行了前面讨论的“准备合并”步骤,就可以通过执行 git merge 来启动合并。
Fast Forward 合并
当从当前分支 HEAD 到目标分支存在线性路径时,就会发生 fast-forward 合并。 与“实际”合并分支不同,Git 整合历史所要做的就是将当前分支提示移动(即“快进”)到目标分支提示。 这有效地结合了历史,因为现在可以通过当前分支访问从目标分支可到达的所有提交。 例如,将 some-feature 快速向前合并到 main 中,如下所示:
但是,如果分支已经发散,则不可能进行 Fast-Forward 合并。 当目标分支没有线性路径时,Git 别无选择,只能通过 三路合并
来组合它们。 三路合并使用专用提交将两个历史记录联系在一起。 命名法来自这样一个事实,即 Git 使用三个提交来生成合并提交:两个分支提示和它们的共同祖先。
虽然我们可以使用这两种合并策略中的任何一种,但许多开发人员喜欢使用 Fast-Forward 合并(通过rebase)来处理小功能或错误修复,同时保留 三路合并以集成更长时间运行的功能。 在后一种情况下,生成的合并提交充当两个分支的符号连接。
我们的第一个示例演示了 Fast-Forward 合并。 下面的代码创建一个新分支,向其添加两个提交,然后通过 Fast-Forward 合并将其集成到主线中。
# 创建一个新分支 并切换到它
$ git checkout -b new-feature main
# 修改 hello.txt 文件
$ git status
$ git add hello.txt
$ git commit -m "Start a feature"
# 修改 undoing.txt 文件
$ git add undoing.txt
$ git commit -m "Finish a feature"
# 查看当前分支
$ git branch
# 将 new-feature 合并到main
$ git checkout main
$ git merge new-feature
$ git branch -d new-feature
这是短期功能开发所用分支的常见工作流程,这些分支更多地用作独立开发,而不是用于长期运行的功能。
如果需要在快进合并期间进行合并提交以保存记录,可以在 git merge 后面跟上--no-ffoption
选项。
git merge --no-ff <branch>
此命令将指定分支合并到当前分支,但始终生成合并提交(即使它是快进合并)。 这对于记录仓库中发生的所有合并非常有用。
3-way 合并
下一个示例非常类似,但需要一个三路合并,因为 main 分支在 new-feature 分支进行时也在往前推进。这是大型功能或多个开发人员同时处理一个项目时的常见场景。
# 创建一个新分支 并切换到它
$ git checkout -b new-feature main
# 编辑 hello.txt
$ git add hello.txt
$ git commit -m "Start a feature"
# 编辑 undoing.txt
$ git add undoing.txt
$ git commit -m "Finish a feature"
# 切换 main 分支进行开发
$ git checkout main
# 编辑 hello.txt
$ git add hello.txt
$ git commit -m "Make some super-stable changes to main"
# 合并两个分支
$ git merge new-feature
$ git branch -d new-feature
注意
- Git 不可能执行 Fast-Forward 合并,因为在不回溯的情况下无法将 main 分支向上移动到 new-feature。
对于大多数工作流来说,新功能将会是一个需要很长时间开发的大功能,这就是为什么在开发新功能期间,main分支上也会有修改。 如果功能分支实际上与上面示例中的一样小,可能最好将其重新设置为 main 并进行快进合并。 这可以防止多余的合并提交使项目历史变得混乱。
解决冲突
如果尝试合并的两个分支都更改了同一文件的同一部分,Git 将无法确定要使用哪个版本。 当这种情况发生时,它会在合并提交之前停止,以便我们可以手动解决冲突。
Git 合并过程的重要部分是它使用熟悉的 编辑/暂存/提交 工作流来解决合并冲突。 当遇到合并冲突时,运行 git status 命令会显示需要解决哪些文件。
我们看下面的示例,将 jiyik 分支合并到 main 分支
$ git merge jiyik
Auto-merging undoing.txt
CONFLICT (add/add): Merge conflict in undoing.txt
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.
冲突呈现形式
当 Git 在合并过程中遇到冲突时,它会编辑受影响文件的内容,并使用标记符来标记冲突内容的双方。 这些视觉标记是:<<<<<<<
、========
和 >>>>>>
。 在合并期间搜索这些指标的项目有助于找到需要解决的冲突。
一般=======
标记符之前的内容是接收分支,之后的部分是合并分支。
一旦确定了冲突的部分,就可以根据自己的喜好进行合并。当准备好完成合并时,我们所要做的就是运行 git add 冲突文件以告诉 Git 它们已解决。然后,运行正常git commit以生成合并提交。这与提交普通快照的过程完全相同,这意味着普通开发人员可以轻松管理自己的合并。
注意
- 合并冲突只会在 三路合并的情况下发生。在 **Fast-Forward **合并中不可能有相互冲突的变化。
git merge 总结
本章节是对 git merge 命令的介绍。 在使用 Git 时,合并是一个必不可少的过程。 我们讨论了合并背后的内部机制,以及 Fast-Forward 合并和三路合并之间的区别。 一些关键要点是:
- Git 合并将提交序列组合成一个统一的提交历史。
- Git 合并的主要方式有两种:Fast Forward 和 三路合并。
- Git 可以自动合并提交,除非在两个提交序列中存在冲突的更改。
本文档集成并引用了其他 Git 命令,例如:git branch、git pull 和 git fetch。 访问他们相对应的章节以获取更多知识。