Git Rebase, verfolgt 'lokal' und 'remote'

174

Wenn ich eine Git-Rebase mache, habe ich oft Schwierigkeiten herauszufinden, was mit dem "lokalen" und "entfernten" passiert, wenn Konflikte gelöst werden. Ich habe manchmal den Eindruck, dass sie die Seiten von einem Commit zum nächsten tauschen.

Dies liegt wahrscheinlich (definitiv) daran, dass ich es immer noch nicht richtig verstanden habe.

Wer ist beim Umbasieren "lokal" und wer "entfernt"?

(Ich benutze P4Merge zur Lösung von Konflikten)

Benjol
quelle
Ist es möglich , dass das Lesen dieses würde Ihnen helfen? Der Rest des Tutorials ist auch sehr hilfreich ....
ivans
Eine weitere ausgezeichnete Git- Ressource .
Unbekannt
Würde stackoverflow.com/questions/2959443/… helfen? (nicht für den git svnTeil, nur für den git rebaseTeil)
VonC
@VonC, ja, genau das ist es. Wenn Sie das relevante Stück Ihrer Antwort hier kopieren möchten, werde ich es
ankreuzen
alles klar ... ich werde beißen;) Relevante Auszüge gepostet.
VonC

Antworten:

243

TL; DR;

Zusammenfassend (wie Benubird kommentiert ), wenn:

git checkout A
git rebase   B    # rebase A on top of B
  • localist B(Rebase auf ),
  • remote ist A

Und:

git checkout A
git merge    B    # merge B into A
  • localist A(verschmelzen in ),
  • remote ist B

Eine Rebase wechselt ours(aktueller Zweig vor dem Start der Rebase) und theirs(der Zweig, über dem Sie die Basis neu erstellen möchten).


kutschkem weist darauf hin, dass in einem GUI-Mergetool-Kontext :

  • lokale Verweise auf die teilweise neu basierten Commits : " ours" (der Upstream-Zweig)
  • remote bezieht sich auf die eingehenden Änderungen : " theirs" - der aktuelle Zweig vor der Rebase.

Siehe Abbildungen im letzten Teil dieser Antwort.


Inversion beim Rebase

Die Verwirrung könnte mit der Inversion von oursund theirswährend einer Rebase zusammenhängen .
(relevante Auszüge)

git rebaseManpage :

Beachten Sie, dass eine Rebase-Zusammenführung funktioniert, indem jedes Commit aus dem Arbeitszweig über dem <upstream>Zweig wiedergegeben wird.

Aus diesem Grund, wenn ein Zusammenführungskonflikt auftritt:

  • Die Seite, die als ' ours' gemeldet wird , ist die bisher neu basierte Serie, beginnend mit <upstream>,
  • und ' theirs' ist der Arbeitszweig. Mit anderen Worten, die Seiten werden getauscht.

Inversion dargestellt

Bei einer Fusion

x--x--x--x--x(*) <- current branch B ('*'=HEAD)
    \
     \
      \--y--y--y <- other branch to merge

Wir ändern den aktuellen Zweig 'B' nicht. Wir haben also immer noch das, woran wir gearbeitet haben (und wir verschmelzen aus einem anderen Zweig).

x--x--x--x--x---------o(*)  MERGE, still on branch B
    \       ^        /
     \     ours     /
      \            /
       --y--y--y--/  
               ^
              their

Auf einer Rebase:

Bei einer Rebase wechseln wir jedoch die Seite, da das erste, was eine Rebase bewirkt, das Auschecken des Upstream-Zweigs ist! (um die aktuellen Commits darüber abzuspielen)

x--x--x--x--x(*) <- current branch B
    \
     \
      \--y--y--y <- upstream branch

A wechselt git rebase upstreamzuerst HEADvon B in den vorgelagerten Zweig HEAD(daher der Wechsel von "unserem" und "ihrem" im Vergleich zum vorherigen "aktuellen" Arbeitszweig).

