So führen Sie ein bestimmtes Commit in Git zusammen

1034

Ich habe einen Zweig aus einem Repository in GitHub gegabelt und etwas Spezielles für mich festgelegt. Jetzt fand ich, dass das ursprüngliche Repository eine gute Funktion hatte, die bei war HEAD.

Ich möchte es nur ohne vorherige Zusagen zusammenführen. Was sollte ich tun? Ich weiß, wie alle Commits zusammengeführt werden:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
netawater
quelle
Wenn Sie dies in Bezug auf Github versuchen, wird dieser Artikel Sie durch den Artikel führen. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Antworten:

1174

' git cherry-pick' sollte hier deine Antwort sein.

Übernehmen Sie die durch ein vorhandenes Commit eingeführte Änderung.

Vergessen Sie nicht, die Antwort von bdonlan über die Konsequenzen des Kirschpflückens in diesem Beitrag zu lesen :
"Alle Commits aus einem Zweig ziehen, bestimmte Commits in einen anderen verschieben" , wobei:

A-----B------C
 \
  \
   D

wird:

A-----B------C
 \
  \
   D-----C'

Das Problem bei diesem Commit ist, dass git feststellt, dass Commits den gesamten Verlauf vor ihnen enthalten

Wobei C 'eine andere SHA-1ID hat.
Ebenso beinhaltet das Auswählen eines Commits von einem Zweig zum anderen im Wesentlichen das Generieren eines Patches und das anschließende Anwenden dieses Patches, wodurch auch die Historie auf diese Weise verloren geht.

Diese Änderung der Festschreibungs-IDs unterbricht unter anderem die Zusammenführungsfunktion von git (wenn sie jedoch sparsam verwendet wird, gibt es Heuristiken, die dies dokumentieren).
Noch wichtiger ist jedoch, dass funktionale Abhängigkeiten ignoriert werden. Wenn C tatsächlich eine in B definierte Funktion verwendet hat, werden Sie es nie erfahren .

VonC
quelle
1
@ openid000: "feinkörnigere Zweige": Genau das schlug bdonlan in seiner Antwort vor.
VonC
8
Hinweis: "git rebase" ändert auch SHA-1. Siehe auch "Git-Rebase vs. Git-Merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) und" Git-Workflow "( stackoverflow.com/questions/457927/… ) für Fälle, in denen" Git-Rebase " "ist legitim.
VonC
1
Zwischen "feinkörnigen Zweigen", "Cherry-Pick" und "Rebase" haben Sie dann alle Möglichkeiten, Code in Zweigen mit Git zu verwalten.
VonC
@VonC "feinkörnige Zweige" ja. Ich hatte zwei Zweige und ich hatte Upgrades für ein bestimmtes Visualisierungsmodul in einem Zweig vorgenommen. Dieses Modul war unabhängig von allen anderen Codes, so dass Cherry Pick-Pick verdammt praktisch war, um diese Änderungen auch auf andere Zweige anzuwenden
Cheeku
1
Beachten Sie jedoch, dass beim Zusammenführen sowohl die Commits C 'als auch C im Commit-Verlauf Vorrang haben.
Rahul Shah
738

Sie können Git Cherry-Pick verwenden, um ein einzelnes Commit für sich auf Ihren aktuellen Zweig anzuwenden.

Beispiel: git cherry-pick d42c389f

bdonlan
quelle
68
+1 für Ihren früheren Beitrag zum Thema Kirschernte ( stackoverflow.com/questions/880957/… ). Ich habe mir erlaubt, einen Auszug davon in meine eigene Antwort oben zu kopieren.
VonC
4
Wahrscheinlich git cherry-pick d42coder git cherry-pick d42c3 wird funktionieren. Git ist schlau. ;)
Guneysus
2
tödlich: schlechte Revision
Ievgen Naida
2
Ich habe gerade diesen Befehl ausgeführt und es scheint funktioniert zu haben, aber wenn ich den Git-Status mache, heißt es "nichts zu verpflichten, Baum sauber zu arbeiten". Wenn ich die Commits-Seite auf meiner Bitbucket-Webseite aktualisiere, wird sie nicht angezeigt. Aber es erscheint, wenn ich Git-Protokoll ausführe. Und ich sehe den geänderten Code. Kann mir jemand erklären, ob ich zusätzliche Schritte machen muss?
Ray
1
Leider beantwortet dies nicht die Frage: Es wird keine Zusammenführung erstellt. Es gibt keine Vorfahren, auf die hingewiesen wird d42c389f. Vielleicht war es dem OP nicht wichtig , eine Zusammenführung per se zu erstellen , aber der Unterschied spielt manchmal eine Rolle.
LarsH
29

Versuchen wir ein Beispiel zu nehmen und zu verstehen:

Ich habe einen Zweig, sagen wir Master , der auf X <commit-id> zeigt, und ich habe einen neuen Zweig, der auf Y <sha1> zeigt.

Wobei Y <commit-id> = <master> Zweig festschreibt - wenige Festschreibungen

Sagen wir nun für den Y-Zweig, ich muss die Commits zwischen dem Hauptzweig und dem neuen Zweig schließen. Nachfolgend finden Sie die Vorgehensweise, die wir befolgen können:

Schritt 1:

