Beginner’s Guide to Interactive Rebasing
@StrideStride
Technologie i usługi informacyjne
Na początku tego roku po raz pierwszy zrobiłem interaktywny rebase i byłem pod wrażeniem tego, co można z nim zrobić. Stwierdziłem również, że na początku jest to trochę skomplikowane. Mam nadzieję, że ten przewodnik pomoże usunąć trochę niepewności wokół niego.
A także, ponieważ jest to tak potężne i możesz zasadniczo przepisać historię, małe ostrzeżenie zanim zaczniemy: Istnieje wiele szkół myślenia na temat Git i tego, czy rebasing jest dobrym pomysłem czy nie. Ten post nie będzie zagłębiał się w te dyskusje i ma na celu jedynie przedstawienie podstaw używania interaktywnego rebasingu.
TL;DR
- Interaktywny rebasing może być używany do zmiany commitów na wiele sposobów, takich jak edycja, usuwanie i squashowanie.
- Aby powiedzieć Gitowi, gdzie ma zacząć interaktywną rebase, użyj SHA-1 lub indeksu commitu, który bezpośrednio poprzedza commit, który chcesz zmodyfikować.
- Podczas interaktywnej rebase, gdy Git zatrzymuje się na commicie, który oznaczyłeś do edycji, przepływ pracy nie różni się od normalnego procesu commitowania – etapujesz pliki, a następnie je commitujesz. Jedyną różnicą jest użycie polecenia
git commit --amend
zamiastgit commit
. - Interaktywna przebudowa utworzy nowe SHA-1, dlatego najlepiej jest używać interaktywnej przebudowy na commitach, które nie zostały wysłane do zdalnej gałęzi.
Problem
W tym przykładzie przeanalizujemy sytuację, w której pracowaliśmy w gałęzi funkcji i mamy kilka commitów, które chcielibyśmy zmienić lub usunąć. Oto jak wygląda nasz dziennik gita:
Z powyższego dziennika gita, oto dwa zobowiązania, które chcemy zmienić: 4a4d705
– W tym zobowiązaniu przypadkowo popełniliśmy konflikt scalania6c01350
– W tym zobowiązaniu usunęliśmy konflikt scalania
To, co chcielibyśmy zrobić, to cofnąć się w czasie do 4a4d705
, usunąć konflikt scalania w zobowiązaniu, a następnie usunąć 6c01350
, ponieważ konflikt scalania został rozwiązany i nie potrzebujemy już tego zobowiązania. To poprawi naszą historię commitów z dwóch powodów:
- Nie będziemy mieć zepsutych commitów (konfliktów scalania)
- Będziemy mieć tylko sensowne commity, żadnych commitów związanych wyłącznie z naprawą pominiętego konfliktu scalania
Rozwiązanie
Ta sytuacja jest dobrym kandydatem do interaktywnego rebasingu. Scott Chacon, w swojej książce Pro Git, opisuje interaktywną rebasing w następujący sposób: „Czasami rzecz naprawiona … nie może być zmieniona do nie do końca doskonałego commitu, który naprawia, ponieważ ten commit jest głęboko zakopany w serii poprawek. To jest dokładnie to, do czego służy interaktywny rebase: użyj go po wielu, zmieniając układ i edytując commity oraz zgniatając wiele commitów w jeden.”
Jakie commity chcemy zmodyfikować?
Aby rozpocząć interaktywny rebase, musimy powiedzieć Gitowi, które commity chcemy zmodyfikować. Robimy to odwołując się do commitu bezpośrednio poprzedzającego najwcześniejszy commit, który chcemy zmodyfikować. Albo, mówiąc prościej, odwołujemy się do „ostatniego commitu, który chcemy zachować jako-is”, według Scotta Chacona.
Spójrzmy na nasz przykład, aby lepiej to zrozumieć. Istnieją dwa sposoby na odwołanie się do tego commitu:
By SHA-1 – Ostatni commit, który chcemy zachować jako-is ma SHA-1 o wartości 528f82e
, więc możemy go przekazać do naszego interaktywnego polecenia rebase.
Przez indeks – Ostatni commit, który chcemy zachować jako-is ma indeks 3 (Git używa indeksowania opartego na zerze), więc możemy przekazać HEAD~3
do naszego interaktywnego polecenia rebase.
Uwaga – Jeśli masz tylko kilka commitów do interaktywnego rebase, używanie indeksu jest prawdopodobnie łatwiejsze dla większości ludzi. Jednakże, jeśli masz wiele commitów, użycie SHA-1 jest prawdopodobnie łatwiejsze, więc nie musisz liczyć całej drogi w dół dziennika git.
Start interactive rebasing
Based on our example, we will run either:
$ git rebase -i 528f82e
Or
$ git rebase -i HEAD~3
What opens up this window in Vim:
Zauważ, że commity są w odwrotnej kolejności niż w git log. W dzienniku git najnowszy commit jest na górze. W tym widoku, najnowszy commit jest na dole. Zauważ również, że komentarze poniżej dają pomocną listę ważnych poleceń, których możemy użyć na każdym z commitów.
Jeśli nie znasz Vima, po prostu kliknij na każde słowo pick
, które chcesz edytować, a następnie naciśnij klawisz <i>
(dla trybu wstawiania). Po zakończeniu pisania naciśnij klawisz <esc>
, aby wyjść z trybu wstawiania.
W naszym przykładzie zmieniliśmy polecenie na edit
dla commitu, który chcemy zmodyfikować i zmieniliśmy polecenie na drop
dla commitu, który chcemy usunąć. Następnie uruchamiamy :wq
, aby zapisać i zamknąć okno Vima.
Amend the commit
Powracając do terminala widzimy tę wiadomość:
To ma sens, że jesteśmy zatrzymani na 4a4d705
. Jest to najstarszy commit w serii commitów, które chcemy zmodyfikować. Zaczniemy od tego commitu i będziemy pracować nad każdym z nich, aż do najnowszego.
Przypominamy, że 4a4d705
był commitem z konfliktem scalania, który przypadkowo popełniliśmy. Kiedy otworzymy nasz edytor, zobaczymy tam konflikt scalania:
A więc naprawiliśmy konflikt scalania w pliku, ale co teraz zrobimy? W razie wątpliwości, git status
:
Cool! To jest rzeczywiście pomocne. Widzimy, że obecnie edytujemy 4a4d705
i widzimy dwa następne polecenia do wykonania po tym jednym.
Reszta wiadomości wyjaśnia nam znajomy przepływ pracy. Git mówi nam, że jeśli chcemy zmienić commit, uruchamiamy git commit --amend
. To będzie zasadniczo działać jak nasz typowy git commit
, którego używamy w normalnym przepływie pracy. Na dole tej wiadomości widzimy, że nasz plik został zmodyfikowany, odzwierciedlając zmiany, które właśnie wprowadziliśmy, aby usunąć konflikt scalania. Musimy zainscenizować plik zanim go popełnimy. To nie różni się od normalnego przepływu pracy.
Wszystko co robimy to git add tempChanger.js
aby zainscenizować edytowany plik, a następnie git commit --amend
aby zatwierdzić zainscenizowany plik! To teraz otworzy ponownie okno Vima z komunikatem commit:
Możemy albo edytować komunikat commit, albo pozostawić go takim, jaki jest. Wybierzmy, aby wiadomość commit pozostała taka sama i wpiszemy :wq
, aby zapisać i zamknąć okno.
Teraz edytowaliśmy nasz stary commit. Więc co teraz? Uruchamiamy git status
:
Continue interactive rebase
Nie mamy nic więcej do zmiany w commicie, więc kontynuujmy!
Uruchamiamy git rebase --continue
i widzimy następujący komunikat:
Woah, skończyliśmy? Ale co z tymi dwoma pozostałymi commitami? Cóż, następnym commitem do działania był 6c01350
. Ten commit oznaczyliśmy do usunięcia (drop
), gdy rozpoczynaliśmy interaktywną rebase. Git automatycznie go usunął i przeszedł do następnego zobowiązania, 41aa9d2
. Ten commit nigdy nie został zmodyfikowany w początkowej interaktywnej przebudowie. Jego domyślną komendą było pick
, co oznacza, że ten commit zostanie użyty. Git zastosował ten commit i ponieważ był to ostatni commit, interaktywny rebase został zakończony.
Uwaga, gdybyśmy mieli więcej commitów do edycji, po prostu przeszlibyśmy na następny commit i zaczęlibyśmy proces jego zmiany, tak jak zrobiliśmy to powyżej. Cykl trwa do momentu, gdy nie ma już żadnych commitów.
Przycisk wyrzucania
Warto zauważyć, że jeśli w którymkolwiek momencie naszego interaktywnego rebase’u coś spieprzymy i nie wiemy, jak to naprawić, zawsze możemy przerwać. W dowolnym momencie możemy uruchomić git rebase --abort
w terminalu i interaktywny rebase zostanie przerwany bez zapisania żadnych zmian. Musielibyśmy wtedy rozpocząć interaktywną przebudowę od nowa.
Po interaktywnej przebudowie
Nasz dziennik git wygląda teraz tak:
Zauważysz, że kilka rzeczy zmieniło się od czasu, gdy rozpoczęliśmy interaktywną przebudowę:
- Nie mamy już commitu
6c01350
z komunikatem commit „Usuń konflikt scalania”. To jest commit, który usunęliśmy w naszym interaktywnym rebase. - Nasz commit, który edytowaliśmy,
4a4d705
, ma inny SHA-1,2b7443d
. - Nasz najnowszy commit z naszego oryginalnego dziennika git,
41aa9d2
, również ma nowy SHA-1,2b95e92
. Ten commit nie został zmieniony, ale został po prostu zastosowany do poprzedniego2b7443d
.
Side effects of interactive rebasing
Dla dwóch ostatnich commitów w naszym dzienniku git, ponieważ mają nowe SHA-1, Git widzi je jako zupełnie nowe commity. Tak jest nawet w przypadku naszego ostatniego commitu, 2b95e92
, gdzie ani komunikat commitu, ani pliki w ogóle się nie zmieniły. To podnosi ważną kwestię związaną z interaktywnym rebasingiem: Jeśli zmodyfikujesz commit, ten commit i wszystkie kolejne będą miały nowe SHA-1.
Nie wpłynie to na nic, jeśli commity, które zmodyfikowałeś, nie zostały przepchnięte do zdalnej gałęzi. Jednakże, jeśli faktycznie wykonałeś interaktywną rebase na commitach, które zostały już wysłane do zdalnej gałęzi, a następnie ponownie wysłałeś swoją gałąź, zobaczysz:
Technicznie, mógłbyś obejść to używając git push --force
, ale jest to bardzo niebezpieczne. Jeśli zdalna gałąź ma commity od innych osób, ale twoja lokalna gałąź nie ma jeszcze tych commitów, skutecznie usuniesz ich commity.
Innym rozwiązaniem jest użycie git push --force-with-lease
, które zmodyfikuje tylko twoje commity, ale nie commity należące do innych, choć to również może być problematyczne. Na przykład, jeśli inny programista ma już te zobowiązania, które otrzymały nowe SHA-1 w swojej lokalnej gałęzi, kiedy pociągnie zdalną gałąź, będzie miał konflikty scalania z każdym z tych zobowiązań.
Kiedy używać --force-with-lease
jest poza zakresem tego wpisu na blogu, ale najlepiej byłoby skonsultować się z innymi członkami zespołu przed zrobieniem tego. Możesz przeczytać więcej o git push --force-with-lease
tutaj.
Kluczowym wnioskiem z tej sekcji jest to, że znacznie łatwiej i bezpieczniej jest używać interaktywnego rebasingu na commitach, które nie zostały jeszcze wysłane do zdalnej gałęzi.
Written by Blake DeBoer. Originally posted on Dev.to
Senior, Lead, or Principal developer in NYC? Stride zatrudnia! Chcesz podnieść poziom swojego zespołu technicznego? Zobacz jak my to robimy! www.stridenyc.com
Tagi
Załóż darmowe konto, aby odblokować niestandardowe doświadczenie czytelnicze.
Leave a Reply