Wie und warum man Bäume in Git stiehlt

Bäume


In diesem Artikel werde ich über einen nützlichen, aber wenig bekannten Trick bei der Arbeit mit Git sprechen - wie man einfach ein Commit mithilfe eines Baums aus einem anderen Commit erstellt. Einfach ausgedrückt, wie Sie den gewünschten Status des Projekts in einem Zweig erhalten, wenn dieser Status bereits zuvor irgendwo im Repository vorhanden war. Es werden einige Beispiele gegeben, wie dies es ermöglicht, einige praktische Probleme elegant zu lösen. Insbesondere werde ich über die Methode sprechen, die ich gefunden habe und die die Korrektur mehrerer Konflikte während der Rebase erheblich vereinfachen kann. Darüber hinaus ist dieser Artikel eine gute Möglichkeit, in der Praxis zu verstehen, was ein Commit in Git ausmacht.


Inhalt


Der theoretische Teil. Über Commits und Bäume
Git Commit-Baum
Praktischer Teil
1. Synchronisation mit einem anderen Zweig
2. Vergleich zweier Zweige
3. Branch Reverse
4. Teilweise Umkehrung
5. Künstliche Verschmelzung
6a. Rebase-Methode über Merge - Beschreibung
6b. Rebase-Methode über Merge-Skript
7. Alias
Fazit



Der theoretische Teil. Über Commits und Bäume


Commit ist wahrscheinlich das grundlegendste Konzept in Git. Mal sehen, woraus es besteht. Jedes Commit hat eine eigene eindeutige Kennung in Form eines Hashs , z. B. 5e45ecb . Und mit dem folgenden Befehl können wir, wenn wir den Hash kennen, seinen Inhalt sehen.


git cat-file -p 5e45ecb 

 tree 8640790949c12690fc71f9abadd7b57ec0539376 parent 930741d1f5fd2a78258aa1999bb4be897ba3d015 author Mark Tareshawty <tareby...@github.com> 1542718283 -0500 committer Mark Tareshawty <tareby...@github.com> 1542718283 -0500 gpgsig -----BEGIN PGP SIGNATURE----- ... -----END PGP SIGNATURE----- Fix scoping so that we don't break the naming convention 

Diese wenigen Zeilen sind der gesamte Inhalt des Commits:


  • Baum - eine Verknüpfung zu einem bestimmten Projektstatus
  • parent - Link zum übergeordneten Commit
  • Autor - Autor des ursprünglichen Commits + Datums
  • Committer - Ersteller dieses Commits + Datums
  • gpgsig - digitale Signatur (falls verfügbar)
  • message - Commit-Text

Ich möchte Sie daran erinnern, dass das Commit in git (im Gegensatz zu anderen VCS) die vorgenommenen Änderungen nicht beschreibt. Das Gegenteil ist der Fall: Jedes Commit beschreibt den spezifischen Status des Projekts als Ganzes, und was wir als Änderungen betrachten, ist tatsächlich ein dynamisch berechneter Unterschied zum vorherigen Status. Es ist auch erwähnenswert, dass alle Commits unveränderlich (unveränderlich) sind, dh mit Rebase / Cherry-Pick / Amendment werden absolut neue Commits erstellt.


Baum (Baum) - Tatsächlich ist es nur ein Ordner mit einem bestimmten unveränderlichen Inhalt. Ein Objekt vom Typ Baum enthält eine Liste von Dateien mit bestimmten Inhalten (Blobs) und Unterordnern (Bäumen). Der Baum, auf den jedes Commit verweist, ist der Stammordner des Projekts bzw. dessen spezifischer Status.


Bauminhalte anzeigen

