Wie vergleicht Git zwei Dateien beim Zusammenführen?

Lesezeit: 10 Minuten

Benutzer-Avatar
erncnerky

Wie vergleicht Git zwei Dateien? Welche Algorithmen werden verwendet, um zwei Dateien zu vergleichen? Wird beim Zusammenführen Zeile für Zeile verglichen?

Ich kann nicht sicher sein, ob der Vergleich zweier Dateien beim Zusammenführen zu einem Konflikt führt oder nicht.

  • Was meinst du mit “Konflikt produzieren”? Der Vergleich zweier Dateien mit Git kann nicht schaden.

    – mkrieger1

    4. Juli 2019 um 13:52 Uhr


  • Mögliches Duplikat von Git-Merge-Interna

    – promov

    4. Juli 2019 um 15:06 Uhr

  • stackoverflow.com/search?q=%5Bgit-merge%5D+wie+funktioniert+zusammenführen+

    – promov

    4. Juli 2019 um 15:06 Uhr

  • stackoverflow.com/a/44724154/7976758

    – promov

    4. Juli 2019 um 15:07 Uhr

  • Das müssen Sie nicht wissen: Wenn es einen Merge-Konflikt gibt, wird git Ihnen sagen, dass es einen Merge-Konflikt gibt, und git status wird dir zeigen wo. Wenn Sie die Befehlszeile verwenden – wenn Sie eine IDE oder einen vernünftigen Code-Editor mit Git-Integration verwenden, werden die Dinge normalerweise noch schöner. Protip: Lesen Sie ein Tutorial zur Verwendung von Git und lernen Sie das Tool ein wenig kennen. Es wird sicher von allen verwendet, aber es ist immer noch ein Werkzeug, das Sie lesen sollten, bevor Sie Fragen stellen, die auf Annahmen basieren, die hätten vermieden werden können

    – Mike „Pomax“ Kamermans

    4. Juli 2019 um 15:19 Uhr

Der Schlüssel zum Verständnis git merge ist, dass Git nicht vergleichbar ist zwei Dinge. Git vergleicht drei Dinge.

Git kann nicht alle drei direkt vergleichen. Es muss sie beide gleichzeitig vergleichen. Zwei der Dinge sind die beiden Branch-Tip-Versionen der Dateien (oder Branch-Tip-Commits; ich werde gleich mehr darüber sprechen), aber Git vergleicht diese nicht zueinander. Hier kommt die dritte ins Spiel: Die dritte Datei ist die Basis zusammenführen Version der Datei.

Denken Sie daran, dass das Ziel einer Zusammenführung darin besteht Änderungen kombinieren. Aber Git speichert keine Änderungen. Git-Stores Schnappschüsse. Jeder Commit speichert jede Datei vollständig und intakt: Bei einem Commit erhält Git das Ganze README.mddas Ganze main.pywas auch immer andere Dateien in diesem bestimmten Commit sind, das ist die Version im Commit.

Um Änderungen von Snapshots zu erhalten, benötigen wir zwei Schnappschüsse: der alte und der neue. Dann spielen wir ein Spiel Erkenne den Unterschied. Für Git ist das git diff: Sie geben ihm die Hash-ID des alten Commit und die Hash-ID des neuen Commit, und es macht einen Unterschied für jede Datei, die zwischen den beiden geändert wird. Die Ausgabe von git diff ist eine Reihe von Anweisungen: Löschen Sie diese Zeilen, fügen Sie diese anderen Zeilen hinzu. Wenn Sie den ursprünglichen Snapshot erstellen und die Anweisungen anwenden, erhalten Sie den neuen Snapshot.

Wann waren verschmelzenaber wir wollen die Arbeit von (sagen wir) Alice übernehmen, und kombinieren es mit der Arbeit von Bob. Was Git also tut, ist:

  • Finden Sie das Beste geteilt Commit, mit dem sowohl Alice als auch Bob begonnen haben.
  • Vergleich die geteilt commit’s Dateien zu Alice’s Dateien. Das ist was Alice geändert hat.
  • Vergleich die geteilt Commit-Dateien zu Bobs Dateien. Das ist was Bob geändert hat.

