Wie wende ich einen Git-Patch auf eine Datei mit einem anderen Namen und Pfad an?

100

Ich habe zwei Repositories. In einem nehme ich Änderungen an der Datei vor ./hello.test. Ich übertrage die Änderungen und erstelle einen Patch aus diesem Commit mit git format-patch -1 HEAD. Jetzt habe ich ein zweites Repository, das eine Datei enthält, die den gleichen Inhalt wie hello.test hat, aber in einem anderen Verzeichnis unter einem anderen Namen abgelegt ist : ./blue/red/hi.test. Wie gehe ich vor, um den oben genannten Patch auf die hi.testDatei anzuwenden ? Ich habe es versucht, git am --directory='blue/red' < patch_fileaber das beschwert sich natürlich darüber, dass die Dateien nicht den gleichen Namen haben (was Git meiner Meinung nach egal war?). Ich weiß, dass ich das Diff wahrscheinlich bearbeiten könnte, um es auf diese bestimmte Datei anzuwenden, aber ich suche nach einer Befehlslösung.

mart1n
quelle
In Verbindung mit: stackoverflow.com/q/3367254/1959808
Ioannis Filippidis

Antworten:

97

Sie können den Patch mithilfe git diffdes patchDienstprogramms erstellen und dann mit dem Dienstprogramm anwenden, mit dem Sie die Datei angeben können, auf die Sie das Diff anwenden möchten.

Beispielsweise:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Georgebrock
quelle
7
Ah, schön, daran habe ich nicht gedacht. Gibt es jedoch eine Möglichkeit, dies mit Git-Befehlen zu tun, damit die Festschreibungsdaten (Datum und Uhrzeit, Festschreibungsautor, Festschreibungsnachricht) gleich bleiben?
Mart1n
2
Es ist möglich, dass Sie etwas mit amoder tun können apply, aber ich kann es nicht finden. Wenn Sie feststellen, dass Sie Änderungen häufig duplizieren, gibt es möglicherweise eine bessere Lösung für die Verwendung von Submodulen oder für eine beliebige Sprache Ihrer Wahl, um Code freizugeben (z. B. können Sie in Ruby den doppelten Code als Juwel extrahieren).
Georgebrock
1
Dies ist eigentlich dokumentationsbezogen (Quelldateien sind XMLs). Submodule sind keine Option, da ich sie in unserer vorhandenen Infrastruktur stark vertreten müsste.
Mart1n
52

Es gibt eine einfache Lösung, die weder manuelle Patch-Bearbeitung noch externes Skript umfasst.

Im ersten Repository (dies kann auch einen Bereich von Commits exportieren, verwenden -1Sie , wenn Sie nur ein Commit auswählen möchten):

git format-patch --relative <committish> --stdout > ~/patch

Im zweiten Repository:

git am --directory blue/red/ ~/patch

Anstatt --relativein zu verwenden git format-patch, besteht eine andere Lösung darin, die -p<n>Option in git amzu verwenden, um nVerzeichnisse aus dem Pfad der Patches zu entfernen, wie in einer Antwort auf eine ähnliche Frage erwähnt .