Dies kann für jedes andere Objekt (Commit / Tree / Blob) genauso durchgeführt werden. Es reicht aus, die ersten eindeutigen Hash-Zeichen zu verwenden: 8640790949c12690fc71f9abadd7b57ec0539376 -> 8640790.


 git cat-file -p 8640790 

 100644 blob 7ab08294a46f158c51460be3e7df6a190e15023b .env.example 100644 blob 0a1a4d1ad9ff3f35b67678ca893811e91b423af5 .gemset 040000 tree 033aa38ce0eab11fe229067c14ccce95e2b8b601 .github 100644 blob ca49bb7ffa6273b0be4ce7ba1accba456032fb11 .gitignore 100644 blob c99d2e7396e14ac072c63ec8419d9b8fede28d86 .rspec 100644 blob 65e77a2f59f635a8f24eb4714e8e43745c5c0eb9 .rubocop.yml 100644 blob 8e8299dcc068356889b365e23c948b92c6dfcd78 .ruby-version 100644 blob 19028f9885948aca2ba61f9d062e9dc21c21ad03 .stylelintrc.json 100644 blob 2f7a032fbc3f4f7195bfd91cb33889a684b572b9 .travis.yml 100644 blob 121615722a6c206a9fe24b9a1c9b647662a460d2 ARCHITECTURE.md 100644 blob 898195daeea0bbf8c5930deeaf1020ba8abab34a Gemfile 100644 blob de7ca707f9fe9172db941b65cdacaba7e024fc06 Gemfile.lock 100644 blob e6ff62fefd071b1a8ca279bae94ddbc4dd17b7a3 Gruntfile.js 100644 blob 0cac5b30fb32d36cce2aeb7d936be7b6207d68c7 MIT-LICENSE.txt 100644 blob c2c566e8cc3d440d3ee8041b79cded416db28136 Procfile 100644 blob d1fb2f575380e1e093a4d82e3f19e51f0b99a0a1 Procfile.dev 100644 blob 3a88e138f10fa65bd2cfe1a1d3292348205508b5 README.md 100644 blob 5366e6e073cc426518894cc379d3a07cf3c9cfb3 Rakefile 100644 blob e6d3d2d3e9d5122c5f75bbeee8ed0917ad38c131 app.json 040000 tree 94f83cf03bd6f1cf14672034877b14604744b7a2 app 040000 tree d4d859e82564250b4c4f2047de21e089e7555475 bin 100644 blob 1f71007621f17334fd6f2dd71c87b7a16867119c config.ru 040000 tree 9e8e4bf5ec44541aefff544672b94ca8a9d07bbf config 040000 tree 31b8d0e1fa2bb789dbd6319e04fc9f115952cf2a db 040000 tree 38e7a13e0e772c2a13e46d2007e239f679045bee doc 040000 tree a6e35ded8b35837660cf786e637912377f845515 lib 040000 tree d564d0bc3dd917926892c55e3706cc116d5b165e log 100644 blob 843523565ddee5e00f580d9c4e37fc2478fdaecc package-lock.json 100644 blob 791ee833ad316d75b1d2c83a64a3053fc952d254 package.json 040000 tree 4645317c52675d9889f89b26f4dd4d2ae1d8cbad public 040000 tree 31d3f8ae4a4ffe62787134642743ed32a35dbae2 resources 040000 tree 807ffa29868ef9c25ddb4b4126a4bb7f1b041bf0 script 040000 tree 4c3bf9a7f3679ba059b0f1c214a500d197546462 spec 040000 tree 136c8174412345531a9542cafef25ce558d2664f test 040000 tree e6524eafe066819e4181bc56c503320548d8009b vendor 

Dies ist eigentlich das wichtigste Merkmal der Funktionsweise von Git. Die Festschreibungskennung ist wirklich ein Hash ihres Inhalts. Genau wie die Hashes von darin eingebetteten Objekten (Bäume, Blobs).


Nun wollen wir sehen, was passiert, wenn wir es tun


 git commit -m "Fixed bug" 

Dieser Befehl erstellt ein neues Commit, das Folgendes erfasst:


  • Status des Staging-Projekts (als neues Baumobjekt gespeichert und dessen Hash übernommen)
  • Link zum aktuellen (übergeordneten) Commit
  • Autor + Committer + zwei Daten
  • Text festschreiben

Dies alles wird gespeichert, gehasht und ein neues Festschreibungsobjekt wird erhalten. Und das Team erhöht automatisch den aktuellen Verzweigungszeiger darauf.


ein bisschen über Terminologie

Wie wir bereits wissen, ist Baum ein Objekt, das den Status des Projekts zu einem bestimmten Zeitpunkt in der Vergangenheit enthält - als mit diesem Baum ein Commit erstellt wurde.


Der Arbeitsordner heißt Arbeitsbaum / Arbeitskopie / Arbeitsverzeichnis, was ziemlich logisch ist.


