Jak wiśnia wybrać zakres zobowiązań i połączyć się w inną gałąź?


640

Mam następujący układ repozytorium:

  • oddział główny (produkcja)
  • integracja
  • pracujący

To, co chcę osiągnąć, to wybranie zakresu zatwierdzeń z działającej gałęzi i połączenie ich z gałęzią integracji. Jestem całkiem nowy w Git i nie mogę dowiedzieć się, jak to dokładnie zrobić (wiosenne wybieranie zakresów zatwierdzania w jednej operacji, a nie scalanie) bez zepsucia repozytorium. Wszelkie wskazówki lub przemyślenia na ten temat? Dzięki!


Odpowiedzi:


808

Jeśli chodzi o zakres zobowiązań, wybieranie jest nie było praktyczne.

Jak wspomniano poniżej przez Keitha Kim , Git 1.7.2+ wprowadził możliwość wybrania szeregu zatwierdzeń (ale wciąż musisz być świadomy konsekwencji wybierania wiśni dla przyszłego scalenia )

git cherry-pick ”nauczył się wybierać zakres zatwierdzeń
(np.„ cherry-pick A..B”i„ cherry-pick --stdin”), podobnie jak„ git revert”, jednak nie obsługują one lepszej kontroli sekwencjonowania„ rebase [-i]”.

Damian komentuje i ostrzega:

W formularzu „ cherry-pick A..B” Apowinien być starszy niżB .
Jeśli są w niewłaściwej kolejności, polecenie po cichu się nie powiedzie .

Jeśli chcesz, aby wybrać zakres Bpoprzez D(włącznie) , że byłoby B^..D.
Zobacz „ Git utworzyć gałąź z zakresu poprzednich zatwierdzeń? ” Jako ilustrację.

Jak wspomina Jubobs w komentarzach :

Zakłada się, że Bnie jest to główny zatwierdzenie; w przeciwnym razie pojawi się unknown revisionbłąd „ ”.

Uwaga: od wersji Git 2.9.x / 2.10 (III kwartał 2016 r.) Możesz wybrać zakres zatwierdzania bezpośrednio w gałęzi sierocej (pusta głowa): patrz „ Jak zmienić istniejącą gałąź w sierotę w git ”.


Oryginalna odpowiedź (styczeń 2010 r.)

rebase --ontoByłoby lepiej, gdzie można odtworzyć dany zakres popełnić na szczycie swojego oddziału integracyjnego, jak Charles Bailey opisane tutaj .
(także poszukaj „Oto jak przeszczepiłbyś gałąź tematu opartą na jednej gałęzi do drugiej” na stronie podręcznika git rebase , aby zobaczyć praktyczny przykład git rebase --onto)

Jeśli obecna gałąź to integracja:

# 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

To odtworzy wszystko między:

  • po rodzicu first_SHA-1_of_working_branch_range(stąd ~1): pierwszego zatwierdzenia, które chcesz odtworzyć
  • do „ integration” (co wskazuje na ostatnie zatwierdzenie, które chcesz odtworzyć, z workinggałęzi)

na „ tmp” (który wskazuje, gdzie integrationwcześniej wskazywał)

Jeśli wystąpi konflikt przy odtwarzaniu jednego z tych zatwierdzeń:

  • rozwiąż go i uruchom „ git rebase --continue”.
  • lub pomiń tę łatkę i zamiast tego uruchom „ git rebase --skip”
  • lub anuluj wszystko za pomocą „ git rebase --abort” (i odłóż integrationgałąź na tmpgałąź)

Po tym rebase --onto, integrationbędzie z powrotem na ostatni popełnić oddziału integracyjnego (czyli „ tmp” oddział + wszystkie odtwarzanych zobowiązuje)

Przy zbieraniu wiśni lub rebase --onto, nie zapomnij, ma to konsekwencje dla kolejnych połączeń, jak opisano tutaj .


Omówiono tutaj czyste cherry-pickrozwiązanie , które wymagałoby:

Jeśli chcesz zastosować podejście oparte na łatce, wybierz „git format-patch | git am” i „git cherry”.
Obecnie git cherry-pickprzyjmuje tylko jeden commit, ale jeśli chcesz, aby wybrać zakres Bpoprzez Dktóry byłby B^..Dw git żargon, więc

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

