为什么git merge允许我丢失一条线

时间:2017-07-11 21:13:36

标签: git merge git-merge

我们最近经历了一些与git的合并问题,虽然它可能确实做了“正确的事情”(TM)但它并不是我所期望的。我已将问题减少到位于https://github.com/geretz/merge-quirk的小型公共github存储库。

quirk.c存在于master上。 merge-src和merge-dst都是从master上的相同版本分支出来的,这是最终合并的共同祖先。 merge-src添加了注释和一些代码。 merge-dst重新格式化代码并更改现有注释,但没有添加注释或代码。

git merge检测并声明冲突。

git clone https://github.com/geretz/merge-quirk.git
cd merge-quirk
git checkout merge-dst
git merge origin/merge-src
Auto-merging quirk.c
CONFLICT (content): Merge conflict in quirk.c
Automatic merge failed; fix conflicts and then commit the result.

然而,如果一个天真/信任的开发人员在冲突标记的quirk.c中选择origin / merge-src代码块,那么thisLineMightDisapper(sic)函数调用将会丢失。

在我的心理模型中,如果在冲突状态下HEAD和origin / src中存在thisLineMightDisapper函数调用,则它应存在于两个冲突块中,如果它不存在于冲突状态,则它应存在于冲突状态之外两个冲突块。为什么它只出现在HEAD块内?

<<<<<<< HEAD
    // a few comment lines - change 1
    // that existed - change 2
    // in the common ancestor - change 3
    // that get changed - change 4
    if(1)
    {
        if (f(1, 2))
        {
            if (thisLineMightDisapper(42))
=======
    // a few comment lines
    // that existed 
    // in the common ancestor
    // added this line in merge-src branch
    // that get changed
    if(1) {
            if (f(1, 2))
>>>>>>> origin/merge-src
            {
                t = time(0);
            }
        }
    }

    if (anotherFunction(1,2))
    {
        t = time(0)
        f(0);
    }
}

文件

主/ quirk.c

    // a few comment lines
    // that existed 
    // in the common ancestor
    // that get changed
    if(1) {
            if (f(1, 2))
            {
                if (thisLineMightDisapper(42)) {
                    t = time(0);
                }
            }
    }
}

合并-SRC / quirk.c

    // a few comment lines
    // that existed 
    // in the common ancestor
    // added this line in merge-src branch
    // that get changed
    if(1) {
            if (f(1, 2))
            {
                if (thisLineMightDisapper(42)) {
                    t = time(0);
                }
            }
    }

    if (anotherFunction(1,2))
    {
        t = time(0)
        f(0);
    }
}

合并-dst的/ quirk.c

    // a few comment lines - change 1
    // that existed - change 2
    // in the common ancestor - change 3
    // that get changed - change 4
    if(1)
    {
        if (f(1, 2))
        {
            if (thisLineMightDisapper(42))
            {
                t = time(0);
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

这里发生的事情是你将语义与文本身份混为一谈。 Git认为移位和分裂的结果是完全不同的线条。你最后会遇到一个冲突的大块头和一个没有冲突的大块头,并且在冲突的大块中添加了一条线(非常)容易混淆地类似于在非冲突中删除的线:即使它们在语义上相同,< / p>

                            if (thisLineMightDisapper(42)) {

                    if (thisLineMightDisapper(42))
                    {

不一样,git通常会非常错误,就像它们一样。第二个在master和merge-src中未被触及,并在merge-dst中被删除,与冲突的hunk分开,并通过引入虚假匹配来添加。所以git认为删除是没有冲突的并且自动充当它。

当这里,甚至git checkout -m --conflict diff3令人困惑的时候,你可以拉出与他人合并的看法与

$ sh -xc 'git diff ...MERGE_HEAD; git diff MERGE_HEAD...'
+ git diff ...MERGE_HEAD
diff --git a/quirk.c b/quirk.c
index c149623..79dc4a2 100644
--- a/quirk.c
+++ b/quirk.c
@@ -2,6 +2,7 @@
    // a few comment lines
    // that existed 
    // in the common ancestor
+   // added this line in merge-src branch
    // that get changed
    if(1) {
            if (f(1, 2))
@@ -11,4 +12,10 @@
                }
            }
    }
+
+   if (anotherFunction(1,2))
+   {
+       t = time(0)
+       f(0);
+   }
 }
+ git diff MERGE_HEAD...
diff --git a/quirk.c b/quirk.c
index c149623..0de7516 100644
--- a/quirk.c
+++ b/quirk.c
@@ -1,14 +1,16 @@

-   // a few comment lines
-   // that existed 
-   // in the common ancestor
-   // that get changed
-   if(1) {
-           if (f(1, 2))
+   // a few comment lines - change 1
+   // that existed - change 2
+   // in the common ancestor - change 3
+   // that get changed - change 4
+   if(1)
+   {
+       if (f(1, 2))
+       {
+           if (thisLineMightDisapper(42))
            {
-               if (thisLineMightDisapper(42)) {
-                   t = time(0);
-               }
+               t = time(0);
            }
+       }
    }
 }
+ git diff ...MERGE_HEAD

并且一些研究会告诉你git确定一个单独的大括号作为常见内容,导致它被治疗

-               if (thisLineMightDisapper(42)) {
-                   t = time(0);
-               }
+               t = time(0);

作为一个没有冲突的变化。

这对我有用的一件事是

git merge --abort
git merge -Xignore-space-change origin/merge-src

在识别更改边界时正确忽略重新格式化。与锤击任何东西一样,锤击源变化有时需要exactly the right tool,如果你尝试使用错误的一个,它就不会结束。所以它就在这里:你需要一个略微不同的锤子头,你的合并选择略有不同。在更常见的情况下,忽略空间变化是标签损坏的一个因素。