赞
踩
//在分支1下操作,会将分支1合并到分支2中
git merge <分支2>
最简单的合并算法,它是在一条不分叉的两个分支之间进行合并。快进式合并是默认的合并行为,并没有产生新的commit。如下图所示,虚线部分是合并前,在经过如下命令后:
//当前在Main分支下操作
git merge Dev
Git 将 Main 和 HEAD 指针移动到 Dev 所在的提交对象上,此时合并完成,不涉及到内容的变更和比较,所以这种合并方式效率很高。从上面的图可以发现,这种合并策略要求合并的两个分支上的提交,必须是一条链上的前后关系。
可以通过git merge --no-off
参数来进行关闭快进式合并,关闭后会强制使用Three Way Merge
(三路合并),下面来具体讲讲三路合并
当两个分支的提交对象不在一条提交链上时,Git 会默认执行三路合并的方式进行合并。
首先 Git 会通过算法寻找两个分支的最近公共祖先节点,再将找到的公共祖先节点作为base节点,并使用三路合并的策略来进行合并,也就是我们常见的合并方式(三路指的是两个需要合并的分支的提交对象(比如下图中的提交对象 1,2),最近共同祖先(提交对象 0)三个对象):
可以通过传递不同参数来使用不同的合并策略:
git merge [ -s <strategy>] [-X <strategy-option>] <commit>...
-s <strategy>
用于设定合并策略-X <strategy-option>
用于为所选的合并策略提供附加的参数Resolve
策略Resolve 策略是默认的三路合并策略,既可以使用 git merge <分支>
又可以使用 git merge -s resolve <分支>
来执行合并,该合并策略只能用于合并两个分支,也就是当前分支和另外的一个分支,使用三路合并策略。这种合并策略被认为是最安全、最快的合并分支策略。
比如在 Main 分支下执行以下命令:
//当前在Main分支下操作
git merge Dev
Git 会创建一个新的提交对象(如上图中的提交对象 3),然后该提交对象将合并提交对象 1 和提交对象 2 的内容,形成提交对象 3。但是一旦遇到两个分支对象有多个共同祖先时,此时 Resolve 策略就无法实现合并了,这个时候就需要用 Recursive
策略。
如下图所示,在对两个分支上的提交A和B进行合并时,我们发现了它们有两个共同祖先,分别是:ancestor0
和 ancestor1
。这就是 Criss-Cross
现象:
我们在此处复现一下这个现象:
//1.初始化一个recursiveMerge仓库 $ git init recursiveMerge //2.创建一个master-commit1.txt文件并提交 $ git commit -m "master-commit1" [master (root-commit) 85cba5f] master-commit1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 master-commit1.txt //3.新建一个分支dev,并切换到dev。在dev分支创建一个dev-commit1.txt文件并提交 $ git checkout -b dev Switched to a new branch 'dev' $ git commit -m "dev-commit1" [dev 9763dcc] dev-commit1 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev-commit1.txt //4.切换到master分支,创建一个master-commit2.txt文件并提交 $ git checkout master Switched to branch 'master' $ git commit -m "master-commit2" [master 5c23645] master-commit2 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 master-commit2.txt //5.新建一个分支feature, 切换到feature,合并dev和feature $ git merge dev hint: Waiting for your editor to close the file... unix2dos: converting file D:/recursiveMerge/.git/MERGE_MSG to DOS format... libpng warning: iCCP: known incorrect sRGB profile libpng warning: iCCP: known incorrect sRGB profile dos2unix: converting file D:/recursiveMerge/.git/MERGE_MSG to Unix format... Merge made by the 'ort' strategy. dev-commit1.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev-commit1.txt //6.切换到master,合并dev和master $ git merge dev hint: Waiting for your editor to close the file... unix2dos: converting file D:/recursiveMerge/.git/MERGE_MSG to DOS format... libpng warning: iCCP: known incorrect sRGB profile libpng warning: iCCP: known incorrect sRGB profile dos2unix: converting file D:/recursiveMerge/.git/MERGE_MSG to Unix format... Merge made by the 'ort' strategy. dev-commit1.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 dev-commit1.txt
我们在 Git GUI 中查看此时的分支图像:
那么Recursive 策略对于这种情况是怎么做的呢?
如果Git在寻找共同祖先时,在参与合并的两个分支上找到了不只一个满足条件的共同祖先,它会先对共同祖先进行合并,建立临时快照。然后,把临时产生的“虚拟祖先”作为合并依据,再对分支进行合并。对于上图而言,会将ancestor 0
和 ancestor 1
先合并成一个虚拟祖先 ancestor 2
,最后再将它与A和B提交一起进行三路合并:
在处理合并时,有可能会出现不同分支在相同文件上的冲突,因此可以使用下列选项来在发生冲突时起作用,常见的有:
Ours
选项在遇到冲突时,选择当前分支版本,而忽略其他分支版本。如果他人的改动和本地改动不冲突,会将他人的改动合并进来:
# 在冲突合并中,选择当前分支内容,自动丢弃其他分支内容
git merge -s recursive -Xours
Theirs
选项和 Ours
选项相反,遇到冲突时,选择他人的版本,丢弃当前分支版本
# 选择其他分支内容,自动丢弃当前分支内容
git merge -s recursive -Xtheis
此外还有 subtree[=<path>]
,renormalize
,no-renormalize
等等选项,具体可以看官方文档Git - merge-strategies Documentation
Octopus Merge
多路合并git merge -s octopus <分支1> <分支2> ... <分支N>
此合并策略可以合并两个以上的分支,但是拒绝执行需要手动解决的复杂合并,它的主要用途是将多个分支合并到一起,该策略是对三个及三个以上的分支合并时的默认合并策略。
来做一个实验,此时我的 Git 仓库中有三个分支 feature1,feature2,feature3,执行 octopus 合并:
$ git merge -s octopus feature1 feature2 feature3 Trying simple merge with feature1 Trying simple merge with feature2 Trying simple merge with feature3 Merge made by the 'octopus' strategy. feature1-1.txt | 0 feature3-2.txt | 0 four.txt | 0 second.txt | 0 third-2.txt | 0 third.txt | 0 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 feature1-1.txt create mode 100644 feature3-2.txt create mode 100644 four.txt create mode 100644 second.txt create mode 100644 third-2.txt create mode 100644 third.txt
subtree
策略这是一个经过调整的 recursive
策略,当合并树 A 和树 B 时,如果树 B 和树 A 的一个子树相同,B 树首先进行调整以匹配 A 的树结构,以免两棵树在同一级别进行合并。同时也针对两棵树的共同祖先调整。
git merge -s subtree <树A> <树B>
该部分会单独再出一篇,来姐姐子树合并和子模块合并
ours
策略这种策略可以合并任意数量的分支,但是合并的结果总是使用当前分支的内容,丢弃其他分支的内容。
这种模式和上面的 recursive
策略中的 ours
选项不同,ours
选项是在某个分支下,忽略其他人的版本,ours
策略是忽略其他的分支。
rebase
是指在另一个基端之上重新应用提交。很多时候我们在两个分支上用rebase
。但是**rebase**
命令的作用对象不仅仅限于分支。任何的提交引用,都可以被视作有效的**rebase**
基底对象。包括一个提交ID、分支名称、标签名称或者**HEAD~1**
这样的相对引用。
git rebase
默认是标准模式,一般用于多个分支之间:在一个分支中集成另外分支的最新修改。
#当前在Feature分支下操作,会得到如下的变基状态
git checkout Feature
git rebase Main
#当前在branch1分支下操作,会得到如下的变基状态
git checkout Branch1
#将Main分支中没有Branch1分支的提交,拷贝到Branch2分支中
git rebase Main --onto Branch2
当执行git rebase
命令后,整个变基过程都会自动进行,如果在该命令后加上-i
参数后,就会进入到交互模式:
git rebase -i
或者git rebase -interactive
开始后执行一个交互式rebase 会话。一般用于当前分支的历史提交进行编辑操作。
# 针对该commit 后的提交进行交互式操作
git rebase -i [commit ID]
运行命令后,会跳出交互界面,比如在git bash中输入如下命令:git rebase -i ecd259f
页面会出现下面的界面:
上图中的两条记录是提交列表,指的是 ID 为ecd259f...
的 commit 之前的提交。我们可以对这两个提交进行相关的操作,在上面的页面中也提供了几个命令:
pick
:保留该commit,和drop相对,也就是什么也不做(在IDEA中不选择,直接rebase,也会得到如下结果)reword
:修改该提交的commit message,如果该commit之后也有提交,那么之后的提交信息也会被改变edit
: 选择一个提交并修改该提交的内容squash
:合并该提交与上一个提交,也可以合并多个,两两合并,间隔合并fixup
:与squash类似,但是会抛弃当前的commit messageexec
: 执行shell命令,运行一条自定义命令可以在提交列表前面进行参数修改,比如将 pick
改成 squash
就是将两个提交合并到 ID 为 ecd259f...
的提交对象中
squash a5eb754 first commit --init
squash 2df01ad ignoreList
在日常对冲突的处理中,很明显的区别在于 rebase 的处理方式能让提交链更加清晰,而使用 merge 方式会显得提交链复杂交错。
下面我们具体来看看两者的区别与联系
git merge
创建一个新的合并提交,将两个分支的更改合并到一起,而 git rebase
将一系列提交从一个分支转移到另一个分支,并重新组织这些提交:git merge
保留每个分支的独立提交历史,形成一个合并的提交历史树,而git rebase修改提交历史的结构,使提交历史更线性:git merge
和 git rebase
都需要手动解决冲突。但是,解决冲突的方式略有不同。git merge
在合并提交中保留冲突的解决方案,而 git rebase
在每个冲突点停下来,必须在解决冲突后才能继续进行rebase操作。git rebase
重新应用提交,所以重新组织后的提交具有新的提交ID,而 git merge
创建的合并提交保留了原始提交的ID。比如上图中,git rebase
命令执行后,对应的 master 分支的三个 commit 对象的提交 ID 都要被修改。git merge
还是 git rebase
,它们都是用于将一个分支的更改合并到另一个分支上。两者都是在对分支进行合并,是来解决冲突的。git merge
还是 git rebase
,它们都可以用于将分支与上游分支同步,以确保分支包含上游的最新更改。git merge
还是 git rebase
,在合并过程中都可能存在冲突,需要手动解决冲突。https://www.geeksforgeeks.org/merge-strategies-in-git/
https://morningspace.github.io/tech/git-merge-stories-2/
https://git-scm.com/docs/merge-strategies/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。