Wir haben auch - Staging-Bereich / Index - den Bereich der vorbereiteten Änderungen. Logischerweise ist dies aber auch ein Baum oder vielmehr der Status, der beim Festschreiben als Baum gespeichert wird. Daher scheint es mir logischer, einen inszenierten Baum zu nennen.



Git Commit-Baum


Schließlich können wir den von uns benötigten Befehl git commit-tree beschreiben. Formal ist dies einer der Befehle auf niedriger Ebene, daher wird er selten erwähnt und verwendet. Andere damit verbundene Befehle auf niedriger Ebene werden nicht berücksichtigt (z. B. Git-Schreibbaum, Git-Update-Index, sie werden auch als Installationsbefehle bezeichnet). Wir sind nur an einer bestimmten Konsequenz interessiert: Mit diesem Befehl können wir den Projektstatusbaum einfach von jedem anderen Commit kopieren (wiederverwenden).


Schau dir ihre Herausforderung an


 git commit-tree 4c835c2 -m "Fixed bug" -p a8fc5e3 

 d9aded78bf57ca906322e26883644f5f36cfdca5 

Der Befehl git commit-tree wird ebenfalls festgeschrieben, jedoch auf niedriger Ebene. Hier müssen Sie den bereits vorhandenen Baum (Baum) 4c835c2 und einen Link zum übergeordneten Commit a8fc5e3 explizit angeben. Und es gibt den Hash des neuen Commits d9aded7 zurück, und die Position des Zweigs ändert sich nicht (daher scheint dieses Commit in der Luft zu frieren).



Praktischer Teil


Beispiele für die Verwendung dieses Befehls werden im folgenden einfachen Repository gezeigt.



Es enthält drei Zweige:


Master - Hauptzweig
alpha - der Zweig, an dem wir arbeiten und arbeiten
Beta - ein Zweig, der zuvor im Master blockiert war


Alle Aktionen können einfach lokal wiederholt werden. Dazu reicht es aus, das Repository zu klonen, in den Alpha-Zweig zu gelangen und dann die Befehle aus den Beispielen auszuführen. Dieser Ausgangszustand ist allen Beispielen gemeinsam.


 git clone https://github.com/capslocky/git-commit-tree-example.git cd ./git-commit-tree-example/ git checkout alpha 

unter Fenstern

Alle Befehle, einschließlich des Skripts, funktionieren auch unter Windows. Sie müssen beispielsweise nur das Bash-Terminal im Projektordner öffnen


 "C:\Program Files\Git\git-bash.exe" --cd="D:\path\project" 


1. Synchronisation mit einem anderen Zweig


Herausforderung:
Synchronisieren Sie den Projektstatus auf dem Alpha- Zweig mit dem Beta- Zweig. Das heißt, Sie müssen ein solches neues Commit für den Alpha- Zweig erstellen, damit der Status des Projekts genau dem des Beta- Zweigs entspricht.


Insbesondere ist es unwahrscheinlich, dass eine solche Aufgabe jemals auftritt, aber dies ist der am besten geeignete Fall, um den Ansatz zu demonstrieren.


Die einfachste Lösung besteht darin, den vorhandenen Baum, auf den der Beta-Zweig zeigt, zu übernehmen und nur aus dem neuen Commit für den Alpha-Zweig darauf zu verweisen. Da dies das erste Beispiel ist, wird seine gesamte Logik ausreichend detailliert betrachtet.


Suchen Sie zunächst den Commit-Hash, auf den der Beta-Zweig verweist:


 git rev-parse origin/beta 

 280c30ff81a574f8dd41721726cf60b22fb2eced 

280c30f - nimm einfach die ersten paar Zeichen


Suchen Sie nun den Hash seines Baums, indem Sie den Inhalt des Commits über die Git-Cat-Datei anzeigen:


 git cat-file -p 280c30f 

 tree 3c1afe75f54518dbd82ea7a4e3c4ff50389a573a <--- parent 560b449675513bc8f8f4d6cda56a922d4e36917a author Baur <atanov...@gmail.com> 1540619512 +0600 committer Baur <atanov...@gmail.com> 1540619512 +0600 Added info about windows 

3c1afe7 - das ist der Baum, den wir brauchen


