代码之家  ›  专栏  ›  技术社区  ›  Chaim Geretz

为什么git merge让我丢了一行

  •  2
  • Chaim Geretz  · 技术社区  · 9 年前

    我们最近遇到了一些git的合并问题,虽然它可能做了“正确的事情”(TM),但这并不是我所期望的。我已将一个问题简化为位于 https://github.com/geretz/merge-quirk

    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.
    

    在我的心智模型中,如果thislinemightUnsifer函数调用在冲突状态下同时存在于HEAD和origin/src中,那么它应该存在于两个冲突块中,如果它在冲突状态下不存在,那么它应该存在于两个冲突块之外。为什么它只出现在头部模块内?

    <<<<<<< 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);
        }
    }
    

    大师级/怪癖级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);
                    }
                }
        }
    }
    

        // 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 回复  |  直到 9 年前
        1
  •  3
  •   jthill    9 年前

    这里发生的事情是你混淆了语义和文本身份。Git认为移位和拆分的结果是完全不同的。你最终得到了一个冲突的块头和一个不冲突的块头,并且在冲突的块头中添加了一行,这(非常)令人困惑地类似于在不冲突的块头中删除的一行:即使它们在语义上是相同的,

                                if (thisLineMightDisapper(42)) {
    

                        if (thisLineMightDisapper(42))
                        {
    

    是 git如果表现得像这样通常是非常错误的。第二个在master和merge src中未被触及,在merge dst中被删除,通过引入虚假匹配从冲突的块中分离出来。因此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 如果你试错了,结果也不会好。就是这样:你需要一个稍微不同的锤头,一个稍微不同的合并选项。在更常见的情况下,忽略空间变化会导致选项卡损坏。