Kiedy powinienem używać git pull --rebase?

806

Znam niektóre osoby, które używają git pull --rebasedomyślnie, i inne, które nalegają, aby nigdy ich nie używać. Wydaje mi się, że rozumiem różnicę między łączeniem a ponownym przetwarzaniem, ale staram się umieścić to w kontekście git pull. Czy chodzi tylko o to, że nie chce się wyświetlać wielu komunikatów zatwierdzania scalania, czy są też inne problemy?

Jason Baker
źródło
1
Źródło dla osób odradzających git pull --rebase? Rebase lub git rebase to oddzielne działanie od git pull --rebase!
przemo_li

Odpowiedzi:

583

Powinieneś użyć git pull --rebasekiedy

  • twoje zmiany nie zasługują na osobną gałąź

Rzeczywiście - dlaczego więc nie? Jest to bardziej jasne i nie narzuca logicznego grupowania twoich zobowiązań.


Ok, chyba wymaga wyjaśnienia. W Git, jak zapewne wiesz, zachęca się do rozgałęziania i łączenia. Twój oddział lokalny, do którego ściągasz zmiany, i oddział zdalny to w rzeczywistości różne oddziały i git pullchodzi o ich scalenie. Jest to rozsądne, ponieważ nie naciskasz zbyt często i zwykle gromadzisz szereg zmian, zanim utworzą one ukończoną funkcję.

Czasami jednak - z jakiegokolwiek powodu - wydaje ci się, że byłoby lepiej, gdyby te dwa - odległe i lokalne - były jedną gałęzią. Jak w SVN. To tutaj git pull --rebasewchodzi w grę. Nie łączysz się już - faktycznie zatwierdzasz na oddziale zdalnym . Właśnie o to chodzi.

To, czy jest to niebezpieczne, czy nie, jest kwestią tego, czy traktujesz lokalny i zdalny oddział jako jedną nierozerwalną rzecz. Czasami jest to uzasadnione (gdy twoje zmiany są niewielkie lub jeśli jesteś na początku solidnego rozwoju, gdy ważne zmiany są wprowadzane przez małe zmiany). Czasami tak nie jest (kiedy normalnie tworzysz inną gałąź, ale jesteś zbyt leniwy, aby to zrobić). Ale to inne pytanie.

P Shved
źródło
17
Myślę, że jest to przydatne, gdy pracujesz w tym samym oddziale, ale możesz zmienić stację roboczą. Mam tendencję do zatwierdzania i wypychania moich zmian z jednej stacji roboczej, a następnie ściągania bazy w drugiej i kontynuowania pracy na tym samym oddziale.
Pablo Pazos
11
Przydatnym sprawdzonym rozwiązaniem jest skonfigurowanie Git do automatycznego bazowania po pociągnięciu za pomocą git config --global pull.rebase preserve (zachowaj, oprócz włączenia zmiany, mówi, aby próbować zachować scalenia, jeśli dokonałeś tego lokalnie).
Colin D Bennett,
2
Nie zgadzam się, że powinieneś używać pull --rebase tylko podczas pracy na jednym oddziale. Powinieneś używać go cały czas, chyba że jest to niemożliwe z powodu innych okoliczności.
Tomáš Fejfar
@P Shved ... „Czasami jednak - z jakiegokolwiek powodu - wydaje ci się, że byłoby lepiej, gdyby te dwa - odległe i lokalne - były jedną gałęzią” ... czy można to zrobić? że w środowisku lokalnym mogę mieć moją gałąź i zdalne odbicie lustrzane gałęzi jako origin / master. Czy możesz tutaj podać dane wejściowe?
Mandroid
736

Chciałbym przedstawić inną perspektywę tego, co tak naprawdę oznacza „git pull --rebase”, ponieważ wydaje się, że czasami się gubi.

Jeśli kiedykolwiek korzystałeś z Subversion (lub CVS), możesz być przyzwyczajony do zachowania „svn update”. Jeśli masz zmiany do zatwierdzenia, a zatwierdzenie nie powiedzie się, ponieważ zmiany zostały wprowadzone wcześniej, „svn update”. Subversion przechodzi przez scalenie wcześniejszych zmian z twoimi, potencjalnie powodując konflikty.