Und jetzt erstellen wir ein Commit, das auf diesen Baum verweist, und geben mit dem übergeordneten Commit das aktuelle Commit an:


 git commit-tree 3c1afe7 -m "Synced with branch 'beta'" -p HEAD 

 eb804d403d4ec0dbeee36aa09da706052a7cc687 

Das war's, das Commit wurde erstellt, das Team hat seinen Hash bekommen. Darüber hinaus ist dieser Wert immer eindeutig, da er nicht nur aus dem Baum, sondern auch aus dem Autor und der Zeit berechnet wird. Das Commit selbst ist in der Luft eingefroren, solange es keinen der Zweige betritt. Es reicht für uns, die ersten paar Zeichen zu nehmen: eb804d4 , dieser Wert , der für jeden Fall eindeutig ist, werde ich als xxxxxxx bezeichnen . Schauen wir uns den Inhalt an:


 git cat-file -p xxxxxxx 

 tree 3c1afe75f54518dbd82ea7a4e3c4ff50389a573a <--- parent 64fafc79e8f6d22f5226490daa5023062299fd6c author Peter <peter...@gmail.com> 1545230299 +0600 committer Peter <peter...@gmail.com> 1545230299 +0600 Synced with branch 'beta' 

Großartig, es hat den gleichen Baum wie das Commit für den Origin / Beta-Zweig. Und da dieses Commit ein direkter Nachkomme des aktuellen Zweigs ist, führen Sie es einfach in den Zweig ein, um es in den Zweig aufzunehmen


 git merge --ff xxxxxxx 

 Updating 64fafc7..xxxxxxx Fast-forward Azure.txt | 3 --- Bill.txt | 6 +----- Linus.txt | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) 

Fertig. Jetzt ist der Status des Projekts im Alpha-Zweig genau der gleiche wie im Beta-Zweig. [update] Und wenn Sie sich ansehen, was dieses Commit schließlich geändert hat, werden wir sehen: Es hat alle seine eigenen Commits (Änderungen) des Alpha-Zweigs rückgängig gemacht und alle eindeutigen Commits (Änderungen) des Beta-Zweigs relativ zu ihrem gemeinsamen Vorfahren-Commit hinzugefügt.




2. Vergleich zweier Zweige


Herausforderung:
Vergleichen Sie den Alpha- Zweig mit dem Beta- Zweig.


Das erste Beispiel zeigt, dass das erstellte Commit alle Änderungen anzeigt, die den tatsächlichen Unterschied zwischen den beiden Zweigen darstellen. Diese Eigenschaft erleichtert den Vergleich eines Zweigs mit einem anderen. Es reicht aus, einen dritten temporären Zweig zu erstellen und darin ein ähnliches Commit durchzuführen.


Bringen Sie also zuerst den Alpha-Zweig in seinen ursprünglichen Zustand zurück


 git reset --hard origin/alpha 

Lassen Sie uns einen temporären Zweig auf dem aktuellen erstellen und darauf stehen


 git checkout -b temp 

Und wir müssen das Gleiche tun wie im vorherigen Beispiel. Aber diesmal werden wir eine Linie treffen. Zu diesem Zweck verwenden wir die spezielle Syntax, um auf den Commit-Tree- Ursprung / Beta ^ {Tree} oder denselben 280c30f ^ {Tree} zuzugreifen .


 git merge --ff $(git commit-tree origin/beta^{tree} -m "Diff with branch 'beta'" -p HEAD) 

Fertig, im Wesentlichen haben wir uns als Commit-Diff zwischen zwei Zweigen materialisiert


 git show 

 git diff alpha origin/beta 

Natürlich können wir ein solches "vergleichendes" Commit für zwei beliebige Commits (Zustände) im Repository erstellen.




3. Branch Reverse


Herausforderung:
Setzen Sie die letzten Commits zurück.


Kehren wir zum Alpha-Zweig zurück und löschen den temporären Zweig


 git checkout alpha git branch -D temp 

Angenommen, wir müssen die letzten beiden Commits für den Alpha-Zweig zurücksetzen. Es gibt zwei klassische Möglichkeiten, dies zu tun:


  1. Führen Sie git revert zweimal aus - pro Commit
  2. Git-Reset, d. h. Zurücksetzen der Verzweigungsposition

Aber Sie können dies auf eine dritte Weise tun:


 git merge --ff $(git commit-tree 7a714bf^{tree} -m "Reverted to commit 7a714bf" -p HEAD) 

