Dlaczego git pull domyślnie wykonuje scalanie zamiast rebase?

71

Rozważ następującą sytuację:

  • Masz klon repozytorium git
  • Masz kilka lokalnych zatwierdzeń (które nie zostały jeszcze nigdzie wypchnięte)
  • W zdalnym repozytorium znajdują się nowe zatwierdzenia, których jeszcze nie uzgodniono

Więc coś takiego:

Niezintegrowane zobowiązania

Jeśli wykonasz git pullustawienia domyślne, otrzymasz coś takiego:

scalanie zatwierdzenia

Jest tak, ponieważ git wykonał scalenie.

Istnieje jednak alternatywa. Możesz zamiast tego powiedzieć pullowi, aby zrobił rebase:

git pull --rebase

a dostaniesz to:

rebased

Moim zdaniem, wersja rebased ma wiele zalet, które głównie skupiają się na utrzymaniu czystości zarówno twojego kodu, jak i historii, więc jestem nieco zaskoczony faktem, że git domyślnie łączy się. Tak, skróty twoich lokalnych zobowiązań zostaną zmienione, ale wydaje się, że to niewielka cena za prostszą historię, którą dostajesz w zamian.

W żadnym wypadku jednak nie sugeruję, że jest to jakaś zła lub niewłaściwa domyślna wartość. Mam problem z ustaleniem powodów, dla których scalenie może być preferowane jako domyślne. Czy mamy jakiś wgląd w to, dlaczego został wybrany? Czy istnieją korzyści, które sprawiają, że jest bardziej odpowiedni jako domyślny?

Główną motywacją tego pytania jest to, że moja firma próbuje ustalić pewne podstawowe standardy (mam nadzieję, że bardziej podobne wytyczne) dotyczące tego, jak organizujemy nasze repozytoria i zarządzamy nimi, aby ułatwić programistom dostęp do repozytorium, z którym wcześniej nie pracowali. Interesuje mnie uzasadnienie, że zwykle powinniśmy dokonywać zmian w bazie w tego typu sytuacjach (i prawdopodobnie za zalecenie programistom domyślnego ustawienia globalnej konfiguracji na bazę), ale gdybym się temu sprzeciwił, z pewnością zapytałbym, dlaczego zmiana bazy nie jest Domyślne, jeśli jest tak świetne. Zastanawiam się więc, czy czegoś mi brakuje.

Zasugerowano, że to pytanie jest duplikatem Dlaczego tak wiele stron internetowych woli „git rebase” niż „git merge”? ; pytanie to jest jednak odwrotnością tego pytania . Omówiono zalety ponownego łączenia w stosunku do scalania, podczas gdy to pytanie dotyczy korzyści płynących z scalania w stosunku do łączenia. Odpowiedzi tam odzwierciedlają to, koncentrując się na problemach ze scalaniem i korzyściach z rebase.

jpmc26
źródło
Drugi problem polega na tym, że jeśli przypadkowo zobowiązujesz się do opanowania, a następnie wykonaj połączenie, które zostało scalone, dwa wzorce mogą nie być zsynchronizowane, nawet jeśli scalenie wygląda normalnie. Właśnie dlatego mój alias pull zawiera --ff-only.
Ixrec
jeśli sourcetree kiedykolwiek zmieni widok wykresu, aby wyświetlał rebases / scalenia inaczej, czy przejdziesz do scalania?
Ewan
1
@Ewan SourceTree nie powinien zmieniać widoku tego wykresu. Dokładnie reprezentuje wykres.
jpmc26
4
W przypadku głosujących na duplikaty pamiętaj, że treść odpowiedzi, którą zaakceptowałem, zdecydowanie nie występuje w pytaniu, które, jak twierdzisz, jest duplikatem.
jpmc26

Odpowiedzi:

56

Trudno ustalić na pewno, dlaczego scalenie jest domyślne, bez wiadomości od osoby, która podjęła tę decyzję.

Oto teoria ...

Git nie może zakładać, że można - zerwać przy każdym pociągnięciu. Posłuchaj, jak to brzmi. „Rebase przy każdym pociągnięciu”. po prostu brzmi źle, jeśli używasz żądań ściągania lub podobnych. Czy zrezygnowałbyś z bazy na żądanie ściągnięcia?

