Wie kann ich Änderungen aus einem anderen Zweig in Git selektiv zusammenführen oder auswählen?

1450

Ich verwende git für ein neues Projekt, das zwei parallele - aber derzeit experimentelle - Entwicklungszweige hat:

  • master: Import der vorhandenen Codebasis plus ein paar Mods, deren ich mir allgemein sicher bin
  • exp1: experimenteller Zweig # 1
  • exp2: experimenteller Zweig # 2

exp1und exp2repräsentieren zwei sehr unterschiedliche architektonische Ansätze. Bis ich weiter komme, kann ich nicht wissen, welches (wenn überhaupt) funktionieren wird. Wenn ich in einem Zweig Fortschritte mache, habe ich manchmal Änderungen, die im anderen Zweig nützlich sind, und möchte nur diese zusammenführen.

Was ist der beste Weg, um selektive Änderungen von einem Entwicklungszweig zum anderen zusammenzuführen und alles andere hinter sich zu lassen?

Ansätze, die ich in Betracht gezogen habe:

  1. git merge --no-commit gefolgt von der manuellen Deaktivierung einer großen Anzahl von Änderungen, die ich nicht zwischen den Zweigen gemeinsam machen möchte.

  2. Manuelles Kopieren gängiger Dateien in ein temporäres Verzeichnis, gefolgt von git checkouteinem Wechsel in den anderen Zweig und anschließendem manuellen Kopieren aus dem temporären Verzeichnis in den Arbeitsbaum.

  3. Eine Variation des oben genannten. Verlassen Sie die expZweige vorerst und verwenden Sie zwei zusätzliche lokale Repositories zum Experimentieren. Dies macht das manuelle Kopieren von Dateien viel einfacher.

Alle drei Ansätze scheinen mühsam und fehleranfällig zu sein. Ich hoffe, es gibt einen besseren Ansatz. so etwas wie ein Filterpfadparameter, der git-mergeselektiver machen würde .

David Joyner
quelle
5
Wenn Änderungen in Ihren experimentellen Zweigen in separaten Commits gut organisiert sind, ist es besser, selektive Commits anstelle selektiver Dateien zusammenzuführen. Die meisten der folgenden Antworten gehen davon aus, dass dies der Fall ist.
Akaihola
2
Wäre eine Kombination von git merge -s ours --no-commitgefolgt von einigen git read-treenicht eine gute Lösung dafür? Siehe stackoverflow.com/questions/1214906/…
VonC
34
Eine neuere Frage hat eine einzeilige, gut geschriebene Antwort: stackoverflow.com/questions/10784523/…
brahn
Kasse dieses Blog für das Zusammenführen bestimmter Dateien nur jasonrudolph.com/blog/2009/02/25/…
Ashutosh Chamoli

Antworten:

475

Sie verwenden den Befehl cherry-pick , um einzelne Commits von einem Zweig abzurufen.

Wenn sich die gewünschten Änderungen nicht in einzelnen Commits befinden, verwenden Sie die hier gezeigte Methode, um das Commit in einzelne Commits aufzuteilen . Grob gesagt verwenden Sie diese Option git rebase -i, um das ursprüngliche Commit zu bearbeiten, git reset HEAD^Änderungen selektiv zurückzusetzen und git commitdieses Bit dann als neues Commit im Verlauf festzuschreiben.

Es gibt eine andere nette Methode hier im Red Hat Magazine, bei der Sie nur Teile eines Hunks hinzufügen git add --patchoder möglicherweise git add --interactivehinzufügen können, wenn Sie verschiedene Änderungen an einer einzelnen Datei aufteilen möchten (suchen Sie auf dieser Seite nach "split").

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