Dadurch wird ein neues Commit hinzugefügt, das die Änderungen der beiden vorherigen Commits rückgängig macht. Im Gegensatz zur ersten Methode wird nur ein Commit erstellt, selbst wenn Sie die letzten zehn Commits zurücksetzen müssen. Und der Unterschied zwischen der Methode mit Git-Reset besteht darin, dass wir diese Commits nicht aus dem Zweig selbst werfen.


Wenn Sie dann den ursprünglichen Status des Zweigs zurückgeben müssen, kann dies auf die gleiche Weise erfolgen


 git merge --ff $(git commit-tree 64fafc7^{tree} -m "Reverted back to commit 64fafc7" -p HEAD) 

Gleichzeitig bleiben diese beiden Commits in der Geschichte der Niederlassung erhalten, wonach ersichtlich ist, dass sie zurückgesetzt und zurückgegeben wurde.




4. Teilweise Umkehrung


Herausforderung:
Führen Sie in den letzten Commits Änderungen an einigen Dateien zurück.


Bringen Sie den Alpha-Zweig wieder in seinen ursprünglichen Zustand zurück


 git reset --hard origin/alpha 

Der Alpha-Zweig enthält 3 Commits, bei denen jeweils Änderungen an der Datei Bill.txt vorgenommen werden. Beim letzten Commit wird auch die Datei Azure.txt hinzugefügt. Angenommen, wir müssen die Änderungen an der Bill.txt-Datei für die letzten 2 Commits zurücksetzen, ohne andere Dateien zu berühren.


Führen Sie zunächst alle Dateien zurück, die 2 festgeschrieben wurden


 git merge --ff $(git commit-tree 7a714bf^{tree} -m "any text" -p HEAD) 

Setzen Sie als Nächstes den Zweig auf das vorherige Commit zurück, ohne jedoch den Status des Projekts auf der Festplatte zu berühren


 git reset HEAD~1 

Und jetzt reicht es aus, die erforderlichen Dateien zu zasteydieren und festzuschreiben, und andere Änderungen können verworfen werden.


 git add Bill.txt git commit -m "Reverted file Bill.txt to 7a714bf" git reset --hard HEAD 



5. Künstliche Verschmelzung


Herausforderung:
Schieben Sie einen Zweig in einen anderen und erhalten Sie ein vorbestimmtes Ergebnis.


Stellen Sie sich diese Situation vor. In der Produktion wurde ein kritischer Fehler festgestellt, der wie üblich dringend behoben werden muss. Es ist jedoch nicht klar, wie viel Zeit es dauern wird, es zu studieren und den richtigen Hotfix zu erstellen. Daher wurde schnell ein temporärer Hotfix erstellt, der die Interaktion mit dem Problemmodul isolierte. Im Master-Zweig wird also ein Commit mit diesem temporären Patch angezeigt. Nach einiger Zeit müssen Sie anstelle des Commits einen bereits vollwertigen Hotfix starten.


Daher müssen wir den Alpha-Zweig im Master zusammenführen, dies sollte jedoch keine herkömmliche Zusammenführung sein, wenn alle eindeutigen Änderungen des Alpha-Zweigs von oben zum Master hinzugefügt werden und ein Konflikt vorliegt und wir den Master vollständig mit dem Alpha-Zweig überschreiben müssen.


Lassen Sie uns zunächst daran erinnern, worum es beim Zusammenführungs-Commit geht - es ist eigentlich das gleiche normale Commit, aber es hat nur zwei übergeordnete Commits. Dies ist deutlich sichtbar, wenn Sie sich den Inhalt ansehen (wie der Baum gebildet wird, ist ein separates Problem). . Und wer von wem bestimmt wird, ist einfach - das erste übergeordnete Commit wird als das wichtigste angesehen.


 git cat-file -p 7229df8 

 tree 3c1afe75f54518dbd82ea7a4e3c4ff50389a573a parent fd54ab7dde87593b9892b6d1ffbf1afd39ba6f9e parent 280c30ff81a574f8dd41721726cf60b22fb2eced author Baur <atanov...@gmail.com> 1540619579 +0600 committer Baur <atanov...@gmail.com> 1540619592 +0600 Merge branch 'beta' into 'master' 

