polecenie git rebase
ma reputację magicznego Git voodoo, od którego początkujący powinni trzymać się z daleka, ale może znacznie ułatwić życie zespołowi programistów, gdy jest używane z należytą starannością. W tym artykule porównamygit rebase
z powiązaną komendągit merge
I zidentyfikujemy wszystkie potencjalne możliwości włączenia rebasingu do typowego obiegu pracy Gita.,
przegląd koncepcyjny
pierwszą rzeczą do zrozumienia git rebase
jest to, że rozwiązuje ten sam problem co git merge
. Oba te polecenia są zaprojektowane tak, aby integrować zmiany z jednej gałęzi do innej gałęzi—po prostu robią to na bardzo różne sposoby.
zastanów się, co się stanie, gdy zaczniesz pracować nad nową funkcją w dedykowanej gałęzi, a następnie inny członek zespołu zaktualizujemaster
z nowymi zatwierdzeniami. Skutkuje to rozwidloną historią, która powinna być znana każdemu, kto używał Gita jako narzędzia do współpracy.,
Załóżmy teraz, że nowe commity wmaster
są istotne dla funkcji, nad którą pracujesz. Aby włączyć nowe commity do gałęzi feature
, masz dwie opcje: scalanie lub rebasing.,
opcja scalania
najprostszą opcją jest scalenie gałęzi master
do gałęzi funkcji za pomocą czegoś takiego:
git checkout feature git merge master
lub, można skondensować to do jednej linii:
git merge feature master
tworzy to nowy „merge commit” w gałęzi feature
, która łączy ze sobą historie obu gałęzi, dając strukturę gałęzi, która wygląda tak:
scalanie jest przyjemne, ponieważ jest to operacja nieniszcząca. Istniejące oddziały nie są w żaden sposób zmieniane., Pozwala to uniknąć wszystkich potencjalnych pułapek rebasingu (omówionych poniżej).
z drugiej strony oznacza to również, że gałąź feature
będzie miała dodatkowy commit scalający za każdym razem, gdy będziesz musiał włączyć zmiany w źródle. Jeśli master
jest bardzo aktywny, może to znacznie zanieczyścić historię twojej gałęzi funkcji. Chociaż możliwe jest złagodzenie tego problemu za pomocą zaawansowanych opcji git log
, może to utrudnić innym programistom zrozumienie historii projektu.,
opcja Rebase
jako alternatywę do scalania, możesz zmienić feature
gałąź na master
gałąź za pomocą następujących poleceń:
git checkout feature git rebase master
to przenosi cały feature
Branch rozpoczyna się na końcu master
branch, skutecznie zawierając wszystkie nowe commity w master
. Ale zamiast używać commita scalającego, rebasing ponownie zapisuje historię projektu, tworząc zupełnie nowe commity dla każdego commita w oryginalnej gałęzi.,
główną zaletą rebasingu jest to, że masz znacznie czystszą historię projektu. Po pierwsze, eliminuje niepotrzebne zmiany scalania wymagane przez git merge
. Po drugie, jak widać na powyższym diagramie, rebasing powoduje również idealnie liniową historię projektu—możesz podążać za końcówką feature
aż do początku projektu bez żadnych widelców. Ułatwia to nawigację po projekcie za pomocą poleceń takich jak git log
, git bisect
I gitk
.,
ale są dwa kompromisy dla tej nieskazitelnej historii zatwierdzeń: Bezpieczeństwo i identyfikowalność. Jeśli nie zastosujesz się do złotej reguły Rebasingu, ponowne pisanie historii projektu może mieć katastrofalne skutki dla przepływu pracy w ramach współpracy. I, co mniej ważne, rebasing traci kontekst dostarczony przez commit scalający—nie widać, kiedy zmiany w źródle zostały włączone do funkcji.
interaktywny Rebasing
interaktywny rebasing daje Ci możliwość zmiany commitów podczas ich przenoszenia do nowej gałęzi., Jest to jeszcze potężniejsze niż zautomatyzowana rebase, ponieważ oferuje pełną kontrolę nad historią zmian w gałęzi. Zazwyczaj jest to używane do czyszczenia niechlujnej historii przed scaleniem gałęzi funkcji do master
.,
aby rozpocząć interaktywną sesję rebasingu, przekaż i
opcję git rebase
polecenie:
git checkout feature git rebase -i master
spowoduje to otwarcie edytora tekstu z listą wszystkich zmian, które mają zostać przeniesione:
pick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Ta lista określa dokładnie, jak Branch będzie wyglądał po wykonaniu rebase. Zmieniając polecenie pick
I / lub zmieniając kolejność wpisów, możesz sprawić, że historia gałęzi będzie wyglądać tak, jak chcesz., Na przykład, jeśli drugi commit rozwiązuje mały problem w pierwszym commicie, możesz skondensować je w jeden commit za pomocą polecenia fixup
:
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
Kiedy zapiszesz i zamkniesz plik, Git wykona rebase zgodnie z Twoimi instrukcjami, w wyniku czego historia projektu wygląda następująco:
pick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3
d8e38838f6 ” >
wyeliminowanie takich nieistotnych zmian sprawia, że historia twojej funkcji jest znacznie łatwiejsza do zrozumienia. Jest to coś, czego git merge
po prostu nie może zrobić.,
złota zasada Rebasingu
gdy zrozumiesz, czym jest rebasing, najważniejszą rzeczą do nauczenia się jest to, kiedy tego nie robić. Złota zasada git rebase
to nigdy nie używać jej na publicznych gałęziach.
na przykład zastanów się, co się stanie, jeśli zmienisz master
na swoją feature
branch:
rebase przeniesie wszystkie commity w master
na końcówkę feature
. Problem polega na tym, że stało się to tylko w repozytorium., Wszyscy inni programiści nadal pracują z oryginalnym master
. Ponieważ rebasing skutkuje nowymi commitami, Git pomyśli, że historia gałęzimaster
różni się od historii wszystkich innych gałęzi.
jedynym sposobem na zsynchronizowanie dwóch gałęzimaster
jest połączenie ich z powrotem, co skutkuje dodatkowym commitem merge i dwoma zestawami commitów, które zawierają te same zmiany (Oryginalne i z nowej gałęzi). Nie trzeba dodawać, że jest to bardzo myląca sytuacja.,
zanim uruchomiszgit rebase
, zawsze pytaj siebie: „Czy ktoś jeszcze zagląda do tej gałęzi?”Jeśli odpowiedź brzmi tak, zdejmij ręce z klawiatury i zacznij myśleć o nieniszczącym sposobie wprowadzania zmian (np. polecenie git revert
). W przeciwnym razie możesz pisać historię na nowo, ile chcesz.
wymuszanie
Jeśli spróbujesz wypchnąć rebasowaną gałąź master
z powrotem do zdalnego repozytorium, Git uniemożliwi ci to, ponieważ koliduje ze zdalną gałąź master
., Ale możesz wymusić przejście przez flagę --force
, w ten sposób:
# Be very careful with this command! git push --force
to nadpisuje zdalną gałąźmaster
aby dopasować zrebasowaną gałąź z twojego repozytorium i sprawia, że rzeczy są bardzo mylące dla reszty twojego zespołu. Dlatego należy bardzo uważać, aby używać tego polecenia tylko wtedy, gdy dokładnie wiesz, co robisz.
jednym z niewielu przypadków, w których należy wymuszać wymuszenie, jest wykonanie lokalnego czyszczenia po wypchnięciu prywatnej gałęzi funkcji do zdalnego repozytorium (np. do celów tworzenia kopii zapasowych)., To tak, jakby powiedzieć: „Oops, tak naprawdę nie chciałem naciskać tej oryginalnej wersji gałęzi funkcji. Zamiast tego weź obecną.”Ponownie, ważne jest, aby nikt nie pracował nad commitami z oryginalnej wersji gałęzi feature.
Workflow Walkthrough
Rebasing można włączyć do istniejącego obiegu pracy Git tak dużo lub tak mało, jak jest to wygodne dla Twojego zespołu. W tej sekcji przyjrzymy się korzyściom, jakie rebasing może zaoferować na różnych etapach rozwoju funkcji.,
pierwszym krokiem w dowolnym obiegu pracy wykorzystującymgit rebase
jest utworzenie dedykowanej gałęzi dla każdej funkcji. Daje to niezbędną strukturę gałęzi do bezpiecznego korzystania z rebasingu:
czyszczenie lokalne
jednym z najlepszych sposobów włączenia rebasingu do obiegu pracy jest czyszczenie lokalnych, będących w toku funkcji. Okresowo wykonując interaktywną rebase, możesz upewnić się, że każdy commit w twojej funkcji jest skoncentrowany i znaczący., Pozwala to na pisanie kodu bez obawy o rozbicie go na pojedyncze commity—możesz to naprawić po fakcie.
wywołując git rebase
, masz dwie opcje dla nowej bazy: nadrzędną gałąź funkcji (np. master
) lub wcześniejszy commit w twojej funkcji. Widzieliśmy przykład pierwszej opcji w sekcji interaktywnego Rebasingu. Ta ostatnia opcja jest dobra, gdy musisz tylko naprawić kilka ostatnich commitów. Na przykład, poniższe polecenie rozpoczyna interaktywną rebase tylko ostatnich 3 commitów.,
git checkout feature git rebase -i HEAD~3
podającHEAD~3
jako nową bazę, Nie przenosisz gałęzi—po prostu interaktywnie przepisujesz 3 commity, które za nią podążają. Zauważ, że nie będzie to uwzględniało zmian w gałęzi feature
.
Jeśli chcesz ponownie napisać całą funkcję za pomocą tej metody, poleceniegit merge-base
może być przydatne do znalezienia oryginalnej bazy gałęzifeature
., Poniższe zwraca identyfikator zatwierdzania oryginalnej bazy, którą można następnie przekazać do git rebase
:
git merge-base feature master
to użycie interaktywnego rebasingu jest świetnym sposobem na wprowadzeniegit rebase
do obiegu pracy, ponieważ dotyczy tylko lokalnych gałęzi. Jedyną rzeczą, którą inni programiści zobaczą, jest gotowy produkt, który powinien być czystą, łatwą do śledzenia historią gałęzi funkcji.
ale to działa tylko dla gałęzi private feature., Jeśli współpracujesz z innymi programistami za pośrednictwem tej samej gałęzi funkcji, ta gałąź jest publiczna i nie możesz ponownie pisać jej historii.
nie magit merge
alternatywy do czyszczenia lokalnych commitów za pomocą interaktywnej rebase.
Włączanie zmian w funkcji
w sekcji Przegląd koncepcyjny widzieliśmy, jak gałąź funkcji może włączać zmiany zmaster
używającgit merge
lubgit rebase
., Scalanie jest bezpieczną opcją, która zachowuje całą historię repozytorium, podczas gdy rebasing tworzy historię liniową, przenosząc gałąź funkcji na końcówkę master
.
to użyciegit rebase
jest podobne do lokalnego czyszczenia (i może być wykonywane jednocześnie), ale w procesie zawiera te commity zmaster
.
pamiętaj, że rebase do zdalnej gałęzi jest całkowicie legalne zamiastmaster
., Może się to zdarzyć podczas współpracy nad tą samą funkcją z innym deweloperem i trzeba włączyć ich zmiany do repozytorium.,
na przykład, jeśli ty i inny programista o imieniu John dodaliście commity do gałęzi feature
, Twoje repozytorium może wyglądać następująco po pobraniu zdalnej gałęzi feature
z repozytorium Johna:
możesz rozwiązać ten fork dokładnie w ten sam sposób, w jaki integrujesz zmiany z master
: albo połącz lokalny feature
z john/feature
, albo zmień lokalny feature
na końcówkę john/feature
.,
zauważ, że ten rebase nie narusza złotej reguły Rebasingu, ponieważ tylko Twoje lokalnefeature
commity są przenoszone—wszystko przed tym jest nietknięte. To jest jak powiedzenie: „dodaj moje zmiany do tego, co John już zrobił.”W większości przypadków jest to bardziej intuicyjne niż synchronizacja ze zdalną gałęzią poprzez commit scalający.
domyślnie poleceniegit pull
wykonuje scalanie, ale możesz wymusić integrację zdalnej gałęzi z rebase, przekazując ją opcją--rebase
.,
przeglądanie funkcji za pomocą Pull Request
Jeśli używasz pull request jako części procesu weryfikacji kodu, musisz unikać używaniagit rebase
po utworzeniu pull request. Jak tylko wykonasz pull request, inni deweloperzy będą sprawdzać Twoje commity, co oznacza, że jest to gałąź publiczna. Ponowne zapisanie jego historii sprawi, że Git i twoi koledzy z drużyny nie będą mogli śledzić kolejnych commitów dodanych do tej funkcji.
wszelkie zmiany od innych programistów muszą być włączone zgit merge
zamiastgit rebase
.,
z tego powodu zwykle dobrym pomysłem jest wyczyszczenie kodu za pomocą interaktywnej rebase przed wysłaniem żądania pull request.
Integracja zatwierdzonej funkcji
Po zatwierdzeniu funkcji przez twój zespół, masz możliwość rebasingu funkcji na końcu gałęzimaster
przed użyciemgit merge
, aby zintegrować tę funkcję z główną bazą kodu.,
jest to sytuacja podobna do włączania zmian w gałęzi feature, ale ponieważ nie możesz ponownie pisać commitów w gałęzi master
, musisz ostatecznie użyć git merge
, aby zintegrować tę funkcję. Jednak wykonując rebase przed połączeniem, masz pewność, że połączenie zostanie szybko przekazane, co skutkuje idealnie liniową historią. Daje to również szansę na zmiażdżenie kolejnych commitów dodanych podczas pull request.,
Jeśli nie jesteś całkowicie zadowolony zgit rebase
, zawsze możesz wykonać rebase w tymczasowej gałęzi. W ten sposób, jeśli przypadkowo pomylisz historię swojej funkcji, możesz sprawdzić oryginalną gałąź i spróbować ponownie. Na przykład:
git checkout feature git checkout -b temporary-branch git rebase -i master # git checkout master git merge temporary-branch
podsumowanie
i to wszystko, co musisz wiedzieć, aby rozpocząć rebasing swoich oddziałów., Jeśli wolisz czystą, liniową historię bez zbędnych zmian scalających, powinieneś sięgnąć po git rebase
zamiast git merge
podczas integrowania zmian z innej gałęzi.
z drugiej strony, jeśli chcesz zachować pełną historię swojego projektu i uniknąć ryzyka ponownego pisania publicznych commitów, możesz trzymać się git merge
. Każda z tych opcji jest całkowicie poprawna, ale przynajmniej teraz masz możliwość skorzystania z zalet git rebase
.
Dodaj komentarz