W jaki sposób git scala się po pracy w cherry pick?

190

Wyobraźmy sobie, że mamy masteroddział.

Następnie tworzymy newbranch

git checkout -b newbranch

i wykonaj dwa nowe commity do newbranch: commit1 i commit2

Następnie przełączamy się na master i make cherry-pick

git checkout master
git cherry-pick hash_of_commit1

Patrząc na gitkto, widzimy, że commit1 i jego wybrana wersja mają różne skróty, więc technicznie są to dwa różne zatwierdzenia.

Wreszcie łączymy się newbranchw master:

git merge newbranch

i zobacz, że te dwa zatwierdzenia z różnymi skrótami zostały połączone bez problemów, chociaż implikują, że te same zmiany powinny zostać zastosowane dwukrotnie, więc jedna z nich powinna zawieść.

Czy git naprawdę dokonuje inteligentnej analizy zawartości zatwierdzenia podczas łączenia i decyduje, że zmiany nie powinny być stosowane dwukrotnie lub te zatwierdzenia są wewnętrznie oznaczone jako powiązane ze sobą?

Paweł
źródło

Odpowiedzi:

131

Krótka odpowiedź

Nie martw się, Git sobie z tym poradzi.

Długa odpowiedź

W przeciwieństwie do np. SVN 1 , Git nie przechowuje zatwierdzeń w formacie delta, ale jest oparty na migawkach 2,3 . Podczas gdy SVN naiwnie próbowałby zastosować każdy scalony zatwierdzenie jako łatkę (i nie powiedzie się, z dokładnie tego powodu, który opisałeś), Git ogólnie jest w stanie poradzić sobie z tym scenariuszem.

Podczas scalania Git spróbuje połączyć migawki obu zatwierdzeń HEAD w nową migawkę. Jeśli część kodu lub pliku jest identyczna w obu migawkach (tj. Ponieważ zatwierdzenie zostało już wybrane), Git go nie dotknie.

Źródła

1 Skip-Deltas w Subversion
2 Podstawy Git
3 Model obiektowy Git

helmbert
źródło
40
Właściwie powiedziałbym, że powinieneś się martwić połączeniem, a „git sobie z tym poradzi” nie jest dobrą regułą.
cregox,
4
W rzeczywistości scalenie CAN może w niektórych przypadkach powodować powielanie treści. Git czasami sobie z tym radzi, ale czasem nie.
donquixote
To jest okropnie złe. Mam prawie przechowuje pliki plików we wszystkich możliwych formach. A jeśli dobrze pamiętam, SVN służył do przechowywania migawek.
he_the_great
2
@he_the_great, no. Format pamięci przeskakiwania SVN (! = Migawki) jest dobrze udokumentowany w instrukcji . I naprawdę nie rozumiem, co masz na myśli przez kondevable . Nie jestem native speakerem, ale jestem pewien, że to nie jest prawdziwe słowo.
helmbert
2
@he_the_great Ale nawet jako paczki plików, każdy dany skrót dla pliku daje pełny plik. Tak, kompresuje za pomocą delt, ale nie jest to delta dla zmian w zatwierdzeniu, ale delta między skrótami dla pliku. Jeśli chodzi o obiekt zatwierdzania, odnosi się on do drzewa, które odwołuje się do skrótu pełnego pliku. To, że pod maską dane są kompresowane, nie wpływa na sposób działania git. Git przechowuje kompletne pliki, jeśli chodzi o zatwierdzenie, SVN przechowuje delty dla zatwierdzeń, jak rozumiem.
Peter Olson,
44

Po takim połączeniu możesz mieć dwa razy wybitne wybory w historii.

Rozwiązanie, aby temu zapobiec, cytuję z artykułu, który zaleca dla oddziałów ze zduplikowanymi (wybieranymi przez wiśniami) zatwierdzeniami użycie bazy przed scaleniem:

git merge po git cherry-pick: unikanie podwójnych zatwierdzeń

Wyobraź sobie, że mamy gałąź główną i gałąź b:

   o---X   <-- master
    \
     b1---b2---b3---b4   <-- b

Teraz pilnie potrzebujemy zatwierdzeń b1 i b3 w master, ale nie pozostałych zatwierdzeń w b. Więc to, co robimy, to sprawdzanie głównej gałęzi i zatwierdzanie wyborów wiśni b1 i b3:

$ git checkout master
$ git cherry-pick "b1's SHA"
$ git cherry-pick "b3's SHA"

Wynik byłby:

   o---X---b1'---b3'   <-- master
    \
     b1---b2---b3---b4   <-- b

Powiedzmy, że wykonujemy kolejne zatwierdzenie master i otrzymujemy:

   o---X---b1'---b3'---Y   <-- master
    \
     b1---b2---b3---b4   <-- b

Gdybyśmy teraz scalili gałąź b w master:

$ git merge b

Otrzymalibyśmy następujące:

   o---X---b1'---b3'---Y--- M  <-- master
     \                     /
      b1----b2----b3----b4   <-- b

Oznacza to, że zmiany wprowadzone przez b1 i b3 pojawią się dwukrotnie w historii. Aby tego uniknąć, możemy scalić zamiast scalać:

$ git rebase master b

Co dałoby:

   o---X---b1'---b3'---Y   <-- master
                        \
                         b2'---b4'   <-- b

Wreszcie:

$ git checkout master
$ git merge b

daje nam:

   o---X---b1'---b3'---Y---b2'---b4'   <-- master, b

EDYCJA Korekty przypuszczalne przez komentarz Davida Lemon

efemerydy
źródło
1
świetna wskazówka na temat rebase! automatycznie „pominie” wszystkie wybrane przez siebie zatwierdzenia.
iTake
2
Szczerze mówiąc, to brzmi zbyt dobrze, aby mogło być prawdziwe, muszę to zobaczyć na własne oczy. Także rebase modyfikuje zatwierdzenia, twoja ostatnia oś czasu powinna być---Y---b2'---b4'
David Lemon
Działa świetnie. Jest to bardzo pomocne, jeśli nie chcesz, aby dwa razy w historii zatwierdzone wybory były wiśniowe.
user2350644
1
Nie należy zauważyć, że chociaż rebase jest piękny, niebezpieczeństwo z jego użyciem polega na tym, że wszelkie widelce lub gałęzie utworzone ze starego b nie będą zsynchronizowane, a użytkownicy mogą potrzebować uciekać się do rzeczy takich jak git reset - hard i git push -f?
JHH
1
@JHH dlatego właśnie tutaj
dokonujemy zmiany bazy