Frage Wie man Änderungen von einem anderen Zweig in Git selektiv zusammenführt oder auswählt?


Ich benutze git für ein neues Projekt mit zwei parallelen - aber momentan experimentellen - Entwicklungszweigen:

  • master: Import der bestehenden Codebase plus ein paar Mods, von denen ich mir im Allgemeinen sicher bin
  • exp1: experimenteller Zweig # 1
  • exp2: experimenteller Zweig # 2

exp1 und exp2 repräsentieren zwei sehr unterschiedliche architektonische Ansätze. Bis ich weiterkomme habe ich keine Möglichkeit zu wissen, welcher (wenn) auch funktionieren wird. Wenn ich in einem Zweig Fortschritte mache, habe ich manchmal Änderungen vorgenommen, die in der anderen Branche nützlich wären, und möchte nur diese zusammenführen.

Was ist der beste Weg, um selektive Änderungen von einem Entwicklungszweig zum anderen zusammenzuführen, während alles andere zurückgelassen wird?

Ansätze, die ich in Betracht gezogen habe:

  1. git merge --no-commit gefolgt von manuellem Staging einer großen Anzahl von Änderungen, die ich nicht zwischen den Zweigen vornehmen möchte.

  2. Manuelles Kopieren von gemeinsamen Dateien in ein temporäres Verzeichnis gefolgt von git checkout um zum anderen Zweig zu gelangen und dann mehr manuell aus dem Temp-Verzeichnis in den Arbeitsbaum zu kopieren.

  3. Eine Abwandlung des Obigen. Gib die auf exp Zweige für jetzt und verwenden Sie zwei zusätzliche lokale Repositories für Experimente. Dies macht das manuelle Kopieren von Dateien viel einfacher.

Alle drei Ansätze erscheinen langwierig und fehleranfällig. Ich hoffe, dass es einen besseren Ansatz gibt; etwas, das einem Filterpfadparameter ähnlich wäre git-merge selektiver.


1158
2018-01-16 04:55


Ursprung


Antworten:


Du benutzt das Kirsche-Pick Befehl, um einzelne Commits von einem Zweig zu erhalten.

Wenn die gewünschte (n) Änderung (en) nicht in einzelnen Commits erfolgt, verwenden Sie die hier gezeigte Methode Teilen Sie das Commit in einzelne Commits. Grob gesagt verwendest du git rebase -i um das ursprüngliche Commit zu bearbeiten, dann git reset HEAD^ um Änderungen selektiv rückgängig zu machen git commit um dieses Bit als ein neues Commit in der Geschichte zu begehen.

Es gibt eine andere schöne Methode hier im Red Hat Magazine, wo sie verwenden git add --patch oder möglicherweise git add --interactive mit dem Sie nur Teile eines Hunks hinzufügen können, wenn Sie verschiedene Änderungen in eine einzelne Datei teilen wollen (suchen Sie auf dieser Seite nach "split").

Nachdem Sie die Änderungen aufgeteilt haben, können Sie nun die gewünschten auswählen.


390
2018-01-16 06:01



Ich hatte genau das gleiche Problem wie oben erwähnt. Aber ich habe gefunden Dies klarer in der Erklärung der Antwort.

Zusammenfassung:

  • Überprüfen Sie den / die Pfad (e) von dem Zweig, den Sie zusammenführen möchten,

    $ git checkout source_branch -- <paths>...
    
  • oder zum selektiven Zusammenführen von Hunks

    $ git checkout -p source_branch -- <paths>...
    

    Alternativ können Sie reset verwenden und dann mit der Option hinzufügen -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Schließlich begehen

    $ git commit -m "'Merge' these changes"
    

816
2017-09-03 08:48



Um Dateien von einer Verzweigung in eine andere Verzweigung selektiv zusammenzuführen, führen Sie sie aus

git merge --no-ff --no-commit branchX

woher branchX ist der Zweig, den Sie in den aktuellen Zweig zusammenführen möchten.

Das --no-commit Die Option stuft die Dateien ein, die von Git zusammengeführt wurden, ohne sie tatsächlich zu binden. Dadurch haben Sie die Möglichkeit, die zusammengeführten Dateien wie gewünscht zu ändern und sie dann selbst zu übernehmen.

Je nachdem, wie Sie Dateien zusammenführen möchten, gibt es vier Fälle:

1) Sie wollen eine echte Zusammenführung.

In diesem Fall akzeptieren Sie die zusammengeführten Dateien so, wie Git sie automatisch zusammengeführt und anschließend übernommen hat.

2) Es gibt einige Dateien, die Sie nicht zusammenführen möchten.

Beispielsweise möchten Sie die Version im aktuellen Zweig beibehalten und die Version in dem Zweig ignorieren, aus dem Sie zusammenführen.

Um die Version im aktuellen Zweig auszuwählen, führen Sie Folgendes aus:

git checkout HEAD file1

Dies wird die Version von abrufen file1 im aktuellen Zweig und überschreiben Sie die file1 von Git automatisiert.

3) Wenn Sie die Version in branchX (und nicht eine echte Zusammenführung) wollen.

Lauf:

git checkout branchX file1

Dies wird die Version von abrufen file1 im branchX und überschreiben file1 automatisch von Git zusammengeführt.

4) Der letzte Fall ist, wenn Sie nur bestimmte Zusammenführungen in auswählen möchten file1.

In diesem Fall können Sie die Änderung bearbeiten file1 direkt, aktualisieren Sie es auf was auch immer Sie die Version von wollen file1 zu werden und dann zu begehen.

Wenn Git eine Datei nicht automatisch zusammenführen kann, wird die Datei als "nicht zusammengelegt"und erstellen Sie eine Kopie, in der Sie die Konflikte manuell lösen müssen.



Um es mit einem Beispiel zu erklären, nehmen wir an, Sie möchten eine Zusammenführung vornehmen branchX in den aktuellen Zweig:

git merge --no-ff --no-commit branchX

Sie führen dann die git status Befehl, um den Status von geänderten Dateien anzuzeigen.

Beispielsweise:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Woher file1, file2, und file3 Sind die Dateien git erfolgreich automatisch zusammengeführt worden?

Was das bedeutet ist, dass Änderungen in der master und branchX für alle diese drei Dateien wurden ohne Konflikte miteinander kombiniert.

Sie können überprüfen, wie die Zusammenführung ausgeführt wurde, indem Sie die Option ausführen git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Wenn Sie eine Zusammenführung unerwünscht finden, können Sie das tun

  1. Bearbeiten Sie die Datei direkt
  2. sparen
  3. git commit

Wenn Sie nicht zusammenführen möchten file1 und möchte die Version im aktuellen Zweig beibehalten

Lauf

git checkout HEAD file1

Wenn Sie nicht zusammenführen möchten file2 und will nur die Version in branchX

Lauf

git checkout branchX file2

wenn du willst file3 automatisch zusammengeführt werden, nichts tun.

Git hat es bereits an dieser Stelle zusammengeführt.


file4 oben ist eine fehlgeschlagene Zusammenführung von Git. Dies bedeutet, dass Änderungen in beiden Zweigen in derselben Zeile vorkommen. Hier müssen Sie die Konflikte manuell lösen. Sie können die zusammengeführte Ausführung verwerfen, indem Sie die Datei direkt bearbeiten oder den Befehl checkout für die Version in der gewünschten Verzweigung ausführen file4 werden.


Schließlich, vergiss nicht zu git commit.


239
2018-05-21 03:00



Ich mag die obigen Ansätze nicht. Die Verwendung von cherry-pick ist ideal, um eine einzelne Änderung auszuwählen, aber es ist ein Schmerz, wenn Sie alle Änderungen mit Ausnahme einiger schlechter einbringen möchten. Hier ist meine Herangehensweise.

Es gibt kein --interactive Argument, das Sie an Git Merge weitergeben können.

Hier ist die Alternative:

