Warum verschmilzt Git benachbarte Zeilen nicht konfliktfrei?

25

Ich habe kürzlich erfahren, dass beim Zusammenführen von zwei Zweigen in git ein Konflikt vorliegt, wenn Änderungen an zwei benachbarten Zeilen vorgenommen werden. Wenn die Datei beispielsweise folgenden test.txtInhalt hat:

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

und im Zweig masterändern wir dies auf

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

im Zweig testingändern wir dies auf

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

und dann versuchen , fusionieren testingin mastergit einen Merge - Konflikt erklärt. Meine naive Erwartung war, dass die Fusion konfliktfrei ablaufen würde und dies ergeben würde:

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

Ich bin mir sicher, dass es einen guten Grund gibt, warum Git nicht auf diese Weise verschmilzt. Kann jemand diesen Grund erklären?

rlandster
quelle
Hey, ich habe das letzte Woche auch bemerkt. Vielleicht haben wir das gleiche Tutorial gemacht.
Detly
5
Die Fusionsfähigkeiten von Git sind eigentlich ziemlich schlecht, IMO
James
@ James haben Sie versucht, den Geduld-Algorithmus zu verwenden? Ich finde, dass ich damit bessere Ergebnisse erhalte, insbesondere, wenn es darum geht, wo Hunks aufgeteilt werden (z. B. einen Funktionskörper anstelle von zwei zu greifen). Wenn du gits nicht magst , kannst du auch dein eigenes verwenden (siehe blog.wuwon.id.au/2010/09/… für ein Beispiel).
Deterb
1
Die Hauptursache ist, dass git versucht, die Zusammenführung selbst durchzuführen, anstatt sie in ein spezielles Tool zu zerlegen. Ganz und gar nicht die Unix-Philosophie. Für Quelldateien können Sie die Sprachgrammatik tatsächlich verwenden, um Unterschiede zuverlässig zu bestimmen.
MSalters
Der einzige gemeinsame Kontext ist A und D. Warum ist A / C1 / B1 / D also nicht die richtige Zusammenführung?
Izkata

Antworten:

13

Angenommen, dieser Codeausschnitt

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

wurde in einem Zweig in diesen geändert

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

und in einem anderen Zweig in diese

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

dann würde ich nicht wollen, dass git es damit verschmilzt

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

ohne mich zu alarmieren.

Um solche Probleme zu vermeiden, weigert sich git normalerweise, Änderungen, die benachbarte Linien berühren, automatisch zusammenzuführen. Hier können Sie überprüfen, ob die Programmlogik fehlerhaft ist oder nicht.

Dieses Beispiel ist trivial, aber beim Zusammenführen großer Zweige ist das Risiko ähnlicher "logischer" Konflikte viel größer. Manchmal würde ich es sogar lieben, wenn der Kontext noch größer wäre, als er derzeit ist.

Arsen7
quelle
5
Das hat aber nichts damit zu tun, füge einfach eine unveränderliche Linie zwischen diesen beiden hinzu und füge sie plötzlich ohne Probleme zusammen.
Darkhogg
Ja, ich verstehe diese Antwort nicht. Sie können dieselbe Logik verwenden, um alle automatischen Zusammenführungen zu vermeiden.
Mehrdad
Es muss eine Grenze zwischen Sicherheit und Nützlichkeit gezogen werden. Automatische Zusammenführungen bergen das Risiko, den Programmfluss zu beschädigen - wie ich in der Antwort gezeigt habe -, aber wenn git sich weigern würde, etwas zusammenzuführen, würde es unbrauchbar werden. Die Schöpfer von Git haben sich gerade für einen Kontext entschieden, der die meisten "gefährlichen" Fälle erfassen soll. Das Hinzufügen einer unveränderten Zeile (wie Darkhogg herausfand) täuscht darüber nach, dass das Zusammenführen sicher sein könnte.
Arsen7
11

Ist das nur Schwachsinn?

Nach einem Gespräch mit einem Kollegen habe ich es gerade versucht, und SVN behandelt es problemlos: Sie erhalten die 2 Zeilen geändert.

Die Zusammenführungsfähigkeiten mehrerer VCS werden hier für Basar, Darcs, Git und Mercurial getestet : https://github.com/mndrix/merge-this

Es scheint, dass nur Darcs den Fall "benachbarter Linien" erfolgreich zusammenführen.

Das Anwenden benachbarter Änderungen auf Dateien ist kein schwieriges Problem. Ich denke wirklich, dass dieses Verhalten absichtlich gewählt wurde.

Warum sollte jemand entscheiden, dass das Ändern benachbarter Zeilen zu einem Konflikt führt?

