Wie kann man eine Reihe von Commits auswählen und sich in einen anderen Zweig verschmelzen?
-
22-09-2019 - |
Frage
Ich habe das folgende Repository -Layout:
- Master Branch (Produktion)
- Integration
- Arbeiten
Was ich erreichen möchte, ist, eine Reihe von Commits aus dem Arbeitszweig auszuwählen und sie in den Integrationszweig zu verschmelzen. Ich bin ziemlich neu in Git und ich kann nicht herausfinden, wie ich genau das machen soll (die Kirschwolke von Commit -Bereichen in einer Operation, nicht die Verschmelzung), ohne das Repository durcheinander zu bringen. Irgendwelche Zeiger oder Gedanken dazu? Vielen Dank!
Lösung
Wenn es um eine Reihe von Commits geht, Cherry-Picking ist war nicht praktisch.
Wie unten genannten durch Keith Kim, Git 1.7.2+ führte die Fähigkeit ein, eine Reihe von Commits zu packen (aber Sie müssen sich dessen bewusst sein Folge des Kirschpicks für die zukünftige Zusammenführung)
Git Cherry-Pick "lernte, eine Reihe von Commits zu wählen
(z.B "cherry-pick A..B
" und "cherry-pick --stdin
"), auch"git revert
"; diese unterstützen die schönere Sequenzierungskontrolle nicht"rebase [-i]
"hat aber.
Damian Kommentare und warnt uns:
In dem "
cherry-pick A..B
" bilden,A
sollte älter sein alsB
.
Wenn sie die falsche Reihenfolge sind, scheitert der Befehl stillschweigend.
Wenn Sie die auswählen möchten Angebot B
durch D
(inklusive) das wäre B^..D
.
Sehen "Git erstellen Branch aus Reichweite früherer Commits?" als Illustration.
Wie Jubobs Erwähnungen in den Kommentaren:
Dies setzt das voraus
B
ist kein Wurzelbefehl; Du wirst eine "bekommen"unknown revision
"Fehler ansonsten.
Hinweis: Ab GIT 2.9.x/2.10 (Q3 2016) können Sie eine Reihe von Läufen direkt auf einem Orphan-Zweig (leerer Kopf) einstellen: siehe "Wie man den bestehenden Zweig zu einem Waisen in Git macht".
Originalantwort (Januar 2010)
EIN rebase --onto
Wäre besser, wo Sie die angegebene Spektrum des Commits zusätzlich zu Ihrer Integrationszweig wiederholen, als Charles Bailey beschrieben hier.
(Suchen Sie auch nach "Hier ist, wie Sie einen Themenzweig basierend auf einem Zweig zur anderen transplantieren" in der Git Rebase Man Page, um ein praktisches Beispiel von zu sehen git rebase --onto
)
Wenn Ihr aktueller Zweig Integration ist:
# Checkout a new temporary branch at the current location
git checkout -b tmp
# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range
# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration
Das wird alles zwischen:
- Nach dem Elternteil von
first_SHA-1_of_working_branch_range
(daher die~1
): Das erste Commit, das Sie wiederholen möchten - bis zu "
integration
"(was auf das letzte Commit hinweist, das Sie wiederholen möchten, von derworking
Zweig)
zu "tmp
"(was darauf hinweist, wo integration
zeigte vorher)
Wenn es einen Konflikt gibt, wenn einer dieser Commits wiedergegeben wird:
- Entweder lösen und rennen "
git rebase --continue
". - Oder überspringen Sie diesen Patch und laufen Sie statt. "
git rebase --skip
" - Oder abbrechen alles mit einem "
git rebase --abort
"(und leg das zurückintegration
Zweig auf dertmp
Zweig)
Danach rebase --onto
, integration
wird am letzten Commit der Integrationszweig zurück sein (dh "tmp
"Zweig + alle wiederholten Commits)
Mit Kirschpicking oder rebase --onto
, Vergessen Sie nicht, dass es Konsequenzen auf nachfolgende Zusammenführungen hat wie hier beschrieben.
Ein reines "cherry-pick
"Lösung ist Hier besprochen, und würde so etwas wie:
Wenn Sie einen Patch-Ansatz verwenden möchten, sind "git format-patch | git am" und "git cherry" Ihre Optionen.
Zur Zeit,git cherry-pick
Akzeptiert nur ein einziges Commit, aber wenn Sie den Bereich auswählen möchtenB
durchD
das wäreB^..D
in Git lingo so
git rev-list --reverse --topo-order B^..D | while read rev
do
git cherry-pick $rev || break
done
Wenn Sie jedoch eine Reihe von Commits "wiedergeben" müssen, sollte das Wort "Wiederholung" Sie dazu drängen, das zu verwenden "rebase
"Merkmal von Git.
Andere Tipps
Ab GIT v1.7.2 Cherry Pick kann eine Reihe von Commits akzeptieren:
git cherry-pick
lernte, eine Reihe von Commits auszuwählen (z. B. z. B.cherry-pick A..B
undcherry-pick --stdin
), auchgit revert
; Diese unterstützen die schönere Sequenzierungskontrolle nichtrebase [-i]
hat jedoch.
Angenommen, Sie haben 2 Zweige,
"Brancha": Beinhaltet Commits, die Sie kopieren möchten (von "Commita" zu "Commitb"
"Branchb": Die Zweigstelle, die die Commits von "Brancha" übertragen werden sollen
1)
git checkout <branchA>
2) Holen Sie sich die IDs von "Commita" und "Commitb"
3)
git checkout <branchB>
4)
git cherry-pick <commitA>^..<commitB>
5) Falls Sie einen Konflikt haben, lösen Sie ihn und tippen
git cherry-pick --continue
den Kirschpick-Prozess fortzusetzen.
Sind Sie sicher, dass Sie die Zweige nicht wirklich zusammenführen möchten? Wenn die Arbeitsabteilung einige aktuelle Commits hat, die Sie nicht möchten, können Sie einfach einen neuen Zweig mit einem Kopf an dem gewünschten Punkt erstellen.
Wenn Sie nun wirklich eine Reihe von Commits einfügen möchten, können Sie aus irgendeinem Grund eine elegante Möglichkeit, dies zu tun, nur ein Patchset zu ziehen und auf Ihren neuen Integrationszweig anzuwenden:
git format-patch A..B
git checkout integration
git am *.patch
Dies ist im Wesentlichen das, was Git-Rebase sowieso tut, aber ohne die Notwendigkeit, Spiele zu spielen. Du kannst hinzufügen --3way
zu git-am
Wenn Sie zusammenführen müssen. Stellen Sie sicher, dass es im Verzeichnis, in dem Sie dies tun, keine anderen *.patch -Dateien vorhanden sind, wenn Sie den wörtlichen Anweisungen befolgen ...
Ich habe eingewickelt VONCs Code in ein kurzes Bash -Skript, git-multi-cherry-pick
, zum einfachen Laufen:
#!/bin/bash
if [ -z $1 ]; then
echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
echo "";
echo "Usage: $0 start^..end";
echo "";
exit 1;
fi
git rev-list --reverse --topo-order $1 | while read rev
do
git cherry-pick $rev || break
done
Ich benutze dies derzeit, da ich die Geschichte eines Projekts wieder aufbaue, das sowohl Code auf dem dritten Party als auch die Anpassungen im selben SVN-Koffer miteinander gemischt hatte. Ich spreche jetzt den Core -Code von Drittanbietern, Modulen von Drittanbietern und Anpassungen auf ihre eigenen Git -Zweige für ein besseres Verständnis der künftigen Anpassungen auf. git-cherry-pick
ist in dieser Situation hilfreich, da ich zwei Bäume im selben Repository habe, aber ohne gemeinsamen Vorfahren.
Alle oben genannten Optionen fordern Sie zur Lösung von Zusammenführungskonflikten auf. Wenn Sie Änderungen für ein Team verschmelzen, ist es schwierig, die Zusammenführungskonflikte von Entwicklern zu lösen und fortzufahren. "Git Merge" wird jedoch die Zusammenführung in einer Einstellung ausführen, aber Sie können eine Reihe von Überarbeitungen nicht als Argument bestehen. Wir müssen "Git Diff" und "Git Apply" -Beharten verwenden, um den Zusammenführungsbereich von Drehzählern durchzuführen. Ich habe beobachtet, dass "Git Apply" fehlschlägt, wenn die Patch -Datei für zu viele Dateien differenziert ist. Daher müssen wir einen Patch pro Datei erstellen und dann anwenden. Beachten Sie, dass das Skript die Dateien, die in Quellzweig gelöscht werden, nicht löschen können. Dies ist ein seltener Fall. Sie können solche Dateien manuell aus Target Branch löschen. Der Exit -Status von "Git Apply" ist nicht Null, wenn er den Patch nicht anwenden kann. Wenn Sie jedoch -3way -Option verwenden, fällt er auf 3 Wege zurück und Sie müssen sich keine Sorgen um diesen Fehler machen.
Unten ist das Skript.
enter code here
#!/bin/bash
# This script will merge the diff between two git revisions to checked out branch
# Make sure to cd to git source area and checkout the target branch
# Make sure that checked out branch is clean run "git reset --hard HEAD"
START=$1
END=$2
echo Start version: $START
echo End version: $END
mkdir -p ~/temp
echo > /tmp/status
#get files
git --no-pager diff --name-only ${START}..${END} > ~/temp/files
echo > ~/temp/error.log
# merge every file
for file in `cat ~/temp/files`
do
git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
if [ $? -ne 0 ]
then
# Diff usually fail if the file got deleted
echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
echo Skipping the merge: git diff command failed for $file
echo "STATUS: FAILED $file" >> /tmp/status
echo "STATUS: FAILED $file"
# skip the merge for this file and continue the merge for others
rm -f ~/temp/git-diff
continue
fi
git apply --ignore-space-change --ignore-whitespace --3way --allow-binary-replacement ~/temp/git-diff
if [ $? -ne 0 ]
then
# apply failed, but it will fall back to 3-way merge, you can ignore this failure
echo "git apply command filed for $file"
fi
echo
STATUS=`git status -s $file`
if [ ! "$STATUS" ]
then
# status is null if the merged diffs are already present in the target file
echo "STATUS:NOT_MERGED $file"
echo "STATUS: NOT_MERGED $file$" >> /tmp/status
else
# 3 way merge is successful
echo STATUS: $STATUS
echo "STATUS: $STATUS" >> /tmp/status
fi
done
echo GIT merge failed for below listed files
cat ~/temp/error.log
echo "Git merge status per file is available in /tmp/status"
Eine andere Option könnte darin bestehen, sich mit Strategie zu dem Commit vor dem Bereich zu verschmelzen und dann eine „normale“ Verschmelzung mit dem letzten Commit für diesen Bereich (oder Zweig, wenn es der letzte ist). Nehmen wir also an, nur 2345 und 3456 Commits of Master, die in Feature Branch zusammengeführt werden sollen:
master: 1234 2345 3456 4567
In Feature Branch:
git merge -s ours 4567 git merge 2345