Es ist auch möglich, git format-patch --relative <committish>ohne das auszuführen --stdout, und es wird eine Reihe von .patchDateien generiert . Diese Dateien können dann direkt git ammit eingezogen werden git am --directory blue/red/ path/to/*.patch.

Magiraud
quelle
9
Dies hängt immer noch davon ab, dass die Dateinamen gleich sind, oder?
Mart1n
3
Es sollte beachtet werden, dass die --directoryOption anscheinend erfordert, dass Sie den vollständigen Pfad des Verzeichnisses relativ zum Repo-Stamm angeben. So etwas wie " --directory=./chdir'd in ein Unterverzeichnis im Repo" funktioniert nicht.
Reid
1
Verwenden --3wayhilft bei does not exist in index:git am --3way --directory (relative-path) (patch)
Brent Bradburn
Verwenden Sie die -kTaste in beiden Befehlen, um die erste Zeile der Festschreibungsnachricht nicht zu entfernen.
Ruvim
Die Verwendung --3wayhilft nicht nur bei Fehlern, die im Index nicht vorhanden sind (wie von @nobar hervorgehoben), sondern ermöglicht Ihnen auch, Zusammenführungskonflikte sauber zu behandeln. Anstatt in Konflikt stehende Dateien unberührt zu lassen, wird ein Konfliktblock hinzugefügt, der dann gelöst werden kann.
Daniel Wolf
11

Beantwortung meiner eigenen Frage mit einem Skript, das genau dies tut: https://github.com/mprpic/apply-patch-to-file

Anstatt die Patch-Datei manuell zu ändern, fordert sie den Benutzer zur Eingabe der Zieldatei auf, ändert den Patch und wendet ihn auf das Repo an, in dem Sie sich gerade befinden.

mart1n
quelle
5

Aufbauend auf der Antwort von @georgebrock habe ich folgende Lösung verwendet:

Erstellen Sie zunächst die Patch-Dateien wie gewohnt (z. B. git format-patch commitA..commitB).

Stellen Sie dann sicher, dass Ihr Ziel-Repository sauber ist (es sollten keine geänderten oder nicht verfolgten Dateien vorhanden sein), und wenden Sie die Patches wie folgt an:

cd second-repo
git am ~/00*.patch

Für jede Patch-Datei wird ein Fehler wie "Fehler: XYZ existiert nicht im Index" angezeigt. Sie können diese Patch-Datei jetzt manuell anwenden:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Sie müssen diese drei Schritte für jede Patch-Datei ausführen.

Dadurch bleibt die ursprüngliche Festschreibungsnachricht usw. erhalten, ohne dass ein spezieller git format-patchBefehl erforderlich ist oder die Patch-Dateien bearbeitet werden müssen.

oliver
quelle
1
Gute Antwort, ich denke, dies ist die beste Grundlage für jede Art von "nicht standardmäßiger" Patch-Manipulation. Ich mache es in 3 Schritten. (1) Verpflichtung zum Text - git format-patch -1 commitA --stdout > thing.diff; (2) Bearbeiten Sie die Patch-Datei, bis sie das tut, was ich brauche. (3) Zu festschreibender Text, git am --3way thing.diff der den Vorteil hat, dass Sie die Teile des Patches akzeptieren können, die sauber gitangewendet werden , und den Standardprozess zur Konfliktlösung für die Teile verwenden können, die dies nicht tun.
Wir sind alle Monica
2

Ich verstehe, dass die beiden Dateien in Ihrer Situation genau gleich sind, daher ist der Patch wahrscheinlich erfolgreich.

Jedoch im Fall , dass Sie einen Patch auf ein ähnliches anwenden, aber nicht genau die gleiche Datei, oder möchten Sie eine interaktive Patching tun, werden Sie drei Wege - Merge verwenden.

Angenommen, Sie haben die Datei geändert A, bezeichnen sie A~1als die vorherige Version und möchten den Unterschied zwischen A~1to Aauf File anwenden B.

Öffnen Sie ein Drei-Wege-Zusammenführungswerkzeug, z. B. Beyond Compare. Der Pfad des linken Fensters ist A, das mittlere Feld ist der gemeinsame Vorfahr, also ist der Pfad A~1der Pfad des rechten Fensters B. Dann wird die untere Tafel zeigt das Ergebnis der Anwendung den diff zwischen A~1bis Azu Datei B.

Die folgende Abbildung veranschaulicht die Idee.

Geben Sie hier die Bildbeschreibung ein

Gqqnbig
quelle
0

Zu Ihrer Information: Ich hatte kürzlich Probleme beim Versuch, einen Patch von Github herunterzuladen und auf eine lokale Datei anzuwenden (was eine "Überschreibung" an einem neuen Speicherort war).

git amIch würde den Patch auch nicht anwenden, da die Datei "nicht im Index" oder "schmutzig" war. Aber ich fand , dass der einfache patchBefehl könnte den Patch anwenden. Ich wurde aufgefordert, den Namen der zu patchenden Datei anzugeben.

Habe den Job sowieso erledigt ...

Mike Robinson
quelle