Teilweise ein Commit mit Git


501

Ich arbeite an 2 verschiedenen Zweigen: Release und Entwicklung .

Mir ist aufgefallen, dass ich noch einige Änderungen, die für den Release- Zweig vorgenommen wurden, wieder in den Entwicklungszweig integrieren muss .

Das Problem ist, ich brauche nicht das gesamte Commit, nur einige Hunks in bestimmten Dateien, also ein einfaches

git cherry-pick bc66559

macht den Trick nicht.

Wenn ich a mache

git show bc66559

Ich kann den Unterschied sehen, kenne aber keine gute Möglichkeit, ihn teilweise auf meinen aktuellen Arbeitsbaum anzuwenden.

Antworten:


792

Das Kernstück, das Sie hier wollen, ist git add -p( -pist ein Synonym für --patch). Dies bietet eine interaktive Möglichkeit zum Einchecken von Inhalten, mit der Sie entscheiden können, ob jeder Hunk eingehen soll, und bei Bedarf können Sie den Patch sogar manuell bearbeiten.

So verwenden Sie es in Kombination mit Cherry-Pick:

git cherry-pick -n <commit> # get your patch, but don't commit (-n = --no-commit)
git reset                   # unstage the changes from the cherry-picked commit
git add -p                  # make all your choices (add the changes you do want)
git commit                  # make the commit!

(Vielen Dank an Tim Henigan, der mich daran erinnert hat, dass git-cherry-pick die Option --no-commit hat, und an Felix Rabe, der darauf hingewiesen hat, dass Sie zurücksetzen müssen! Wenn Sie nur ein paar Dinge aus dem Commit herauslassen möchten , könnten Sie verwenden git reset <path>..., um nur diese Dateien zu entfernen.)

Natürlich können Sie bei Bedarf bestimmte Pfade add -pangeben. Wenn Sie mit einem Patch fängst könnten Sie die ersetzen cherry-pickmit apply.


Wenn Sie wirklich eine möchten git cherry-pick -p <commit>(diese Option gibt es nicht), können Sie diese verwenden

git checkout -p <commit>

Dadurch wird das aktuelle Commit von dem von Ihnen angegebenen Commit unterschieden, und Sie können Hunks aus diesem Diff einzeln anwenden. Diese Option kann mehr sinnvoll , wenn der Commit Sie in hat merge Konflikte in einem Teil der du ziehen begehen Sie nicht interessiert sind (Beachten Sie jedoch, dass. checkoutUnterscheidet sich von cherry-pick: checkoutVersuchen anwenden <commit>‚s Inhalte vollständig, cherry-pickgilt die Diff Dies bedeutet, dass checkoutmehr als nur dieses Commit angewendet werden kann, was möglicherweise mehr ist, als Sie möchten.)


Wenn ich den Ratschlägen mit git 1.7.5.4 folge, sagt 'git add -p' 'Keine Änderungen', da alles bereits im Index enthalten ist. Ich muss vor 'git add' einen 'git reset HEAD' durchführen - wie kann ich diesen Reset mit einer Option vermeiden?
Felix Rabe

@FelixRabe: Ich bin tatsächlich überrascht, dass die Änderungen cherry-pick -nanscheinend irgendwann nicht mehr inszeniert wurden - die Konvention besagt definitiv, dass --no-commitOptionen unmittelbar vor dem Festschreiben aufhören , dh mit allen inszenierten Änderungen. Ich werde den Reset in die Antwort einfügen.
Cascabel

1
@Blixt Das heißt, wenn die Commits , die auf dem anderen Zweig sind aber nicht Ihre aktuellen Zweig ABCDEF sind, git checkout -p <F>ist nicht erhalten Sie nur die Änderungen von F, erhält man ABCDEF alle zusammen püriert und lässt Sie aussortieren , was Teil , dass Sie wollen . Es ist schmerzhaft, das auf einige der Änderungen von F zu reduzieren. Auf der anderen Seite erhalten git cherry-pick -n <F>Sie nur die Änderungen von F - und wenn einige dieser Änderungen in Konflikt stehen, werden Sie hilfreich informiert, damit Sie herausfinden können, wie Sie richtig zusammenführen.
Cascabel

Ich hatte Probleme damit, als die Änderung eine Dateizusetzung war. Das git resetwürde die bereitgestellten Dateien entfernen und add -pnur "nichts hinzufügen" sagen.
Ian Grainger