x--x--x--x--x <- former "current" branch, new "theirs"
    \
     \
      \--y--y--y(*) <- upstream branch with B reset on it,  
                       new "ours", to replay x's on it

und dann wird die Rebase 'ihre' Commits auf dem neuen 'unser' B-Zweig wiedergeben:

x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
    \
     \
      \--y--y--y--x'--x'--x'(*) <-  branch B with HEAD updated ("ours")
               ^
               |
        upstream branch

Hinweis: Der Begriff "Upstream" ist der referenzielle Datensatz (ein All-Repo oder, wie hier, ein Zweig, der ein lokaler Zweig sein kann), aus dem Daten gelesen oder neue Daten hinzugefügt / erstellt werden.


' local' und ' remote' vs. ' mine' und ' theirs'

Pandawood fügt in den Kommentaren hinzu :

Für mich bleibt immer noch die Frage, welches "lokal" und wer "fern" ist (da die Begriffe "unsere" und "ihre" beim Umbasieren in git nicht verwendet werden, scheint es nur verwirrender zu sein, sich auf sie zu beziehen). .

GUI Git Mergetool

kutschkem fügt hinzu, und das zu Recht:

Bei der Lösung von Konflikten sagt Git etwas wie:

local: modified file and remote: modified file. 

Ich bin mir ziemlich sicher, dass die Frage an dieser Stelle auf die Definition von lokal und fern abzielt. An diesem Punkt scheint es mir aus meiner Erfahrung, dass:

  • lokale Verweise auf die teilweise neu basierten Commits : " ours" (der Upstream-Zweig)
  • remote bezieht sich auf die eingehenden Änderungen : " theirs" - der aktuelle Zweig vor der Rebase.

git mergetoolerwähnt in der Tat "lokal" und "fern" :

Merging:
f.txt

Normal merge conflict for 'f.txt':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (kdiff3):

Zum Beispiel würde KDiff3 die Zusammenführungsauflösung folgendermaßen anzeigen :

kdiff3

Und meld würde es auch anzeigen :

Meld diff

Gleiches gilt für VimDiff , das Folgendes anzeigt :

Rufen Sie Vimdiff als Mergetool mit git mergetool -t gvimdiff auf. Neuere Versionen von Git rufen Vimdiff mit dem folgenden Fensterlayout auf:

+--------------------------------+
| LOCAL  |     BASE     | REMOTE |
+--------------------------------+
|             MERGED             |
+--------------------------------+
  • LOCAL:
    Eine temporäre Datei, die den Inhalt der Datei im aktuellen Zweig enthält.
  • BASE:
    Eine temporäre Datei, die die gemeinsame Basis für die Zusammenführung enthält.
  • REMOTE:
    Eine temporäre Datei, die den Inhalt der zusammenzuführenden Datei enthält.
  • MERGED:
    Die Datei mit den Konfliktmarkierungen.

Git hat so viel automatische Konfliktlösung wie möglich durchgeführt und der Status dieser Datei ist eine Kombination aus beidem LOCALund REMOTEmit Konfliktmarkierungen, die alles umgeben, was Git selbst nicht lösen konnte.
Das mergetoolsollte das Ergebnis der Auflösung in diese Datei schreiben.