1800 INFORMATIONEN
quelle
14
Nach meinem Verständnis ist dies unnötig komplizierter als die Antwort mit den höheren Stimmen.
Alexander Bird
54
Dies ist technisch die richtige Antwort, die richtige Antwort erscheint "verschlungen". --- Die höher gewählte Antwort ist nur eine schnelle und schmutzige "Do's the Trick" -Antwort, die für die meisten Menschen alles ist, worum es geht (:
Jacob
3
@akaihola: HEAD ^ ist richtig. Siehe man git-rev-parse: Ein Suffix ^ zu einem Revisionsparameter bedeutet das erste übergeordnete Element dieses Festschreibungsobjekts. Die Präfixnotation wird verwendet, um festschreibbare Commits von einem Commit auszuschließen.
Tyler Rick
13
Ich wollte nur einen anderen Ansatz teilen, der der sauberste und weniger verworrene von allen zu sein scheint: jasonrudolph.com/blog/2009/02/25/… totale Einfachheit und großartig
superuseroi
14
Verwirrt von der Debatte darüber, welcher Ansatz „richtig“ ist? Berücksichtigen Sie den Unterschied zwischen Dateien und Commits (siehe Anmerkungen unten) . OP möchte FILES zusammenführen und erwähnt COMMITS nicht. Die Antwort mit der höheren Stimme ist spezifisch für Dateien. Die akzeptierte Antwort verwendet Cherry-Pick, was spezifisch für Commits ist. Cherry-Pick kann der Schlüssel zum selektiven Zusammenführen von Commits sein, aber es kann sehr schmerzhaft sein, Dateien von einem Zweig in einen anderen zu verschieben. Obwohl Commits das Herzstück von gits Stärke sind, vergessen Sie nicht, dass Dateien immer noch eine Rolle spielen!
Kay V
970

Ich hatte genau das gleiche Problem wie oben von Ihnen erwähnt. Aber ich fand das klarer, als ich die Antwort erklärte.

Zusammenfassung:

  • Überprüfen Sie die Pfade des Zweigs, den Sie zusammenführen möchten.

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

    Hinweis: Es funktioniert auch ohne --wie im verlinkten Beitrag zu sehen.

  • oder um selektiv Hunks zusammenzuführen

    $ 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"
    
Chris Steinbach
quelle
9
Der verlinkte Artikel von Bart J ist der beste Ansatz. Klar, einfach, ein Befehl. Es ist das, was ich gleich benutzen werde. :)
Pistos
256
Dies ist keine echte Verschmelzung. Sie wählen Änderungen nach Datei anstatt nach Festschreiben aus und verlieren alle vorhandenen Festschreibungsinformationen (Autor, Nachricht). Zugegeben, dies ist gut, wenn Sie alle Änderungen in einigen Dateien zusammenführen möchten und es in Ordnung ist, dass Sie alle Commits erneut ausführen müssen. Wenn Dateien jedoch sowohl Änderungen zum Zusammenführen als auch andere zum Verwerfen enthalten, ist eine der in anderen Antworten angegebenen Methoden für Sie besser geeignet.
Akaihola
10
@mykhal und andere: Dadurch werden die Dateien im Index automatisch inszeniert. Wenn Sie ausgecheckt haben, foo.cmüssen git reset HEAD foo.cSie diese Datei entfernen, und Sie können sie dann unterscheiden. Ich fand das heraus, nachdem ich es ausprobiert hatte und hierher zurückgekommen war, um nach einer Antwort darauf zu suchen
michiakig
12
um die Änderungen zu sehen, die Sie auch verwenden könnten:git diff --cached
OderWat
9
Nach dieser Antwort git checkout -p <revision> -- <path> wird das gleiche sein wie die ersten drei Befehle, die Sie beschrieben haben :)
7hi4g0
338

Führen Sie aus, um Dateien von einem Zweig selektiv in einen anderen Zweig zusammenzuführen

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

Wo branchXist der Zweig, von dem Sie in den aktuellen Zweig zusammenführen möchten?

Mit dieser --no-commitOption werden die von Git zusammengeführten Dateien bereitgestellt, ohne sie tatsächlich festzuschreiben. Dies gibt Ihnen die Möglichkeit, die zusammengeführten Dateien nach Belieben zu ändern und sie dann selbst festzuschreiben.

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

1) Sie möchten eine echte Zusammenführung.

In diesem Fall akzeptieren Sie die zusammengeführten Dateien so, wie Git sie automatisch zusammengeführt hat, und schreiben sie dann fest.

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

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

Führen Sie Folgendes aus, um die Version im aktuellen Zweig auszuwählen:

git checkout HEAD file1

Dadurch wird die Version von file1im aktuellen Zweig abgerufen und die file1von Git zusammengestellte Version überschrieben .

3) Wenn Sie die Version in branchX möchten (und keine echte Zusammenführung).

Lauf:

git checkout branchX file1