To, co właśnie zrobił Subversion, było zasadniczo „pull -rebase”. Przeformułowanie lokalnych zmian w stosunku do nowszej wersji jest częścią „zmiany”. Jeśli zrobiłeś „svn diff” przed nieudaną próbą zatwierdzenia, a następnie porównujesz wynikowy diff z wyjściem „svn diff” później, różnica między tymi dwoma różnicami jest tym, co zrobiła operacja zmiany wartości.

Główną różnicą między Git i Subversion w tym przypadku jest to, że w Subversion „twoje” zmiany istnieją tylko jako niezaangażowane zmiany w kopii roboczej, podczas gdy w Git masz faktyczne zatwierdzenia lokalnie. Innymi słowy, w Git rozwinęliście historię; Twoja historia i historia górna są rozbieżne, ale masz wspólnego przodka.

Moim zdaniem, w normalnym przypadku posiadania lokalnego oddziału po prostu odzwierciedla upstream oddział i robi na nim ciągły rozwój, właściwą rzeczą do zrobienia jest zawsze „--rebase”, bo to jest to, czego semantycznie faktycznie robi . Ty i inni wyrywacie się z zamierzonej liniowej historii oddziału. Fakt, że ktoś inny naciskał nieco przed twoją próbą pchnięcia, jest nieistotny i wydaje się, że każdy taki przypadek pomiaru czasu przynosi efekt przeciwny do skutku, łącząc się w historii.

Jeśli z jakiegoś powodu odczuwasz potrzebę bycia filią, jest to inna sprawa. Ale chyba, że ​​masz konkretne i aktywne pragnienie przedstawienia swoich zmian w formie scalenia, moim zdaniem domyślne zachowanie powinno brzmieć „git pull --rebase”.

Rozważ inne osoby, które muszą obserwować i rozumieć historię Twojego projektu. Czy chcesz, aby historia była zaśmiecona setkami połączeń w całym miejscu, czy może chcesz tylko kilka wybranych połączeń, które reprezentują prawdziwe połączenia celowych rozbieżnych wysiłków rozwojowych?

scode
źródło
18
@MarceloCantos Żeby było jasne, nie mówię, że git (narzędzie) powinno domyślnie się zmieniać. Byłoby to niebezpieczne, ponieważ baza zasadniczo niszczy historię. Mówię, że jeśli chodzi o przepływ pracy, kiedy nie masz zamiaru rozgałęziać się i po prostu hackujesz jakąś gałąź, nad którą pracują również inni ludzie, domyślnym zachowaniem użytkownika powinno być „git pull --rebase”.
scode 1'11
1
Mówisz, że nie należy git config --global branch.autosetupmerge always?
Marcelo Cantos
9
@MarceloCantos Nie, nie jestem;) Osobiście pozostawiłbym autosetupmerge ast domyślną wartością true (jeśli łączę z powrotem i czwartą między gałęziami innymi niż lokalny <-> zdalny, podoba mi się, że jest jawny). Mówię tylko, że jako człowiek zawsze używam polecenia „git pull --rebase” jako części mojego normalnego przepływu pracy „pobierz najnowsze w gałęzi głównej”, ponieważ nigdy nie chcę tworzyć zatwierdzenia scalania, chyba że wyraźnie rozgałęziam się.
przesiadł
9
+1 @ kod. Po wielu bolesnych godzinach zmagania się z pytaniem o rebase / merge, w końcu oto odpowiedź, która go przykuwa.
AgileYogi,
10
Ta odpowiedź to tylko próba przystosowania użytkowników Git do niepodzielnych systemów kontroli wersji, zamiast dać im szansę na właściwe zrozumienie korzyści z posiadania odpowiednich gałęzi.
Alexey Zimarev
211

