Historia ponownego zapisywania
Polecenie git commit --amend i inne metody przepisywania historii
Wstęp
W tym samouczku przedstawiono różne metody przepisywania i modyfikowania historii w Git. W Git jest używanych kilka różnych metod rejestrowania zmian. Opiszemy tutaj ich mocne i słabe strony oraz podamy przykładowe sposoby ich wykorzystania. W tym samouczku przedstawiono również najczęściej spotykane przyczyny zastępowania zatwierdzonych migawek oraz sposoby unikania związanych z tym pułapek.
Głównym zadaniem systemu Git jest zagwarantowanie, że żadna zatwierdzona zmiana nigdy nie zostanie utracona. System zaprojektowano jednak również z myślą o zapewnieniu pełnej kontroli nad przepływem prac programistycznych. Dzięki temu można dokładnie określić, jak ma wyglądać historia projektu, ale wiąże się to z ryzykiem utraty commitów. Git udostępnia polecenia przepisywania historii z zastrzeżeniem, że ich użycie może doprowadzić do utraty zawartości.
Git oferuje kilka mechanizmów przechowywania historii i zapisywania zmian. Do mechanizmów tych należą: commit --amend
, git rebase
i git reflog
. Te opcje zapewniają zaawansowane możliwości dostosowania przepływu pracy. Po ukończeniu tego samouczka będziesz znać polecenia, które umożliwią Ci zmianę struktury commitów Git, oraz będziesz w stanie uniknąć pułapek, na jakie często natrafia się podczas przepisywania historii.
materiały pokrewne
Git — ściągawka
POZNAJ ROZWIĄZANIE
Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud
Zmiana ostatniego commita: git commit --amend
Polecenie git commit --amend
jest wygodnym sposobem modyfikowania najnowszego commita. Umożliwia połączenie zmian w przechowalni z poprzednim commitem, zamiast tworzenia całkiem nowego commita. Można go również użyć do prostej edycji komunikatu dotyczącego poprzedniego commita bez zmiany jego migawki. Jednak wprowadzenie poprawki za pomocą tego polecenia powoduje nie tyle modyfikację najnowszego commita, co jego całkowite zastąpienie. Oznacza to, że poprawiony commit będzie zupełnie nowym obiektem posiadającym własną referencję. Git będzie go rozpoznawał jako zupełnie nowy commit, który na poniższym diagramie został oznaczony gwiazdką (*). Istnieje kilka typowych scenariuszy użycia polecenia git commit --amend
. W kolejnych sekcjach opiszemy takie przykłady użycia.
Zmiana komunikatu dotyczącego najnowszego commita Git
git commit --amend
Załóżmy, że właśnie udało Ci się wykonać commit, po czym orientujesz się, że w komunikacie dotyczącym commita w dzienniku jest błąd. Wykonanie tego polecenia, gdy nie ma niczego w przechowalni, pozwala edytować komunikat dotyczący poprzedniego commita bez zmiany jego migawki.
W trakcie codziennych prac programistycznych przedwczesne commity zdarzają się bez przerwy. Łatwo zapomnieć o zapisaniu pliku w przechowalni lub sformatowaniu komunikatu dotyczącego commita we właściwy sposób. Flaga --amend
to wygodny sposób na naprawienie tych drobnych pomyłek.
git commit --amend -m "an updated commit message"
Dodanie opcji -m
umożliwia przekazanie nowego komunikatu z poziomu wiersza polecenia bez wyświetlania monitu o otwarcie edytora.
Zmiana zatwierdzonych plików
Poniższy przykład ilustruje scenariusz często spotykany w procesach programistycznych opartych na systemie Git. Załóżmy, że wprowadzono zmiany w kilku plikach, które chcemy zatwierdzić w pojedynczej migawce, ale za pierwszym razem zapomniano dodać jeden z plików. Aby naprawić błąd, wystarczy dodać brakujący plik do przechowalni i zatwierdzić za pomocą flagi --amend
:
# Edit hello.py and main.py
git add hello.py
git commit
# Realize you forgot to add the changes from main.py
git add main.py
git commit --amend --no-edit
Flaga --no-edit
pozwala wprowadzić poprawkę do commita bez zmiany jego komunikatu. Wynikowy commit zastąpi ten niekompletny i będzie wyglądać tak, jakby zmiany wprowadzone w hello.py
i main.py
zostały zatwierdzone w pojedynczej migawce.
Nie poprawiaj publicznych commitów
Poprawione commity są w istocie zupełnie nowymi commitami, a poprzedniego commita nie ma już w bieżącej gałęzi. Konsekwencje są takie same, jak w przypadku zresetowania migawki publicznej. Należy unikać poprawiania commitów, na których opiera się praca innych programistów. Może to spowodować zamieszanie wśród programistów oraz trudności, z którymi trudno sobie poradzić.
Podsumowanie
Podsumowując, polecenie git commit --amend
pozwala dodać do najnowszego commita nowe zmiany z przechowalni. Można dodać zmiany z przechowalni Git lub je z niej usunąć, aby następnie zastosować je za pomocą commita --amend
. Jeśli w przechowalni nie ma żadnych zmian, użycie polecenia --amend
nadal spowoduje wyświetlenie monitu o modyfikację komunikatu dotyczącego ostatniego commita w dzienniku. Należy zachować ostrożność, stosując polecenie --amend
do commitów współdzielonych z innymi członkami zespołu. Wprowadzenie poprawki do commita współdzielonego z innym użytkownikiem będzie prawdopodobnie wymagać trudnego i długotrwałego rozwiązywania konfliktów scalania.
Zmiana starszych lub wielu commitów
Aby zmodyfikować starszy commit lub wiele commitów jednocześnie, można użyć polecenia git rebase
, które pozwala połączyć sekwencję commitów w nowy commit bazowy. W trybie standardowym polecenie git rebase
pozwala dosłownie przepisać historię poprzez automatyczne zastosowanie commitów w bieżącej gałęzi roboczej do końcówki przekazywanej gałęzi. Nowe commity zastąpią stare, dlatego ważne jest, aby nie używać polecenia git rebase
do commitów, które zostały wypchnięte publicznie, bo będzie to wyglądać, jakby historia Twojego projektu zniknęła.
W takich lub podobnych przypadkach, w których ważne jest zachowanie przejrzystej historii projektu, dodanie opcji -i
do polecenia git rebase
umożliwi wykonanie polecenia rebase interactive
. Pozwala to na zmianę poszczególnych commitów w trakcie procesu zamiast przenoszenia wszystkich commitów. Więcej informacji na temat interaktywnej zmiany bazy oraz dodatkowych poleceń „rebase” można znaleźć na stronie git rebase.
Zmiana zatwierdzonych plików
W trakcie wykonywania zmiany bazy próba edycji lub użycie polecenia e
spowodują wstrzymanie zmiany bazy danego commita, umożliwiając wprowadzenie dodatkowych zmian za pomocą polecenia git commit --amend
. Wówczas Git przerwie wykonywanie zmiany bazy i wyświetli komunikat:
Stopped at 5d025d1... formatting
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Wiele komunikatów
Każdy zwykły commit Git obejmuje komunikat w dzienniku z objaśnieniem zmian, jakich dokonano w commicie. Te komunikaty zapewniają wartościowy wgląd w historię projektu. W trakcie operacji zmiany bazy można zastosować do commitów kilka poleceń, które pozwalają zmodyfikować ich komunikaty.
Łączenie (squashowanie) commitów w celu uzyskania przejrzystej historii
Polecenie „squash” (s
) pozwala realnie dostrzec przydatność operacji zmiany bazy. Umożliwia ono wskazanie commitów, które mają zostać scalone z poprzednimi commitami. Pozwala to uzyskać „przejrzystą historię”. W trakcie zmiany bazy Git wykona określone polecenie „rebase” dla każdego commita. W przypadku wykonania polecenia „squash” w odniesieniu do commitów Git otworzy skonfigurowany edytor tekstu i wyświetli monit o połączenie komunikatów dotyczących wskazanych commitów. Cały ten proces można zwizualizować w następujący sposób:
Warto zwrócić uwagę, że commity zmodyfikowane za pomocą polecenia „rebase” mają inny identyfikator niż commity pierwotne. Commity oznaczone za pomocą polecenia „pick” otrzymają nowy identyfikator, jeśli poprzednie commity zostały przepisane.
Nowoczesne rozwiązania hostingowe systemu Git, takie jak Bitbucket, oferują obecnie możliwość automatycznego stosowania polecenia „squash” przy scalaniu. Gdy korzystasz z interfejsu użytkownika hostowanych rozwiązań, te funkcje automatycznie wykonują polecenia „rebase” i „squash” w odniesieniu do commitów gałęzi za Ciebie. Więcej informacji na ten temat zawiera artykuł „Squashowanie commitów podczas scalania gałęzi Git w rozwiązaniu Bitbucket”.
Podsumowanie
Polecenie „git rebase” umożliwia modyfikowanie historii, a zmiana bazy w trybie interaktywnym pozwala robić to bez pozostawiania bałaganu. Dzięki temu nie należy obawiać się popełniania błędów, gdyż zawsze można je skorygować i poprawić jakość pracy przy jednoczesnym zachowaniu przejrzystej, liniowej historii projektu.
Siatka asekuracyjna: git reflog
Dzienniki referencji, nazywane również dziennikami „reflog”, są mechanizmem wykorzystywanym w systemie Git do rejestrowania aktualizacji zastosowanych do końcówek gałęzi oraz innych referencji commitów. Dziennik reflog pozwala cofnąć się do commitów, nawet jeśli nie odwołuje się do nich żadna gałąź ani żaden tag. Po przepisaniu historii dziennik reflog zawiera informacje na temat starego stanu gałęzi i w razie potrzeby umożliwia cofnięcie się do tego stanu. Za każdym razem, gdy końcówka gałęzi zostanie z dowolnego powodu zaktualizowana (w wyniku przełączenia gałęzi, ściągnięcia nowych zmian, przepisania historii lub po prostu dodania nowych commitów), do dziennika reflog zostanie dodany nowy wpis. W tej sekcji przyjrzymy się ogólnie poleceniu git reflog
oraz kilku jego typowym zastosowaniom.
Użycie
git reflog
To polecenie pozwala wyświetlić dziennik reflog lokalnego repozytorium.
git reflog --relative-date
To polecenie pozwala wyświetlić dziennik reflog wraz z informacją o dacie względnej (np. 2 tygodnie temu).
Przykład
Aby zrozumieć zasadę działania polecenia git reflog
, spójrzmy na przykład.
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
Powyższy dziennik reflog zawiera wykonanie polecenia „checkout” z gałęzi głównej do gałęzi 2.2 i z powrotem. Następnie wykonano twardy reset w celu przywrócenia starszego commita. Najnowsza aktywność jest wyświetlana u góry i jest oznaczona wskaźnikiem HEAD@{0}
.
Jeśli się okaże, że cofnięcie było przypadkowe, dziennik reflog będzie zawierał główną gałąź commita wskazującą na wskaźnik (0254ea7)
sprzed przypadkowego usunięcia 2 commitów.
git reset --hard 0254ea7
Za pomocą polecenia „git reset” można teraz przywrócić główną gałąź do wcześniejszego commita. Ta funkcja stanowi „siatkę asekuracyjną” na wypadek przypadkowej zmiany historii.
Należy pamiętać, że dziennik reflog zapewnia siatkę asekuracyjną tylko wtedy, gdy zmiany zostały zatwierdzone w repozytorium lokalnym, i pozwala śledzić działania tylko na poziomie końcówek gałęzi repozytoriów. Ponadto wpisy w dzienniku reflog mają okres ważności. Domyślnie wpisy w dzienniku reflog wygasają po 90 dniach.
Dodatkowe informacje można znaleźć na stronie dotyczącej polecenia git reflog.
Podsumowanie
W tym artykule opisano kilka metod modyfikowania historii oraz cofania zmian w Git. Ogólnie przyjrzeliśmy się również procesowi „git rebase”. Najważniejsze wnioski:
- Istnieje wiele sposobów przepisywania historii za pomocą poleceń git.
- Polecenie
git commit --amend
pozwala zmienić ostatni komunikat w dzienniku. - Polecenie
git commit --amend
umożliwia wprowadzenie modyfikacji w najnowszym commicie. - Polecenie
git rebase
służy do łączenia commitów i modyfikowania historii gałęzi. - Polecenie
git rebase -i
pozwala z dużo większą precyzją kontrolować modyfikacje historii, w porównaniu ze standardowym poleceniem „git rebase”.
Więcej informacji na temat omówionych poleceń można znaleźć na poszczególnych stronach dotyczących każdego z nich:
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.