Du hast einige Änderungen in der Zweigfunktion und möchtest einige, aber nicht alle von ihnen auf eine nicht schlampige Art und Weise zum "Meister" bringen (d. H. Du willst nicht jede einzelne auswählen und festlegen)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Also wickeln Sie das in ein Shell-Skript, ändern Sie den Master in $ in und ändern Sie das Feature in $ from und Sie können loslegen:

#! /bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp

85
2017-08-28 18:47



Es gibt noch einen anderen Weg:

git checkout -p

Es ist eine Mischung zwischen git checkout und git add -p und könnte genau das sein, wonach Sie suchen:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.

73
2017-08-25 01:31



Während einige dieser Antworten ziemlich gut sind, habe ich das Gefühl, dass keiner die ursprüngliche Beschränkung von OP tatsächlich beantwortet hat: die Auswahl bestimmter Dateien aus bestimmten Zweigen. Diese Lösung macht das, kann aber mühsam sein, wenn viele Dateien vorhanden sind.

Sagen wir, du hast das master, exp1, und exp2 Geäst. Sie möchten eine Datei aus jedem der experimentellen Zweige in Master zusammenführen. Ich würde so etwas tun:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Dadurch erhalten Sie für jede der gewünschten Dateien In-File-Diffs. Nichts mehr. Nicht weniger. Es ist nützlich, dass Sie zwischen den Versionen radikal verschiedene Dateiänderungen haben - in meinem Fall eine App von Rails 2 auf Rails 3 umstellen.

BEARBEITEN: Dies wird Dateien zusammenführen, aber eine intelligente Zusammenführung. Ich konnte nicht herausfinden, wie man diese Methode benutzt, um In-File-Diff-Informationen zu erhalten (vielleicht wird es immer noch für extreme Unterschiede sorgen. Ärgerliche kleine Dinge wie Whitespace werden wieder zusammengeführt, es sei denn, du benutzt das -s recursive -X ignore-all-space Möglichkeit)


48
2018-05-07 11:38



Die Antwort von 1800 INFORMATION ist völlig korrekt. Als git noob war "git cherry-pick" jedoch nicht genug, um das herauszufinden, ohne ein bisschen mehr im Internet zu graben, also dachte ich mir, ich würde einen ausführlicheren Leitfaden veröffentlichen, falls jemand anders in einem ist ähnliches Boot.

In meinem Anwendungsfall wollte ich Änderungen aus der GitHub-Zweigstelle von jemand anderem selektiv in meine eigenen übernehmen. Wenn Sie bereits eine lokale Niederlassung mit den Änderungen haben, müssen Sie nur die Schritte 2 und 5-7 durchführen.

  1. Erstellen Sie (falls nicht erstellt) einen lokalen Zweig mit den gewünschten Änderungen.

    $ git branch mybranch <base branch>

  2. Schalte hinein.

    $ git checkout mybranch

  3. Ziehen Sie die gewünschten Änderungen vom Konto der anderen Person herunter. Wenn Sie dies noch nicht getan haben, sollten Sie sie als Fernbedienung hinzufügen.

    $ git remote add repos-w-changes <git url>

  4. Ziehe alles von ihrem Ast herunter.

    $ git pull repos-w-changes branch-i-want

  5. Sehen Sie sich die Commit-Protokolle an, um zu sehen, welche Änderungen Sie wünschen:

    $ git log 

  6. Wechseln Sie zurück zu dem Zweig, in den Sie die Änderungen übernehmen möchten.

    $ git checkout originalbranch

  7. Cherry wählen Sie Ihre Commits nacheinander mit den Hashes.

    $ git cherry-pick -x hash-of-commit

Hut Tipp: http://www.sourcemage.org/Git_Guide


42
2017-07-29 08:37



Hier ist, wie Sie ersetzen können Myclass.java Datei in master verzweigen mit Myclass.java im feature1 Ast. Es wird funktionieren, auch wenn Myclass.java existiert nicht auf master.

git checkout master
git checkout feature1 Myclass.java

Beachten Sie, dass dies überschreibt - nicht merge - und lokale Änderungen im Masterzweig eher ignoriert.


37
2018-02-27 22:42