Wir nennen das gemeinsame Commit – dasjenige, mit dem sowohl Alice als auch Bob begonnen haben – das Basis zusammenführen. Das ist die dritte Eingabe für eine Zusammenführung. Git findet diesen Merge-Basis-Commit automatisch anhand des Verlaufs – der Commits – in Ihrem Repository. Das bedeutet, dass Sie beide Alices haben müssen und Bobs Commits und alle Commits, die zu diesen beiden Zweigspitzen führen, sodass Sie auch den gemeinsamen Startpunkt-Commit haben.

Denken Sie daran, dass jeder Commit zusammen mit seinem Snapshot einige Informationen aufzeichnet Über der Schnappschuss: zum Beispiel der Name und die E-Mail-Adresse der Person, die ihn gemacht hat. Es gibt einen Datums- und Zeitstempel für Wenn sie es geschafft haben, und eine Protokollnachricht, die sie zur Erklärung verwenden können warum Sie haben es geschafft. Es speichert auch die Roh-Hash-ID seines Unmittelbaren Elternteil commit: das Commit, das sie verwendet haben, via git checkoutum damit zu beginnen, bevor sie es gemacht haben ihr begehen. Diese übergeordneten Hash-IDs bilden eine rückwärtsgerichtete Kette: wenn sowohl Alice als auch Bob mit dem Commit begonnen haben Hund Alice hat zwei Commits vorgenommen I und J und Bob machte zwei Commits K und Ldie Rückwärtsketten sehen so aus:

                I <-J   <-- (Alice's latest)
               /
... <-F <-G <-H
               \
                K <-L   <-- (Bob's latest)

Git findet automatisch Hwo Alice und Bob beide gestartet sind.1

Gefunden HGit führt jetzt tatsächlich diese beiden aus git diff Befehle:

  • git diff --find-renames hash-of-H hash-of-J: was Alice geändert hat
  • git diff --find-renames hash-of-H hash-of-L: was Bob geändert hat

Der Zusammenführungsprozess führt nun diese Änderungen zusammen. Für jede Datei in H:

  • Hat Alice die Datei geändert? Hat Bob die Datei geändert?
  • Wenn keiner die Datei geändert hat, verwenden Sie eine beliebige Kopie der Datei: Alle drei sind gleich.
  • Wenn Alice die Datei geändert hat und Bob nicht, verwenden Sie Alices Version.
  • Wenn Bob die Datei geändert hat und Alice nicht, verwenden Sie Bobs Version.
  • Wenn beide die Datei geändert haben, kombinieren ihre Veränderungen. Hier entsteht ein Zusammenführungskonflikt könnten geschehen.

Tut [Git] Zeile für Zeile beim Zusammenführen vergleichen?

Die Antwort darauf ist sowohl nein als auch ja. Wie Sie jetzt sehen können, gibt es keinen Vergleich zwischen Alices Version und Bobs Version. Dort ist ein Vergleich – eine Art Zeile für Zeile; Es ist was auch immer git diff tut für den Vergleich – der Base Version, zu Alice, und es gibt einen identischen Vergleich der Base Version zu Bobs. Der gesamte Prozess beginnt mit einem vollständigen Commit-weiten Vergleich der beiden Paare von begeht. Innerhalb dieses Commit-weiten Vergleichs haben wir herausgefunden, dass sich sowohl Alice als auch Bob verändert haben einige bestimmte Datei(en), jetzt die Zeile-für-Zeile- oder wirklich Diff-Hunk-by-Diff-Hunk-Vergleiche sind wichtig. Aber sie sind von a Dritter Ausführung.

Ich möchte nicht jedes Mal manuell mit “git diff” überprüfen.

Das müssen Sie nicht. Sie können, wenn Sie wollen dazu, aber um das zu tun, müssen Sie den Merge-Basis-Commit finden, indem Sie verwenden git merge-base vielleicht. Aber wenn du nicht willst, dann … tu es nicht. Git findet den Merge-Basis-Commit; Git werde die beiden getrennt machen git diff Operationen; Git kombiniert Alices Änderungen mit Bobs Änderungen und erklärt einen Konflikt, wenn sich die geänderten Zeilen überschneiden – oder in einigen Fällen anstoßenoder wenn beide bis zum Ende der Datei reichen.

(Für Git, wenn sowohl Alice als auch Bob gemacht haben exakt die gleichen Änderungen an exakt die gleichen Zeilen, Git nimmt nur eine Kopie der Änderung. Andere VCSs können hier einen Konflikt erklären, entweder aus Faulheit – sie überprüfen nicht, ob die Änderungen gleich waren, sondern dass sie sich überschnitten haben – oder aus Paranoia: Wenn beide die gleichen Zeilen geändert haben, ist vielleicht das richtige Ergebnis nicht nur um eine Kopie der Änderung zu verwenden. Git sagt nur “das richtige Ergebnis ist eine Kopie der Änderung”.)

In jedem Fall wendet Git die kombiniert Änderungen an der Basis zusammenführen Version der Datei. Das ist das Ergebnis, möglicherweise mit einem Merge-Konflikt (und Merge-Konfliktmarkern in der Work-Tree-Kopie der Datei).

Beachten Sie abschließend die --find-renames in den beiden git diff Befehle. Git wird versuchen festzustellen, ob Alice und/oder Bob umbenannt eine der Dateien im Merge-Basis-Commit. Wenn dies der Fall ist, versucht Git, die Umbenennung im Endergebnis beizubehalten. Dies gilt unabhängig davon, ob Alice oder Bob die Umbenennung vorgenommen haben. Wenn beide Alice und Bob hat die Datei umbenannt, Git weiß nicht, welchen endgültigen Namen es verwenden soll, und deklariert a umbenennen/umbenennen Konflikt. Es gibt ähnliche Probleme bei Alice oder Bob löscht die Datei, während der andere sie modifiziert, und es gibt einen letzten Konflikt, der auftritt, wenn sowohl Alice als auch Bob a hinzufügen Neu Datei mit dem gleichen Namen. Solche Konflikte nenne ich hohes Level Konflikte: Sie betreffen ganze Dateien (und/oder ihre Namen) und nicht einzelne Zeilen innerhalb eine Datei. Dieser Unterschied zwischen einem Low-Level-Konflikt (Zeilen innerhalb einer Datei) und einem High-Level-Konflikt spielt eine Rolle, wenn und wann Sie die verwenden -Xours oder -Xtheirs Möglichkeit.


1Das funktioniert auch, wenn Alice nur gemacht hat ein begehen, sagen wir Jauf (sagen wir) Carols einem Commit I dass Carol oben drauf gemacht hat H. Das Gemeine Startpunkt ist immer noch H. Git achtet nicht einmal auf die Urheberschaft jedes Commits: Es arbeitet einfach rückwärts von den beiden Branch-Spitzen.

  • Danke !! Das ist die schönste und grundlegendste Erklärung, die ich bisher gefunden habe.

    – rahulaga-msft

    7. Januar um 11:59 Uhr

Es gibt mehrere Merge-Strategien. Die Rekursion des 3-Wege-Merge-Algorithmus wird standardmäßig in Git verwendet.

Der 3-Wege-Algorithmus verwendet den letzten gemeinsamen Commit.

Zum Beispiel:

master: A -> B -> C

Neuen Zweig erstellen

master: A -> B -> C
                   \
branch:             D

Einige neue Commits

master: A -> B -> C -> E
                   \
branch:             D -> F

Alle Änderungen in a.txt übernehmen (leere Zelle entspricht leerer Zeile)

 commit C         commit E         commit F 
----------       ----------       ----------
  line a                            line a
  line b         new line d
  line c                          new line e
                   line a           line b
                   line b         new line f
                   line c           
                 new line g         line c

Was passiert, wenn wir zwei Zweige zusammenführen (Commit E, Commit F). Erzeugt es einen Merge-Konflikt?. Antwort ist nein. Denn git vergleicht eine Datei nicht Zeile für Zeile. Es vergleicht den Kontext der Zeilen.

Richten Sie die a.txt-Datei aus

 commit C         commit E         commit F 
----------       ----------       ----------

                 new line d

  line a-----------line a-----------line a

                                  new line e
  line b-----------line b-----------line b
                                  new line f

  line c-----------line c-----------line c
                 new line g

In der obigen Tabelle sind Änderungen ausgerichtet. Zeilen im Commit C (Vorfahren-Commit) sind unsere Referenzen. git vergleicht den Nachbarn der Referenzzeilen. Im Beispiel haben wir 4 Steckplätze:

  • über der Zeile a: Commit e fügt neue Zeile d hinzu
  • unterhalb der Zeile a : commit f fügt neue Zeile e hinzu
  • unterhalb der Zeile b : Commit e fügt neue Zeile f hinzu
  • unter der Zeile c : commit g fügt eine neue Zeile g hinzu

Wie Sie sehen, kann nur einer der Zweige (Commit E, Commit F) etwas Neues hinzufügen, oder beide können dasselbe hinzufügen. Andernfalls ist ein Zusammenführungskonflikt aufgetreten.

Benutzer-Avatar
mnestorov

Es benutzt Delta-Komprimierung. Das müssen wir verstehen, wenn wir add eine Datei in get, erstellen wir ein Objekt, dessen Sha-Summe berechnet und im Index aufgezeichnet wird. Was git macht, ist das, durch git-repack, nimmt es komprimierte Objekte (komprimiert mit Delta-Komprimierung) in ein Paket (eine Datei). Wenn Sie Commits vornehmen, nimmt Git die nicht komprimierten Objekte und verwendet einige interne Regeln, um eine Datei zu erstellen, die die Unterschiede und Ähnlichkeiten zwischen den Objekten enthält. Diese Erstellung eines Pakets verwendet Delta-Komprimierung.

Diese Deltakomprimierung, die nur eine Deltadifferenzierung ist, ist das, wonach Sie fragen. Ich denke, der Umfang der Funktionsweise dieses Algorithmus geht über diese Frage hinaus, also hier sind ein paar Referenzen, um Ihnen den Einstieg zu erleichtern.

Algorithmen für die Delta-Komprimierung

Wie git jede Datei behandelt

git-repack

Delta-Differenzierung

  • Die Frage bezieht sich auf Diffs zum Zusammenführen, nicht Diffs zum Packen.

    – Raymond Chen

    4. Juli 2019 um 15:16 Uhr

  • @RaymondChen nein. Die Frage bezieht sich auf Algorithmen, die zur Erzeugung von Diffs verwendet werden. Ich habe nur zusätzliche Informationen zu diesem Thema gegeben. Meine Antwort deckte die vorliegende Frage ab.

    – mnestorov

    4. Juli 2019 um 17:25 Uhr

  • Die Delta-Komprimierung ist ein Algorithmus zur Unterscheidung von Binärdateien. Ich weiß nicht, ob git nutzt es dazu Geschäft Blobs, aber es verwendet es sicherlich nicht für Diff und Merge.

    – promov

    4. Juli 2019 um 17:28 Uhr

  • fair genug und Punkt genommen. Wenn ich mir die Zeit genommen hätte, besser zu suchen, hätte ich diese beiden Quellen gefunden – Artikel und Dokumentation

    – mnestorov

    4. Juli 2019 um 17:36 Uhr

1014650cookie-checkWie vergleicht Git zwei Dateien beim Zusammenführen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy