不清楚 git 冲突的表示方法,不了解 git 的合并原理,不知道 git 解冲突的多种策略。即便如此,大多数人依然可以正常使用 git 完成合并、拉取操作,并且解一些冲突。这得益于 git 默认情况下的合并方式可以处理大多数情况下的正常合并。
然而,你是否遭遇 git 自动合并炸掉的情况?命名提示没有冲突,代码却早已无法编译通过。
本文将介绍 git 的合并策略,你可能可以更好的使用不同的策略来解决冲突。
git 合并策略
典型的使用指定 git 合并策略的命令这么写:
1
$ git merge 要合并进来的分支名 --strategy=合并策略
例如:
1
$ git merge origin/master --strategy=resolve
或者使用简写 -s
,例如:
1
$ git merge origin/master -s resolve
可以指定的合并策略有:
- resolve
- recursive
- octopus
- ours
- subtree
resolve
这使用的是三路合并算法。不过我们在 git 的合并原理(递归三路合并算法) 中说过,普通的三路合并算法会存在发现多个共同祖先的问题。此策略会“仔细地”寻找其中一个共同祖先。
由于不需要递归合并出虚拟节点,所以此方法合并时会比较快速,但也可能会带来更多冲突。不敢说带来更多冲突是好事还是坏事,因为自动合并成功并不一定意味着在代码含义上也算是正确的合并。所以如果自动合并总是成功但代码含义上会失败,可以考虑此合并策略,这将让更多的冲突变成手工合并而不是自动合并。
recursive
这是默认的合并策略,如果你不指定策略参数,那么将使用这个合并策略。这将直接使用递归三路合并算法进行合并,详见:git 的合并原理(递归三路合并算法)。
当指定为此策略时,可以额外指定下面的这些参数,方法是:
1
$ git merge 要合并进来的分支名 --strategy=合并策略 -X diff-algorithm=参数
例如:
1
$ git merge origin/master -s recursive -X diff-algorithm=patience
由于 recursive
是默认的合并策略,所以可以简化成:
1
$ git merge origin/master -X diff-algorithm=patience
ours
如果不冲突,那么与默认的合并方式相同。但如果发生冲突,将自动应用自己这一方的修改。
注意策略里面也有一个 ours,与这个不同的。
theirs
这与 ours 相反。如果不冲突,那么与默认的合并方式相同。但如果发生冲突,将自动应用来自其他人的修改(也就是 merge 参数中指定的那个分支的修改)。
patience
此策略的名称叫“耐心”,因为 git 将话费更多的时间来进行合并一些看起来不怎么重要的行,合并的结果也更加准确。当然,使用的算法是 recursive 即递归三路合并算法。
不过此名称也难以准确描述到底如何准确,不过可以举一个例子来说明:
1
2
3
4
5
6
7
8
9
int Foo()
{
// 一些省略的代码。
}
int Baz()
{
// 一些省略的代码。
}
然后在这两个函数中增加另一个函数:
1
2
3
4
int Bar()
{
// 一些省略的代码。
}
默认情况下 git 会认为修改是这样的:
1
2
3
4
5
+ }
+
+ int Bar()
+ {
+ // 一些省略的代码。
然而使用 patience
策略后,git 将认为修改是这样的:
1
2
3
4
5
+ int Bar()
+ {
+ // 一些省略的代码。
+ }
+
如果你经常合并出现这些括号丢失或者符号不再匹配的问题,可以考虑使用 patience
策略进行合并。
no-renames
默认情况下 git 会识别出你重命名或者移动了文件,以便在你移动了文件之后依然可以与原文件进行合并。如果指定此策略,那么 git 将不再识别重命名,而是当作增加和删除了文件。
其他的参数
diff-algorithm=[patience|minimal|histogram|myers]
renormalize
no-renormalize
find-renames[=<n>]
rename-threshold=<n>
subtree[=<path>]
octopus
又是一个奇怪的名字——章鱼。章鱼有很多的触手,此合并策略就像这么多的触手一样。
此策略允许合并多个 git 提交节点(分支)。不过,如果会出现需要手工解决的冲突,那么此策略将不会执行。
此策略就是用来把多个分支聚集在一起的。
1
2
3
4
5
$ git merge t/lvyi t/walterlv -s octopus
error: Merge requires file-level merging
Trying really trivial in-index merge...
Nope.
Merge with strategy octopus failed.
ours
在合并的时候,无论有多少个合并分支,当前分支就直接是最终的合并结果。无论其他人有多少修改,在此次合并之后,都将不存在(当然历史里面还有)。
你可能觉得这种丢失改动的合并策略没有什么用。但如果你准备重新在你的仓库中进行开发(程序员最喜欢的重构),那么当你的修改与旧分支合并时,采用此合并策略就非常有用,你新的重构代码将完全不会被旧分支的改动所影响。
注意 recursive 策略中也有一个 ours 参数,与这个不同的。
subtree
此策略使用的是修改后的递归三路合并算法。与 recursive 不同的是,此策略会将合并的两个分支的其中一个视为另一个的子树,就像 git subtree 中使用的子树一样。
参考资料
- Git merge strategy options & examples - Atlassian Git Tutorial
- diff - Any example to use git merge patience strategy? - Stack Overflow
- Git - merge-strategies Documentation
- When would you use the different git merge strategies? - Stack Overflow
-X patience
vs-X diff-algorithm=patience
withgit merge-recursive
- Stack Overflow- Git diff –patience not working - Stack Overflow
本文会经常更新,请阅读原文: https://blog.walterlv.com/post/git-merge-strategy.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 ([email protected]) 。