W zespole, który nie korzysta tylko z Git do scentralizowanej kontroli źródła ...

  • Możesz ciągnąć z góry i z dołu. Niektóre osoby często ściągają od dostawców, od współpracowników itp.

  • Możesz pracować nad funkcjami w ścisłej współpracy z innymi programistami, czerpiąc z nich lub ze współdzielonej gałęzi tematów i wciąż od czasu do czasu aktualizując z góry. Jeśli zawsze będziesz bazować, zmienisz wspólną historię, nie mówiąc już o zabawnych cyklach konfliktów.

Git został zaprojektowany z myślą o dużym, rozproszonym zespole, w którym każdy nie ściąga i nie naciska na jedno centralne repo. Domyślnie ma to sens.

  • Programiści, którzy nie wiedzą, kiedy można dokonać zmiany bazy, będą domyślnie scalani.
  • Programiści mogą dokonywać zmian bazy, kiedy chcą.
  • Dowódcy, którzy wykonują dużo pociągnięć i mają dużo pociągnięć, otrzymują ustawienia domyślne, które najbardziej im odpowiadają.

Oto dowód zamiaru, oto link do dobrze znanego e-maila od Linusa Torvaldsa z jego poglądami na temat tego, kiedy nie powinni się opierać. Dri-devel git pull email

Jeśli podążysz za całym wątkiem, zobaczysz, że jeden programista pobiera od innego programisty, a Linus ciągnie od obu. Wyraźnie wyraża swoją opinię. Ponieważ prawdopodobnie zdecydował o domyślnych ustawieniach Gita, może to wyjaśniać dlaczego.

Wiele osób korzysta teraz z Git w sposób scentralizowany, w którym wszyscy w małym zespole korzystają tylko z centralnego repozytorium i przekazują dane do tego samego pilota. W tym scenariuszu unika się niektórych sytuacji, w których zmiana bazy nie jest dobra, ale zwykle ich nie eliminuje.

Sugestia: Nie rób polityki zmiany wartości domyślnej. Za każdym razem, gdy umieścisz Git razem z dużą grupą programistów, niektórzy programiści nie zrozumieją Git aż tak głęboko (łącznie ze mną). Pójdą do Google, SO, uzyskają porady dotyczące książek kucharskich, a następnie zastanowią się, dlaczego niektóre rzeczy nie działają, np. Dlaczego git checkout --ours <path>otrzymano niewłaściwą wersję pliku? Zawsze możesz zmienić swoje lokalne środowisko, stworzyć aliasy itp., Aby dopasować je do swoich upodobań.

joshp
źródło
5
+1 za brak zmiany wartości domyślnej - zamiast uruchamiać własny przepływ pracy git, prawdopodobnie lepiej jest pobrać taki, który istnieje (np. Dowolny z tych dokumentowanych przez Atlassian atlassian.com/git/tutorials/comparing-workflows/... )
Rob Church
1
Dziękuję za odpowiedź. Brakowało mi informacji o pobieraniu z downstream. Co ciekawe, podany przez ciebie link zachęca dokładnie do tego, co chciałbym osiągnąć: „Ludzie mogą (i prawdopodobnie powinni) opierać swoje prywatne drzewa (własną pracę)”.
jpmc26
2
@jpmc Świetnie. W rzeczy samej. Odzyskiwanie prywatnych drzew może powstrzymać wiele rozpraszających ludzi z wspólnej historii. Robię to. Ważna jest jasność, kiedy należy tego nie robić.
joshp
„niektórzy programiści nie zrozumieją Gita aż tak głęboko”. Rebase NIE jest super fantazyjną („głęboką” itp.) Funkcją Git. Czy naprawdę uważasz to za trudne do zrozumienia? Myślę, że powinno to być powodem, by nazwać dev niekompetentnym.
Victor Yarema
15

Jeśli czytasz stronę git dla rebase, mówi :