Być może najlepszym sposobem na wyjaśnienie tego jest przykład:

  1. Alice tworzy gałąź tematyczną A i nad nią pracuje
  2. Bob tworzy niepowiązaną gałąź tematu B i pracuje nad tym
  3. Alice tak git checkout master && git pull. Mistrz jest już aktualny.
  4. Bob to robi git checkout master && git pull. Mistrz jest już aktualny.
  5. Alice tak git merge topic-branch-A
  6. Bob to robi git merge topic-branch-B
  7. Bob robi to git push origin masterprzed Alice
  8. Alice robi git push origin master, co zostało odrzucone, ponieważ nie jest to szybkie przewijanie do przodu.
  9. Alice przegląda dziennik origin / master i widzi, że zatwierdzenie nie jest powiązane z jej.
  10. Alice tak git pull --rebase origin master
  11. Zatwierdzenie scalania Alice jest rozwijane, zatwierdzanie Boba jest wyciągane, a zatwierdzenie Alice jest stosowane po zatwierdzeniu Boba.
  12. Alice tak robi git push origin masteri wszyscy są szczęśliwi, że nie muszą czytać niepotrzebnego zatwierdzenia scalania, gdy patrzą na dzienniki w przyszłości.

Zauważ, że konkretna gałąź, do której się włączasz, nie ma znaczenia w tym przykładzie. Mistrz w tym przykładzie może równie łatwo być gałęzią wydania lub gałęzią deweloperską. Kluczową kwestią jest to, że Alice i Bob jednocześnie łączą swoje lokalne oddziały ze współdzielonym zdalnym oddziałem.

Ankieta Cody
źródło
2
Miły. Zwykle wyrażam się jasno i git co master && git pull; git checkout topic-branch-A; git rebase master; git checkout master; git merge topic-branch-A; git push origin masterpowtarzam, jeśli czyjaś próba opanowania nastąpiła przed moim. Chociaż widzę zwięzłe zalety twojego przepisu.
HankCa,
Ładne wyjaśnienie @Cody Poll
Rajanikanta Pradhan
148

Myślę, że powinieneś używać git pull --rebase, współpracując z innymi w tej samej branży. Jesteś w pracy → zatwierdzanie → praca → cykl zatwierdzania, a kiedy decydujesz się na wypchnięcie swojej pracy, twoje wypychanie jest odrzucane, ponieważ w tej samej gałęzi była równoległa praca. W tym momencie zawsze robię pull --rebase. Nie używam squasha (do spłaszczania zatwierdzeń), ale bazuję, aby uniknąć dodatkowych zatwierdzeń scalania.

Wraz ze wzrostem wiedzy na temat Git, patrzysz o wiele bardziej na historię niż na inne systemy kontroli wersji, z których korzystałem. Jeśli masz mnóstwo drobnych zatwierdzeń scalania, łatwo stracić ostrość większego obrazu, który dzieje się w twojej historii.

To właściwie jedyny raz, kiedy dokonuję zmiany wersji (*), a reszta mojego przepływu pracy oparta jest na scalaniu. Ale tak długo, jak robią to najczęściej polecający, historia wygląda o wiele lepiej.

(*) Podczas nauczania kursu Git kazałem uczniowi mnie za to aresztować, ponieważ w niektórych okolicznościach opowiadałem się również za zmianą gałęzi fabularnych. I przeczytał tę odpowiedź;) Takie bazowanie jest również możliwe, ale zawsze musi być zgodne z wcześniej ustalonym / uzgodnionym systemem i jako takie nie powinno być „zawsze” stosowane. I w tym czasie zwykle też nie robię pull --rebase, o to właśnie chodzi;)

krosenvold
źródło
2
na pewno można napisać skrypt, aby ukryć zatwierdzenia scalania w dzienniku
hasen
3
Fuzje mogą również zawierać różnice, co oznacza, że ​​nie jest to całkowicie trywialne
krosenvold
9
@hasen j Tak, ale TREŚĆ tych połączeń może mieć znaczenie
krosenvold
Ta odpowiedź jest niejasna i wyrażona w porównaniu z wybraną odpowiedzią: co dokładnie rozumiesz przez „tę samą gałąź”? Istnieją jednak pewne dobre punkty, których nie ma w wybranej odpowiedzi.
iwein
1
Niejasność wokół „gałęzi” jest dość celowa, ponieważ istnieje tak wiele sposobów korzystania z referencji; „linia pracy” to tylko jedna opcja.
krosenvold
49