Dadurch wird die von Git automatisch zusammengeführte Version von file1in abgerufen branchXund überschrieben file1.

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

In diesem Fall können Sie die geänderte file1Version direkt bearbeiten, auf die gewünschte Version aktualisieren file1und dann festschreiben.

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



Um dies anhand eines Beispiels weiter zu erläutern, nehmen wir an, Sie möchten branchXin den aktuellen Zweig einbinden:

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

Anschließend führen Sie den git statusBefehl aus, um den Status geänderter Dateien anzuzeigen.

Zum Beispiel:

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
#

Wo file1, file2und file3sind die Dateien, die git erfolgreich automatisch zusammengeführt hat.

Dies bedeutet, dass Änderungen in masterund branchXfür alle drei Dateien ohne Konflikte miteinander kombiniert wurden.

Sie können prüfen , wie die Zusammenführung , indem Sie das getan wurde git diff --cached;

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

Wenn Sie eine Zusammenführung als unerwünscht empfinden, können Sie dies tun

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

Wenn Sie nicht zusammenführen file1und die Version im aktuellen Zweig beibehalten möchten

Lauf

git checkout HEAD file1

Wenn Sie nicht zusammenführen möchten file2und nur die Version in möchtenbranchX

Lauf

git checkout branchX file2

Wenn Sie file3automatisch zusammengeführt werden möchten , tun Sie nichts.

Git hat es zu diesem Zeitpunkt bereits zusammengeführt.


file4oben ist eine fehlgeschlagene Zusammenführung von Git. Dies bedeutet, dass in beiden Zweigen Änderungen in derselben Zeile auftreten. Hier müssen Sie die Konflikte manuell lösen. Sie können die Zusammenführung verwerfen, indem Sie die Datei direkt bearbeiten oder den Befehl checkout für die Version in dem Zweig ausführen file4, der Sie werden möchten .


Schließlich nicht vergessen git commit.

Alvinabad
quelle
10
Achtung: Wenn git merge --no-commit branchXes sich nur um einen Schnellvorlauf handelt, wird der Zeiger aktualisiert und das --no-commit wird daher stillschweigend ignoriert
vgl.
16
@cfi Was ist mit Hinzufügen --no-ff, um dieses Verhalten zu verhindern?
Eduardo Costa
5
Ich empfehle auf jeden Fall, diese Antwort mit Eduardos Option "--no-ff" zu aktualisieren. Ich habe das Ganze durchgelesen (was ansonsten großartig war), nur um meine Zusammenführung schnell vorzuspulen.
Funktr0n
7
Diese Lösung bietet die besten Ergebnisse und Flexibilität.
Thiago Macedo
20
Im Gegensatz zu der Antwort mit den meisten Stimmen hat diese Lösung meine Zusammenführungshistorie beibehalten, was für mich wichtig ist, da ich teilweise Commits über Zweige hinweg hin und her webe. Ich habe nicht alle anderen Lösungsvorschläge ausprobiert, also tun dies vielleicht auch einige.
ws_e_c421
107

Ich mag die obigen Ansätze nicht. Die Verwendung von Cherry-Pick ist ideal, um eine einzelne Änderung auszuwählen, aber es ist schmerzhaft, wenn Sie alle Änderungen mit Ausnahme einiger schlechter Änderungen vornehmen möchten. Hier ist mein Ansatz.

Es gibt kein --interactiveArgument, das Sie an git merge übergeben können.

Hier ist die Alternative:

Sie haben einige Änderungen an der Zweigfunktion vorgenommen und möchten einige, aber nicht alle auf nicht schlampige Weise an den Master übergeben (dh Sie möchten nicht jede einzelne auswählen und festschreiben).

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

Wickeln Sie das einfach in ein Shell-Skript ein, ändern Sie den Master in $ in und ändern Sie das Feature in $ from, und schon kann es losgehen:

#!/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
nosatalian
quelle
Ich habe die Formatierung korrigiert - dies ist eine ziemlich nette Methode, wenn Sie eine Auswahl von Commits durchführen möchten
1800 INFORMATION
Ich benutze diese Technik jetzt und sie scheint wirklich gut funktioniert zu haben.
Dylanfm
4
Vielleicht möchten Sie ändern git rebase -i $tozu git rebase -i $to || $SHELL, so dass der Benutzer aufrufen kann git --skipusw., wie erforderlich , wenn die Fütterungsmaterial ausfällt. Es lohnt sich auch, die Zeilen mit &&anstelle von Zeilenumbrüchen zu verketten.
Sircolinton
2
Leider scheint der Link in der Antwort tot zu sein.
ThomasW
Der Link ist nicht nur tot, sondern hat auch eine WOT-Warnung vor schlechtem Ruf. Deshalb habe ich es entfernt.
Jean-François Corbett
93