Odbudowywanie (lub jakiejkolwiek innej formy przepisywania) gałęzi, na której inni pracują, jest złym pomysłem: każdy, kto jest za nią zmuszony, musi ręcznie naprawić swoją historię. W tej sekcji wyjaśniono, jak wykonać poprawkę z punktu widzenia dalszego użytkownika. Prawdziwym rozwiązaniem byłoby jednak przede wszystkim uniknięcie ponownego upstreamu.

Myślę, że jest to wystarczający powód, aby w ogóle nie używać bazy, a co dopiero automatycznie robić to przy każdym pociągnięciu. Niektórzy uważają, że rebase jest szkodliwy . Być może nigdy nie powinien był zostać wprowadzony do git, ponieważ wszystko, co wydaje się robić, to upiększać historię, coś, co nie powinno być konieczne w żadnym SCM, którego jednym zasadniczym zadaniem jest zachowanie historii.

Kiedy mówisz „utrzymywanie ... historii w czystości”, myśl, że się mylisz. To może wyglądać ładniej, ale w przypadku narzędzia zaprojektowanego do przechowywania historii poprawek, o wiele czystsze jest utrzymywanie każdego zatwierdzenia, abyś mógł zobaczyć, co się stało. Odkażanie historii jest jak polerowanie patyny, dzięki czemu bogaty antyk wygląda jak błyszczące repro :-)

gbjbaanb
źródło
3
@Ewan IMHO „nie działa, ale wygląda ładnie” to coś, co powinno być zarezerwowane wyłącznie dla branży modowej, a nie IT. IMHO git powinien go usunąć, ponieważ oczywiście zachęca zbyt wielu ludzi do myślenia, że ​​styl jest ważniejszy niż treść.
gbjbaanb
12
Kwestionowałbym twoją sugestię, że utrzymanie historii w czystości jest bezużyteczne. To nie jest. Historia repozytorium jest przeznaczona do spożycia przez ludzi i dlatego jest bardzo przydatna, aby była czytelna. W moim szczególnym przypadku zamierzamy nawet, aby menedżerowie projektów niebędący programistami mogli czytać historię. Czysta historia może tylko w tym pomóc i może pomóc nowemu deweloperowi, który nigdy nie widział podstawy kodu i musi zanurkować i naprawić błąd, aby zrozumieć, co się stało w danym obszarze kodu.
jpmc26
3
Kod istnieje przede wszystkim do wykonania przez maszynę (być może po kompilacji). Stosując powyższą filozofię, doszlibyśmy do wniosku, że czytelność kodu nie ma znaczenia, ponieważ żadna ilość czytelności nie rozwiąże trudności w zrozumieniu kodu. Nigdy nie sugerowałem, że coś należy zgnieść. Mówię tylko, że skomplikowany wykres, który dzieli się na wiele przecinających się ścieżek, jest trudny do naśladowania, dlatego warto zainwestować trochę w utrzymanie dość czystej historii. Zauważ też, że historia liniowa bardziej sprzyja wykorzystywaniu narzędzi git, takich jak obwinianie i dzielenie na dwie części.
jpmc26
6
Dokumenty nie mówią „nie bazuj”. Mówi: „nie bazuj na zmianach, które dotyczą innych ludzi ”. Istnieje świat różnic między nimi. Sam Linus przypomina trochę podścielanie organizowania historię, jak to widać w linku w przyjętym odpowiedź. A skąd każde narzędzie wiedziałoby, które zatwierdzenia są nieistotne (zwłaszcza jeśli narzędzia mają trudności z analizą nieliniowej historii!) I skąd miałbyś wiedzieć, z którymi zobowiązaniami chcesz się różnić (zwłaszcza bez możliwości przekopywania się przez historię!)? Ostrożnie podchodzisz do konkretnej sytuacji do skrajności dogmatycznej.
jpmc26
4
Na pewno nie chcesz „przechowywać historii poprawek”, jeśli obejmuje to każde zatwierdzenie, które kiedykolwiek wykonał programista. Zgodnie z tą logiką powinniśmy również zapisywać oryginalną wersję kodu za każdym razem, gdy zostanie wciśnięty klawisz Backspace. Każde zatwierdzenie powinno być minimalną i kompletną zmianą, która nadal utrzymuje cały projekt w stabilnym stanie. Jeśli później twoje oryginalne zatwierdzenie okaże się błędne, o wiele lepiej jest zmienić bazę / edytować / zmienić zatwierdzenie, niż utworzyć inne. Jednak patrz również mail-archive.com/[email protected]/msg39091.html
Mikko Rantalainen,
12