Ale w każdym razie, kiedy trzeba „odtworzyć” zakres zatwierdzeń, słowo „powtórz” powinno zachęcić cię do korzystania z funkcji „ rebase” Git.


1
Jeśli masz zatwierdzenia z rodzicami, które wymagają takiej -mopcji, jak sobie z nimi radzisz? Czy istnieje sposób na odfiltrowanie tych zatwierdzeń?
— sierpnia

@ aug -mma je dla Ciebie obsłużyć, wybierając linię główną, do której odwołuje się -mparametr wybrany dla tego zbioru.
— VonC

Chodzi o to, że jeśli wybieracie zakres zatwierdzeń, to wybije to zatwierdzenie nadrzędne, ale kiedy trafi w normalne zatwierdzenie, kończy się niepowodzeniem i mówi, że zatwierdzenie nie jest scaleniem. Wydaje mi się, że moje pytanie jest lepiej sformułowane, jak sprawić, by przechodziło ono przez -mopcję tylko wtedy, gdy trafi w zatwierdzenie nadrzędne, gdy zakres wyborów jest bardzo szeroki? W tej chwili, jeśli zdam, że -mtak, git cherry-pick a87afaaf..asfa789 -m 1to dotyczy wszystkich zatwierdzeń w zakresie.
— sierpnia

@ aug Strange, nie odtworzyłem problemu. Jaka jest Twoja wersja git i jaki dokładnie komunikat o błędzie widzisz?
— VonC

Ach, używam gita w wersji 2.6.4 (Apple Git-63). Błąd, który widzę, byłby czymś, co tak error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.naprawdę zdałem sobie sprawę, że możesz po prostu git cherry-pick --continuei byłby w porządku (ale nie obejmowałby zatwierdzenia przez rodzica)
— sierpień

136

Od wersji git v1.7.2 cherry pick może przyjmować szereg zatwierdzeń:

git cherry-picknauczył się wybierać zakres zatwierdzeń (np. cherry-pick A..Bi cherry-pick --stdin), podobnie jak git revert; nie obsługują one jednak lepszej kontroli sekwencjonowania rebase [-i].


103
Zauważ, że cherry-pick A..Bnie dostanie zatwierdzenia A (będziesz tego potrzebować A~1..B), a jeśli pojawią się jakieś konflikty, git nie będzie automatycznie kontynuował tak, jak robi to rebase (przynajmniej od 1.7.3.1)
— Gabe Moothart

3
Warto również zauważyć, że git cherry-pick A..B Cnaiwnie nie działa tak, jak można się tego spodziewać. Nie wybierze wszystkiego z zakresu A..Bi zatwierdzi C! Aby to zrobić, trzeba podzielić na dwie linie, po pierwsze git cherry-pick A..B, a następnie git cherry-pick C. Tak więc, gdy masz zasięg, musisz wykonać go osobno.
— MicroVirus

42

Załóżmy, że masz 2 oddziały,

„branchA”: obejmuje zatwierdzenia, które chcesz skopiować (z „commitA” do „commitB”

„gałąź B”: gałąź, którą chcesz, aby zatwierdzenia były przekazywane z „gałęzi A”

1)

 git checkout <branchA>

2) pobierz identyfikatory „commitA” i „commitB”

3)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) W przypadku konfliktu rozwiąż go i wpisz

git cherry-pick --continue

aby kontynuować proces wyboru.


Działa jak urok! Wielkie dzięki!
— snukone

28

Czy na pewno nie chcesz scalać gałęzi? Jeśli działająca gałąź zawiera kilka ostatnich zatwierdzeń, których nie chcesz, możesz po prostu utworzyć nowy oddział z HEAD w żądanym momencie.

Teraz, jeśli naprawdę chcesz wybrać zakres zatwierdzeń, z jakiegokolwiek powodu, eleganckim sposobem na to jest po prostu wyciągnięcie zestawu łat i zastosowanie go w nowej gałęzi integracji:

git format-patch A..B
git checkout integration
git am *.patch