Ich würde denken, das soll dich zwingen, es dir anzusehen .

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

Modif Nummer 1 am Master:

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

Modif Nummer 2, aus einem Zweig zusammengeführt:

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

Nach dem Zusammenführen möchten Sie das nicht:

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

Dieses Verhalten als Feature betrachten

Sie können das Zusammenführungsverhalten von Git zu einem Vorteil machen. Wenn Sie zwei Zeilen konsistent halten müssen, dies aber nicht erkennen können (zum Zeitpunkt der Kompilierung, zu Beginn Ihrer Tests oder ansonsten), können Sie versuchen, sie zusammenzufügen.

Schreiben Sie dies neu ...:

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

... dazu:

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

Wenn Sie also Modif 1 zusammenführen ...:

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

... mit 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 erzeugt einen Konflikt und du wirst dich zwingen, ihn anzuschauen.

ofaurax
quelle
2
Ich werde nur weitermachen und sagen, dass ich Ihre Antwort für absolut vernünftig halte, aber abhängig von der komplizierten, implementierungsdefinierten Wechselwirkung zwischen Ihrem Code und Ihrer Quellcodeverwaltung für die Überprüfung der Integrität ist dies ein schneller Weg zu thedailywtf.com. Das blinde Zusammenführen von Code ohne einen Sprachparser ist sowieso IMMER das Beste, und ich hatte ein paar Fälle, in denen git etwas, das es nicht haben sollte, automatisch erstellt und Code erzeugt hat, der nicht einmal kompiliert werden konnte.
Wug
5

Ich rate meistens, aber ich denke, es hat damit zu tun, dass Zeile 2 als Kontext für die Änderung von Zeile 3 verwendet wird.

Git kann nicht einfach sagen, dass "Die Zeile mit C eine Zeile mit C1 wurde", weil es eine andere Zeile mit "C" geben könnte, also "Die Zeile mit C, das ist direkt nach dem Dateianfang die Zeile mit A und die Linie mit B ist jetzt C1 "

Wenn "die Zeile mit B" nicht mehr vorhanden ist, geht ein Teil des Kontexts verloren und git kann nur ungefähr sagen, wohin die neue Zeile gehen muss.

Mike Gossmann
quelle
5
Es ist auch sehr wahrscheinlich, dass C von B abhängt, so dass eine naive Verschmelzung problematisch sein kann, selbst wenn git weiß, wie es geht
Lucina
Glauben Sie mir, Git nur "denkt, es weiß". Git ist ein Friedhof von falschen Konzepten, die versuchen, sich begradigen zu lassen!
User3833732
2

Die anderen Antworten hier sind alle zutreffend, aber für mich schien dies immer eine unnötige Einschränkung zu sein.

Wie andere gesagt haben, möchten Sie in diesen Fällen definitiv nicht, dass Git die Zeilen ohne Vorwarnung zusammenfügt.

Aber ich wollte immer noch die Option, dies automatisch zu tun, nachdem ich gewarnt wurde. Deshalb habe ich einen benutzerdefinierten Git-Merge-Treiber geschrieben, mit dem Konflikte auf benachbarten (oder einzelnen) Zeilen interaktiv zusammengeführt werden können:

Bildbeschreibung hier eingeben

Das spart mir sehr viel Zeit, da ich ein Projekt verwalte, bei dem häufig an denselben Dateien gearbeitet und viel Code überarbeitet wird.

Das Skript ist auf GitHub unter einer GPLv3 + -Lizenz verfügbar. Vielleicht finden Sie es nützlich:

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

Deltacrux
quelle
4
Würde es jemandem etwas ausmachen zu erklären, warum dies abgelehnt wurde? Ich bin ziemlich neu hier. Wenn ich also etwas falsch gemacht habe, würde ich gerne wissen, was es war, damit ich es in Zukunft vermeiden kann. Mir ist klar, dass mein Beitrag die gestellte Frage nicht genau beantwortet, aber immer noch relevant ist, und ich denke, die meisten Leute, die hierher kommen, möchten nicht nur wissen, warum git dies tut, sondern auch, was sie dagegen tun können (wie ich es getan habe, als ich Diese Frage erreichte ich zuerst über eine Google-Suche.
Deltacrux
Ich habe es noch nicht ausprobiert, aber ich habe nach einer Möglichkeit gesucht, dies zu automatisieren, und war froh, sie hier zu finden. Danke :) du bist großartig.
Mehrdad
1
Kein Problem! Ich hoffe, Sie finden es nützlich und freuen sich über Ihr Feedback zu Github, wenn Sie auf Probleme stoßen oder Verbesserungsvorschläge haben. Vielen Dank!
Deltacrux