Masz rację, zakładając, że masz tylko jedno lokalne / zmienione repozytorium. Weźmy jednak na przykład pod uwagę drugi lokalny komputer.

Gdy twoja lokalna / zmodyfikowana kopia zostanie wypchnięta gdzieś indziej, cofanie bazy spowoduje zepsucie tych kopii. Oczywiście możesz wymusić pchanie, ale szybko to się komplikuje. Co się stanie, jeśli jeden lub więcej z nich ma inne całkowicie nowe zatwierdzenie?

Jak widać, jest to bardzo sytuacyjne, ale strategia podstawowa wydaje mi się (o wiele) bardziej praktyczna w sprawach nietypowych / współpracy.


Druga różnica: strategia łączenia zachowa przejrzystą i spójną czasowo strukturę. Po zmianach bazy bardzo prawdopodobne jest, że starsze zmiany mogą śledzić nowsze zmiany, co utrudnia zrozumienie całej historii i jej przebiegu.

Mario
źródło
1
„Po zmianach istnieje duże prawdopodobieństwo, że starsze zmiany mogą śledzić nowsze zmiany” - dzieje się tak również w przypadku scalania i dlatego (może) potrzebny jest ładny wykres, aby to zrozumieć. Czas, w którym dokonano zatwierdzenia, może być na długo przed scaleniem, które wprowadziło go do tej gałęzi.
Steve Jessop
6

Głównym powodem jest prawdopodobnie to, że domyślne zachowanie powinno „po prostu działać” w publicznych repozytoriach. Odbudowywanie historii, którą inni już scalili, spowoduje kłopoty. Wiem, że mówisz o swoim prywatnym repozytorium, ale ogólnie mówiąc, git nie wie ani nie obchodzi, co jest prywatne lub publiczne, więc wybrana domyślna będzie domyślna dla obu.

Używam git pull --rebasecałkiem sporo w moim prywatnym repozytorium, ale nawet tam ma potencjalną wadę, polegającą na tym, że historia mojego HEADnie odzwierciedla już drzewa, tak jak faktycznie nad nim pracowałem.

Na przykład: załóżmy, że zawsze uruchamiam testy i upewniam się, że zdają przed zatwierdzeniem. Ma to mniejsze znaczenie po zrobieniu a git pull --rebase, ponieważ nie jest już prawdą, że drzewo w każdym z moich zatwierdzeń przeszło testy. Dopóki zmiany nie ingeruje w żaden sposób, a kod pociągnąć został przetestowany, to prawdopodobnie to by przejść testy, ale nie wiem, bo ja nigdy nie próbowałem. Jeśli ciągła integracja jest ważną częścią twojego przepływu pracy, to każdy rodzaj bazowania w repozytorium, który jest CIed, jest kłopotliwy.

Nie przeszkadza mi to, ale przeszkadza niektórym ludziom: woleliby, aby ich historia w git odzwierciedlała kod, nad którym faktycznie pracowali (a może zanim to pchnęli, uproszczoną wersję prawdy po pewnym użyciu) „naprawy”).

Nie wiem, czy ten problem jest przyczyną, dla której Linus zdecydował się na scalenie, a nie na domyślną wersję bazową. Mogą występować inne wady, z którymi się nie spotkałem. Ale ponieważ nie boi się wyrażać swojej opinii w kodzie, jestem prawie pewien, że sprowadza się to do tego, co uważa za odpowiedni przepływ pracy dla osób, które nie chcą za dużo o tym myśleć (a zwłaszcza tych, które pracują publicznie niż prywatne repozytorium). Unikanie równoległych linii na ładnym wykresie, na korzyść czystej linii prostej, która nie reprezentuje równoległego rozwoju, jak to się stało, prawdopodobnie nie jest jego najwyższym priorytetem, chociaż należy do ciebie :-)

Steve Jessop
źródło