为什么git不合并没有冲突的相邻行?


25

我最近了解到,当在git中合并两个分支时,如果相邻两行发生变化,则git声明这是冲突。例如,如果文件test.txt具有以下内容:

Line 1: A
Line 2: B
Line 3: C
Line 4: D

在分支中,master我们将其更改为

Line 1: A
Line 2: B1
Line 3: C
Line 4: D

而在分支中,testing我们将其更改为

Line 1: A
Line 2: B
Line 3: C1
Line 4: D

然后尝试合并testingmaster,则git声明合并冲突。我天真的期望是合并不会发生冲突并产生以下结果:

Line 1: A
Line 2: B1
Line 3: C1
Line 4: D

我确信git不以这种方式合并是有充分的理由的。有人可以解释这个原因吗?


嘿,我上周也刚注意到。也许我们在做同样的教程。
2013年

5
IMO的合并能力实际上很差,IMO
James

@James您是否尝试过使用耐心算法?我发现我得到了更好的结果,特别是在处理块分割的地方(例如,抓取一个函数体而不是两个)。如果您不喜欢git,也可以使用自己的(例如,参见blog.wuwon.id.au/2010/09/…)。
威慑

1
根本原因是git尝试自行进行合并,而不是将其分解为专用工具。完全不是Unix哲学。对于源文件,您实际上可以使用语言语法可靠地确定差异。
MSalters 2013年

唯一常见的上下文是A和D,那么A / C1 / B1 / D为什么不是正确的合并?
Izkata

Answers:


13

假设这段代码

x=0
x+=1 if foo
x+=1 if bar
return x

已在一个分支中更改为

x=0
x+=1 if foo && xyzzy
x+=1 if bar
return x

在另一个分支

x=0
x+=1 if foo
x+=1 if bar && xyzzy
return x

那么我不想git将其合并到这个

x=0
x+=1 if foo && xyzzy
x+=1 if bar && xyzzy
return x

不用惊动我

为了避免引起此类问题,git通常拒绝自动合并涉及附近行的更改。它使您有机会验证程序逻辑是否被破坏。

这个例子很简单,但是当合并巨大的分支时,类似的“逻辑”冲突的风险就更大了。有时我什至希望上下文比现在更大。


5
不过,这与之无关,只是在这两个之间添加了一条不变的线,然后突然git合并了它们而没有问题。
Darkhogg

是的,我没有得到这个答案。您可以使用相同的逻辑来避免所有自动合并。
Mehrdad

在安全性和实用性之间必须划清界限。自动合并有破坏程序流程的风险-正如我试图在答案中所示-,但是如果git只拒绝合并任何内容,它将变得无用。git的创建者刚刚决定了某种环境,它将抓住大多数“危险”案例。添加一些未更改的行(如Darkhogg所发现的)使git误认为git合并可能是安全的。
Arsen7

11

这是仅git的行为吗?

与同事讨论之后,我尝试了一下,SVN可以毫无问题地处理它:您修改了两行。

在这里,针对集市,darcs,git和mercurial测试了多个VCS的合并功能:https : //github.com/mndrix/merge-this

似乎只有darcs成功地合并了“相邻行”的情况。

将相邻的更改应用于文件不是一个难题。我真的认为这种行为是故意选择的。

为什么有人会决定修改相邻线会产生冲突?

我认为这是要强迫您看一下

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

主模块上的Modif数字1:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif 2,从分支合并:

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

合并后,您不需要:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

将此行为视为功能

您可以将git合并行为变成一种优势。当需要保持两行一致而又无法检测到它时(在编译时,测试初期或其他时候),可以尝试将它们合并。

重写此...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    // Need to do something else
    do_something_else(r);

...对此:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    do_something_else(r); // Need to do something else

因此,当您合并Modif 1 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i)/2; // we need only the half
    do_something_else(r); // Need to do something else

...使用Modif 2 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    if(r < 0) // do_stuff can return an error
        handle_error(r);
    do_something_else(r/2); // Need to do something else

...,git会产生冲突,您将迫使您查看它。


2
我将继续说,我认为您的回答是完全合理的,但是取决于代码与源代码控件之间是否存在复杂的,由实现定义的相互作用,这是thedailywtf.com的快速跟踪。无论如何,盲目地合并没有语言解析器的代码始终是最大的努力,而且我有几个实例,其中git有助于自动合并它不应该包含的内容,并生成甚至无法编译的代码。
武格

5

我主要是在猜测,但是我认为这与将第2行用作第3行更改的上下文有关。

Git不能只说“与C的行成为与C1的行”,因为可能还有另一行与“ C”,所以它说“与C的行,即在文件开始之后,即与A,并且与B的线现在是C1”

如果“带有B的行”不再存在,那么某些上下文将丢失,并且git只能大致判断出新行的去向。


5
它也很可能C依赖于B,因此即使git“知道如何做”,天真的合并也可能很麻烦
Lucina 2013年

相信我Git只“认为知道”。Git是错误观念的坟墓场,试图弄清楚!
user3833732

2

这里的其他答案都是正确的,但对我而言,这似乎始终是不必要的限制。

正如其他人所说,在这些情况下,您绝对不希望Git在没有警告的情况下合并这些行。

但是在被警告后,我仍然希望该选项可以自动执行。因此,我编写了一个自定义git merge驱动程序,该驱动程序可以交互方式合并相邻(或单个)行上的冲突:

在此处输入图片说明

在我管理一个项目时,人们经常在同一个文件上工作并重构大量代码,这为我节省了大量时间。

该脚本可在GPLv3 +许可下在GitHub上使用。也许您会发现它很有用:

https://github.com/paulaltin/git-subline-merge


4
有人介意解释为什么这被否决吗?我在这里很新,所以如果我做错了什么,我想知道那是什么,以便将来避免。我意识到我的帖子并没有完全回答所提出的问题,但是它仍然很重要,我想大多数来这里的人不仅想知道git为什么这样做,而且想知道他们可以做什么(就像我在我做的那样)首先通过Google搜索解决了这个问题)。
deltacrux

我还没有尝试过,但是我来寻找一种自动化的方法,很高兴在这里找到它。谢谢:)你真棒。
Mehrdad

1
没问题!我希望您觉得它有用,如果遇到问题或有任何改进建议,请随时在Github上留下反馈。谢谢!
deltacrux
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.