Git-Workflow und Rebase vs Merge-Fragen


971

Ich benutze Git jetzt seit ein paar Monaten für ein Projekt mit einem anderen Entwickler. Ich habe mehrere Jahre Erfahrung mit SVN , also bringe ich wohl viel Gepäck in die Beziehung.

Ich habe gehört, dass Git hervorragend zum Verzweigen und Zusammenführen geeignet ist, und bis jetzt sehe ich es einfach nicht. Sicher, das Verzweigen ist denkbar einfach, aber wenn ich versuche, es zusammenzuführen, geht alles zur Hölle. Nun, ich bin das von SVN gewohnt, aber es scheint mir, dass ich gerade ein unterdurchschnittliches Versionsverwaltungssystem gegen ein anderes getauscht habe.

Mein Partner sagt mir, dass meine Probleme auf meinem Wunsch beruhen, wohl oder übel zusammenzuführen, und dass ich in vielen Situationen Rebase anstelle von Zusammenführung verwenden sollte. Hier ist zum Beispiel der Workflow, den er festgelegt hat:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

Erstellen Sie im Wesentlichen einen Feature-Zweig, der IMMER von Master zu Zweig zurückgesetzt und von Zweig zurück zu Master zusammengeführt wird. Es ist wichtig zu beachten, dass die Niederlassung immer lokal bleibt.

Hier ist der Workflow, mit dem ich begonnen habe

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

Es gibt zwei wesentliche Unterschiede (glaube ich): Ich verwende Merge immer anstelle von Rebasing und schiebe meinen Feature-Zweig (und meine Feature-Branch-Commits) in das Remote-Repository.

Meine Argumentation für den Remote-Zweig ist, dass ich möchte, dass meine Arbeit während der Arbeit gesichert wird. Unser Repository wird automatisch gesichert und kann wiederhergestellt werden, wenn etwas schief geht. Mein Laptop ist nicht oder nicht so gründlich. Daher hasse ich es, Code auf meinem Laptop zu haben, der nicht woanders gespiegelt wird.

Meine Argumentation für das Zusammenführen anstelle des erneuten Basierens ist, dass das Zusammenführen Standard zu sein scheint und das erneute Basieren eine erweiterte Funktion zu sein scheint. Mein Bauchgefühl ist, dass das, was ich versuche, kein fortgeschrittenes Setup ist, daher sollte eine Neustart unnötig sein. Ich habe sogar das neue Pragmatic Programming-Buch auf Git gelesen, und sie behandeln das Zusammenführen ausführlich und erwähnen kaum die Basis.

Wie auch immer, ich habe meinen Workflow in einem kürzlich durchgeführten Zweig verfolgt, und als ich versuchte, ihn wieder zum Master zusammenzuführen, ging alles zur Hölle. Es gab Unmengen von Konflikten mit Dingen, die keine Rolle hätten spielen sollen. Die Konflikte machten für mich einfach keinen Sinn. Ich brauchte einen Tag, um alles zu klären, und gipfelte schließlich in einem erzwungenen Druck auf den Remote-Master, da mein lokaler Master alle Konflikte gelöst hat, aber der Remote-Master immer noch nicht glücklich war.

Was ist der "richtige" Workflow für so etwas? Git soll das Verzweigen und Zusammenführen sehr einfach machen, und ich sehe es einfach nicht.

Update 15.04.2011

Dies scheint eine sehr beliebte Frage zu sein, daher dachte ich, ich würde meine zweijährige Erfahrung seit meiner ersten Frage aktualisieren.

Es stellt sich heraus, dass der ursprüngliche Workflow zumindest in unserem Fall korrekt ist. Mit anderen Worten, das ist was wir tun und es funktioniert:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

Tatsächlich ist unser Workflow etwas anders, da wir eher Squash-Merges als Raw-Merges durchführen. ( Hinweis: Dies ist umstritten, siehe unten. ) Auf diese Weise können wir unseren gesamten Feature-Zweig in ein einziges Commit für den Master verwandeln. Dann löschen wir unseren Feature-Zweig. Dies ermöglicht es uns, unsere Commits auf Master logisch zu strukturieren, auch wenn sie in unseren Filialen etwas chaotisch sind. Das machen wir also:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

Kontroverse um Squash-Zusammenführung - Wie mehrere Kommentatoren hervorgehoben haben, wirft die Squash-Zusammenführung den gesamten Verlauf Ihres Feature-Zweigs weg. Wie der Name schon sagt, werden alle Commits zu einem einzigen zusammengefasst. Für kleine Funktionen ist dies sinnvoll, da es zu einem einzigen Paket zusammengefasst wird. Für größere Funktionen ist dies wahrscheinlich keine gute Idee, insbesondere wenn Ihre individuellen Commits bereits atomar sind. Es kommt wirklich auf die persönlichen Vorlieben an.

