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..BApowinien 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.