Es gibt noch einen anderen Weg:

git checkout -p

Es ist eine Mischung aus git checkoutund git add -pkö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.
Chronial
quelle
10
Dies ist bei weitem die einfachste und einfachste Methode, solange Sie nur eine überschaubare Anzahl von Änderungen zum Zusammenführen haben. Ich hoffe, dass mehr Menschen diese Antwort bemerken und sie positiv bewerten werden. Beispiel: Git Checkout - Patch exp1 file_to_merge
Tyler Rick
1
Ähnliche Antwort auf diese Frage gepostet: stackoverflow.com/a/11593308/47185
Tyler Rick
Oh, ich wusste nicht, dass die Kasse einen Patch hat! Ich habe stattdessen -p ausgecheckt / zurückgesetzt / -p hinzugefügt.
Daniel C. Sobral
2
Wirklich die einfachste Methode. git checkout -p featurebranch Dateiname. Und das Beste ist, wenn der Befehl ausgeführt wird, gibt es Ihnen ay / n / e /? / ... etc. Option, um zu entscheiden, wie die Datei zusammengeführt werden soll. Ich habe es mit e versucht und konnte den Patch sogar vor dem Anwenden bearbeiten ... Wie cool ist es? Ein echter Einzeiler zum Zusammenführen ausgewählter Dateien aus anderen Zweigen.
Infoclogged
55

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

Können sagen , Sie haben master, exp1und exp2Zweige. Sie möchten eine Datei aus jedem der experimentellen Zweige in Master zusammenführen. Ich würde so etwas machen:

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

Auf diese Weise erhalten Sie Unterschiede in der Datei für jede der gewünschten Dateien. Nichts mehr. Nicht weniger. Es ist nützlich, dass Sie zwischen den Versionen radikal unterschiedliche Dateiänderungen haben - in meinem Fall, wenn Sie eine App von Rails 2 auf Rails 3 ändern.

BEARBEITEN : Dadurch werden Dateien zusammengeführt, es wird jedoch eine intelligente Zusammenführung durchgeführt. Ich konnte nicht herausfinden, wie ich diese Methode verwenden kann, um Diff-Informationen in der Datei abzurufen (möglicherweise wird dies immer noch zu extremen Unterschieden führen. Ärgerliche kleine Dinge wie Leerzeichen werden wieder zusammengeführt, es sei denn, Sie verwenden die -s recursive -X ignore-all-spaceOption).