Zasadniczo to właśnie robi git-rebase, ale bez potrzeby grania w gry. Możesz dodać --3waydo, git-amjeśli chcesz scalić. Upewnij się, że nie ma innych plików * .patch w katalogu, w którym to robisz, jeśli postępujesz zgodnie z instrukcjami dosłownie ...


1
Należy pamiętać, że podobnie jak w przypadku innych zakresów wersji, należy ją A^uwzględnić A.
— Gino Mempin,

9

Owinąłem kod VonC w krótki skrypt bash git-multi-cherry-pick, aby ułatwić uruchamianie:

#!/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 

Obecnie używam tego, ponieważ odbudowuję historię projektu, w którym zarówno kod innej firmy, jak i dostosowania zostały zmieszane razem w tym samym pniu svn. Teraz dzielę główny kod innej firmy, moduły innej firmy i dostosowania na ich własne gałęzie git, aby lepiej zrozumieć modyfikacje w przyszłości. git-cherry-pickjest pomocny w tej sytuacji, ponieważ mam dwa drzewa w tym samym repozytorium, ale bez wspólnego przodka.


3

Wszystkie powyższe opcje podpowiedzą o rozwiązaniu konfliktów scalania. Jeśli scalasz zmiany zatwierdzone dla zespołu, trudno jest rozwiązać konflikty scalania ze strony programistów i kontynuować. Jednak „git merge” wykona scalenie za jednym razem, ale nie można przekazać szeregu poprawek jako argumentu. musimy użyć poleceń „git diff” i „git Apply”, aby wykonać zakres scalania obrotów. Zauważyłem, że „git Apply” zakończy się niepowodzeniem, jeśli plik łatki różni się dla zbyt wielu plików, więc musimy utworzyć łatkę dla każdego pliku, a następnie zastosować. Zauważ, że skrypt nie będzie mógł usunąć plików usuniętych w gałęzi źródłowej. Jest to rzadki przypadek, możesz ręcznie usunąć takie pliki z gałęzi docelowej. Status wyjścia „git Apply” nie wynosi zero, jeśli nie jest w stanie zastosować łatki,

Poniżej znajduje się skrypt.

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"

2

Testowałem to kilka dni temu, po przeczytaniu bardzo jasnego wyjaśnienia Vonc.

Moje kroki

Początek

  • Oddział dev: ABCDEFGHIJ
  • Oddział target: ABCD
  • Nie chcę EaniH

Kroki, aby skopiować funkcje bez kroku E i H w oddziale dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff Dgdzie umieszczam dropprzód Ei Hedytor rebase
  • rozwiązywać konflikty, kontynuować i commit

Kroki, aby skopiować oddział dev_feature_wo_E_Hna miejscu docelowym.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • rozwiązywać konflikty, kontynuować i commit

Kilka uwag

  • Zrobiłem to ze względu na zbyt wiele cherry-pickw poprzednich dniach
  • git cherry-pick jest potężny i prosty, ale

    • tworzy duplikaty zatwierdzeń
    • a kiedy chcę, mergemuszę rozwiązać konflikt początkowych zatwierdzeń i duplikatów zatwierdzeń, więc dla jednego lub dwóch cherry-pickjest w porządku „wybieranie czereśni”, ale dla więcej jest zbyt gadatliwy i gałąź stanie się zbyt złożona
  • Moim zdaniem kroki, które zrobiłem, są wyraźniejsze niż git rebase --onto

1
Dobry post Pozytywne. Przypomina mi stackoverflow.com/a/38418941/6309 . W 2012 r. Podsumowałem wady zbierania danych: stackoverflow.com/a/13524494/6309 .
— VonC

1

Inną opcją może być połączenie naszej strategii ze zmianą przed zakresem, a następnie scalenie „normalne” z ostatnim zatwierdzeniem tego zakresu (lub rozgałęzienie, gdy jest to ostatnie). Załóżmy więc, że tylko 2345 i 3456 zatwierdzeń mastera zostaną połączone w gałąź funkcji:

mistrz:
1234
2345
3456
4567

w gałęzi funkcji:

git merge - nasz 4567
git merge 2345
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.