VonC
quelle
13
Für mich bleibt immer noch die Frage, welches "lokal" und wer "fern" ist (da die Begriffe "unsere" und "ihre" beim Umbasieren in git nicht verwendet werden, scheint es nur verwirrender zu sein, sich auf sie zu beziehen). . Die Frage ist "wer ist lokal und wer ist entfernt" - so erfordert eine Antwort sicherlich die Erwähnung der Wörter "lokal" und "entfernt"
PandaWood
@PandaWood: "local" ist "aktueller Zweig" (der zu "ihrem" wird), "remote" ist "Upstream-Zweig" (der zu "unserem" wird).
VonC
3
git checkout A; git rebase B
Um
1
Git ist so ein Clusterfk der Benutzerfreundlichkeit. Das macht keinen Sinn: wenn Sie git checkout A; git rebase Blokale B, Remote A ist . Wenn ich checkout Adann ich bin auf die Dateien derzeit auf der Suche , wie sie existieren auf A, wie ist , dass die in irgendeiner Weise entfernten ? (Ich sage nicht, dass Benubird falsch ist; ich sage, dass Git eine dumme UX hat)
Rafa
1
@VonC sicher; Mein (schimpfender) Punkt ist, dass es nicht nötig sein sollte, Dokumentation zu lesen, Diagramme zu betrachten und StackOverflow zu durchsuchen. Wenn nur der Befehl ein klares, eindeutiges Feedback geben würde. Zum Beispiel, anstatt lokal / remote / ihre / unsere / meine / deine, nur zeigen {branch A}und {branch B}oder ähnlich.
Rafa
45

Das Endergebnis

Git Rebase

  • LOCAL = die Basis Sie Rebasing auf
  • REMOTE = die Commits, die Sie nach oben bringen

Git Merge

  • LOCAL = der ursprüngliche Zweig, in den Sie zusammenführen
  • REMOTE = der andere Zweig, in dessen Commits Sie zusammenführen

Mit anderen Worten, LOCAL ist immer das Original, und REMOTE ist immer der Typ, dessen Commits vorher nicht da waren, weil sie oben zusammengeführt oder neu basiert werden

Beweise es!

Bestimmt. Nimm mein Wort nicht dafür! Hier ist ein einfaches Experiment, das Sie durchführen können, um sich selbst davon zu überzeugen.

Stellen Sie zunächst sicher, dass Sie git mergetool richtig konfiguriert haben. (Wenn Sie dies nicht tun würden, würden Sie diese Frage wahrscheinlich sowieso nicht lesen.) Suchen Sie dann ein Verzeichnis, in dem Sie arbeiten können.

Richten Sie Ihr Repository ein:

md LocalRemoteTest
cd LocalRemoteTest

Erstellen Sie ein erstes Commit (mit einer leeren Datei):

git init
notepad file.txt  (use the text editor of your choice)
  (save the file as an empty file)
git add -A
git commit -m "Initial commit."

Erstellen Sie ein Commit für einen Zweig, der kein Master ist:

git checkout -b notmaster
notepad file.txt
  (add the text: notmaster)
  (save and exit)
git commit -a -m "Add notmaster text."

Erstellen Sie ein Commit für den Hauptzweig:

git checkout master
notepad file.txt
  (add the text: master)
  (save and exit)
git commit -a -m "Add master text."

gitk --all

Zu diesem Zeitpunkt sollte Ihr Repository folgendermaßen aussehen:

Repository mit einem Basis-Commit und zwei One-Commit-Zweigen

Nun zum Rebase-Test:

git checkout notmaster
git rebase master
  (you'll get a conflict message)
git mergetool
  LOCAL: master
  REMOTE: notmaster

Nun der Zusammenführungstest. Schließen Sie Ihr Mergetool, ohne Änderungen zu speichern, und brechen Sie die Rebase ab:

git rebase --abort

Dann:

git checkout master
git merge notmaster
git mergetool
  LOCAL: master
  REMOTE: notmaster
git reset --hard  (cancels the merge)

Ihre Ergebnisse sollten mit denen übereinstimmen, die oben angezeigt werden.

Ryan Lundy
quelle
1
+1. Das verdeutlicht die local/ remoteAspekte, mit denen ich in meiner eigenen Antwort oben zu kämpfen hatte (in der es sowieso mehr um die Umkehrung von oursvs geht theirs)
VonC
3

Ich habe Ihr Problem nicht genau verstanden, aber ich denke, das folgende Diagramm behebt Ihr Problem. (Rebase: Remote Repository ---> Arbeitsbereich)

http://assets.osteele.com/images/2008/git-transport.png

Quelle: Mein Git-Workflow

Chathuranga Chandrasekara
quelle