Resetowanie, wyewidencjonowywanie i przywracanie
Polecenia git reset
, git checkout
i git revert
to niektóre z najprzydatniejszych narzędzi w arsenale systemu Git. Wszystkie pozwalają cofnąć zmianę w repozytorium, a pierwsze dwa mogą być używane do obsługi commitów lub pojedynczych plików.
Ponieważ są tak do siebie podobne, bardzo łatwo jest zapomnieć, które polecenie należy zastosować w danym przypadku. W tym artykule porównamy najpopularniejsze konfiguracje git reset
, git checkout
i git revert
. Mamy nadzieję, że po przeczytaniu tego tekstu nabierzesz pewności siebie w poruszaniu się po repozytorium za pomocą każdego z tych poleceń.
Pomocne jest myślenie o każdym z tych poleceń w kategoriach ich wpływu na trzy mechanizmy zarządzania stanem repozytorium Git: katalog roboczy, migawkę w przechowalni i historię commitów. Komponenty te czasami określa się jako „trzy drzewa” systemu Git. Wszystkie z nich dokładnie omawiamy na stronie polecenia git reset
. Pamiętaj o tych mechanizmach, czytając ten artykuł.
Wyewidencjonowanie to operacja, która przesuwa wskaźnik odnośnika HEAD
do określonego commitu. Aby to zademonstrować, rozważmy następujący przykład.

Powyższy przykład przedstawia sekwencję commitów na gałęzi main
. Odwołania HEAD
i main
wskazują obecnie na commit d. Wykonajmy teraz polecenie git checkout b
.

Na ilustracji widzimy aktualizację drzewa „historii commitów”. Polecenie git checkout
można zastosować na poziomie commitu lub na poziomie pliku. Wyewidencjonowanie na poziomie pliku zmieni jego zawartość na zawartość określonego commitu.
Przywrócenie to operacja, która powoduje pobranie określonego commitu i utworzenie nowego, odwracającego ten poprzedni. Polecenie git revert
można uruchamiać tylko w zakresie poziomu commitu i nie obejmuje funkcjonalności poziomu pliku.
Resetowanie to operacja, która powoduje przywrócenie „trzech drzew” do takiego stanu, aby pasowały do repozytorium dla określonego commitu. Reset można wywołać w trzech różnych trybach, które odpowiadają poszczególnym drzewom.
Wyewidencjonowanie i reset zwykle używa się do dokonywania lokalnych lub prywatnych „cofnięć”. Modyfikują historię repozytorium, co może powodować konflikty podczas przepychania do zdalnych repozytoriów współdzielonych. Przywrócenie uznaje się za bezpieczniejsze rozwiązanie do „cofnięć publicznych”, ponieważ tworzy nową historię, która może być udostępniana zdalnie i nie nadpisuje dotychczasowej, od której mogą być zależni zdalni członkowie zespołu.
Odniesienia Git — reset, przywracanie, wyewidencjonowanie
Poniższa tabela stanowi podsumowanie najczęstszych przypadków użycia każdego z tych poleceń. Warto zawsze ją mieć pod ręką, ponieważ może się nieraz przydać podczas używania systemu Git.
Polecenie | Zakres | Popularne przykłady zastosowania |
---|---|---|
git reset | Poziom commitu | Odrzucenie commitów w prywatnej gałęzi lub niezatwierdzonych zmian |
git reset | Poziom pliku | Wycofanie pliku z przechowalni |
git checkout | Poziom commitu | Przełączanie między gałęziami lub przeglądanie starszych migawek |
git checkout | Poziom pliku | Odrzucenie zmian w katalogu roboczym |
git revert | Poziom commitu | Cofnięcie commitów w gałęzi publicznej |
git revert | Poziom pliku | (nie dotyczy) |
Operacje na poziomie commitu
Parametry, które przekazujesz do poleceń git reset
i git checkout
, określają ich zakres. Jeśli nie uwzględnisz ścieżki pliku jako parametru, operują na całych commitach. To właśnie będziemy omawiać w tej sekcji. Zauważ, że polecenie git revert
nie ma odpowiednika na poziomie pliku.
Resetowanie określonego commitu
Na poziomie commitu resetowanie służy do przesunięcia końcówki gałęzi do innego commitu. Można to wykorzystać do usunięcia commitów z bieżącej gałęzi. Na przykład poniższe polecenie przesuwa gałąź hotfix
do tyłu o dwa commity.
git checkout hotfix git reset HEAD~2
Dwa commity, które znajdowały się na końcu gałęzi hotfix
, to teraz commity „zwisające” lub „osierocone”. Oznacza to, że zostaną usunięte następnym razem, gdy Git wykona procedurę eliminacji zbędnych elementów. Innymi słowy zaznaczasz, że chcesz się pozbyć tych commitów. Można to zwizualizować w następujący sposób:

Ten sposób użycia git reset
stanowi prostą metodę cofnięcia zmian, które nie zostały jeszcze udostępnione nikomu innemu. To przydatne polecenie na wypadek, gdy zaczniesz pracować nad funkcją, ale nagle pomyślisz „O kurczę, co ja robię? Muszę zacząć od nowa”.
Oprócz przeniesienia bieżącej gałęzi możesz również użyć git reset
do modyfikacji migawki w przechowalni i/lub katalogu roboczego przez przekazanie jednej z następujących flag:
--soft
— migawka w przechowalni i katalog roboczy nie zostają zmienione w żaden sposób.—mixed
— migawka w przechowalni zostaje aktualizowana tak, aby pasowała do określonego commitu, ale katalog roboczy nie ulega zmianie. To jest opcja domyślna.—hard
— migawka w przechowalni i katalog roboczy zostają aktualizowane tak, aby pasowały do określonego commitu.
Łatwiej jest myśleć o tych trybach jako o definiowaniu zakresu operacji git reset
. Aby uzyskać więcej szczegółowych informacji, odwiedź stronę polecenia poleceniu git reset
.
Wyewidencjonowanie starych commitów
Polecenie git checkout
służy do aktualizacji stanu repozytorium do określonego punktu w historii projektu. W przypadku przekazania z nazwą gałęzi umożliwia przełączanie się między gałęziami.
git checkout hotfix
Wewnętrznie, jedynym efektem powyższego polecenia jest przeniesienie wskaźnika HEAD
do innej gałęzi i uaktualnienie katalogu roboczego, aby do niej pasował. Ponieważ może to spowodować nadpisanie lokalnych zmian, system Git wymusza na użytkowniku zatwierdzenie lub dodanie do schowka zmian w katalogu roboczym, które zostaną utracone podczas operacji wyewidencjonowania. W przeciwieństwie do git reset
polecenie git checkout
nie przenosi gałęzi.