Pull-Anforderungen von Github und Bitbucket (andere?) - Falls Sie sich fragen, wie sich das Zusammenführen / Wiederherstellen auf Pull-Anforderungen bezieht, empfehle ich, alle oben genannten Schritte auszuführen, bis Sie bereit sind, wieder zum Master zusammenzuführen. Anstatt manuell mit git zu verschmelzen, akzeptieren Sie einfach die PR. Beachten Sie, dass dies keine Squash-Zusammenführung durchführt (zumindest nicht standardmäßig), aber Nicht-Squash, Nicht-Schnellvorlauf ist die akzeptierte Zusammenführungskonvention in der Pull-Request-Community (soweit ich weiß). Im Einzelnen funktioniert es so:

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

Ich habe Git geliebt und möchte nie wieder zu SVN zurückkehren. Wenn Sie Probleme haben, bleiben Sie einfach dabei und irgendwann sehen Sie das Licht am Ende des Tunnels.


31
Leider wurde das neue Pragmstic Programming-Buch hauptsächlich mit Git geschrieben, während man noch in SVN denkt, und in diesem Fall hat es Sie in die Irre geführt. In Git hält Rebase die Dinge einfach, wenn sie sein können. Ihre Erfahrung könnte Ihnen sagen, dass Ihr Workflow in Git nicht funktioniert, nicht, dass Git nicht funktioniert.
Paul

18
Ich würde das Zusammenführen von Squashs in diesem Fall nicht empfehlen, da hier keine Informationen darüber gespeichert werden, was zusammengeführt wird (genau wie bei svn, aber hier keine Zusammenführungsinformationen).
Marius K

7
Ich liebe die Notiz unten, ich hatte eine ähnliche Erfahrung mit Git, aber jetzt fällt es mir schwer, mir vorzustellen, sie nicht zu benutzen. Vielen Dank für die abschließende Erklärung, die viel zum rebaseVerständnis beigetragen hat
Jon Phenow

6
Sollten Sie nach Abschluss der Funktion nicht ein letztes Mal neu starten, bevor Sie new_feature mit master zusammenführen?
Softarn

