如何';git合并&x27;详细工作?

如何';git合并&x27;详细工作?,git,merge,conflict,Git,Merge,Conflict,我想知道“git merge”背后的确切算法(或接近该算法)。至少这些子问题的答案将很有帮助: git如何检测特定非冲突更改的上下文 git是如何发现这些确切的行中存在冲突的 git会自动合并哪些内容 当没有合并分支的公共基础时,git如何执行 当有多个合并分支的公共基础时,git如何执行 当我同时合并多个分支时会发生什么 合并策略之间有什么区别 但是对整个算法的描述会更好 git如何检测特定非冲突更改的上下文? git是如何发现这些确切的行中存在冲突的 如果同一行在合并的两边都发生了变化,

我想知道“git merge”背后的确切算法(或接近该算法)。至少这些子问题的答案将很有帮助:

  • git如何检测特定非冲突更改的上下文
  • git是如何发现这些确切的行中存在冲突的
  • git会自动合并哪些内容
  • 当没有合并分支的公共基础时,git如何执行
  • 当有多个合并分支的公共基础时,git如何执行
  • 当我同时合并多个分支时会发生什么
  • 合并策略之间有什么区别
但是对整个算法的描述会更好

git如何检测特定非冲突更改的上下文?
git是如何发现这些确切的行中存在冲突的

如果同一行在合并的两边都发生了变化,那就是冲突;如果没有,则接受来自一侧(如果存在)的更改

git会自动合并哪些内容

不冲突的更改(见上文)

当有多个合并分支的公共基础时,git如何执行

根据a的定义,只有一个(最新的共同祖先)

当我同时合并多个分支时会发生什么

这取决于合并策略(只有
八达通
我们的
/
他们的
策略支持合并两个以上的分支)

合并策略之间有什么区别


这在中有解释。

您最好寻找一个3路合并算法的描述。高级描述如下所示:

  • 找到一个合适的合并基
    B
    ——文件的一个版本,它是两个新版本(
    X
    Y
    )的祖先,通常是最新的此类基(尽管在某些情况下它必须进一步返回,这是
    git
    s default
    recursive
    合并的一个特性)
  • 使用
    B
    执行
    X
    的差异,使用
    B
    执行
    Y
    的差异
  • 浏览两个差异中确定的变更块。如果双方在同一地点提出相同的变更,则接受其中一方;如果一个引入了一个变更,而另一个不涉及该区域,则在最终版本中引入该变更;如果两者都在一个点中引入了更改,但它们不匹配,则标记要手动解决的冲突

  • 完整的算法更详细地处理了这个问题,甚至有一些文档(例如,连同
    git help XXX
    页面,其中XXX是
    merge base
    merge file
    merge
    merge one file
    ,可能还有一些其他页面)。如果这还不够深入,总会有源代码…

    我也很感兴趣。我不知道答案,但是

    人们总是发现,一个有效的复杂系统是从一个有效的简单系统演化而来的

    我认为git的合并非常复杂,很难理解,但一种方法是从它的前身开始,并关注您关注的核心。也就是说,给定两个没有共同祖先的文件,git merge如何解决如何合并它们,以及冲突在哪里

    让我们试着找到一些先兆。从
    git帮助合并文件

    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).
    
    来自维基百科:->->->

    最后一个链接是一篇详细描述
    diff3
    算法的论文的pdf。这是一个例子。它只有12页长,算法也只有几页,但完全是数学处理。这看起来可能有点过于正式,但如果您想了解git的合并,您需要先了解更简单的版本。我还没有检查过,但是使用像
    diff3
    这样的名称,您可能还需要了解diff(它使用一种算法)。然而,如果你有一个谷歌,可能会有一个更直观的解释
    diff3


    现在,我刚刚做了一个实验,比较了
    diff3
    git合并文件
    。它们采用相同的三个输入文件version1 oldversion2,并以相同的方式标记冲突,与
    >version2
    diff3
    也有
    | oldversion
    ),显示它们的共同遗产

    我对oldversion使用了一个空文件,对version1和version2使用了几乎相同的文件,只在version2中添加了一行

    结果:
    git merge file
    将单个更改行识别为冲突;但是
    diff3
    将整个两个文件视为冲突。因此,尽管diff3很复杂,git的合并甚至更复杂,即使对于这种最简单的情况也是如此

    以下是实际结果(我在文本中使用了@twalberg的答案)。注意所需的选项(参见相应的手册页)

    $git合并文件-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...
    
    <<<<<<< 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
    

    如果你真的对此感兴趣,那就有点像兔子洞。对我来说,它似乎和正则表达式、最长的diff公共子序列算法、上下文无关语法或关系代数一样深刻。如果你想弄清真相,我想你可以,但这需要一些坚定的研究。

    这是最初的实现


    基本上,您为两个提交创建一个公共祖先列表,然后递归地合并它们,或者快速转发它们,或者创建虚拟提交,用于对文件进行三方合并

    当存在多个合并分支的公共基础时,git的表现如何?

    这篇文章很有帮助:(这里是)

    递归使用diff3递归地生成一个虚拟分支,该分支将用作祖先

    例如:

    然后:

    有两个最好的共同祖先(c
    (A)----(B)----(C)-----(F)
            |      |       |
            |      |   +---+
            |      |   |
            |      +-------+
            |          |   |
            |      +---+   |
            |      |       |
            +-----(D)-----(E)
    
    git checkout E
    git merge F
    
    (A)----(B)----(C)--------(F)
            |      |          |
            |      |      +---+
            |      |      |
            |      +----------+
            |      |      |   |
            |      +--(V) |   |
            |          |  |   |
            |      +---+  |   |
            |      |      |   |
            |      +------+   |
            |      |          |
            +-----(D)--------(E)
    
       +--B
       |
    A--+--C
       |
       +--D
    
    git checkout B
    git merge -Xoctopus C D
    
       +--B--+
       |     |
    A--+--C--+--E
       |     |
       +--D--+
    
    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
    <<<<<<< ours
    b
    =======
    >>>>>>> theirs
    c
    
    git checkout --conflict=diff3 -- .
    
    <<<<<<< ours
    a
    b
    c
    ||||||| base
    =======
    a
    c
    >>>>>>> theirs