Ich möchte Rezepte zur Lösung einiger Probleme teilen, die manchmal bei der Arbeit mit Git auftreten und die nicht "direkt ganz offensichtlich" sind.
Zuerst dachte ich daran, mehr solche Rezepte anzusammeln, aber alles hat seine Zeit. Ich denke, wenn es irgendeinen Nutzen gibt, dann ist es möglich und nach und nach ...
Also ...
Alte Zweige mit minimalen Schmerzen zusammenführen
Präambel. Es gibt einen Hauptzweig ( master
), der aktiv neue Funktionen und Korrekturen festlegt. Es gibt einen parallelen feature
Zweig, in dem die Entwickler eine Weile in ihrem eigenen Nirvana davonfuhren und dann plötzlich entdeckten, dass sie seit einem Monat nicht mehr beim Master waren, und die Zusammenführung „auf der Stirn“ (Kopf an Kopf) war bereits nicht trivial.
(Ja, es geht nicht um eine ideale Welt, in der alles in Ordnung ist, kein Verbrechen vorliegt, Kinder immer gehorsam sind und die Straße sogar streng mit der Hand ihrer Mutter überqueren und sich sorgfältig umsehen.)
Zweck: wütend sein. Zur gleichen Zeit, so dass es eine "reine" Zusammenführung ohne Features war. Das heißt, Damit werden im öffentlichen Repository im Zweigdiagramm zwei Threads an einem einzigen Punkt mit der Meldung "Zweig 'Master' in Feature zusammengeführt" verbunden. Und die ganzen "dieser" Kopfschmerzen darüber, wie viel Zeit und Mühe es gekostet hat, wie viele Konflikte gelöst wurden und wie viel Haar unnötig fleckig war.
Die Handlung. Die Tatsache, dass Sie im Git das letzte Commit mit dem Schlüssel bearbeiten können - --amend
kennt. Der Trick ist, dass dieses "letzte Commit" überall gefunden werden kann und alles enthält. Zum Beispiel kann es nicht nur das „letzte Festschreiben an den linearen Zweig“ sein, bei dem vergessen wurde, den Tippfehler zu korrigieren, sondern auch das Zusammenführungs-Festschreiben aus der üblichen Zusammenführung oder dem „Octopus“ -Zusammenführen. --amend
nur die vorgeschlagenen Änderungen --amend
und das geänderte Commit in den Baum "einbetten", als ob es tatsächlich als Ergebnis einer ehrlichen Zusammenführung und Lösung von Konflikten erschienen wäre. Im Wesentlichen können git commit --amend
mit git merge
und git commit --amend
den " git commit --amend
" ("dieses Commit im Baum befindet sich HIER") und den Inhalt des Commits selbst vollständig trennen.
Die Hauptidee eines komplexen Zusammenführungs-Commits mit einem sauberen Verlauf ist einfach: Erstellen Sie zunächst einen Ort, indem Sie ein sauberes Zusammenführungs-Commit erstellen (unabhängig vom Inhalt), und schreiben Sie es dann mit --amend
, um den Inhalt „korrekt“ zu machen.
"Pille einen Platz." Dies ist einfach, indem Sie eine Fusionsstrategie festlegen, die keine unnötigen Fragen zur Konfliktlösung stellt.
git checkout feature git merge master -s ours
Oh ja. Vor dem Zusammenführen musste aus dem Feature-Kopf ein "Backup" -Zweig erstellt werden. Schließlich wird nichts wirklich zusammengeführt ... Aber es sei der 2. Absatz und nicht der 0 .. Im Allgemeinen wechseln wir zur nicht zusammengeführten Funktion und führen sie nun ehrlich zusammen. Auf jeden Fall möglich, trotz "schmutziger Hacks". Mein persönlicher Weg ist es, den Hauptzweig vom Moment der letzten Zusammenführung an zu betrachten und mögliche Problem-Commits zu bewerten (zum Beispiel: Korrigieren Sie einen Tippfehler an einer Stelle - nicht an einer problematischen. Massiv (viele Dateien) haben sie eine Entität umbenannt - eine problematische usw.). Aus Problem-Commits erstellen wir neue Zweige (ich mache es genial - master1, master2, master3 usw.). Und dann verschmelzen wir Zweig für Zweig und wechseln von alten zu neuen und korrigierenden Konflikten (die bei diesem Ansatz normalerweise selbstverständlich sind). Schlagen Sie andere Methoden vor (ich bin kein Zauberer; ich lerne nur; ich freue mich über konstruktive Kommentare!). Wenn wir (vielleicht) ein paar Stunden für rein routinemäßige Operationen aufwenden (die dem Junior anvertraut werden können, weil es einfach keine komplizierten Konflikte mit diesem Ansatz gibt), erhalten wir den endgültigen Status des Codes: Alle Innovationen / Korrekturen des Assistenten wurden erfolgreich in den Feature-Zweig portiert, alle relevanten Tests ging auf diesen Code usw. Ein erfolgreicher Code muss festgeschrieben werden.
Umschreiben der "Erfolgsgeschichte". Führen Sie Folgendes aus, wenn Sie sich auf dem Commit befinden, wo "alles erledigt ist":
git tag mp git checkout mp git reset feature git checkout feature git tag -d mp
(Ich entschlüssele: Mit dem Tag (mp - merge point) wechseln wir in den getrennten HEAD-Status und setzen ihn von dort auf den Kopf unserer Zweigstelle zurück, wo zu Beginn durch betrügerisches Zusammenführungs-Commit eine Absteckung vorgenommen wurde. Das Tag wird nicht mehr benötigt, daher löschen wir es.) Jetzt befinden wir uns im ursprünglichen "reinen" Merge-Commit. Gleichzeitig haben wir in der Arbeitskopie die "richtigen" Dateien, in denen sich alles befindet, was Sie brauchen. Jetzt müssen Sie alle geänderten Dateien zum Index hinzufügen und besonders sorgfältig auf nicht bereitgestellte Dateien achten (es werden alle neuen Dateien im Hauptzweig angezeigt). Von dort aus fügen wir auch alles hinzu, was wir brauchen.
Wenn alles fertig ist, geben wir unser korrektes Commit an der reservierten Stelle ein:
git commit --amend
Hurra! Alles hat geklappt! Sie können einen Zweig beiläufig in ein öffentliches Repository verschieben, und niemand wird wissen, dass Sie tatsächlich einen halben Tag an dieser Zusammenführung gearbeitet haben.
Upd: Prägnanter Weg
Drei Monate nach dieser Veröffentlichung erscheint der Artikel " Wie und warum man Bäume in Git stiehlt " von Capslocky
Basierend auf ihren Motiven ist es möglich, genau das gleiche Ziel auf kürzere Weise und ohne Hilfsmechanismen zu erreichen: Keine Notwendigkeit, Speicherplatz zu "posten", nicht bereitgestellte Dateien nach dem Zurücksetzen zu berücksichtigen und Änderungen vorzunehmen; Sie können in einem Schritt ein direktes Zusammenführungs-Commit mit dem gewünschten Inhalt erstellen.
Wir beginnen sofort mit der Fusion unter Verwendung aller verfügbaren Methoden (wie in Absatz 2 oben). Die weitläufige Zwischengeschichte und die Hacks sind immer noch irrelevant. Und dann führen wir anstelle von Anspruch 3 mit der Ersetzung des Zusammenführungs-Commits eine künstliche Zusammenführung 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 ganze Magie hier wird in einem Schritt durch den dritten Befehl (git commit-tree) erledigt.
Wählen Sie einen Teil der Datei aus und behalten Sie den Verlauf bei
Präambel: Die Datei wurde codiert-codiert und schließlich codiert, sodass sogar das visuelle Studio langsamer wurde und sie verdaute (ganz zu schweigen von JetBrains). (Ja, wir sind zurück in der "unvollkommenen" Welt. Wie immer).
Smart Brains dachte, dachte und wählte mehrere Entitäten aus, die in einer separaten Datei gerendert werden können. Aber! Wenn Sie es einfach nehmen, kopieren Sie ein Stück der Datei und fügen Sie es in ein anderes ein - dies wird aus der Sicht von git eine völlig neue Datei sein. Bei Problemen zeigt eine Verlaufssuche eindeutig nur an, wo sich diese behinderte Person befindet. Wer hat die Datei freigegeben? Und es kann notwendig sein, die ursprüngliche Quelle überhaupt nicht "für Repressionen", sondern rein konstruktiv zu finden - um herauszufinden, warum diese Linie geändert wurde; Welcher Fehler wurde behoben (oder nicht behoben). Ich möchte, dass die Datei neu ist, aber gleichzeitig bleibt die gesamte Änderungshistorie erhalten!
Die Handlung. Mit einigen etwas nervigen Randeffekten kann dies durchgeführt werden. Zur Sicherheit gibt es eine file.txt
Datei, aus der ich einen Teil in file2.txt
hervorheben file2.txt
. (und immer noch die Geschichte behalten, ja). Führen Sie dieses Snippet aus:
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.txt
und file2.txt
. Sie haben beide genau die gleiche Geschichte (echt; wie die Originaldatei). Ja, die ursprüngliche file.txt
musste umbenannt werden. Dies ist der "etwas nervige" Kanteneffekt. Leider konnte ich keine Möglichkeit finden, die Geschichte zu speichern, aber um die Quelldatei NICHT umzubenennen (wenn jemand könnte, sag es mir!). Der Idiot wird jedoch alles aushalten; Jetzt stört es niemanden, die Datei in einem separaten Commit umzubenennen:
git mv $f1 $f git commit -m"split finish, rename $f1 to $f"
Jetzt file2.txt
Gold den gleichen Zeilenverlauf wie die Originaldatei. Die Hauptsache ist, diese beiden Commits nicht zusammenzuführen (sonst verschwindet die ganze Magie; ich habe es versucht!). Gleichzeitig stört es niemanden, Dateien direkt während des Trennungsprozesses zu bearbeiten. Es ist nicht erforderlich, dies später mit separaten Commits zu tun. Und ja, Sie können viele Dateien gleichzeitig auswählen!
Der entscheidende Punkt des Rezepts: Benennen Sie die Quelldatei im selben Commit in eine andere um, wo eine Kopie (Kopien) erstellt (und möglicherweise bearbeitet) wird. Und lassen Sie dieses Commit in der Zukunft leben (es wird niemals einen Fehler bei der umgekehrten Umbenennung machen).
Upd: ein paar Rezepte von Lissov
Trennen Sie den Verlaufs-Repository-Teil
Sie befinden sich in der neuesten Version des ursprünglichen Repositorys. Die Aufgabe besteht darin, einen Ordner zu trennen. (Ich habe Optionen für mehrere Ordner gesehen, aber es ist einfacher und verständlicher, entweder zuerst alles in einen Ordner zu falten oder das Folgende mehrmals zu wiederholen.)
Wichtig! Alle Bewegungen sollten mit dem git mv
, da sonst der Git den Verlauf verlieren kann.
Wir führen aus:
git filter-branch --prune-empty --subdirectory-filter "{directory}" [branch]
{Verzeichnis} ist der zu trennende Ordner. Infolgedessen erhalten wir den Ordner zusammen mit dem vollständigen Verlauf der Festschreibungen nur für ihn, dh, bei jedem Festschreiben werden nur Dateien aus diesem Ordner angezeigt. Natürlich ist ein Teil der Commits leer, --prune-empty entfernt sie.
Ändern Sie nun 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 direkt zum Master wechseln. Nun, drücken Sie:
git push -u move_from_Repo_1
Gesamtes Snippet (zum einfachen 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
Führen Sie zwei Repositorys zusammen
Angenommen, Sie haben etwas 2-mal höheres move_from_Repo_1
und Brunchs move_from_Repo_1
und move_from_Repo_2
, und in jedem haben Sie Dateien mit git mv
move_from_Repo_2
übertragen, wo sie nach dem Zusammenführen sein sollten. Jetzt bleibt es zu kontrollieren:
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 ganze Trick ist in --allow-nicht-verwandten-Geschichten. Als Ergebnis erhalten wir ein Repository mit einem vollständigen Verlauf aller Änderungen.