git checkout -b local origin/new

Dabei ist local der Filialname. Jeder Name kann angegeben werden.

Schritt 2:

  git merge origin/master --no-ff --stat -v --log=300

Führen Sie die Commits vom Hauptzweig zum neuen Zweig zusammen und erstellen Sie ein Zusammenführungs-Commit der Protokollnachricht mit einzeiligen Beschreibungen von höchstens <n> tatsächlichen Commits, die zusammengeführt werden.

Weitere Informationen und Parameter zu Git Merge finden Sie unter:

git merge --help

Wenn Sie ein bestimmtes Commit zusammenführen müssen, können Sie Folgendes verwenden:

git cherry-pick <commit-id>
Spyder
quelle
Hast du die Definition Yin deinem 3D-Satz geändert ? "Ich habe einen neuen Zweig, der auf Y zeigt" vs "Jetzt sag für Y-Zweig", klingt wie Y war früher ein Commit und dann wurde es ein Zweig
Purefan
wie kann ich das tun von einem bestimmten Punkt einer Niederlassung verpflichten , die ich zusammenführen möchten
Kulbhushan Singh
3

In meinem Anwendungsfall hatten wir einen ähnlichen Bedarf an CI-CD. Wir haben Git Flow mit Entwicklungs- und Master-Zweigen verwendet. Entwickler können ihre Änderungen direkt zusammenführen, um sie zu entwickeln, oder über eine Pull-Anfrage aus einem Feature-Zweig. Um dies zu meistern, führen wir jedoch nur die stabilen Commits aus dem Entwicklungszweig automatisiert über Jenkins zusammen.

In diesem Fall ist Kirschpflücken keine gute Option. Wir erstellen jedoch einen lokalen Zweig aus der Commit-ID und führen diesen lokalen Zweig dann zusammen, um mvn clean verify zu meistern und durchzuführen (wir verwenden maven). Wenn dies erfolgreich ist, geben Sie das Artefakt der Produktionsversion mithilfe des Maven-Release-Plugins mit der Option localCheckout = true und pushChanges = false an nexus frei. Wenn alles erfolgreich ist, schieben Sie die Änderungen und das Tag zum Ursprung.

Ein Beispielcode-Snippet:

Angenommen, Sie sind auf dem Master, wenn Sie dies manuell tun. Bei Jenkins befinden Sie sich jedoch beim Auschecken des Repos im Standardzweig (Master, falls konfiguriert).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Dies gibt Ihnen die volle Kontrolle mit einer furchtlosen Verschmelzung oder Konflikthölle.

Fühlen Sie sich frei zu beraten, falls es eine bessere Option gibt.

nrkkalyan
quelle
3

In den wichtigsten Antworten wird beschrieben, wie Sie die Änderungen aus einem bestimmten Commit auf den aktuellen Zweig anwenden . Wenn Sie das mit "Zusammenführen" meinen, verwenden Sie einfach Cherry-Pick, wie sie vorschlagen.

Aber wenn Sie tatsächlich eine Zusammenführung wünschen , dh Sie möchten eine neue Verpflichtung mit zwei Eltern - das vorhandene Commit für den aktuellen Zweig und das Commit, von dem aus Sie Änderungen anwenden -, kann ein Cherry-Pick dies nicht erreichen.

Ein echter Zusammenführungsverlauf kann beispielsweise wünschenswert sein, wenn Ihr Erstellungsprozess die Git-Abstammung nutzt, um automatisch Versionszeichenfolgen basierend auf dem neuesten Tag (unter Verwendung git describe) festzulegen .

Anstelle von Cherry-Pick können Sie eine tatsächliche git merge --no-commitund dann den Index manuell anpassen, um alle Änderungen zu entfernen, die Sie nicht möchten.

Angenommen, Sie befinden sich in einer Verzweigung Aund möchten das Commit an der Spitze der Verzweigung zusammenführen B:

git checkout A
git merge --no-commit B

Jetzt können Sie ein Commit mit zwei Elternteilen erstellen, die aktuellen Tipp-Commits von AundB . Möglicherweise werden jedoch mehr Änderungen angewendet, als Sie möchten, einschließlich Änderungen aus früheren Commits im B-Zweig. Sie müssen diese unerwünschten Änderungen rückgängig machen und dann festschreiben.

(Es gibt möglicherweise eine einfache Möglichkeit, den Status des Arbeitsverzeichnisses und des Index auf den Stand vor dem Zusammenführen zurückzusetzen, sodass Sie eine saubere Tabelle haben, auf der Sie das gewünschte Commit auswählen können ich weiß nicht , wie das reine Weste zu erreichen. git checkout HEADund git reset HEADwerde sowohl den Fusions - Zustand entfernen, um den Zweck dieses Verfahrens zu besiegen.)

Machen Sie die unerwünschten Änderungen manuell rückgängig. Zum Beispiel könnten Sie

git revert --no-commit 012ea56

für jedes unerwünschte Commit 012ea56.

Wenn Sie mit dem Anpassen fertig sind, erstellen Sie Ihr Commit:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Jetzt haben Sie nur die gewünschte Änderung, und der Ahnenbaum zeigt, dass Sie technisch von B zusammengeführt wurden.

LarsH
quelle