903 Stimmen

Wie wählt man eine Reihe von Commits aus und fügt sie in einen anderen Zweig ein?

Ich habe das folgende Repository-Layout:

  • Master-Zweig (Produktion)
  • Integration
  • arbeiten

Was ich erreichen möchte, ist eine Reihe von Commits aus dem Arbeitszweig zu nehmen und in den Integrationszweig zusammenzuführen. Ich bin ein ziemlicher Neuling in Git und kann nicht herausfinden, wie ich das genau machen kann (die Auswahl von Commit-Bereichen in einem Vorgang, nicht das Zusammenführen), ohne das Repository durcheinander zu bringen. Hat jemand einen Tipp oder eine Idee dazu? Vielen Dank!

1 Stimmen

5 Stimmen

Der TLDR; ist: git cherry-pick <one-sha-before-the-oldest-sha-to-pick>..<sha-of-latest-to-p‌​ick>

9voto

Adam Franco Punkte 75030

I eingewickelt VonCs Code in ein kurzes Bash-Skript, git-multi-cherry-pick für leichtes 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 verwende dies derzeit, da ich den Verlauf eines Projekts wiederherstelle, das sowohl Code von Drittanbietern als auch Anpassungen im selben svn-Stamm vermischt hatte. Ich trenne jetzt den Kerncode von Drittanbietern, die Module von Drittanbietern und die Anpassungen in eigene Git-Zweige auf, um ein besseres Verständnis der zukünftigen Anpassungen zu ermöglichen. git-cherry-pick ist in dieser Situation hilfreich, da ich zwei Bäume im selben Repository habe, aber ohne einen gemeinsamen Vorfahren.

8voto

kxr Punkte 3782

git cherry-pick FIRST^..LAST funktioniert nur für einfache Szenarien.

Um eine anständige "Merge es in die Integration Zweig" zu erreichen, während mit den üblichen Komfort mit Dingen wie Auto-Skipping von bereits integrierten Picks, Transplantation von Diamant-Merges, interaktive Kontrolle ...) besser eine Rebase verwenden. Eine Antwort hier wies darauf hin, allerdings enthielt das Protokoll eine heikle git branch -f und ein Jonglieren mit einem temporären Zweig. Hier eine gerade robuste Methode:

git rebase -i FIRST LAST~0 --onto integration
git rebase @ integration

El -i ermöglicht eine interaktive Kontrolle. Die ~0 sorgt für einen abgetrennten Rebase (ohne Verschieben des / in einen anderen Zweig), wenn LAST ein Zweigname ist. Ansonsten kann es weggelassen werden. Der zweite Rebase-Befehl verschiebt nur den integration Branch Ref auf sichere Weise an den zwischengeschalteten abgetrennten Kopf weiter - es werden keine neuen Commits eingeführt. Um eine komplexe Struktur mit Merge-Diamanten usw. zu rebasen, sollten Sie --rebase-merges oder --rebase-merges=rebase-cousins in der ersten Umbasierung.

6voto

phili_b Punkte 635

Ich habe das vor einigen Tagen getestet, nachdem ich die sehr klare Erklärung von Vonc gelesen hatte.

Meine Schritte

Start

  • Zweigstelle dev : A B C D E F G H I J
  • Zweigstelle target : A B C D
  • Ich will nicht, dass E ni H

Schritte zum Kopieren von Merkmalen ohne den Schritt E und H im Zweig dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff D wo ich die drop vor E y H im rebase-Editor
  • Konflikte zu lösen, fortzusetzen und commit

Schritte zum Kopieren der Verzweigung dev_feature_wo_E_H am Ziel.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • Konflikte zu lösen, fortzusetzen und commit

Einige Bemerkungen

  • Ich habe das getan, weil ich zu viel cherry-pick in den Tagen vor
  • git cherry-pick ist leistungsstark und einfach, aber

    • erstellt er doppelte Übertragungen
    • und wenn ich es möchte merge Ich muss Konflikte zwischen den ursprünglichen Übertragungen und doppelten Übertragungen lösen, also für ein oder zwei cherry-pick Für eine "Rosinenpickerei" ist es OK, aber für mehr ist es zu langatmig und der Zweig wird zu komplex.
  • Meiner Meinung nach sind die Schritte, die ich gemacht habe, klarer als git rebase --onto

1 Stimmen

Guter Beitrag. Hochgevotet. Erinnert mich an stackoverflow.com/a/38418941/6309 . Ich habe bereits 2012 auf die Nachteile der Rosinenpickerei hingewiesen: stackoverflow.com/a/13524494/6309 .

4voto

Bei allen oben genannten Optionen werden Sie aufgefordert, Zusammenführungskonflikte aufzulösen. Wenn Sie Änderungen zusammenführen, die für ein Team vorgenommen wurden, ist es schwierig, die Zusammenführungskonflikte von den Entwicklern lösen zu lassen und fortzufahren. Der Befehl "git merge" führt die Zusammenführung in einem Schritt durch, aber man kann keinen Revisionsbereich als Argument übergeben. Wir müssen die Befehle "git diff" und "git apply" verwenden, um einen Revisionsbereich zusammenzuführen. Ich habe festgestellt, dass "git apply" fehlschlägt, wenn die Patchdatei einen Diff für zu viele Dateien hat, also müssen wir einen Patch pro Datei erstellen und dann anwenden. Beachten Sie, dass das Skript nicht in der Lage ist, Dateien zu löschen, die im Quellzweig gelöscht wurden. Dies ist ein seltener Fall, Sie können solche Dateien manuell aus dem Zielzweig löschen. Der Exit-Status von "git apply" ist nicht Null, wenn es nicht in der Lage ist, den Patch anzuwenden. Wenn Sie jedoch die Option -3way verwenden, fällt das Skript auf den 3-Wege-Merge zurück und Sie müssen sich keine Sorgen über diesen Fehler machen.

Nachstehend finden Sie 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"

1voto

Koos Vriezen Punkte 11

Eine andere Möglichkeit wäre, mit unserer Strategie bis zum Commit vor dem Bereich zusammenzuführen und dann einen "normalen" Merge mit dem letzten Commit dieses Bereichs (oder einen Branch, wenn es der letzte ist). Nehmen wir also an, dass nur 2345 und 3456 Commits von Master in den Feature-Zweig zusammengeführt werden sollen:

master:
1234
2345
3456
4567

im Feature-Zweig:

git merge -s ours 4567
git merge 2345

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X