36

Angenommen, die gewünschten Änderungen befinden sich am Anfang des Zweigs, in dem Sie die Änderungen vornehmen möchten, verwenden Sie git checkout

für eine einzelne Datei:

git checkout branch_that_has_the_changes_you_want path/to/file.rb

für mehrere Dateien nur Daisy Chain:

git checkout branch_that_has_the_changes_you_want path/to/file.rb path/to/other_file.rb

5
Das kopiert die gesamte Datei: nicht nur die Änderungen.
Jeremy List

1
Diese Antwort hat, wie jede andere Antwort hier bisher, einen großen Nachteil: Sie bewahrt nicht den ursprünglichen Autor der Änderung, sondern verpflichtet sich unter Ihrem Namen. Wenn eine Änderung gut ist, stehlen Sie jemandem das Guthaben, wenn eine Änderung schlecht ist, setzen Sie sich selbst in Brand. Es scheint, dass es keinen Weg gibt, Git Cherry-Pick-P zu haben, und schade, dass es immer noch nicht da ist.
Pfalcon

15

Aufbauend auf der Antwort von Mike Monkiewicz können Sie auch eine oder mehrere Dateien angeben, die aus dem mitgelieferten sha1 / branch ausgecheckt werden sollen.

git checkout -p bc66559 -- path/to/file.java 

Auf diese Weise können Sie interaktiv die Änderungen auswählen, die Sie auf Ihre aktuelle Version der Datei anwenden möchten.


5
Dies ist problematisch, wenn sich die aktuelle Version der Datei erheblich von dieser Version unterscheidet. Sie werden mit zahlreichen irrelevanten Änderungen aufgefordert (die nicht im Commit angezeigt werden). Außerdem können die Änderungen, die Sie wirklich möchten, in einer "getarnten Form" als Unterschied zum aktuellen und nicht zum ursprünglichen Unterschied angezeigt werden. Es ist möglich, dass der ursprüngliche Unterschied, den Sie möchten, in Konflikt gerät und ordnungsgemäß zusammengeführt werden muss. Sie werden diese Gelegenheit hier nicht haben.
Kaz

1

Wenn Sie eine Liste von Dateien in der Befehlszeile angeben und das Ganze in einem einzigen atomaren Befehl erledigen möchten, versuchen Sie Folgendes:

git apply --3way <(git show -- list-of-files)

--3way: Wenn ein Patch nicht sauber angewendet wird, erstellt Git einen Zusammenführungskonflikt, damit Sie ihn ausführen können git mergetool. Durch das Weglassen --3waygibt Git Patches auf, die nicht sauber angewendet werden.


0

Wenn "teilweise Kirschernte" "innerhalb von Dateien, einige Änderungen auswählen, andere verwerfen" bedeutet, können Sie Folgendes tun git stash:

  1. Mach den vollen Kirschpflücker.
  2. git reset HEAD^ das gesamte von Kirschen gepflückte Commit in nicht inszenierte Arbeitsänderungen umzuwandeln.
  3. Jetzt git stash save --patch: Wählen Sie interaktiv unerwünschtes Material zum Verstauen aus.
  4. Git setzt die versteckten Änderungen aus Ihrer Arbeitskopie zurück.
  5. git commit
  6. Werfen Sie den Vorrat an unerwünschten Änderungen weg : git stash drop.

Tipp: Wenn Sie dem Vorrat an unerwünschten Änderungen einen Namen geben: git stash save --patch junkWenn Sie jetzt vergessen (6), erkennen Sie den Vorrat später als das, was er ist.


Wenn Sie nur Kirschen pflücken, dann zurücksetzen ... Sie werden nichts zu verstauen. Wie oben erwähnt, müssen Sie entweder einen Cherry-Pick mit --no-commit oder einen Reset -hard HEAD ~ durchführen. Beachten Sie, dass Sie negativ vorgehen (auswählen, was Sie nicht wollen). Die oben akzeptierte Lösung erlaubt entweder einen positiven oder einen negativen Ansatz.
Pierre-Olivier Vares

0

Verwenden Sie git format-patchdiese Option, um den Teil des Commits, den Sie interessieren, herauszuschneiden und git amauf einen anderen Zweig anzuwenden

git format-patch <sha> -- path/to/file
git checkout other-branch
git am *.patch
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.