Nie sądzę, żeby istniał jakiś powód, aby nie używać pull --rebase- dodałem kod do Git specjalnie po to, aby moje git pullpolecenie zawsze bazowało na wcześniejszych zatwierdzeniach.

Przeglądając historię, nigdy nie jest interesujące wiedzieć, kiedy facet / gal pracujący nad funkcją przestał się synchronizować. Może to być przydatne dla faceta / dziewczyny, gdy on to robi, ale po to reflogjest. To tylko hałas dla wszystkich innych.

Dustin
źródło
2
„Przeglądając historię, nigdy nie jest interesujące wiedzieć, kiedy facet pracujący nad funkcją przestał się synchronizować”. / ale czy to nie znaczy, że te pośrednie zatwierdzenia są prawdopodobnie uszkodzonymi kompilacjami?
eglasius
10
Tak i nie są też „całym stanem”. Dlatego ich nie chcemy. Chcę wiedzieć, czego chciał, a nie jak się tam dostał.
Dustin
4
Jeśli pull --rebasenależy zawsze używać, dlaczego nie pullrobi tego domyślnie?
straya
2
Obawiam się, że będziesz musiał sformułować własną opinię. Mam w sobie kilka rzeczy, .gitconfigaby niektóre opcje działały poprawnie. Myślę, że git rebase domyślnie robi coś złego, podobnie jak git tag itp. Jeśli się nie zgadzasz, nie musisz uzasadniać swojej opinii.
Dustin
8
To wydaje się dobrą radą, jeśli pobierasz z „upstream”, powiedzmy z master, i jeśli gałąź, do której podchodzisz, nie została jeszcze upubliczniona. Jeśli wyciągniesz drugą rękę z gałęzi funkcji, masterbędzie to bardziej odwrotne: nigdy nie ma powodu, aby używać --rebase, prawda? To może być powód, dla którego nie jest domyślny. Odkryłem, że Rebases to sposób, w jaki zmiany powinny przechodzić od szczytu hierarchii w dół, a scalanie to sposób, w jaki przepływają z powrotem w górę. derekgourlay.com/blog/git-when-to-merge-vs-when-to-rebase
jolvi
38

Tylko pamiętaj:

  • pull = pobierz + scal
  • pull --rebase = fetch + rebase

Wybierz sposób obsługi swojego oddziału.

Lepiej poznaj różnicę między scalaniem a rebase :)

leohxj
źródło
9

Myślę, że sprowadza się to do osobistych preferencji.

Czy chcesz ukryć swoje głupie błędy przed wprowadzeniem zmian? Jeśli tak, git pull --rebasejest idealny. Pozwala to później zmiażdżyć swoje zobowiązania do kilku (lub jednego). Jeśli masz fuzje w swojej (nienasyconej) historii, nie jest to takie łatwegit rebase późniejszą.

Osobiście nie mam nic przeciwko publikowaniu wszystkich moich głupich błędów, więc zwykle łączę się zamiast bazować.

hasen
źródło
8
Uwaga dla każdego, kto ogląda to pytanie: ta odpowiedź jest całkowicie nieistotna dla pytania „kiedy powinienem użyć git pull --rebase?”
therealklanni
3
@therealklanni Zredagowałem odpowiedź, aby wyjaśnić, w jaki sposób ma ona znaczenie dla pytania (miejmy nadzieję, nie niszcząc intencji).
Paŭlo Ebermann
Dzielenie się brudnym i nieustrukturyzowanym dziennikiem pracy nie jest uczciwym przedsięwzięciem, to po prostu lenistwo. Marnujesz czas ludzi, zmuszając ich, by gonili cię przez twoją króliczą dziurę rozwoju i debugowania; daj im wynik, a nie wędrówki.
krystah
8

git pull --rebasemoże ukryć przepisywanie historii przed współpracownikiem git push --force. Polecam używać git pull --rebase tylko, jeśli wiesz, że zapomniałeś forsować swoje zobowiązania, zanim zrobi to ktoś inny.

Jeśli nic nie popełniłeś, ale twoje miejsce pracy nie jest czyste, tuż git stashprzedtem git pull. W ten sposób nie będziesz w stanie po cichu przepisać swojej historii (co może po cichu usunąć część twojej pracy).

Habax
źródło