Setzen Sie den aktuellen Alpha-Zweig zurück und wechseln Sie zum Master


 git reset --hard origin/alpha git checkout master 

Und jetzt das gleiche Team, aber mit zwei Eltern verpflichtet


 git merge --ff $(git commit-tree alpha^{tree} -m "Merge 'alpha' into 'master', but take 'alpha' tree" -p HEAD -p alpha) 

Als wir fertig waren, haben wir den Alpha-Zweig in master bereitgestellt und mussten den temporären Code nicht löschen und Konflikte lösen, da wir in diesem Fall nur die letzten Änderungen neu schreiben mussten.



Eigentlich ist es nicht so seltsam, eine Zusammenführung mit einem Baum zu erstellen, der von einem anderen Commit kopiert wurde. Solche Situationen können auftreten, weil git ein sehr flexibles Tool ist, mit dem Sie verschiedene Ansätze für die Arbeit mit Zweigen und Repositorys implementieren können. Das häufigste Beispiel war jedoch von Anfang an in unserem Repository - versuchen Sie es selbst zu analysieren oder öffnen Sie einen Spoiler, um die Erklärung zu lesen.


Spoiler

Lassen Sie uns darauf achten, wie der Beta-Zweig zurück zum Master erwürgt wurde. Während der Zeit, als zwei Commits darin erschienen, gab es keine neuen Commits in der Hauptniederlassung selbst. Dies bedeutet, dass mit der Merge Beta im Master Konflikte aufgrund gleichzeitiger Änderungen ausgeschlossen werden.




Wenn wir die Beta-Version für die Git-Zusammenführung durchführen würden, würde eine Schnellvorlauf-Zusammenführung ( Standardverhalten ) stattfinden, dh der Hauptzweig würde nur auf demselben Commit wie der Beta-Zweig stehen, und es würde kein Zusammenführungs-Commit geben. Es wäre so:



Hier wurde jedoch keine Schnellvorlauf-Zusammenführung mit dem Befehl git merge beta --no-ff durchgeführt. Das heißt, wir haben die Erstellung des Merge-Commits erzwungen, obwohl dies nicht erforderlich war. Und da der gewünschte Endzustand des Projekts für die zukünftige Zusammenführung bekannt war - dies ist ein Beta-Baum - hat git den Link zu diesem Baum einfach in ein neues Commit kopiert:


 git cat-file -p origin/beta 

 tree 3c1afe75f54518dbd82ea7a4e3c4ff50389a573a <--- parent 560b449675513bc8f8f4d6cda56a922d4e36917a author Baur <atanov...@gmail.com> 1540619512 +0600 committer Baur <atanov...@gmail.com> 1540619512 +0600 Added info about windows 

 git cat-file -p 7229df8 

 tree 3c1afe75f54518dbd82ea7a4e3c4ff50389a573a <--- parent fd54ab7dde87593b9892b6d1ffbf1afd39ba6f9e parent 280c30ff81a574f8dd41721726cf60b22fb2eced author Baur <atanov...@gmail.com> 1540619579 +0600 committer Baur <atanov...@gmail.com> 1540619592 +0600 Merge branch 'beta' into 'master' 


6a. Rebase-Methode über Merge - Beschreibung


Herausforderung:
Es ist notwendig, einen "schweren" Rebase-Zweig zu erstellen (viele Konflikte bei verschiedenen Commits).


Es gibt so ein klassisches Holivar-Thema in git - rebase vs merge. Aber ich werde nicht holivarit. Im Gegenteil, ich werde darüber sprechen, wie sie im Rahmen dieser Aufgabe Freunde werden können.


Im Allgemeinen wurde git speziell entwickelt, damit wir effektiv zusammenführen können. Und als ich zu dem Projekt kam, bei dem der Workflow auf Rebase basiert, war ich zunächst unbehaglich und ungewöhnlich, bis ich Techniken entwickelte, die meine tägliche Arbeit mit Git vereinfachten. Eine davon ist meine ursprüngliche Methode, um eine schwere Rebase durchzuführen.


Wir müssen also den Alpha-Zweig bei der Entwicklung neu aufbauen, damit er später genauso schön gefärbt wird wie der Beta-Zweig. Wenn wir wie gewohnt mit dem Rebase beginnen, führen das erste und das letzte Commit zu zwei verschiedenen Konflikten an zwei verschiedenen Orten. Wenn wir jedoch anstelle einer erneuten Basis nur eine Zusammenführung durchführen würden, gäbe es nur einen Konflikt an einer Stelle in einem Zusammenführungs-Commit.


