git merge
合并是 Git 将拷贝历史记录重新组合在一起的方式。git merge
命令允许您采用 git branch
创建的独立开发行,并将它们集成到单个分支中。
请注意,下面显示的所有命令都合并到当前分支中。当前分支将更新以反映合并,但目标分支将完全不受影响。同样,这意味着 git merge
通常与 git checkout
结合使用来选择当前分支,使用 git branch-d
来删除过时的目标分支。
工作原理
Git merge
会将多个提交序列合并为一个统一的历史记录。在最常见的用例中,git merge
用于合并两个分支。本文档中的以下示例将重点介绍这种分支合并模式。在这些场景中,git merge
需要两个提交指针,通常是分支提示,并会在它们之间找到一个共同的基础提交。一旦 Git 找到通用基础提交,它将创建一个新的“合并提交”,该提交组合了每个排序合并提交的变更。
假设我们有一个基于 main
分支的新分支功能。我们现在想将这个功能分支合并到 main
分支中。
相关资料
高级 Git 日志
查看解决方案
了解 Bitbucket Cloud 的 Git
调用此命令会将指定的分支功能合并到当前分支中,我们假设为 main
。Git 将自动确定合并算法(如下所述)。
与其他提交相比,合并提交的独特之处在于,它们有两个父项提交。创建合并提交时,Git 会尝试自动为您合并单独的历史记录。如果 Git 遇到在两个历史记录中都发生了变更的数据,它将无法自动合并这些数据。这种情况是版本控制冲突,Git 需要用户干预才能继续。
正在准备合并
执行合并之前,需要采取几个准备步骤来确保合并顺利进行。
确认接收分支
执行 git status
以确保 HEAD
指向正确的合并接收分支。如果需要,执行 git checkout
,切换到接收分支。在我们的例子中,我们将执行 git checkout main
。
获取最新的远程提交
确保接收分支和合并分支是最新的远程变更。执行 git fetch
以提取最新的远程提交。提取完成后,通过执行 git pull
确保 main
分支有最新的更新。
合并
完成前面讨论的“准备合并”步骤后,可以通过执行 git merge
来启动合并,其中
是将要合并到接收分支的分支名称。
快进合并
当存在从当前分支尖端到目标分支的线性路径时,可能会发生快进合并。不是“实际上”合并分支,Git 要整合历史记录,所要做的就是将当前分支的尖端向上移动(即“快进”)到目标分支的尖端。这有效地合并了历史记录,因为所有可从目标分支访问的提交现在都可以通过当前分支获得。例如,将 some-feature 快进合并到 main
会如下所示:
但是,如果分支出现分歧,则不可能进行快进合并。当目标分支没有线性路径时,Git 别无选择,只能通过三向合并将它们合并。三向合并使用专用提交将两个历史记录联系在一起。这个命名法源于 Git 使用三个提交来生成合并提交:两个分支提示及其共同的祖先。
虽然您可以使用这两种合并策略中的任何一种,但许多开发人员喜欢使用快进合并(通过变基来促进)来修复小功能或缺陷,并保留三向合并用于集成长期运行的功能。在后一种情况下,生成的合并提交充当两个分支的符号连接。
我们的第一个示例演示了快进合并。下方代码创建了一个新分支,向其添加了两个提交,然后通过快进合并将其集成到主行中。
# Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Merge in the new-feature branch
git checkout main
git merge new-feature
git branch -d new-feature
这是短期主题分支的常见工作流程,这些分支更多地被用作孤立的开发,而不是用于长期运行的功能的组织工具。
另请注意,Git 不应该抱怨 git branch -d
,因为现在可以从 main 分支访问 new-feature。
如果您在快进合并期间需要合并提交以保存记录,则可以使用 --no-ff
选项执行 git merge
。
git merge --no-ff <branch>
此命令将指定的分支合并到当前分支中,但始终生成合并提交(即使是快进合并)。这对于记录存储库中发生的所有合并很有用。
三向合并
下个示例非常相似,但需要进行三向合并,因为 main
分支在功能进行时会继续进行。这是大型功能或多个开发人员同时开发一个项目的常见场景。
Start a new feature
git checkout -b new-feature main
# Edit some files
git add <file>
git commit -m "Start a feature"
# Edit some files
git add <file>
git commit -m "Finish a feature"
# Develop the main branch
git checkout main
# Edit some files
git add <file>
git commit -m "Make some super-stable changes to main"
# Merge in the new-feature branch
git merge new-feature
git branch -d new-feature
请注意,Git 不可能执行快进合并,因为如果不回溯,就无法将 main
升级到 new-feature
。
对于大多数工作流程来说,new-feature
将是一个更大的功能,需要很长时间才能开发,这就是新提交会同时出现在 main
上的原因。如果您的功能分支实际上和上面例子中的分支一样小,那么您最好将其变基到 main
上,然后进行快进合并。这样可以防止多余的合并提交扰乱项目历史记录。
解决冲突
如果您要合并的两个分支都变更了同一个文件的相同部分,Git 将无法确定要使用哪个版本。发生这种情况时,它会在合并提交之前停止,以便您可以手动解决冲突。
Git 合并流程的很大一部分是它使用熟悉的编辑/暂存/提交工作流程来解决合并冲突。遇到合并冲突时,运行 git status
命令会显示需要解决哪些文件。例如,如果两个分支都修改了 hello.py
的同一部分,您会看到类似下方的内容:
On branch main
Unmerged paths:
(use "git add/rm ..." as appropriate to mark resolution)
both modified: hello.py
冲突是如何呈现的
在合并期间遇到冲突时,Git 将会使用可视指示器来编辑受影响文件的内容,并在冲突内容的两边标上标记。用到的视觉标记包括:<<<<<<<、======= 和 >>>>>>>。在合并期间,在项目中搜索这标记可以轻松找到需要解决冲突的地方,非常实用。
here is some content not affected by the conflict
<<<<<<< main
this is conflicted text from main
=======
this is conflicted text from feature branch
>>>>>>> feature branch;
通常 =======
标记之前的内容是接收分支,之后的部分是合并分支。
一旦您确定了有冲突的部分,您就可以进入并根据自己的喜好修复合并。当您准备好完成合并时,您要做的就是在冲突文件上运行 git add
,告诉 Git 它们已经解决了。然后,运行一个普通的 git commit
来生成合并提交。这与提交普通快照的流程完全相同,这意味着普通开发人员可以轻松管理自己的合并。
请注意,只有在三向合并的情况下才会发生合并冲突。在快进合并中不可能有相互冲突的变更。
摘要
本文档概述了 git merge
命令。使用 Git 时,合并是一个必不可少的过程。我们讨论了合并背后的内部机制,以及快进合并和三向真正合并之间的区别。一些关键的要点如下所示:
1. Git 合并将提交序列合并为一个统一的提交历史记录。
2. Git 的合并方式主要有两种:快进和三向
3. Git 可以自动合并提交,除非两个提交序列中存在冲突的变更。
本文档集成并引用了其他 Git 命令,例如:git branch、git pull 和 git fetch。访问他们相应的独立页面以获取更多信息。
分享此文章
下一主题
推荐阅读
将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。