Eric Hu
quelle
4
Beachten Sie auch: Sie können mehrere Dateien aus einem bestimmten Zweig alle inline git checkout exp1 path/to/file_a path/to/file_x
erstellen
2
Dies ist schön. Ich habe git checkout feature <path>/*Gruppen von Dateien bekommen.
Isherwood
Dies funktioniert in Ordnung, es wurden jedoch zwei zusätzliche Festschreibungsobjekte hinzugefügt. Keine große Sache, aber ein bisschen chaotisch
MightyPork
@MightyPork du hast recht. Da ich dies vor so langer Zeit geschrieben habe, bin ich mir leider nicht mehr sicher, warum die Schritte "git stash" und "git merge stash" anstelle eines "git commit" vorhanden sind.
Eric Hu
2
Oh, das ist klar, denke ich. Auf diese Weise wird die eine Datei zusammengeführt, wobei nicht unbedingt frühere Änderungen im Zielzweig überschrieben werden.
MightyPork
48

Die Antwort von 1800 INFORMATION ist völlig richtig. Als Git-Noob reichte mir "Use Cherry Cherry-Pick" jedoch nicht aus, um dies herauszufinden, ohne ein bisschen mehr im Internet zu stöbern. Deshalb dachte ich, ich würde einen detaillierteren Leitfaden veröffentlichen, falls sich jemand in einem befindet ähnliches Boot.

Mein Anwendungsfall war es, selektiv Änderungen aus dem Github-Zweig eines anderen in meinen eigenen zu ziehen. Wenn Sie bereits eine lokale Niederlassung mit den Änderungen haben, müssen Sie nur die Schritte 2 und 5-7 ausführen.

  1. Erstellen Sie (falls nicht erstellt) einen lokalen Zweig mit den Änderungen, die Sie vornehmen möchten.

    $ git branch mybranch <base branch>

  2. Wechseln Sie hinein.

    $ git checkout mybranch

  3. Ziehen Sie die gewünschten Änderungen aus dem Konto der anderen Person. Wenn Sie dies noch nicht getan haben, möchten Sie sie als Fernbedienung hinzufügen.

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

  4. Ziehen Sie alles von ihrem Ast herunter.

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

  5. Zeigen Sie die Festschreibungsprotokolle an, um zu sehen, welche Änderungen Sie möchten:

    $ git log

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

    $ git checkout originalbranch

  7. Cherry wählt Ihre Commits nacheinander mit den Hashes aus.

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

Hutspitze: http://www.sourcemage.org/Git_Guide

Cory
quelle
3
Tipp: Verwenden Sie zuerst den git cherryBefehl (siehe Handbuch zuerst), um Commits zu identifizieren, die Sie noch nicht zusammengeführt haben.
Akaihola
Dies funktioniert .. 1. einen neuen Zweig erstellt 2. einige Dateien erstellt / einige Änderungen vorgenommen 3. festschreiben 4. den Hauptzweig auschecken 5. git cherry-pick -x Hash-of-Commit ausführen und Zusammenführungskonflikte lösen, wenn Sie gut sind gehen.
RamPrasadBismil
Ihr Link funktioniert nicht mehr. Kannst du es bitte aktualisieren?
Creep3007
42

Hier erfahren Sie, wie Sie eine Myclass.javaDatei in einem masterZweig durch einen Myclass.javain einem feature1Zweig ersetzen können . Es wird funktionieren, auch wenn Myclass.javaes nicht existiert master.

git checkout master
git checkout feature1 Myclass.java

Beachten Sie, dass dies überschreibt - nicht zusammenführt - und lokale Änderungen im Hauptzweig ignoriert.

maestr0
quelle
6
Dies wird nicht zusammengeführt. Es werden nur die Änderungen auf dem Master mit den Änderungen aus dem Zweig feature1 überschrieben.
Skunkwaffle
3
Perfekt, ich suchte nach dieser Art von Zusammenführung, wo theirsüberschreiben ours=> +1 Cheers;)
olibre
1
Manchmal möchten Sie nur die gesamte Datei ersetzen, also wollte ich dies, aber Sie müssen sicherstellen, dass Sie alle Änderungen verlieren möchten, die Sie an dieser Datei vorgenommen haben.
MagicLAMP
1
Die sauberste Lösung, da das OP speziell die gesamte Datei durch die entsprechende Datei in einem anderen Zweig ersetzen wollte:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust
29

Der einfache Weg, um tatsächlich fusioniert bestimmte Dateien aus zwei Zweigen, nicht nur bestimmte Dateien mit denen aus einem anderen Zweig ersetzen.

Schritt eins: Diff die Zweige

git diff branch_b > my_patch_file.patch

Erstellt eine Patch-Datei mit dem Unterschied zwischen dem aktuellen Zweig und 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 das Platzierungsmuster *als Platzhalter verwenden.

Schrägstriche müssen nicht entkommen.

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

Die Option -p1 ist ein Überbleibsel des Befehls * unix patch und der Tatsache, dass der Inhalt der Patch-Datei jedem Dateinamen a/oder b/(oder mehr, je nachdem, wie die Patch-Datei generiert wurde) vorangestellt wird, die Sie entfernen müssen, damit er herausgefunden werden kann Die reale Datei zum Pfad zu der Datei, auf die der Patch angewendet werden muss.

Weitere Optionen finden Sie in der Manpage für git-apply.

Schritt drei: Es gibt keinen Schritt drei

Natürlich möchten Sie Ihre Änderungen festschreiben, aber wer soll sagen, dass Sie keine anderen verwandten Optimierungen haben, die Sie vornehmen möchten, bevor Sie Ihre Festschreibung vornehmen?

masukomi
quelle
1
Dies war sehr nützlich, wenn current_branch viele "zusätzliche" Änderungen hatte, die beibehalten werden mussten. Ich habe den Unterschied nur der von branch_b eingebrachten Änderungen erhalten als: git diff HEAD ... branch_b (ja - drei Perioden machen den Zaubertrick).
Saad Malik
@masukomi, sollten Sie in Schritt 2 nicht die in Schritt 1 erstellte Patch-Datei als Argument hinzufügen?
Spiralis
Für mich werden alle Änderungen abgelehnt. Irgendeine Idee warum?
LinusGeffarth
Der erste Gedanke bei LinusGeffarth ist, dass Sie vielleicht die Zweige beim Erstellen des Patches rückwärts haben? wird außerhalb von SO nachverfolgen, um zu sehen, ob wir es herausfinden können.
Masukomi
24

Hier erfahren Sie, wie Sie den Verlauf dazu bringen können, mit minimalem Aufwand nur ein paar Dateien aus einem anderen Zweig zu verfolgen, selbst wenn eine "einfachere" Zusammenführung viel mehr Änderungen mit sich gebracht hätte, die Sie nicht möchten.

Zunächst machen Sie den ungewöhnlichen Schritt, im Voraus zu erklären, dass das, was Sie festschreiben möchten, eine Zusammenführung ist, ohne dass git irgendetwas mit den Dateien in Ihrem Arbeitsverzeichnis tut:

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

. . . Dabei ist "Filialname" das, von dem Sie behaupten, dass es zusammengeführt wird. Wenn Sie sich sofort festlegen würden, würde dies keine Änderungen vornehmen, aber es würde immer noch die Abstammung des anderen Zweigs anzeigen. Sie können weitere Zweige / Tags / etc. Hinzufügen. bei Bedarf auch zur Befehlszeile. Zu diesem Zeitpunkt müssen jedoch keine Änderungen festgeschrieben werden. Holen Sie sich daher als Nächstes die Dateien aus den anderen Revisionen.

git checkout branchname1 -- file1 file2 etc

Wenn Sie aus mehr als einem anderen Zweig zusammengeführt haben, wiederholen Sie den Vorgang nach Bedarf.

git checkout branchname2 -- file3 file4 etc

Jetzt befinden sich die Dateien aus dem anderen Zweig im Index und können mit dem Verlauf festgeschrieben werden.

git commit

In dieser Commit-Nachricht müssen Sie viel erklären.

Bitte beachten Sie jedoch, falls dies nicht klar war, dass dies eine durcheinandergebrachte Sache ist. Es ist nicht im Sinne dessen, wofür ein "Zweig" ist, und Kirschpflücken ist eine ehrlichere Art, das zu tun, was Sie hier tun würden. Wenn Sie eine weitere "Zusammenführung" für andere Dateien in demselben Zweig durchführen möchten, den Sie beim letzten Mal nicht übernommen haben, wird eine Meldung "bereits aktuell" angezeigt. Es ist ein Symptom, nicht zu verzweigen, wenn wir sollten, in der "von" -Zweig sollte mehr als eine andere Verzweigung sein.

jejese
quelle
3
Ihr erster Befehl ( git merge --no-ff --no-commit -s outs branchname1) ist genau das, wonach ich gesucht habe! Vielen Dank!
RobM
1
Bei mehreren Zweigen, dem erforderlichen Verlauf, dem Zusammenführen einzelner Dateien und dem Ändern des Inhalts der Datei vor dem Pushen scheint dies eine anständige Alternative zu sein. Zum Beispiel dev => master, aber Sie möchten die Host-Definition oder ähnliches ändern, bevor Sie zum Master wechseln.
Timss
15

Ich weiß, dass ich etwas spät dran bin, aber dies ist mein Workflow zum Zusammenführen ausgewählter 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
Felix
quelle
Ich habe eine leichte Variation davon verwendet. Anstatt zu verschmelzen, pflückte ich Kirschen. Es macht den Job. Der einzige Nachteil dieses Ansatzes ist, dass Sie den Verweis auf den ursprünglichen Commit-Hash verlieren.
Matt Florence
15

Am einfachsten ist es, Ihr Repo auf den Zweig einzustellen, mit dem Sie zusammenführen möchten, und dann auszuführen.

git checkout [branch with file] [path to file you would like to merge]

Wenn du läufst

git status

Sie sehen die bereits bereitgestellte Datei ...

Dann renne

git commit -m "Merge changes on '[branch]' to [file]"

Einfach.

dsieczko
quelle
3
Dies ist fast die beste Antwort, die ich gefunden habe. Bitte sehen Sie jasonrudolph.com/blog/2009/02/25/… So klar, prägnant und es funktioniert einfach!
Superuseroi
1
das wird den Dateiinhalt aus dem
Quellzweig
Ich wollte gerade so antworten, ich dachte, ich erfinde neue Dinge, die noch nicht beantwortet wurden! Dies ist jedoch der einfachste Weg , dies zu tun. Dies sollte oben sein!
Irfandy Jip
15

Ich fand, dass dieser Beitrag die einfachste Antwort enthält. Nur tun:

$ #git checkout <branch from which you want files> <file paths>

Beispiel:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Weitere Informationen finden Sie im Beitrag.

Stunner
quelle
3
Dies wird nicht wirklich zusammengeführt, sondern überschreibt die Datei im aktuellen Zweig.
Igor Ralic
1
@igrali Das ist ein nützlicher Kommentar, aber verglichen mit der Schwierigkeit der "richtigen" Methoden, dies zu tun, ist dies eine gute Problemumgehung. Man muss nur sehr vorsichtig sein.
Owensmartin
12

Es ist seltsam, dass Git immer noch kein so praktisches Tool "out of the box" hat. Ich benutze es häufig, wenn ich einen alten Versionszweig (der immer noch viele Software-Benutzer hat) durch nur einige Bugfixes aus dem aktuellen Versionszweig aktualisiere . In diesem Fall ist es oft erforderlich, schnell nur einige Codezeilen aus der Datei im Trunk abzurufen und viele andere Änderungen zu ignorieren (die nicht in die alte Version übernommen werden sollen) ... und natürlich die interaktive Drei-Wege- Zusammenführung wird in diesem Fall benötigt, git checkout --patch <branch> <file path>ist für diesen selektiven Zusammenführungszweck nicht verwendbar.

Sie können es leicht tun:

Fügen Sie diese Zeile einfach zu einem [alias]Abschnitt in Ihrer globalen .gitconfigoder lokalen .git/configDatei hinzu:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Dies bedeutet, dass Sie Beyond Compare verwenden. Wechseln Sie bei Bedarf einfach zu einer Software Ihrer Wahl. Sie können es auch in die automatische Drei-Wege-Zusammenführung ändern, wenn Sie die interaktive selektive Zusammenführung nicht benötigen:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Dann verwenden Sie wie folgt:

git mergetool-file <source branch> <file path>

Dies gibt Ihnen die echte Möglichkeit zum selektiven Zusammenführen von Bäumen für jede Datei in einem anderen Zweig.

JimStar
quelle
10

Es ist nicht genau das, wonach Sie gesucht haben, aber es war nützlich für mich:

git checkout -p <branch> -- <paths> ...

Es ist eine Mischung aus einigen Antworten.

Felipe
quelle
2
Dies ist in der Tat nützlich und könnte zu der für mich besten Antwort, der Antwort von @ alvinabad, hinzugefügt werden. Wenn Sie git checkout HEAD file1Folgendes tun: Um die aktuelle Version beizubehalten und die Datei file1zu entfernen , können Sie mit der -pOption einen Teil der Datei auswählen , die zusammengeführt werden soll. Danke für den Trick!
Simon C.
Dies ist meine Lieblingsantwort. Einfach, auf den Punkt und funktioniert
Jesse Reza Khorasanee
8

Ich würde eine machen

git diff commit1..commit2 Dateimuster | git-apply --index && git commit

Auf diese Weise können Sie den Bereich der Festschreibungen für ein Dateimuster aus einem Zweig begrenzen.

Gestohlen von: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

lumpidu
quelle
In einigen Situationen kann dies sehr praktisch sein. Wenn sich die Änderungen jedoch in einem anderen Zweig befinden, können Sie einfach an der Spitze dieses Zweigs auschecken, wie in der obigen Antwort von Bart J.
cdunn2001
Wer ist Bart Js?
Schwarz
8

Ich hatte genau das gleiche Problem wie oben von Ihnen erwähnt. Aber ich fand diesen Git-Blog klarer, als ich die Antwort erklärte.

Befehl über den obigen Link:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Susheel Javadi
quelle
hast du das getestet Ich bin sicher, dass die Dateien von <branch_you_want_to_merge_from> ersetzt werden, anstatt zusammengeführt zu werden
Amare
7

Ich mag die Antwort "Git-Interactive-Merge" oben, aber es gibt eine einfachere. Lassen Sie git dies für Sie tun, indem Sie eine Rebase-Kombination aus interaktiv und auf:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Der Fall ist also, dass Sie C1 und C2 von der 'Feature'-Verzweigung (Verzweigungspunkt' A ') wollen, aber im Moment keine der anderen.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Wie oben, gelangen Sie in den interaktiven Editor, in dem Sie die Auswahlzeilen für C1 und C2 auswählen (wie oben). Speichern und beenden, und dann wird mit der Rebase fortgefahren und Sie erhalten Zweig 'Temp' und auch HEAD bei Master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Dann können Sie einfach den Master auf HEAD aktualisieren und den temporären Zweig löschen, und schon kann es losgehen:

# git branch -f master HEAD
# git branch -d temp
Waten
quelle
7

Was ist mit git reset --soft branch? Ich bin überrascht, dass es noch niemand erwähnt hat.

Für mich ist es der einfachste Weg, die Änderungen selektiv aus einem anderen Zweig auszuwählen, da dieser Befehl in meinen Arbeitsbaum alle Diff-Änderungen einfügt und ich einfach auswählen kann, welche ich benötige. Auf diese Weise habe ich die volle Kontrolle über die festgeschriebenen Dateien.

GarryOne
quelle
6

Ich weiß, dass diese Frage alt ist und es viele andere Antworten gibt, aber ich habe mein eigenes Skript namens "pmerge" geschrieben, um Verzeichnisse teilweise zusammenzuführen. Es ist in Arbeit und ich lerne immer noch sowohl Git- als auch Bash-Scripting.

Dieser Befehl verwendet git merge --no-commitund wendet dann Änderungen an, die nicht mit dem angegebenen Pfad übereinstimmen.

Verwendung: git pmerge branch path
Beispiel:git merge develop src/

Ich habe es nicht ausgiebig getestet. Das Arbeitsverzeichnis sollte frei von nicht festgeschriebenen Änderungen und nicht verfolgten Dateien sein.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
Andy
quelle
3

Ein einfacher Ansatz zum selektiven Zusammenführen / Festschreiben nach Datei:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes

Dave C.
quelle
3

Wenn Sie nicht zu viele Dateien haben, die geändert wurden, bleiben Ihnen keine zusätzlichen Commits.

1. Zweig vorübergehend duplizieren
$ git checkout -b temp_branch

2. Auf das letzte gewünschte Commit zurücksetzen
$ git reset --hard HEAD~n , wobei ndie Anzahl der Commits angegeben ist, die Sie zurückgehen müssen

3. Überprüfen Sie jede Datei aus dem ursprünglichen Zweig
$ git checkout origin/original_branch filename.ext

Jetzt können Sie bei Bedarf einen Commit ausführen und Push erzwingen (um Remote zu überschreiben).

JBaczuk
quelle
3

Wenn Sie nur ein bestimmtes Verzeichnis und lassen alles andere intakt und noch bewahren Geschichte verschmelzen müssen, könnten Sie vielleicht versuchen , diese ... ein neues erstellen target-branchweg von der , masterbevor Sie experimentieren.

Bei den folgenden Schritten wird davon ausgegangen, dass Sie zwei Zweige target-branchund source-branchhaben und sich das Verzeichnis dir-to-merge, das Sie zusammenführen möchten , im Verzeichnis befindet source-branch. Angenommen, Sie haben andere Verzeichnisse wie dir-to-retainim Ziel, die Sie nicht ändern und den Verlauf beibehalten möchten. Es wird auch davon ausgegangen, dass es Zusammenführungskonflikte in der gibt dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.
code4kix
quelle
2

Wenn sich zwischen den aktuellen Commits der beiden Zweige nur wenige Dateien geändert haben, füge ich die Änderungen manuell zusammen, indem ich die verschiedenen Dateien durchlaufe.

git difftoll <branch-1>..<branch-2>

raratiru
quelle