Możesz także wyewidencjonować dowolne commity przez przekazanie odniesień do commitów zamiast gałęzi. Daje to ten sam efekt co wyewidencjonowanie gałęzi: przenosi odniesienie HEAD
do określonego commitu. Na przykład następujące polecenie spowoduje wyewidencjonowanie elementu nadrzędnego w drugim stopniu względem bieżącego commitu:
git checkout HEAD~2
Przydaje się to do uzyskania szybkiego podglądu starszej wersji projektu. Ponieważ nie ma jednak odniesienia gałęzi do bieżącego wskaźnika HEAD
, to powoduje ustawienie w stanie odłączonego wskaźnika HEAD
. Może to być problematyczne w przypadku dodawania nowych commitów, ponieważ nie będzie możliwości powrotu do nich po przejściu na inną gałąź. Z tego względu przed dodaniem commitów do odłączonego wskaźnika HEAD
zawsze należy utworzyć nową gałąź.
Cofanie publicznych commitów za pomocą przywracania
Przywrócenie powoduje cofnięcie commitu przez utworzenie nowego commitu. To bezpieczny sposób cofania zmian, ponieważ nie grozi nadpisaniem historii commitów. Na przykład poniższe polecenie spowoduje wyszukanie zmian zawartych w przedostatnim commicie, utworzenie nowego commitu cofającego te zmiany oraz dołączenie nowego commitu do istniejącego projektu.
git checkout hotfix git revert HEAD~2
Można to zwizualizować w następujący sposób:
Porównaj to z poleceniem git reset
, które zmienia istniejącą historię commitów. Z tego względu do cofania zmian w gałęzi publicznej należy używać polecenia git revert
, zaś git reset
powinno służyć wyłącznie do cofania zmian w gałęzi prywatnej.
Można powiedzieć, że git revert
to narzędzie do cofania zatwierdzonych zmian, podczas gdy git reset HEAD
służy do cofania zmian niezatwierdzonych.
Tak samo jak git checkout
polecenie git revert
może nadpisać pliki w katalogu roboczym, dlatego poprosi o zatwierdzenie lub dodanie do schowka zmian, które mogłyby zostać utracone podczas operacji przywracania.
Operacje na poziomie pliku
Polecenia git reset
i git checkout
również dopuszczają opcjonalną ścieżkę pliku jako parametr. To znacząco zmienia ich działanie. Zamiast działać na całych migawkach, zostają zmuszone do ograniczenia zakresu do pojedynczego pliku.
Resetowanie określonego pliku
Po wywołaniu ze ścieżką pliku polecenie git reset
aktualizuje migawkę w przechowalni tak, aby pasowała do wersji z określonego commitu. Na przykład polecenie to powoduje pobranie wersji foo.py
w przedostatnim commicie i jej przeniesienie do przechowalni na potrzeby następnego commitu:
git reset HEAD~2 foo.py
Podobnie jak w przypadku git reset
na poziomie commitu rozwiązanie to częściej stosuje się względem wskaźnika HEAD
niż dowolnie wybranego commitu. Uruchomienie polecenia git reset HEAD foo.py
spowoduje cofnięcie pliku foo.py
z przechowalni. Zawarte w nim zmiany będą nadal obecne w katalogu roboczym.
Flagi --soft
, --mixed
i --hard
nie mają wpływu na git reset
na poziomie plików, ponieważ migawka w przechowalni jest zawsze aktualizowana, a katalog roboczy nigdy nie jest aktualizowany.
Wyewidencjonowanie pliku
Wyewidencjonowanie pliku przypomina użycie polecenia git reset
ze ścieżką pliku, z tym, że aktualizuje katalog roboczy zamiast przechowalni. W przeciwieństwie do polecenia na poziomie commitu nie powoduje to przesunięcia odniesienia HEAD
, więc nie będzie możliwe przełączanie między gałęziami.
Na przykład poniższe polecenie powoduje dopasowanie pliku foo.py
w katalogu roboczym do tego z przedostatniego commitu:
git checkout HEAD~2 foo.py
Podobnie jak wywołanie git checkout
na poziomie commitu może to być używane do sprawdzania starszych wersji projektu — zakres ograniczony jest jednak do określonego pliku.
Gdy przenosisz do przechowalni i zatwierdzasz wyewidencjonowany plik, daje to efekt „przywrócenia” jego starszej wersji. Oznacza to usunięcie wszystkich kolejnych zmian w pliku, podczas gdy polecenie git revert
cofa wyłącznie zmiany wprowadzone przez określony commit.
Podobnie jak git reset
polecenie to jest powszechnie używane wraz ze wskaźnikiem HEAD
jako odniesienie do commitu. Na przykład polecenie git checkout HEAD foo.py
powoduje odrzucenie zmian spoza przechowalni w pliku foo.py
. Jest to zachowanie podobne, jak w przypadku polecenia git reset HEAD --hard
, ale dotyczy tylko określonego pliku.
Podsumowanie
Teraz już znasz wszystkie narzędzia, których możesz potrzebować do cofania zmian w repozytorium Git. Polecenia git reset
, git checkout
i git revert
mogą sprawiać wrażenie podobnych, ale znając ich wpływ na katalog roboczy, migawkę w przechowalni i historię commitów możemy łatwo określić, które z nich się nadaje do określonego zadania.