Der einfache Weg, um tatsächlich verschmelzen bestimmte Dateien aus zwei Zweigen ersetzen nicht nur bestimmte Dateien durch solche aus einem anderen Zweig.

Schritt eins: Diff die Zweige

git diff branch_b > my_patch_file.patch

Erstellt eine Patchdatei mit der Differenz zwischen dem aktuellen Zweig und dem Zweig "branch_b"

Schritt zwei: Wenden Sie den Patch auf Dateien an, die einem Muster entsprechen

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

nützliche Hinweise zu den Optionen

Sie können verwenden * als Platzhalter im Include-Muster.

Schrägstriche müssen nicht maskiert werden.

Sie können stattdessen auch --exclude verwenden und auf alles außer den Dateien anwenden, die dem Muster entsprechen, oder das Patch mit -R umkehren

Bei der Option -p1 handelt es sich um einen Holdover vom Befehl * unix patch und die Tatsache, dass der Inhalt der Patch-Datei jedem Dateinamen vorangeht a/oder b/ (oder mehr, je nachdem, wie die Patch-Datei generiert wurde), die Sie entfernen müssen, damit die reale Datei den Pfad zu der Datei ermitteln kann, auf die der Patch angewendet werden soll.

Sieh dir die Manpage für git-apply für weitere Optionen an.

Schritt drei: Es gibt keinen Schritt drei

Offensichtlich möchten Sie Ihre Änderungen bestätigen, aber wer möchte sagen, dass Sie vor dem Commit noch keine anderen Tweaks haben, die Sie in Bezug zueinander setzen wollen?


22
2018-02-18 04:28



Hier erfahren Sie, wie Sie mit nur ein paar Dateien aus einem anderen Zweig Geschichte schreiben können, selbst wenn eine einfachere Zusammenführung viel mehr Änderungen mit sich gebracht hätte, die Sie nicht wollen.

Zuerst nehmen Sie den ungewöhnlichen Schritt, im Voraus zu deklarieren, dass das, was Sie vorhaben, eine Zusammenführung ist, ohne dass git irgendetwas mit den Dateien in Ihrem Arbeitsverzeichnis macht:

git merge --no-ff --no-commit -s ours branchname1

. . . wo "Branchname" ist, was auch immer Sie behaupten, aus zu verschmelzen. Wenn Sie sich sofort verpflichten würden, würde es keine Änderungen vornehmen, aber es würde immer noch Vorfahren aus dem anderen Zweig anzeigen. Sie können weitere Zweige / Tags / etc hinzufügen. zur Kommandozeile, wenn es nötig ist. An diesem Punkt gibt es jedoch keine Änderungen zum Festschreiben, also holen Sie sich die Dateien von den anderen Revisionen als nächstes.

git checkout branchname1 -- file1 file2 etc

Wenn Sie aus mehreren anderen Zweigen zusammengeführt haben, wiederholen Sie diese bei Bedarf.

git checkout branchname2 -- file3 file4 etc

Jetzt befinden sich die Dateien aus dem anderen Zweig im Index, bereit zum Commit mit dem Verlauf.

git commit

und Sie werden eine Menge Erklärungen in dieser Commit-Nachricht haben.

Bitte beachten Sie jedoch, falls es nicht klar war, dass dies eine verpatzte Sache ist. Es ist nicht im Sinne dessen, wofür ein "Zweig" steht, und Cherry Pick ist ein ehrlicherer Weg, um das zu tun, was Sie hier tun würden. Wenn Sie eine andere "Zusammenführung" für andere Dateien in demselben Zweig durchführen möchten, die Sie beim letzten Mal nicht mitgebracht haben, werden Sie mit einer "bereits aktuellen" Nachricht gestoppt. Es ist ein Symptom, dass wir nicht verzweigen sollten, wenn wir sollten, im "von" Zweig sollte mehr als ein anderer Zweig sein.


20
2017-11-04 10:51



Ich weiß, dass ich etwas spät dran bin, aber das ist mein Workflow zum Zusammenführen von ausgewählten Dateien.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit

14
2017-10-29 20:54