なぜgitは競合せずに隣接する行をマージしないのですか?


25

私は最近、gitで2つのブランチをマージするときに、2つの隣接する行に変更がある場合、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がこの方法でマージされないのには十分な理由があると確信しています。誰かがこの理由を説明できますか?


ちょっと私は先週も気づきました。たぶん、私たちは同じチュートリアルをやっていたでしょう。
確実に

5
gitのマージの能力がIMO、実際には非常に貧弱です
ジェームズ・

@ジェームズは忍耐アルゴリズムを使ってみましたか?特にハンクが分割されている場所を扱う場合(たとえば、2つではなく1つの関数本体を取得する場合)、より良い結果が得られます。gitが気に入らない場合は、独自のものを使用することもできます(例についてはblog.wuwon.id.au/2010/09/…を参照してください)。
-deterb

1
根本的な原因は、gitがマージを専用のツールに分解する代わりに、マージを実行しようとすることです。完全にUnixの哲学ではありません。ソースファイルの場合、実際に言語文法を使用して、差分を確実に判断できます。
MSalters

唯一の一般的なコンテキストはAとDですが、なぜA / C1 / B1 / Dが正しいマージではないのですか?
イズカタ

回答:


13

このコードのスニペットと仮定すると

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

1つのブランチでこれに変更されました

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
しかし、それはそれとは何の関係もありませんが、単純にこれらの2つの間に不変の行を追加し、突然問題なくそれらをgitマージします。
ダークホッグ

ええ、私はこの答えを得られません。すべての自動マージを回避するために同じロジックを使用できます。
Mehrdad

セキュリティと有用性の間に線を引く必要があります。自動マージは、プログラムフローを破壊するリスクを負います(答えで示したように)が、gitが単にマージを拒否すると、役に立たなくなります。gitの作成者は、「危険な」ケースのほとんどをキャッチするコンテキストを決定しました。変更されていない行を追加すると(Darkhoggが見つけたように)、gitを欺いて、マージを実行しても安全であると信じ込ませます。
Arsen7

11

これはgitのみの動作ですか?

同僚と話し合った後、試してみたところ、SVNは問題なくそれ処理しました。2行を修正しました。

いくつかのVCSのマージ機能は、bazaar、darcs、git、mercurialについてここでテストされています: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マージの動作を有利にすることができます。2行の一貫性を保つ必要があるが、それを検出できない場合(コンパイル時、テストの早い段階など)、それらを結合しようとすることができます。

これを書き換えます...:

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

私はほとんど推測していますが、3行目の変更のコンテキストとして2行目が使用されていることと関係があると思います。

Gitは、「Cの行はC1の行になった」とは言えません。「C」の行がある可能性があるため、「Cの行、ファイルの開始直後、 A、およびBのある行は現在C1です

「Bの行」がもう存在しない場合、コンテキストの一部が失われ、gitは新しい行がどこに行かなければならないかを大まかにしか知ることができません。


5
そのまた、非常に可能性の高いCがBに依存するため、ナイーブマージはgitのは、それを行うには、「方法を知っている」場合でも、面倒であってもよいこと
ルキナ

Gitが「知っていると思う」だけだと信じてください。Gitは間違った概念の墓地であり、まっすぐにしようとしています!
user3833732

2

ここでの他の答えはすべてポイントにありますが、私にとってこれは常に不必要な制限のように思えました。

他の人が言ったように、これらの場合、Gitが警告なしに行をマージすることは絶対に望ましくありません。

しかし、警告を受けた後、自動的にそれを実行するオプションが必要でした。そこで、隣接する(または個々の)行の競合を対話的にマージできるカスタムgit mergeドライバーを作成しました。

ここに画像の説明を入力してください

人々が頻繁に同じファイルで作業し、多くのコードをリファクタリングするプロジェクトを管理しているので、時間を大幅に節約できます。

このスクリプトは、GitHubでGPLv3 +ライセンスの下で利用できます。たぶんあなたはそれが便利だと思うでしょう:

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


4
これがなぜ投票されたのか、誰かが説明してもいいですか?私はここでかなり新しいので、何か間違ったことをした場合、将来それを避けることができるように、それが何であったかを知りたいです。私の投稿は尋ねられた質問に正確に答えていないことを理解していますが、それはまだ関連性があり、ここに来るほとんどの人は、gitがこれを行う理由だけでなく、それについて何ができるかを知りたいと思うと思います(私がしたときにしたように)最初にGoogle検索からこの質問に到達しました)。
deltacrux

まだ試していませんが、これを自動化する方法を探していて、ここで見つけて嬉しかったです。ありがとう:)あなたは素晴らしいです。
Mehrdad

1
問題ない!問題が発生した場合や改善のためのご提案がありましたら、Githubにご意見をお寄せください。ありがとう!
deltacrux
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.