17
Ihr Workflow verliert den gesamten Commit-Verlauf aus dem gelöschten Zweig :(
Max Nanasy

Antworten:


371

"Konflikte" bedeuten "parallele Entwicklungen desselben Inhalts". Wenn es also während einer Zusammenführung "zur Hölle" geht, bedeutet dies, dass Sie massive Entwicklungen auf demselben Dateisatz haben.

Der Grund, warum eine Rebase dann besser ist als eine Zusammenführung, ist folgender:

  • Sie schreiben Ihren lokalen Commit-Verlauf mit dem des Masters neu (und wenden dann Ihre Arbeit erneut an, um dann alle Konflikte zu lösen).
  • Die endgültige Zusammenführung wird sicherlich ein "schneller Vorlauf" sein, da sie die gesamte Commit-Historie des Masters sowie nur Ihre Änderungen enthält, die erneut angewendet werden müssen.

Ich bestätige, dass der korrekte Workflow in diesem Fall (Weiterentwicklung der allgemeinen Dateigruppe) zuerst neu erstellt und dann zusammengeführt wird .

Dies bedeutet jedoch, dass, wenn Sie Ihren lokalen Zweig (aus Sicherungsgründen) pushen, dieser Zweig von niemand anderem abgerufen (oder zumindest verwendet) werden sollte (da der Festschreibungsverlauf durch die aufeinanderfolgende Neustart neu geschrieben wird).


Zu diesem Thema (Workflow neu starten und dann zusammenführen) erwähnt barraponto in den Kommentaren zwei interessante Beiträge, beide von randyfay.com :

Mit dieser Technik wird Ihre Arbeit immer über dem öffentlichen Zweig angezeigt, wie ein Patch, der auf dem neuesten Stand ist HEAD.

(Eine ähnliche Technik gibt es für Basar )


27
Eine Technik, die das erneute Basieren und Teilen ermöglicht, finden Sie unter softwareswirl.blogspot.com/2009/04/…
mhagger

2
randyfay.com/node/91 und randyfay.com/node/89 sind wundervolle Lektüren . Durch diese Artikel wurde mir klar, was mit meinem Workflow zu tun hatte und was ein idealer Workflow wäre.
Capi Etheriel

Um es klar zu machen: Wenn Sie vom Master-Zweig auf Ihren lokalen Zweig zurückgreifen, müssen Sie im Grunde genommen jeden Verlauf aktualisieren, den Ihr lokaler Zweig möglicherweise übersehen hat und von dem der Master nach jeder Art von Zusammenführung Kenntnis hat.
Hellatan

@dtan Was ich hier beschreibe, ist die lokale Basisierung auf Master. Sie aktualisieren den lokalen Verlauf nicht genau, sondern wenden den lokalen Verlauf erneut auf den Master an, um Konflikte innerhalb des lokalen Zweigs zu lösen.
VonC

386

TL; DR

Ein Git-Rebase-Workflow schützt Sie nicht vor Personen, die schlecht in der Konfliktlösung sind oder an einen SVN-Workflow gewöhnt sind, wie in Vermeiden von Git-Katastrophen: Eine blutige Geschichte vorgeschlagen . Dies macht die Konfliktlösung für sie nur mühsamer und erschwert die Wiederherstellung nach einer schlechten Konfliktlösung. Verwenden Sie stattdessen diff3, damit es gar nicht so schwierig ist.


Der Rebase-Workflow ist für die Konfliktlösung nicht besser!

Ich bin sehr pro-rebase für das Aufräumen der Geschichte. Wenn ich jedoch jemals auf einen Konflikt stoße, brich ich die Rebase sofort ab und führe stattdessen eine Zusammenführung durch! Es bringt mich wirklich um, dass Leute einen Rebase-Workflow als bessere Alternative zu einem Merge-Workflow zur Konfliktlösung empfehlen (genau darum ging es in dieser Frage).

Wenn es während einer Fusion "alles zur Hölle" geht, geht es während einer Rebase "alles zur Hölle" und möglicherweise auch viel mehr zur Hölle! Hier ist der Grund:

Grund Nr. 1: Lösen Sie Konflikte einmal statt einmal für jedes Commit

Wenn Sie die Basis neu erstellen, anstatt sie zusammenzuführen, müssen Sie die Konfliktlösung für denselben Konflikt bis zu so oft durchführen, wie Sie sich zur erneuten Basis verpflichtet haben, für denselben Konflikt!

Reales Szenario

Ich verzweige vom Master, um eine komplizierte Methode in einem Zweig umzugestalten. Meine Refactoring-Arbeit besteht aus insgesamt 15 Commits, während ich daran arbeite, sie zu refactorisieren und Codeüberprüfungen zu erhalten. Ein Teil meines Refactorings besteht darin, die gemischten Tabulatoren und Leerzeichen zu korrigieren, die zuvor im Master vorhanden waren. Dies ist notwendig, steht jedoch leider im Widerspruch zu Änderungen, die später an dieser Methode im Master vorgenommen werden. Sicher genug, während ich an dieser Methode arbeite, nimmt jemand eine einfache, legitime Änderung an derselben Methode im Hauptzweig vor, die mit meinen Änderungen zusammengeführt werden soll.

Wenn es Zeit ist, meinen Zweig wieder mit dem Master zusammenzuführen, habe ich zwei Möglichkeiten:

Git Merge: Ich bekomme einen Konflikt. Ich sehe die Änderung, die sie vorgenommen haben, um sie zu meistern und mit (dem Endprodukt) meiner Branche zu verschmelzen. Erledigt.

Git Rebase: Ich bekomme einen Konflikt mit meinem ersten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem zweiten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem dritten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem vierten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem fünften Commit. Ich löse den Konflikt und setze die Rebase fort. Ich habe einen Konflikt mit meinem sechsten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem siebtenverpflichten. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem achten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem neunten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem zehnten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich habe einen Konflikt mit meinem elften Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem zwölften Commit. Ich löse den Konflikt und setze die Rebase fort. Ich habe einen Konflikt mit meinem dreizehnten Commit. Ich löse den Konflikt und setze die Rebase fort. Ich bekomme einen Konflikt mit meinem vierzehntenverpflichten. Ich löse den Konflikt und setze die Rebase fort. Ich habe einen Konflikt mit meinem fünfzehnten Commit. Ich löse den Konflikt und setze die Rebase fort.

Sie müssen mich veräppeln, wenn dies Ihr bevorzugter Workflow ist. Alles, was es braucht, ist ein Whitespace-Fix, der mit einer am Master vorgenommenen Änderung in Konflikt steht, und jedes Commit wird in Konflikt geraten und muss gelöst werden. Und dies ist ein einfaches Szenario mit nur einem Leerzeichenkonflikt. Der Himmel verbietet Ihnen, einen echten Konflikt mit größeren Codeänderungen zwischen Dateien zu haben, und muss diesen mehrmals lösen .

Mit all der zusätzlichen Konfliktlösung, die Sie benötigen, erhöht sich nur die Wahrscheinlichkeit, dass Sie einen Fehler machen . Aber Fehler sind in git in Ordnung, da Sie rückgängig machen können, oder? Außer natürlich ...

Grund Nr. 2: Mit Rebase gibt es kein Rückgängigmachen!

Ich denke, wir können uns alle einig sein, dass Konfliktlösung schwierig sein kann und dass einige Leute sehr schlecht darin sind. Es kann sehr fehleranfällig sein, weshalb es so großartig ist, dass Git es leicht macht, es rückgängig zu machen!

Wenn Sie einen Zweig zusammenführen, erstellt git ein Zusammenführungs-Commit, das verworfen oder geändert werden kann, wenn die Konfliktlösung schlecht läuft. Selbst wenn Sie das Bad Merge Commit bereits auf das öffentliche / autorisierende Repo übertragen haben, können Sie git revertdie durch das Merge eingeführten Änderungen rückgängig machen und das Zusammenführen in einem neuen Merge Commit korrekt wiederholen.

Wenn Sie einen Zweig neu gründen , sind Sie in dem wahrscheinlichen Fall, dass die Konfliktlösung falsch durchgeführt wird, durcheinander . Jedes Commit enthält jetzt die fehlerhafte Zusammenführung, und Sie können die Rebase * nicht einfach wiederholen. Bestenfalls müssen Sie zurückgehen und jedes der betroffenen Commits ändern. Kein Spaß.

Nach einer erneuten Basis ist es unmöglich festzustellen, was ursprünglich Teil der Commits war und was als Ergebnis einer schlechten Konfliktlösung eingeführt wurde.

* Es kann möglich sein, eine Rebase rückgängig zu machen, wenn Sie die alten Refs aus den internen Protokollen von Git ausgraben können oder wenn Sie einen dritten Zweig erstellen, der auf die letzte Festschreibung vor der Rebasis verweist.

Nehmen Sie die Hölle aus der Konfliktlösung: Verwenden Sie diff3

Nehmen Sie diesen Konflikt zum Beispiel:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Wenn man den Konflikt betrachtet, ist es unmöglich zu sagen, was jeder Zweig geändert hat oder was seine Absicht war. Dies ist meiner Meinung nach der Hauptgrund, warum Konfliktlösung verwirrend und schwierig ist.

diff3 zur rettung!

git config --global merge.conflictstyle diff3

Wenn Sie diff3 verwenden, hat jeder neue Konflikt einen dritten Abschnitt, den zusammengeführten gemeinsamen Vorfahren.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Untersuchen Sie zuerst den zusammengeführten gemeinsamen Vorfahren. Vergleichen Sie dann jede Seite, um die Absicht jedes Zweigs zu bestimmen. Sie können sehen, dass HEAD EmailMessage in TextMessage geändert hat. Es ist beabsichtigt, die verwendete Klasse in TextMessage zu ändern und dieselben Parameter zu übergeben. Sie können auch sehen, dass der Feature-Branch beabsichtigt, für die Option: include_timestamp false anstelle von true zu übergeben. Kombinieren Sie die Absicht beider, um diese Änderungen zusammenzuführen:

TextMessage.send(:include_timestamp => false)

Allgemein:

  1. Vergleichen Sie den gemeinsamen Vorfahren mit jedem Zweig und bestimmen Sie, welcher Zweig die einfachste Änderung aufweist
  2. Wenden Sie diese einfache Änderung auf die Codeversion des anderen Zweigs an, sodass sie sowohl die einfachere als auch die komplexere Änderung enthält
  3. Entfernen Sie alle Abschnitte des Konfliktcodes außer dem, in dem Sie die Änderungen gerade zusammengeführt haben

Alternative: Lösung durch manuelles Anwenden der Änderungen des Zweigs

Schließlich sind einige Konflikte selbst mit diff3 schrecklich zu verstehen. Dies geschieht insbesondere dann, wenn diff gemeinsame Zeilen findet, die nicht semantisch häufig sind (z. B. hatten beide Zweige zufällig eine leere Zeile an derselben Stelle!). Beispielsweise ändert ein Zweig die Einrückung des Hauptteils einer Klasse oder ordnet ähnliche Methoden neu an. In diesen Fällen kann eine bessere Auflösungsstrategie darin bestehen, die Änderung von beiden Seiten der Zusammenführung zu untersuchen und das Diff manuell auf die andere Datei anzuwenden.

Schauen wir uns an, wie wir einen Konflikt in einem Szenario lösen können, in origin/feature1dem lib/message.rbKonflikte zusammengeführt werden.

  1. Entscheiden Sie, ob unser aktuell ausgecheckter Zweig ( HEAD, oder --ours) oder der Zweig, den wir zusammenführen ( origin/feature1oder --theirs), eine einfachere Änderung darstellt. Die Verwendung von diff mit Triple Dot ( git diff a...b) zeigt die Änderungen an, die bseit der letzten Abweichung von a vorgenommen wurden a, oder mit anderen Worten, vergleichen Sie den gemeinsamen Vorfahren von a und b mit b.

    git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1
    git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. Schauen Sie sich die kompliziertere Version der Datei an. Dadurch werden alle Konfliktmarkierungen entfernt und die von Ihnen ausgewählte Seite verwendet.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
    
  3. Ziehen Sie nach dem Auschecken der komplizierten Änderung das Diff der einfacheren Änderung hoch (siehe Schritt 1). Wenden Sie jede Änderung von diesem Unterschied auf die widersprüchliche Datei an.


4
Wie würde das Zusammenführen aller Konflikte auf einmal besser funktionieren als einzelne Commits? Ich habe bereits Probleme beim Zusammenführen einzelner Commits (insbesondere von Personen, die Commits nicht in logische Teile aufteilen UND ausreichende Tests zur Überprüfung bereitstellen). Außerdem ist Rebase nicht schlechter als Zusammenführen, wenn es um Backup-Optionen geht. Die intelligente Verwendung von interaktivem Rebase und Tools wie Tortoisegit (mit denen die Auswahl der Commits berücksichtigt werden kann) wird viel helfen.
Prusswan

8
Ich habe das Gefühl, dass ich den Grund in # 1 angesprochen habe. Wenn einzelne Commits nicht logisch konsistent sind, ist dies umso mehr ein Grund, den logisch konsistenten Zweig zusammenzuführen, damit Sie den Konflikt tatsächlich verstehen können. Wenn Commit 1 fehlerhaft ist und Commit 2 das Problem behebt, ist das Zusammenführen von Commit 1 verwirrend. Es gibt legitime Gründe, warum Sie 15 Konflikte hintereinander bekommen könnten, wie den oben beschriebenen. Auch Ihr Argument, dass die Rebase nicht schlechter ist, ist etwas unbegründet. Rebase mischt schlechte Zusammenführungen in die ursprünglichen guten Commits und lässt die guten Commits nicht herum, damit Sie es erneut versuchen können. Zusammenführen tut.
Edward Anderson

6
Ich stimme dir voll und ganz zu, nilbus. Guter Eintrag; das klärt einige Dinge auf. Ich frage mich, ob Rerere hier eine Hilfe wäre. Vielen Dank für den Vorschlag zur Verwendung von diff3. Ich werde diesen jetzt definitiv einschalten.
Derick

45
+1, weil ich nur von diff3 erzählt habe - wie oft habe ich einen unverständlichen Konflikt gesehen und verflucht, wer dafür verantwortlich ist, mir nicht zu sagen, was der gemeinsame Vorfahr zu sagen hat. Vielen Dank.
John

4
Dies sollte die akzeptierte Antwort gewesen sein. Der Rebase-Workflow ist ebenfalls schrecklich, da er die Tatsache verbirgt, dass zu einem bestimmten Zeitpunkt eine große Divergenz in der Codebasis aufgetreten ist. Dies kann hilfreich sein, wenn Sie wissen möchten, wie der von Ihnen betrachtete Code geschrieben wurde. Nur kleine Zweige, die nicht in Konflikt stehen, sollten auf den Master zurückgesetzt werden.
Robert Rüger

32

In meinem Workflow stelle ich so viel wie möglich neu ein (und ich versuche es oft. Wenn sich die Diskrepanzen nicht ansammeln, werden die Anzahl und die Schwere der Kollisionen zwischen Zweigen drastisch reduziert).

Selbst in einem größtenteils auf Rebase basierenden Workflow gibt es jedoch einen Platz für Zusammenführungen.

Denken Sie daran, dass durch das Zusammenführen tatsächlich ein Knoten mit zwei übergeordneten Elementen erstellt wird. Betrachten Sie nun die folgende Situation: Ich habe zwei unabhängige Feature-Brances A und B und möchte nun Dinge für den Feature-Zweig C entwickeln, die sowohl von A als auch von B abhängen, während A und B überprüft werden.

Was ich dann mache, ist folgendes:

  1. Erstellen Sie (und checken Sie) Zweig C über A.
  2. Füge es mit B zusammen

Jetzt enthält Zweig C Änderungen von A und B, und ich kann ihn weiterentwickeln. Wenn ich etwas an A ändere, rekonstruiere ich das Diagramm der Zweige folgendermaßen:

  1. Erstellen Sie den Zweig T oben auf A.
  2. verschmelze T mit B.
  3. C auf T zurücksetzen
  4. Zweig T löschen

Auf diese Weise kann ich tatsächlich beliebige Diagramme von Zweigen verwalten, aber etwas Komplexeres als die oben beschriebene Situation zu tun, ist bereits zu komplex, da es kein automatisches Werkzeug gibt, um die Neubasierung durchzuführen, wenn sich das übergeordnete Element ändert.


1
Sie könnten das gleiche mit nur Rebases erreichen. Die Zusammenführung ist hier eigentlich nicht notwendig (außer wenn Sie die Commits nicht duplizieren wollen - aber ich sehe das kaum als Argument).
Odwl

1
In der Tat möchte ich die Commits nicht duplizieren. Ich möchte die Bordstruktur meiner Arbeit so sauber wie möglich halten. Aber das ist eine Frage des persönlichen Geschmacks und nicht unbedingt für jeden geeignet.
Alex Gontmakher

Ich stimme dem ersten Absatz zu 100% zu. (@ Edwards Antwort funktioniert, wenn dies nicht der Fall ist, aber ich möchte lieber, dass alle Projekte der Welt so funktionieren, wie Sie es vorschlagen). Der Rest der Antwort scheint etwas weit hergeholt zu sein, da die Arbeit an C während A und B bereits riskant ist (zumindest in dem Maße, in dem es wirklich von A und B abhängt), und sogar am Ende Sie würde die Zusammenführungen wahrscheinlich nicht beibehalten (C würde zusätzlich zu den neuesten und größten neu basiert).
Alois Mahdal

23

Verwenden Sie NICHT git push origin - Spiegel unter fast allen Umständen.

Es wird nicht gefragt, ob Sie sicher sind, dass Sie dies tun möchten, und Sie sollten besser sicher sein, da dadurch alle Ihre Remote-Zweige gelöscht werden, die sich nicht in Ihrer lokalen Box befinden.

http://twitter.com/dysinger/status/1273652486


6
Oder tun Sie keine Dinge, bei denen Sie sich nicht sicher sind, wie das Ergebnis aussehen wird? Eine Maschine, die ich früher als Administrator hatte, war Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions im MOTD.
Richo

Verwenden Sie es, wenn Sie ein gespiegeltes Repo haben (obwohl es in meinem Fall jetzt von einem speziellen Benutzer im Quell-Repo am Post-Receive-Hook ausgeführt wird)
prusswan

14

Ich habe eine Frage, nachdem ich Ihre Erklärung gelesen habe: Könnte es sein, dass Sie nie eine gemacht haben

git checkout master
git pull origin
git checkout my_new_feature

bevor Sie den 'git rebase / merge master' in Ihrem Feature-Zweig ausführen?

Weil Ihr Hauptzweig nicht automatisch aus dem Repository Ihres Freundes aktualisiert wird. Das muss man mit dem machen git pull origin. Dh vielleicht würden Sie sich immer von einer sich nie ändernden lokalen Hauptniederlassung erholen? Und dann kommt die Push-Zeit, Sie pushen in ein Repository, das (lokale) Commits hat, die Sie nie gesehen haben, und daher schlägt der Push fehl.


13

In Ihrer Situation denke ich, dass Ihr Partner richtig ist. Das Schöne an der Neugründung ist, dass Ihre Änderungen für den Außenstehenden so aussehen, als ob sie alle in einer sauberen Reihenfolge von selbst stattgefunden hätten. Das heisst

  • Ihre Änderungen sind sehr einfach zu überprüfen
  • Sie können weiterhin nette, kleine Commits erstellen und dennoch Sätze dieser Commits gleichzeitig öffentlich machen (indem Sie sie mit dem Master zusammenführen)
  • Wenn Sie sich den öffentlichen Hauptzweig ansehen, sehen Sie verschiedene Commits für verschiedene Funktionen von verschiedenen Entwicklern, die jedoch nicht alle miteinander vermischt werden

Sie können Ihren privaten Entwicklungszweig weiterhin zur Sicherung in das Remote-Repository verschieben, andere sollten dies jedoch nicht als "öffentlichen" Zweig behandeln, da Sie die Basis neu festlegen. Übrigens ist ein einfacher Befehl dafür git push --mirror origin.

Der Artikel Verpackungssoftware mit Git macht einen ziemlich guten Job und erklärt die Kompromisse zwischen Zusammenführen und Neu-Basieren. Es ist ein etwas anderer Kontext, aber die Prinzipien sind die gleichen - es kommt im Wesentlichen darauf an, ob Ihre Filialen öffentlich oder privat sind und wie Sie sie in die Hauptlinie integrieren möchten.


1
Der Link zur Verpackungssoftware mit git funktioniert nicht mehr. Ich konnte keinen guten Link finden, um die ursprüngliche Antwort zu bearbeiten.
Chetan

Sie sollten nicht originin ein drittes dediziertes Backup-Repository spiegeln.
Miral

12

Wie auch immer, ich habe meinen Workflow in einem kürzlich durchgeführten Zweig verfolgt, und als ich versuchte, ihn wieder zum Master zusammenzuführen, ging alles zur Hölle. Es gab Unmengen von Konflikten mit Dingen, die keine Rolle hätten spielen sollen. Die Konflikte machten für mich einfach keinen Sinn. Ich brauchte einen Tag, um alles zu klären, und gipfelte schließlich in einem erzwungenen Druck auf den Remote-Master, da mein lokaler Master alle Konflikte gelöst hat, aber der Remote-Master immer noch nicht glücklich war.

Weder in den Workflows Ihres Partners noch in Ihren vorgeschlagenen Workflows sollten Sie auf Konflikte gestoßen sein, die keinen Sinn ergeben haben. Selbst wenn Sie dies getan haben und die vorgeschlagenen Workflows befolgen, sollte nach der Lösung kein "erzwungener" Push erforderlich sein. Dies deutet darauf hin, dass Sie den Zweig, auf den Sie verschoben haben, nicht zusammengeführt haben, sondern einen Zweig verschieben mussten, der nicht von der Remote-Spitze abstammt.

Ich denke, Sie müssen genau hinschauen, was passiert ist. Könnte jemand anderes (absichtlich oder nicht) den Remote-Master-Zweig zwischen Ihrer Erstellung des lokalen Zweigs und dem Punkt, an dem Sie versucht haben, ihn wieder in den lokalen Zweig zusammenzuführen, zurückgespult haben?

Im Vergleich zu vielen anderen Versionskontrollsystemen habe ich festgestellt, dass die Verwendung von Git weniger Kampf gegen das Tool erfordert und es Ihnen ermöglicht, an den Problemen zu arbeiten, die für Ihre Quelldatenströme von grundlegender Bedeutung sind. Git führt keine Magie aus, daher führen widersprüchliche Änderungen zu Konflikten, aber es sollte das Schreiben einfacher machen, indem die Herkunft des Commits verfolgt wird.


Sie implizieren, dass das OP eine unentdeckte Grundlage oder einen Fehler in seinem Prozess hat, oder?
Krosenvold

8

"Selbst wenn Sie ein einzelner Entwickler mit nur wenigen Zweigen sind, lohnt es sich, sich daran zu gewöhnen, Rebase zu verwenden und ordnungsgemäß zusammenzuführen. Das grundlegende Arbeitsmuster sieht folgendermaßen aus:

  • Erstellen Sie einen neuen Zweig B aus dem vorhandenen Zweig A.

  • Änderungen in Zweig B hinzufügen / festschreiben

  • Aktualisieren Sie Updates aus Zweig A.

  • Änderungen von Zweig B auf Zweig A zusammenführen "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/


7

Nach allem, was ich beobachtet habe, neigt Git Merge dazu, die Zweige auch nach dem Zusammenführen getrennt zu halten, während Rebase Merge sie dann zu einem einzigen Zweig kombiniert. Letzteres ist viel sauberer, während bei ersteren auch nach dem Zusammenführen leichter herauszufinden ist, welche Commits zu welchem ​​Zweig gehören.


4

Mit Git gibt es keinen „richtigen“ Workflow. Verwenden Sie alles, was Ihr Boot schwimmt. Wenn Sie jedoch beim Zusammenführen von Zweigen ständig Konflikte bekommen, sollten Sie Ihre Bemühungen möglicherweise besser mit Ihren Mitentwicklern koordinieren. Klingt so, als würden Sie beide immer wieder dieselben Dateien bearbeiten. Achten Sie auch auf Leerzeichen und Subversion-Schlüsselwörter (z. B. "$ Id $" und andere).


0

Ich verwende nur den Rebase-Workflow, weil er visuell klarer ist (nicht nur in GitKraken, sondern auch in Intellij und in gitk, aber ich empfehle den ersten am meisten): Sie haben einen Zweig, der vom Master stammt und zum Master zurückkehrt . Wenn das Diagramm sauber und schön ist, werden Sie wissen, dass nie etwas zur Hölle geht .

Geben Sie hier die Bildbeschreibung ein

Mein Workflow ist fast der gleiche wie der Ihre, aber mit nur einem kleinen Unterschied: Ich squashbegebe mich in einem in meiner lokalen Niederlassung vor rebasemeiner Niederlassung zu den neuesten Änderungen master, weil:

rebasearbeitet auf der Grundlage jedes Commits

Das heißt, wenn Sie 15 Commits haben, die dieselbe Zeile ändern master, müssen Sie 15 Mal überprüfen, ob Sie nicht quetschen, aber was zählt, ist das Endergebnis, richtig?

Der gesamte Workflow lautet also:

  1. Zur Kasse gehen masterund ziehen, um sicherzustellen, dass Sie die neueste Version haben

  2. Erstellen Sie von dort aus einen neuen Zweig

  3. Wenn Sie Ihre Arbeit dort erledigen, können Sie sich mehrmals frei festlegen und ohne Bedenken auf Remote zugreifen, da es sich um Ihre Niederlassung handelt.

  4. Wenn Ihnen jemand sagt: "Hey, mein PR / MR ist genehmigt, jetzt wird er zum Master zusammengeführt", können Sie ihn abrufen / ziehen. Sie können dies jederzeit oder in Schritt 6 tun.

  5. Nachdem Sie alle Ihre Arbeiten ausgeführt haben, legen Sie sie fest. Wenn Sie mehrere Festschreibungen vorgenommen haben, zerquetschen Sie sie (dies sind alle Ihre Arbeiten, und wie oft Sie eine Codezeile ändern, spielt keine Rolle. Das einzig Wichtige ist die endgültige Version). Schieben Sie es oder nicht, es spielt keine Rolle.

  6. Zur Kasse gehen master, pullum sicherzustellen, dass Sie die neuesten masterlokalen Informationen haben . Ihr Diagramm sollte ungefähr so ​​aussehen:

Geben Sie hier die Bildbeschreibung ein

Wie Sie sehen können, befinden Sie sich in Ihrer lokalen Niederlassung, die aus einem veralteten Status stammt master, während master(sowohl lokal als auch remote) Änderungen an Ihrem Kollegen vorgenommen wurden.

  1. Kasse zurück zu deiner Filiale und zurück zum Master. Sie haben jetzt nur noch ein Commit, sodass Sie die Konflikte nur einmal lösen . (Und in GitKraken müssen Sie Ihren Zweig nur auf master"Rebase" ziehen und "Rebase" auswählen; ein weiterer Grund, warum es mir gefällt.) Danach werden Sie es sein mögen:

Geben Sie hier die Bildbeschreibung ein

  1. Jetzt haben Sie alle Änderungen auf dem neuesten Stand master, kombiniert mit Änderungen in Ihrer Branche. Sie können jetzt auf Ihre Fernbedienung drücken, und wenn Sie zuvor gedrückt haben, müssen Sie das Drücken erzwingen. Git wird Ihnen sagen, dass Sie nicht einfach schnell vorspulen können. Das ist normal, da Sie aufgrund der Rebase den Startpunkt Ihres Zweigs geändert haben. Aber Sie sollten keine Angst haben: Setzen Sie die Kraft mit Bedacht ein . Am Ende ist die Fernbedienung auch Ihr Zweig, sodass Sie auch dann keinen Einfluss haben master, wenn Sie etwas falsch machen.

  2. Erstellen Sie PR / MR und warten Sie, bis es genehmigt wurde, damit Sie masterIhren Beitrag erhalten. Glückwunsch! Sie können jetzt auschecken master, Ihre Änderungen abrufen und Ihren lokalen Zweig löschen, um das Diagramm zu bereinigen. Der Remote-Zweig sollte ebenfalls gelöscht werden, wenn dies beim Zusammenführen mit dem Master nicht erfolgt.

Das endgültige Diagramm ist wieder sauber und klar:

Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.