Wenn Sie dies sicherstellen möchten, biete ich fertige Teams unter dem Spoiler an.


Versteckter Text

Bringen Sie die Zweige wieder in ihren ursprünglichen Zustand


 git checkout master git reset --hard origin/master git checkout alpha git reset --hard origin/alpha 

Erstellen Sie den Zweig "Alpha-Rebase-Konflikte" und stellen Sie ihn auf den Master


 git checkout -b alpha-rebase-conflicts git rebase master 

Bei verschiedenen Commits kommt es zu Konflikten, einschließlich eines Phantomkonflikts.


Versuchen wir nun die Zusammenführung, kehren Sie zum Alpha-Zweig zurück und löschen Sie den Zweig für die Rebase.


 git checkout alpha git branch -D alpha-rebase-conflicts 

Wechseln Sie zum Master und führen Sie die Zusammenführung durch


 git checkout master git merge alpha 

Es wird nur einen einfachen Konflikt geben, wir korrigieren ihn und tun es


 git add Bill.txt git commit -m "Merge branch 'alpha' into 'master'" 

Marge erfolgreich abgeschlossen.


Git-Konflikte sind ein natürlicher Bestandteil unseres Lebens, und dieses einfache Beispiel zeigt, dass das Zusammenführen in dieser Hinsicht definitiv bequemer ist als das Wiederherstellen. In einem realen Projekt wird dieser Unterschied durch einen viel größeren Zeit- und Nervenaufwand gemessen. Daher gibt es beispielsweise eine mehrdeutige Empfehlung, alle Commits eines Feature-Zweigs vor dem Zusammenführen zu einem Commit zusammenzufassen.


Die Idee dieser Methode ist es, eine vorübergehende versteckte Zusammenführung durchzuführen, bei der alle Konflikte auf einmal gelöst werden. Merke dir das Ergebnis (Baum). Führen Sie als Nächstes die übliche Rebase aus, jedoch mit der Option "Konflikte automatisch lösen und unsere Änderungen auswählen". Fügen Sie am Ende dem Zweig ein zusätzliches Commit hinzu, wodurch der richtige Baum wiederhergestellt wird.


Fangen wir an. Bringen Sie beide Zweige wieder in ihren ursprünglichen Zustand zurück.


 git checkout master git reset --hard origin/master git checkout alpha git reset --hard origin/alpha 

Lassen Sie uns eine temporäre Verzweigungstemperatur erstellen, in der wir zusammenführen.


 git checkout -b temp git merge origin/master 

Der Konflikt.


Lassen Sie uns einen einfachen Konflikt in der Datei Bill.txt wie gewohnt lösen (in jedem Editor).
Beachten Sie, dass es nur einen Konflikt gibt und nicht zwei, wie bei Rebase.


 git add Bill.txt git commit -m "Merge branch 'origin/master' into 'temp'" 

Wir kehren zum Alpha-Zweig zurück, führen eine Rebase mit automatischer Lösung aller Konflikte zu unseren Gunsten durch, bringen den temporären Zweig in den Status und löschen den temporären Zweig selbst.


 git checkout alpha git rebase origin/master -X theirs git merge --ff $(git commit-tree temp^{tree} -m "Fix after rebase" -p HEAD) git branch -D temp 


Schließlich schön Mergim Alpha in Master.


 git checkout master git merge alpha --no-ff --no-edit 


Beachten Sie, dass Master, Alpha und der Remote-Temp-Zweig alle drei auf denselben Baum zeigen, obwohl dies drei verschiedene Commits sind.


Nachteile dieser Methode:


  • Es gibt keine manuelle Korrektur für jedes Konflikt-Commit - Konflikte werden automatisch gelöst. Ein solches Zwischen-Commit kann möglicherweise nicht kompiliert werden.
  • Es wurde (aber nicht immer) ein zusätzliches Commit für jede Rebase hinzugefügt

