Ein paar manchmal beliebte Tricks, wenn man mit Git arbeitet

  • Tutorial

Ich möchte Rezepte für die Lösung einiger Aufgaben teilen, die sich manchmal bei der Arbeit mit git ergeben und die nicht "direkt offensichtlich" sind.


Zuerst dachte ich daran, mehr ähnliche Rezepte zu sammeln, aber es gibt Zeit für alles. Ich denke, wenn es einen Nutzen gibt, dann ist es möglich und allmählich ...


Also ...


Alte Äste mit minimalen Schmerzen zusammenführen


Präambel Es gibt einen Hauptzweig ( master), in dem neue Funktionen und Korrekturen aktiv festgelegt werden. Es gibt einen Parallelzweig feature, in dem die Entwickler einige Zeit in ihrem eigenen Nirvana geschwommen sind, und plötzlich haben sie festgestellt, dass sie seit einem Monat nicht mehr mit dem Master verschmolzen waren und der Zusammenschluss (Kopf mit Kopf) nicht mehr trivial war.


(Ja, hier geht es nicht um eine ideale Welt, in der alles stimmt, es gibt kein Verbrechen, Kinder sind immer gehorsam und überqueren sogar die Straße strikt an der Hand mit der Mutter, wobei sie sich vorsichtig umsehen).


Zweck: zu tragen. In diesem Fall war es ein "sauberer" Zusammenschluss ohne Merkmale. Ie so dass in einem öffentlichen Repository in der Verzweigungsgrafik zwei Threads an einem Punkt mit der Nachricht "Zusammengefügter Zweig" Master "in Feature" verbunden sind. Und das ganze "dieses hier" Kopfschmerzen darüber, wie viel Zeit und Mühe es brauchte, wie viele Konflikte entschieden wurden und wie viele Haare es grau geworden hat, wurde nicht behalten.


Die Handlung Die Tatsache, dass Sie in der Gita das letzte Commit mit einem Schlüssel bearbeiten können, den --amendjeder kennt. Der Punkt ist, dass dieses "letzte Commit" irgendwo sein kann und alles enthalten kann. Zum Beispiel kann es nicht nur ein "letzter Commit für einen linearen Zweig" sein, bei dem vergessen wurde, einen Tippfehler zu korrigieren, sondern auch ein Merge-Commit aus einer normalen oder "Octopus" -Mischung. --amendauf genau die gleiche Weise werden die vorgeschlagenen Änderungen und die Inline-Änderung des Commit in den Baum gerollt, als ob es wirklich als Ergebnis einer ehrlichen Verschmelzung und Konfliktlösung erscheint. In der Tat git merge, und git commit --amendes erlaubt Ihnen , völlig getrennt „zastolblenie place“ auf ( „die in dem Baum begehen befindet sich hier“) und der Inhalt des begehen.


Die Grundidee eines komplexen Merge-Commits mit einer sauberen Historie ist einfach: Zuerst "Wir stapeln den Ort", erstellen Sie ein sauberes Merge-Commit (unabhängig vom Inhalt), schreiben Sie es dann mit Hilfe um --amendund machen Sie den Inhalt "richtig".


  1. "Spaltenplatz." Dies ist leicht zu bewerkstelligen, indem während des Handels eine Strategie festgelegt wird, die keine unnötigen Fragen zur Konfliktlösung aufwirft.


    git checkout feature
    git merge master -s ours

  2. Oh ja. Es war notwendig, vor dem Zusammenführen einen "Backup" -Zweig aus dem Kopf durch das Feature zu erstellen. Schließlich ist nichts wirklich zusammengeführt ... Aber lass es den 2. Punkt sein, nicht den 0.. Im Allgemeinen wechseln wir zu einem nicht zusammengeführten Feature, und jetzt verschmelzen wir ehrlich. Jeder erschwingliche Weg, unabhängig von "schmutzigem Khaki". Mein persönlicher Weg ist es, den Master-Zweig vom Moment der letzten Zusammenführung aus durchzusehen und mögliche Problem-Commits zu bewerten (zum Beispiel: Sie haben einen Tippfehler an einer Stelle behoben - kein Problem. Massiv (in vielen Dateien) haben sie etwas Wesentliches umbenannt - ein Problem usw.). Aus problematischen Commits erstellen wir neue Zweige (ich mache artlos - master1, master2, master3 usw.). Und dann führen wir einen Zweig für einen Zweig zusammen, bewegen uns von alten zu neuen und korrigieren Konflikte (die bei diesem Ansatz normalerweise selbstverständlich sind). Andere Methoden werden vorgeschlagen (ich bin kein Zauberer, ich lerne nur; Ich freue mich über konstruktive Kommentare!). Am Ende, nachdem wir (vielleicht) mehrere Stunden für reine Routineoperationen verbracht haben (die einem Junior anvertraut werden können, weil es mit diesem Ansatz einfach keine komplizierten Konflikte gibt), erhalten wir den endgültigen Code-Status: Alle Neuerungen / Korrekturen des Assistenten werden erfolgreich in den Feature-Zweig portiert, alle relevanten Tests Dieser Code ging durch usw. Erfolgreicher Code muss festgelegt werden.


  3. Umschreiben der "Erfolgsgeschichte". Führe das Commit aus, "wo alles erledigt ist" und führe folgendes aus:



git tag mp
git checkout mp
git reset feature
git checkout feature
git tag -d mp

(Ich entschlüssle: Mit Hilfe des Tags (mp - merge point) gehen wir in den freistehenden HEAD-Zustand, von dort an den Kopf unserer Niederlassung zurück, wo ganz am Anfang ein "fester Ort" mit einem betrügerischen Merge-Commit vorhanden ist. Jetzt stehen wir auf dem ursprünglichen "sauberen" Merge-Commit. Gleichzeitig haben wir in der Arbeitskopie die "richtigen" Dateien, in denen alles Notwendige zusammengeführt wird. Jetzt müssen Sie alle geänderten Dateien zum Index hinzufügen und vor allem Nicht-Inszenierte sorgfältig prüfen (es werden alle neuen Dateien vorhanden sein, die im Hauptzweig entstanden sind). Alles Notwendige von dort auch dazu.


Wenn alles fertig ist, geben wir unser korrektes Commit in den reservierten Ort ein:


git commit --amend

Hurra! Alles hat sich herausgestellt! Sie können einen Zweig gelegentlich in ein öffentliches Repository verschieben, und niemand wird wissen, dass Sie tatsächlich einen halben Tag Arbeitszeit für diese Zusammenführung aufgewendet haben.


Upd: Kurzer Weg


Drei Monate nach dieser Veröffentlichung erschien der Artikel " Wie und warum Bäume in Git gestohlen " von Capslocky


Aus denselben Gründen können Sie das gleiche Ziel auf kürzere Weise und ohne unterstützende Mechanismen erreichen: Sie müssen nicht "einen Ort stolpern", nicht bereitgestellte Dateien nach dem Zurücksetzen in Betracht ziehen und eine Änderung vornehmen. Sie können in einem Schritt ein direktes Zusammenführungs-Commit mit dem gewünschten Inhalt erstellen.


Wir beginnen sofort mit der Fusion mit allen verfügbaren Methoden (wie in Absatz 2 oben). Eine gewichtige Zwischengeschichte und Khaki spielen trotzdem keine Rolle. Und anstelle von Absatz 3 führen wir mit der Ersetzung einer Merge-Commit eine künstliche Verschmelzung durch , wie im Artikel:


git tag mp
git checkout feature
git merge --ff $(git commit-tree mp^{tree} -m "merged branch 'master' into 'feature'" -p feature -p master)
git tag -d mp

Die gesamte Magie wird durch den dritten Befehl (git commit-tree) in einem Schritt ausgeführt.


Wählen Sie einen Teil der Datei aus und behalten Sie den Verlauf bei


Präambel: In der Datei der Code-Code und schließlich der Code, so dass sogar das Visual Studio langsamer wurde und es verdaute (von JetBrains ganz zu schweigen). (Ja, wir befinden uns wieder in der "unvollkommenen" Welt. Wie immer).


Intelligente Köpfe haben verschiedene Entitäten gedacht, gedacht und identifiziert, die in einer separaten Datei gespeichert werden können. Aber! Wenn Sie nur eine Kopie einer Datei nehmen und in eine andere einfügen, ist dies aus Sicht von git eine völlig neue Datei. Bei Problemen zeigt eine Suche in der Historie eindeutig an, "wo ist das ungültig?" Wer hat die Datei aufgeteilt? Und eine Originalquelle zu finden, ist manchmal nicht „für die Unterdrückung“ notwendig, sondern rein konstruktiv - um herauszufinden, warum diese Linie geändert wurde; Welcher Fehler wurde behoben (oder behoben). Ich möchte, dass die Datei neu ist, aber die gesamte Änderungshistorie bleibt bestehen!


Die Handlung Mit einigen etwas störenden Kanteneffekten kann dies gemacht werden. Für die Definitivität gibt es eine Datei, file.txtaus der Sie ein Teil auswählen möchten file2.txt. (und gleichzeitig die Geschichte retten, ja). Starten Sie dieses Snippet:


f=file.txt; f1=file1.txt; f2=file2.txt
cp $f $f2
git add $f2
git mv $f $f1
git commit -m"split $f step 1, converted to $f1 and $f2"

Als Ergebnis erhalten wir die Dateien file1.txtund file2.txt. Beide haben genau dieselbe Geschichte (echt; wie die Quelldatei). Ja, das Original file.txtmusste umbenannt werden. Dies ist der "etwas nervige" Kanteneffekt. Leider konnte ich keinen Weg finden, um die Historie zu speichern, aber um die Quelldatei nicht umzubenennen, konnte ich nicht (wenn jemand könnte, sagen Sie es mir!). Git kann jedoch alles beherrschen; Niemand stört sich jetzt, die Datei mit einem separaten Commit umzubenennen:


git mv $f1 $f
git commit -m"split finish, rename $f1 to $f"

Nun zeigt file2.txtGOLD denselben Zeilenverlauf wie die Originaldatei. Die Hauptsache ist, diese beiden Commits nicht zusammenzuführen (sonst verschwindet die ganze Magie; ich habe es versucht!). Aber niemand stört sich daran, Dateien direkt im Trennungsprozess zu bearbeiten. Es ist nicht erforderlich, dies später mit separaten Commits auszuführen. Und ja, Sie können viele Dateien gleichzeitig auswählen!


Der Schlüsselpunkt des Rezepts: Umbenennen der Quelldatei in eine andere Datei in demselben Commit, in dem eine Kopie davon erstellt (und möglicherweise bearbeitet) wird. Und lassen Sie dieses Commit in der Zukunft leben (niemals die umgekehrte Umbenennung).


Upd: ein paar Rezepte von Lissov


Trennen Sie einen Teil des Repository mit der Historie


Sie befinden sich auf der neuesten Version des ursprünglichen Repositorys. Die Aufgabe besteht darin, einen Ordner zu trennen. (Ich habe bereits Optionen für mehrere Ordner gesehen, es ist jedoch einfacher und übersichtlicher, entweder zuerst alles in einen zu legen oder das, was unten beschrieben ist, mehrmals zu wiederholen.)


Es ist wichtig! Alle Schritte, um ein Team zu bilden git mv, sonst könnte git Geschichte verlieren.


Wir führen aus:


git filter-branch --prune-empty --subdirectory-filter "{directory}" [branch]

{Verzeichnis} ist der zu trennende Ordner. Als Ergebnis erhalten wir einen Ordner mit der vollständigen Historie der Commits nur darin, dh bei jedem Commit werden nur Dateien aus diesem Ordner angezeigt. Natürlich ist ein Teil der Commits leer und wird von --prune-empty entfernt.
Jetzt ändern wir den Ursprung:


git remote set-url origin {another_repository_url}`
git checkout move_from_Repo_1

Wenn das zweite Repository sauber ist, können Sie es sofort im Master ausführen. Nun drücken Sie:


git push -u move_from_Repo_1

Das gesamte Snippet (für einfaches Kopieren und Einfügen):


directory="directory_to_extract"; newurl="another_repository_url"
git filter-branch --prune-empty --subdirectory-filter "$directory"
git remote set-url origin "$newurl"
git checkout move_from_Repo_1
git push -u move_from_Repo_1

Fassen Sie zwei Repositorys zusammen


Angenommen, Sie haben etwas getan , die über 2 - mal und bekam einen Brunch move_from_Repo_1und move_from_Repo_2in jeder übertragenen Dateien durch git mv, wo sie nach der Fusion sein sollte. Jetzt ist es notwendig zu tragen:


br1="move_from_Repo_1"; br2="move_from_Repo_2"
git checkout master
git merge origin/$br1 --allow-unrelated-histories
git merge origin/$br2 --allow-unrelated-histories
git push

Der Schwerpunkt liegt auf "- nicht zusammenhängenden Geschichten". Als Ergebnis erhalten wir ein Repository mit einer vollständigen Historie aller Änderungen.


Jetzt auch beliebt: