2008-12-01 7 views
60

Nehmen wir an, ich möchte von einem Release-Zweig zum Master-Zweig zusammenführen, und es gibt einige Commits im Release-Zweig, die ich nicht in den Master-Zweig aufnehmen möchte. Gibt es eine Möglichkeit, die Zusammenführung durchzuführen, sodass ein oder mehrere dieser Commits nicht zusammengeführt werden?Ist es möglich, bestimmte Commits auszuschließen, wenn ein Git-Merge durchgeführt wird?

Meine Strategie ist bisher die folgenden (in Master) zu tun:

git merge --no-commit release-branch 
# Resolve conflicts and apply reverse patch of the commits that I don't want included 
git commit # Edit commit message so that it lists the commits that have been reverse-patched 

Gibt es einen besseren Weg, dies zu tun?

+0

Mögliche Duplikate von [git - Überspringen bestimmter commits beim Zusammenführen] (https://stackoverflow.com/questions/727994/git-skipping-specific-commits-when-merging) - Ich weiß, dass es jünger ist, aber seine [ akzeptierte Antwort] (https://Stackoverflow.com/a/729723/321973) ist IMHO besser –

Antwort

41

Erstellen Sie einen neuen Zweig, erstellen Sie den Zweig interaktiv neu und löschen Sie die nicht benötigten Commits, und fügen Sie dann diesen zusammen.

Sie können Änderungen nicht aus der Mitte eines Zweiges übernehmen, ohne erneut zu hashen, aber das Richtige passiert, wenn es dieselben Änderungen in einem späteren Merge (z. B. von Rosinenpicken und was nicht) sieht.

+2

Aber was passiert, wenn Sie versuchen, diesen Zweig wieder zusammenzuführen? – Evgeny

+4

Mit welchen genauen Befehlen soll das gemacht werden? Ich sehe keine Möglichkeit, wie ich die ungewollten Commits "fallen lassen" kann. – Henning

+8

@Henning wenn du "git Rebase -i anderen Zweig" hast, gibt es dir einen Texteditor mit einer Reihe von Commits darin. Löschen Sie die Zeilen, die Sie nicht möchten. – Dustin

4

Der Grund, warum dies nicht direkt getan werden kann, ist, dass jedes Commit Links zu den Eltern-Commits enthält (normalerweise nur ein, aber mehrere für Merges). Auf diese Weise wird, wenn Sie einen Commit (durch seine SHA1-Summe) haben, auch der gesamte Verlauf festgelegt, da die Eltern auch Links zu ihren Eltern usw. enthalten. Die einzige Möglichkeit, Patches in der History wegzulassen, besteht darin, eine neue zu schreiben. git rebase -i auf einem neu erstellten Zweig ist wahrscheinlich der einfachste Weg, um das zu erreichen.

101

Ich habe eine Lösung gefunden, die für mich funktioniert in the Pro Git book.

Angenommen, Sie möchten die Datei config.php ausschließen.

Auf Zweig A:

  1. Erstellen Sie eine Datei .gitattributes in dem gleichen Verzeichnis genannt, mit dieser Zeile: config.php merge=ours. Dies teilt git mit, welche Strategie beim Zusammenführen der Datei verwendet werden soll. In diesem Fall behalten Sie immer Ihre Version, dh. die Version auf dem Zweig, in den Sie sich einfügen.

  2. die .gitattributes Datei hinzufügen und

Auf Zweig B begehen: Wiederholen Sie die Schritte 1-2

jetzt fusionierenden

Versuchen. Ihre Datei sollte nicht verändert werden.

+1

Für zukünftige Leser funktioniert das hervorragend, explizit * nicht * einschließlich bestimmter Dateien. (In meinem Fall stelle ich verschiedene Zweige auf verschiedenen Servern bereit und möchte mein Capistrano-Bereitstellungsskript für jeden Zweig eindeutig halten.) – charliepark

+13

Dies funktioniert aus irgendeinem Grund nicht für mich. Ich habe einen neuen Zweig mit einem Commit erstellt, der die Datei ändert, und in beiden Zweigen die Datei .gitattributes hinzugefügt. Wenn ich wieder in den ursprünglichen Zweig einschließe, scheint es die Zeile in .gitattributes vollständig zu ignorieren und zieht die geänderte Datei trotzdem ein. Gibt es eine Einstellung, die ich vermisse? – robbles

+6

@robbles Sie könnten dies vermissen: http://stackoverflow.com/a/14099431/6309 – VonC

1

Wenn Sie nur einige Commits ausschließen möchten, die am Ende sind, können Sie einfach verpflichten, eine bestimmte Festschreibenummer:

git checkout partlyMergedFrom 
git whatchanged 
--> find the commit hash up to where you want to merge 
git checkout partlyMergedInto 
git merge e40a0e384f58409fe3c864c655a8d252b6422bfc 
git whatchanged 
--> check that you really got all the changes you want to have 
2

Es ist auch möglich, .git/info/Attribute Datei und halten Sie sie ändern innerhalb des .git-Ordners, anstatt gitattribute-Dateien hinzuzufügen, müssen Sie sie der Quellcodeverwaltung hinzufügen.

12

Wenn Sie einen Support-Zweig haben, wo Sie Fehler beheben und neue Versionen erstellen. Auf Master haben Sie die nächste Version, wo Sie auch häufig neue Versionen erstellen.

Jedes Mal, wenn Sie eine neue Version erstellen, ändern Sie die Version in einer Datei, übergeben diese neue Datei, erstellen ein Tag und drücken. Jetzt mergt von Unterstützung zu Master wird immer Konflikte in der Datei, die die Versionsinformationen enthält.

Wenn die Datei mit der Versionsinformation nur die Versionsinformation enthält, können Sie mit der Antwort von fcurella gehen. Wenn es tatsächlich jedoch auch zusammenführbare Informationen enthält (pom.xml, gradle.properties, MANIFEST.MF, ...), müssen Sie zusätzliche Aktionen ausführen.

Hier können Sie das folgende Beispiel

 C---D*---E---F* support 
    /
A---B---G---H*---I master 

verwenden, wo mit Sternen Änderungen enthalten nur Änderungen aufgrund Version verpflichtet, die während der Zusammenführung ignoriert werden sollte.

Um Unterstützung in Master ohne Merge-Konflikte aufgrund der Version baut zu fusionieren, Sie eine der folgenden Möglichkeiten:

Multiple Merge verpflichtet

git checkout master 
git merge C 
git merge D -s ours 
git merge E 
git merge F -s ours 

Mit dem -s ours Argument, das wir sagen git, nur eine Zusammenführung aufzunehmen, ohne den Arbeitsbereich zu verändern. Dies ist vergleichbar to the --record-only option of svn.

Das obige wird nachfolgend Layout-Ergebnis

 -------------C---D*---E---F* support 
    /   \ \ \ \ 
A---B---G---H*---I---J---K----L---M master 

Eine merge commit Verwendung kirsch Pick

git checkout master 
git merge support -s ours --no-commit 
git cherry-pick C E --no-commit 
git commit -m 'merged support into master' 

erste werden wir eine merge sondern nur Datensatz beginnend die wir fusionieren, ohne dass die Änderung Arbeitsbereich und ohne den Merge-Commit auszuführen. Dann pflücken wir die Commits, um zu verschmelzen, wiederum ohne zu begehen. Endlich begehen wir die Zusammenführung.

Die oben wird im folgenden Layout führen

 C---D*---E---F* support 
    /   \ 
A---B---G---H*---I---J master 

Man könnte sogar die Rosinenpickerei automatisieren.

git checkout master 
git merge support -s ours --no-commit 
for id in `git log support --reverse --not HEAD --format="%H [%an] %s" | 
    grep -v "bump version" | 
    sed "s/\(\w*\)\s.*/\1/g"` 
do 
    git cherry-pick --no-commit $id 
done 
git commit -m 'merged support into master' 
+0

Jedes Beispiel, um mehrere Zusammenführungsverpflichtungen zu automatisieren? – Santos

2

Die wichtigste Frage ist: Wie beurteilen Sie die Commits Sie überspringen wollen darstellen wollen?

  1. sie sneakily verstecken (nicht mein Favorit)
  2. ausdrücklich überspringen sie
  3. sie explizit rückgängig machen

Leider nein. 2 ist im Geschichtsgraphen unmöglich auszudrücken.

Nr. 1 ist möglich, aber ich würde nie tun, dass: eine Zusammenführung kann Änderungen enthalten. - Normalerweise zeigt ein Merge-Commit auf das Ergebnis der Zusammenführung von zwei weiteren Verzweigungen: Alle in diesen Zweigen entwickelten Dinge sollten sich nach der Zusammenführung im Code befinden (d. H. In dem Code, auf den die Zusammenführung verweist). Und nichts anderes sollte in diesem Commit sein.

Aber Überraschung, Überraschung, können Sie die gesamte Codebasis ändern und es als eine Zusammenführung darstellen.Die Auswirkung einer Zusammenführung ist zweifach: Sie führt den Verlaufsbaum zusammen und sollte zwei Codebasen zusammenführen. Ersteres ist sicher (sonst wird es nicht als Zusammenführung bezeichnet), für letzteres könnte es fehlschlagen, z. wenn ein Zusammenführungskonflikt auftrat und es falsch aufgelöst wurde (dann wurden die Codebasen nicht richtig zusammengeführt).

Einige der anderen Antworten deuten auf dieses Verstecken hin. Ich empfehle den expliziten Weg: merge plus revert commits.

+0

Dies ist eine großartige Strategie für kurzlebige Branchen. Das Problem mit dieser Strategie besteht darin, dass der Arbeitszweig die Rücksetzung auch dann erhält, wenn Sie Ihren korrekt zusammengeschlossenen Zweig (den mit dem Rücksprung) in den Arbeitszweig zusammenführen, da es sich um einen regulären, echten Festschreibevorgang handelt. Wenn Sie den Fall haben, dass der Arbeitszweig das Commit wirklich nicht haben sollte, müssen Sie den Rückfall rückgängig machen. Dies wird so lange fortgesetzt, wie dieser bestimmte Arbeitszweig verwendet wird. –

+0

@RonWertlen Sie haben Recht mit der Aussage, dass der Master nicht wieder in diesen "Arbeitszweig"/Release Branch zusammengeführt werden kann, ohne den Effekt der Reverts einzuführen. - Aber das gilt nicht nur für meine Strategie 3 meiner Antwort, sondern auch für Strategie 1. - Dies ist auch ein Grund, warum ich den Workflow aus der OP-Frage nicht persönlich verfolgen würde. –

+0

Ich stimme dir auch zu. 1 und 2 sind Anti-Workflows. Wenn Sie langlebige Zweigstellen haben und die Commits dauerhaft aus dem einen und dem anderen heraus behalten wollen, ist es an der Zeit, über die Umstrukturierung Ihres Projekts nachzudenken und Submodule zu verwenden (mit node.js haben Sie auch node_modules als Parallelmechanismus zur Erreichung der gleichen Sache). –

Verwandte Themen