- Git如何检测特定非冲突更改的上下文? - Git如何发现这些确切行中的冲突? - Git自动合并哪些内容? - 当合并分支没有共同基础时,Git如何执行? - 当合并分支有多个共同基础时,Git如何执行? - 当我一次合并多个分支时会发生什么? - 合并策略之间有什么区别?
但是,整个算法的描述会更好。
B
- 该文件版本是新版本(X
和Y
)的祖先,并且通常是最近的这种基础(虽然有时需要进一步回溯,这是git
默认递归合并的一些特性之一)
2. 对X
与B
及Y
与B
进行差异比较
3. 遍历在两个差异中识别出来的更新块。如果两侧在同一位置引入了相同的更改,则接受其中任意一个;如果一侧引入了更改而另一侧保持原样,则在最终结果中引入更改;如果两侧在同一位置都引入了更改,但是它们不匹配,则将其标记为需要手动解决的冲突。git help XXX
页面,其中XXX可以是merge-base
、merge-file
、merge
、merge-one-file
,以及可能还有其他一些页面。如果这还不够深入,总是可以查看源代码...当合并分支时,如果有多个共同的基础,Git会如何执行?
这篇文章非常有帮助:http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html(这里是第二部分)。
递归使用diff3递归地生成一个虚拟分支,将被用作祖先。
例如:
(A)----(B)----(C)-----(F)
| | |
| | +---+
| | |
| +-------+
| | |
| +---+ |
| | |
+-----(D)-----(E)
那么:
git checkout E
git merge F
存在两个最佳共同祖先(不是任何其他祖先的祖先),分别为C
和D
。Git将它们合并成一个新的虚拟分支V
,然后将V
用作基础。
(A)----(B)----(C)--------(F)
| | |
| | +---+
| | |
| +----------+
| | | |
| +--(V) | |
| | | |
| +---+ | |
| | | |
| +------+ |
| | |
+-----(D)--------(E)
如果有更多的共同祖先,Git将继续使用它来将V
与下一个祖先合并。
文章称,如果在生成虚拟分支时出现合并冲突,Git会将冲突标记留在原地并继续执行。
当我同时合并多个分支时会发生什么?
如@Nevik Rehnel所解释的那样,这取决于策略,该策略在man git-merge
MERGE STRATEGIES
部分中有很好的说明。
只有octopus
和ours
/theirs
支持一次性合并多个分支,例如,recursive
不支持。
octopus
将拒绝合并如果存在冲突,则ours
是一个平凡的合并,因此不可能存在冲突。
这些命令将生成一个新的提交记录,其中将有超过2个父节点。
我在Git 1.8.5上执行了一个无冲突的merge -X octopus
,以查看情况如何。
初始状态:
+--B
|
A--+--C
|
+--D
操作:
git checkout B
git merge -Xoctopus C D
新状态:
+--B--+
| |
A--+--C--+--E
| |
+--D--+
不出所料,E
有三个父节点。
待办事项:octopus 如何对单个文件进行修改?递归两两三路合并?
当合并分支时没有共同基础时,Git 如何执行?
@Torek 提到自 2.9 版本以来,合并失败需要使用 --allow-unrelated-histories
参数。
我在 Git 1.8.5 上进行了实际测试:
git init
printf 'a\nc\n' > a
git add .
git commit -m a
git checkout --orphan b
printf 'a\nb\nc\n' > a
git add .
git commit -m b
git merge master
a
包含:
a
<<<<<<< ours
b
=======
>>>>>>> theirs
c
那么:
git checkout --conflict=diff3 -- .
a
包含:
<<<<<<< ours
a
b
c
||||||| base
=======
a
c
>>>>>>> theirs
解释:
a\nc\n
进行三方合并来解决,作为单行添加e379fdf34fee96cd205be83ff4e71699bdc32b18
)的变化已经过时了。自从Git版本2.9起,如果没有合并基础,Git现在会拒绝合并,除非你添加—allow-unrelated-histories
。 - torek--allow-unrelated-histories
。 - Jeremy Listours
合并策略,但没有 theirs
合并策略。recursive
+theirs
策略只能解决两个分支。https://git-scm.com/docs/git-merge#_merge_strategies - nekketsuuu我也很感兴趣。我不知道答案,但是...
一个有效的复杂系统通常都是从一个有效的简单系统演变而来的。
我认为Git的合并功能非常复杂,很难理解 - 但是一种方法是从它的前身入手,并关注你关心的核心问题。也就是说,如果有两个没有共同祖先的文件,Git如何合并它们,并确定冲突在哪里?
让我们尝试找到一些前身。从 git help merge-file
中得知:
git merge-file is designed to be a minimal clone of RCS merge; that is,
it implements all of RCS merge's functionality which is needed by
git(1).
来自维基百科: http://en.wikipedia.org/wiki/Git_%28software%29 -> http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge -> http://en.wikipedia.org/wiki/Diff3 -> http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf
最后一个链接是一篇详细描述diff3
算法的论文pdf。这里有一个谷歌pdf阅读器版本。它只有12页,算法只有几页 - 但是全面的数学处理。这可能看起来有点过于正式,但如果你想理解git的合并,你需要先理解简单版本。我还没有检查过,但像diff3
这样的名称,你可能也需要了解diff(它使用最长公共子序列算法)。然而,如果你有谷歌,可能会有更直观的diff3
解释...
diff3
和git merge-file
。它们使用相同的三个输入文件version1 oldversion version2,并以相同的方式标记冲突,使用<<<<<<< version1
,=======
,>>>>>>> version2
(diff3
还有||||||| oldversion
),显示它们的共同遗产。git merge-file
将单个更改的行视为冲突;但diff3
将整个两个文件视为冲突。因此,尽管diff3
很复杂,但git的合并甚至对于这种最简单的情况也更加复杂。$ git merge-file -p fun1.txt fun0.txt fun2.txt
You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:
Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B. Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.
<<<<<<< fun1.txt
=======
THIS IS A BIT DIFFERENT
>>>>>>> fun2.txt
The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...
$ diff3 -m fun1.txt fun0.txt fun2.txt
<<<<<<< fun1.txt
You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:
Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B. Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.
The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...
||||||| fun0.txt
=======
You might be best off looking for a description of a 3-way merge algorithm. A
high-level description would go something like this:
Find a suitable merge base B - a version of the file that is an ancestor of
both of the new versions (X and Y), and usually the most recent such base
(although there are cases where it will have to go back further, which is one
of the features of gits default recursive merge) Perform diffs of X with B and
Y with B. Walk through the change blocks identified in the two diffs. If both
sides introduce the same change in the same spot, accept either one; if one
introduces a change and the other leaves that region alone, introduce the
change in the final; if both introduce changes in a spot, but they don't match,
mark a conflict to be resolved manually.
THIS IS A BIT DIFFERENT
The full algorithm deals with this in a lot more detail, and even has some
documentation (/usr/share/doc/git-doc/technical/trivial-merge.txt for one,
along with the git help XXX pages, where XXX is one of merge-base, merge-file,
merge, merge-one-file and possibly a few others). If that's not deep enough,
there's always source code...
>>>>>>> fun2.txt
git如何检测单独的非冲突更改所在的上下文?
git如何发现这些确切行存在冲突?
如果合并的两侧都对同一行进行了修改,就会产生冲突;如果没有修改,则会接受其中一个侧面(如果有的话)的更改。
哪些内容可以被git自动合并?
不冲突的更改(见上文)。
当有多个公共基础用于合并分支时,git会如何表现?
根据Git merge-base的定义,始终只会有一个最新的共同祖先。
当我同时合并多个分支时会发生什么?
这取决于合并策略(只有octopus
和ours
/theirs
策略支持合并两个以上的分支)。
合并策略有什么区别?
这在git merge
手册中有解释。
git-merge-recursive
存在? - Edward Thomsongit-merge-recursive
我不知道应该是什么(没有man页面,谷歌也没有相关结果)。关于这个问题的更多信息可以在git merge
和git merge-base
的man页面中找到。 - Nevik Rehnelgit-merge
手册和您指出的git-merge-base
手册讨论了多个共同祖先和递归合并。我认为如果没有讨论这些内容,您的答案是不完整的。 - Edward Thomsonhttp://git.kaarsemaker.net/git/blob/857f26d2f41e16170e48076758d974820af685ff/git-merge-recursive.py
基本上,您需要为两个提交创建一个共同祖先列表,然后递归地合并它们,可以快进它们,也可以创建虚拟提交以用作文件三方合并的基础。