Close

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.

Logo Git
materiały pokrewne

Git — ściągawka

Logo Bitbucket
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.

Poprawka commita Git

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:

Samouczek Git: przykład użycia polecenia git rebase -i

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.

Ludzie współpracujący przy ścianie pełnej narzędzi

Blog Bitbucket

Ilustracja DevOps

Ścieżka szkoleniowa DevOps

Demonstracje funkcji z ekspertami Atlassian

Zobacz, jak Bitbucket Cloud współpracuje z Atlassian Open DevOps

Zapisz się do newslettera DevOps

Thank you for signing up