git rebase
Ten dokument zawiera szczegółowe omówienie polecenia git rebase
. O poleceniu „rebase” wspomniano już na stronach dotyczących konfiguracji repozytorium i przepisywania historii. Ta strona zawiera bardziej dogłębny opis konfiguracji i wykonywania polecenia git rebase
. Omówione zostaną również typowe przypadki jego użycia i związane z nimi pułapki.
Polecenie „rebase” jest jednym z dwóch narzędzi Git przeznaczonych specjalnie do integracji zmian z jednej gałęzi w drugiej. Drugim narzędziem integracji zmian jest polecenie git merge
. Scalanie zawsze oznacza posunięcie rejestru zmian naprzód. Polecenie „rebase” zapewnia natomiast zaawansowane funkcje przepisywania historii. Szczegółowe porównanie poleceń „merge” i „rebase” można znaleźć w naszym przewodniku dotyczącym porównania operacji scalania i zmiany bazy. Polecenie „rebase” obejmuje 2 podstawowe tryby: ręczny i interaktywny. Poszczególne tryby polecenia „rebase” zostaną omówione szczegółowo w dalszej części.
Jak działa polecenie „git rebase”?
Zmiana bazy (rebasing) jest procesem przenoszenia lub łączenia sekwencji commitów do postaci nowego commita bazowego. Najlepiej się sprawdza i najłatwiej go zwizualizować w kontekście przepływu pracy związanego z tworzeniem gałęzi funkcji. Ogólny proces można przedstawić w następujący sposób:
Z punktu widzenia zawartości operacja zmiany bazy polega na zmianie bazy gałęzi z jednego commita na drugi tak, aby gałąź sprawiała wrażenie utworzonej z poziomu innego commita. Na poziomie wewnętrznym Git uzyskuje taki rezultat poprzez utworzenie nowych commitów oraz zastosowanie ich do wskazanej bazy. Trzeba jednak pamiętać, że choć gałąź wygląda tak samo, składa się ona z zupełnie nowych commitów.
materiały pokrewne
Git — ściągawka
POZNAJ ROZWIĄZANIE
Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud
Użycie
Z operacji zmiany bazy korzysta się zazwyczaj po to, aby zachować liniową historię projektu. Przykładowo rozważmy sytuację, w której od momentu rozpoczęcia pracy nad gałęzią funkcji dokonano postępów w obrębie gałęzi głównej. Chcesz zatem uwzględnić najnowsze aktualizacje gałęzi głównej w swojej gałęzi funkcji, ale jednocześnie zachować przejrzystość historii gałęzi tak, aby wyglądało na to, że prace były prowadzone na najnowszej gałęzi głównej. Dzięki temu później można bez problemu scalić gałąź funkcji z powrotem z gałęzią główną. Dlaczego zależy nam na zachowaniu „przejrzystej historii”? Korzyści płynące z posiadania przejrzystej historii stają się namacalne podczas wykonywania operacji Git w celu zbadania wprowadzenia regresji. Odwołajmy się więc do bardziej praktycznego scenariusza:
1. W obrębie gałęzi głównej wykryto błąd. Funkcja, która do tej pory działała, teraz jest uszkodzona.
2. A developer examines the history of the main branch using git log
because of the "clean history" the developer is quickly able to reason about the history of the project.
3. The developer can not identify when the bug was introduced using git log
so the developer executes a git bisect
.
4. Because the git history is clean, git bisect
has a refined set of commits to compare when looking for the regression. The developer quickly finds the commit that introduced the bug and is able to act accordingly.
Więcej informacji na temat poleceń git log oraz git bisect można znaleźć na stronach dotyczących użycia każdego z nich.
Są dostępne dwie opcje integracji funkcji z gałęzią główną: bezpośrednie scalenie lub scalenie poprzedzone zmianą bazy. Pierwsza opcja powoduje scalanie trójstronne i utworzenie commita scalenia, a druga umożliwia scalenie z przewijaniem i uzyskanie idealnie liniowej historii. Poniższy diagram pokazuje, w jaki sposób zmiana bazy zastosowana do gałęzi głównej ułatwia scalenie z przewijaniem.
Zmiana bazy jest typowym sposobem integracji zmian z gałęzi nadrzędnej z lokalnym repozytorium. Ściągnięcie zmian nadrzędnych za pomocą polecenia „git merge” powoduje powstanie zbędnego commita scalenia za każdym razem, gdy chcesz sprawdzić postępy projektu. Natomiast zmiana bazy przypomina bardziej podejście w stylu „Chcę oprzeć moje zmiany na tym, co zrobili dotychczas wszyscy inni”.
Nie zmieniaj bazy historii publicznej
Jak już wspominaliśmy w sekcji dotyczącej przepisywania historii, nie należy stosować polecenia „rebase” do commitów wypchniętych do repozytorium publicznego. Spowodowałoby to zastąpienie starych commitów nowymi, co wyglądałoby, jakby część historii Twojego projektu nagle zniknęła.
Git rebase standard vs git rebase interactive
Polecenie „git rebase” jest interaktywne, gdy zostanie zastosowany argument -- i
. Litera „i” oznacza tutaj słowo „interaktywny”. Jeśli nie użyje się żadnych argumentów, polecenie zostanie wykonane w trybie standardowym. W obydwu przypadkach załóżmy, że została utworzona odrębną gałąź funkcji.
# Create a feature branch based off of main
git checkout -b feature_branch main
# Edit files
git commit -a -m "Adds new feature"
Polecenie „git rebase” w trybie standardowym spowoduje automatyczne zastosowanie commitów z bieżącej gałęzi roboczej do końcówki przekazywanej gałęzi.
git rebase <base>
To polecenie spowoduje automatyczną zmianę bazy gałęzi bieżącej na bazę < base >
, która może być dowolnego rodzaju odwołaniem commita (np. identyfikatorem, nazwą gałęzi, tagiem lub względnym odwołaniem referencją do wskaźnika HEAD
).
Wykonanie polecenia git rebase
z flagą -i
rozpoczyna interaktywną sesję zmiany bazy. Zamiast przenoszenia wszystkich commitów na ślepo do nowej bazy, interaktywna zmiana bazy umożliwia zmodyfikowanie poszczególnych commitów w trakcie procesu. Pozwala to oczyścić historię poprzez usunięcie, rozdzielenie i zmodyfikowanie istniejących serii commitów. To niczym polecenie git commit --amend
na sterydach.
git rebase --interactive <base>
To polecenie spowoduje zmianę bazy gałęzi bieżącej na bazę
, ale w sesji interaktywnej zmiany bazy. W rezultacie nastąpi otwarcie edytora, w którym można wprowadzić (opisane poniżej) polecenia dla każdego commita do uwzględnienia w operacji zmiany bazy. Te polecenia określają sposób przekazywania poszczególnych commitów do nowej bazy. Można również zmienić kolejność na liście commitów, aby zmienić kolejność samych commitów. Po zdefiniowaniu poleceń dla każdego commita w ramach operacji zmiany bazy Git rozpocznie przetwarzanie commitów z zastosowaniem poleceń „rebase”. Polecenia edycji operacji zmiany bazy są następujące:
pick 2231360 some old commit
pick ee2adc2 Adds new feature
# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
Dodatkowe polecenia „rebase”
Jak już opisano szczegółowo na stronie przepisywania historii, za pomocą polecenia zmiany bazy można zmieniać starsze commity lub wiele commitów jednocześnie, zatwierdzone pliki oraz wiele komunikatów. Choć są to właśnie najbardziej typowe zastosowania, polecenie git rebase
ma również opcje dodatkowe, które mogą się okazać przydatne w bardziej złożonych przypadkach.
- Polecenie
git rebase -- d
oznacza, że w trakcie wykonywania commit zostanie pominięty w końcowym bloku połączonych commitów. - Polecenie
git rebase -- p
umożliwia pozostawienie commita bez zmian. Zawartość ani komunikat takiego commita nie zostaną zmodyfikowane, a commit nadal będzie widoczny w historii gałęzi jako pojedynczy commit. - Polecenie
git rebase -- x
w trakcie wykonywania stosuje skrypt powłoki wiersza polecenia do każdego oznaczonego commita. Przydatnym przykładem jego zastosowania może być uruchomienie pakietu testowego bazy kodu na konkretnych commitach, co ułatwia rozpoznanie regresji w trakcie zmiany bazy.
Podsumowanie
Interaktywna zmiana bazy zapewnia pełną kontrolę nad tym, jak wygląda historia projektu. Dzięki temu programiści zyskują dużą swobodę, ponieważ mogą zatwierdzać historię bez obaw o porządek i skoncentrować się na pisaniu kodu, a następnie cofnąć się i wyczyścić historię po fakcie.
Większość programistów chętnie wykorzystuje interaktywną zmianę bazy do dopracowania gałęzi funkcji przed scaleniem jej z główną bazą kodu. Dzięki temu mogą połączyć za pomocą polecenia „squash” nieistotne commity, usunąć przestarzałe i upewnić się, że gałąź jest w porządku pod każdym innym względem, zanim włączą ją do „oficjalnej” historii projektu. Dla wszystkich innych użytkowników będzie wyglądało, jakby cała funkcja została opracowana w pojedynczej serii dobrze zaplanowanych commitów.
Prawdziwą moc interaktywnej zmiany bazy widać w historii wynikowej gałęzi głównej. Dla każdej innej osoby będziesz sprawiać wrażenie wybitnego programisty, który zaimplementował nową funkcję z idealną liczbą commitów już za pierwszym razem. W ten właśnie sposób interaktywna zmiana bazy pozwala prowadzić przejrzystą i sensowną historię projektu.
Opcje konfiguracji
Za pomocą polecenia git config
można ustawić kilka właściwości zmiany bazy. Te opcje pozwalają modyfikować wygląd wyniku i działanie polecenia git rebase
.
rebase.stat
: wartość logiczna domyślnie ustawiona na fałsz. Ta opcja włącza wyświetlanie wizualnej zawartości diffstat, która pokazuje zmiany wprowadzone od ostatniego wykonania operacji zmiany bazy.rebase.autoSquash:
wartość logiczna, która pozwala przełączać sposób działania opcji--autosquash
.rebase.missingCommitsCheck:
dla tej opcji można ustawić wiele wartości, które pozwalają zmienić sposób działania polecenia „rebase” w odniesieniu do brakujących commitów.
| Wyświetla ostrzeżenie o usuniętych commitach w trybie interaktywnym. |
| Zatrzymuje operację zmiany bazy i wyświetla komunikaty ostrzegawcze o usuniętych commitach. |
| Ustawienie domyślne. Ignoruje wszelkie ostrzeżenia o brakujących commitach. |
rebase.instructionFormat:
ciąg formatu poleceniagit log
, który zostanie użyty do sformatowania widoku operacji zmiany bazy w trybie interaktywnym.
Zaawansowane zastosowanie operacji zmiany bazy
Do polecenia git rebase
można przekazać argument wiersza polecenia --onto
. W trybie --onto
polecenie „git rebase” zostaje rozwinięte w następujący sposób:
git rebase --onto <newbase> <oldbase>
The --onto
command enables a more powerful form or rebase that allows passing specific refs to be the tips of a rebase. Let’s say we have an example repo with branches like:
o---o---o---o---o main
\
o---o---o---o---o featureA
\
o---o---o featureB
Gałąź featureB bazuje na gałęzi featureA. Uzmysławiamy sobie jednak, że gałąź featureB nie jest zależna od żadnej ze zmian w gałęzi featureA i można ją wyprowadzić z gałęzi głównej.
git rebase --onto main featureA featureB
Gałąź featureA staje się starą bazą — <oldbase>
. Gałąź main
staje się nową bazą —
, a featureB stanowi odniesienie, które będzie wskazywać wskaźnik HEAD
gałęzi
. Rezultat będzie następujący:
o---o---o featureB
/
o---o---o---o---o main
\
o---o---o---o---o featureA
Niebezpieczeństwa związane z operacją zmiany bazy
Jednym z problemów, które należy wziąć pod uwagę podczas pracy z poleceniem „git rebase”, jest to, że w związanym z nim przepływie pracy może zwiększyć się liczba konfliktów scalania. Dochodzi do tego w przypadku istnienia długotrwałej gałęzi odchodzącej od gałęzi głównej. Gdy w końcu zechcesz połączyć ją z gałęzią główną za pomocą operacji zmiany bazy, może ona zawierać wiele nowych commitów, z którymi zmiany wprowadzone w Twojej gałęzi mogą być sprzeczne. Można temu łatwo zaradzić poprzez częste łączenie gałęzi z gałęzią główną za pomocą polecenia „rebase” oraz częstsze wykonywanie commitów. W razie wystąpienia konfliktów można również kontynuować lub zresetować operację zmiany bazy, przekazując do polecenia git rebase
argumenty wiersza polecenia --continue
lub --abort
.
Istotniejszym problemem związanym z operacją zmiany bazy jest utrata commitów w wyniku interaktywnego przepisania historii. Uruchomienie operacji zmiany bazy w trybie interaktywnym i wykonanie podpoleceń, takich jak „squash” lub „drop”, spowoduje usunięcie commitów z bezpośredniego dziennika Twojej gałęzi. Na pierwszy rzut oka może się wydawać, że commity zniknęły na zawsze. Można je jednak przywrócić oraz cofnąć całą operację zmiany bazy, korzystając z polecenia git reflog
. Więcej informacji na temat wyszukiwania zagubionych commitów za pomocą polecenia git reflog
można znaleźć na stronie dokumentacji dotyczącej polecenia „git reflog”.
Samo polecenie „git rebase” nie stwarza poważnego zagrożenia. Faktyczne niebezpieczeństwo pojawia się podczas wykonywania operacji zmiany bazy w trybie interaktywnym w celu przepisania historii oraz wymuszonego wypchania wyników do gałęzi zdalnej współdzielonej z innymi użytkownikami. Należy unikać takiego postępowania ze względu na możliwość nadpisania pracy innych użytkowników zdalnych, gdy wykonają operację ściągania.
Odzyskiwanie po wykonaniu operacji zmiany bazy w gałęzi nadrzędnej
Jeśli inny użytkownik wykonał operację zmiany bazy i wymusił wypchnięcie do gałęzi, do której dodajesz commity, użycie polecenia git pull
spowoduje zastąpienie wszelkich commitów wychodzących od tej poprzedniej gałęzi wymuszoną końcówką. Na szczęście polecenie git reflog
pozwala pobrać dziennik reflog gałęzi zdalnej. W dzienniku reflog gałęzi zdalnej możesz znaleźć referencję sprzed wykonania operacji zmiany bazy. Następnie możesz wykonać operację zmiany bazy gałęzi względem zdalnej referencji, używając opcji --onto
w sposób opisany powyżej w sekcji Zaawansowane zastosowanie operacji zmiany bazy.
Podsumowanie
W tym artykule opisano użycie polecenia git rebase
. Omówiliśmy podstawowe i zaawansowane przypadki użycia oraz bardziej zaawansowane przykłady. Najważniejsze poruszone zagadnienia:
- Polecenie „git rebase” w trybie standardowym i interaktywnym
- Opcje konfiguracji polecenia „git rebase”
- Opcja „--onto” w poleceniu „git rebase”
- Utrata commitów po użyciu polecenia „git rebase”
We looked at git rebase
usage with other tools like git reflog, git fetch, and git push. Visit their corresponding pages for further information.
Udostępnij ten artykuł
Następny temat
Zalecane lektury
Dodaj te zasoby do zakładek, aby dowiedzieć się więcej na temat rodzajów zespołów DevOps lub otrzymywać aktualności na temat metodyki DevOps w Atlassian.