Vorteile:


  • Wir beheben nur echte Konflikte (keine Phantomkonflikte)
  • Alle Konflikte werden nur einmal behoben.
  • Die ersten beiden Punkte sparen Zeit
  • Ein vollständiger Verlauf der Commits und aller Änderungen wird gespeichert (z. B. können Sie Cherry Pick auswählen).
  • Die Methode ist in Form eines Skripts implementiert und kann bei Bedarf jederzeit zum erneuten Basieren verwendet werden (es sind keine Kenntnisse über Bäume usw. erforderlich).


6b. Rebase-Methode über Merge-Skript


Das Skript wird hier veröffentlicht: https://github.com/capslocky/git-rebase-via-merge


Lassen Sie uns die Arbeit an unserem Beispiel überprüfen. Bringen Sie beide Zweige wieder in ihren ursprünglichen Zustand zurück


 git checkout master git reset --hard origin/master git checkout alpha git reset --hard origin/alpha 

Laden Sie das Skript herunter und machen Sie es ausführbar


 curl -L https://git.io/rebase-via-merge -o ~/git-rebase-via-merge.sh chmod +x ~/git-rebase-via-merge.sh 

Fenster

Die Datei wird hier angezeigt: C: \ Benutzer \ Benutzername \ git-rebase-via-merge.sh


Ändern Sie den Standardzweig, für den Sie eine Neubasis benötigen. In unserem Fall benötigen wir origin / master


 nano ~/git-rebase-via-merge.sh 

 default_base_branch='origin/master' 

Lassen Sie uns auch einen temporären Zweig erstellen und darauf stehen, um den Alpha-Zweig selbst nicht zu berühren


 git checkout -b alpha-rebase-test 

Und jetzt können Sie das Skript ausführen (anstelle des traditionellen Ursprungs / Masters der Git-Rebase).


 ~/git-rebase-via-merge.sh 


Skriptergebnis
 $ ~/git-rebase-via-merge.sh This script will perform rebase via merge. Current branch: alpha-rebase-test (64fafc7) Base branch: origin/master (9c6b60a) Continue (c) / Abort (a) c Auto-merging Bill.txt CONFLICT (content): Merge conflict in Bill.txt Automatic merge failed; fix conflicts and then commit the result. You have at least one merge conflict. Fix all conflicts in the following files, stage them up and type 'c': Bill.txt Continue (c) / Abort (a) c [detached HEAD 785d49e] Hidden temp commit to save result of merging 'origin/master' into 'alpha-rebase-test' as detached head. Merge succeeded on hidden commit: 785d49e Starting rebase automatically resolving any conflicts in favor of current branch. First, rewinding head to replay your work on top of it... Auto-merging Bill.txt [detached HEAD a680316] Added history of windows Author: Baur <atanov...@gmail.com> Date: Sat Oct 27 11:45:50 2018 +0600 1 file changed, 6 insertions(+), 3 deletions(-) Committed: 0001 Added history of windows Auto-merging Bill.txt [detached HEAD dcd34a8] Replaced history of windows Author: Baur <atanov...@gmail.com> Date: Sat Oct 27 11:55:42 2018 +0600 1 file changed, 4 insertions(+), 5 deletions(-) Committed: 0002 Replaced history of windows Auto-merging Bill.txt [detached HEAD 8d6d82c] Added file about Azure and info about Windows 10 Author: Baur <atanov...@gmail.com> Date: Sat Oct 27 12:06:27 2018 +0600 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Azure.txt Committed: 0003 Added file about Azure and info about Windows 10 All done. Restoring project state from hidden merge with single additional commit. Updating 8d6d82c..268b320 Fast-forward Bill.txt | 4 ++++ 1 file changed, 4 insertions(+) Done. 


 ~/git-rebase-via-merge.sh origin/develop 

, ours / theirs :


ours — (HEAD)
theirs — (, origin/develop)


rebase — .



7.


git .


.


 git config alias.copy '!git merge --ff $(git commit-tree ${1}^{tree} -p HEAD -m "Tree copy from ${1}")' 

git copy xxx, xxx — .


 git copy xxx 


 git merge --ff $(git commit-tree xxx^{tree} -p HEAD -m "Tree copy from xxx") 


 git copy a8fc5e3 

 git copy origin/beta 

 git copy HEAD~3 

git amend.


 git commit --amend -m "Just for test" 

:


 git commit --amend 



, , . , . — . , , . . " " " " . ( , devops), , .

Source: https://habr.com/ru/post/de433748/


All Articles