git reset
Polecenie git reset
jest złożonym i wszechstronnym narzędziem do cofania zmian. Istnieją trzy podstawowe sposoby jego wywoływania. Odpowiadają one argumentom wiersza polecenia --soft, --mixed, --hard
. Każdy z tych trzech argumentów odpowiada trzem wewnętrznym mechanizmom zarządzania stanem w Git — drzewu commitów (HEAD
), indeksowi przechowalni i katalogowi roboczemu.
Polecenie git reset i trzy drzewa Git
Aby właściwie zrozumieć działanie polecenia git reset
, najpierw trzeba zapoznać się z wewnętrznymi systemami zarządzania stanem w Git. Czasami te mechanizmy nazywane są „trzema drzewami” Git. Pojęcie to może być mylące, ponieważ nie są to klasyczne struktury danych w formie drzewa. Są one jednak strukturami danych opartymi na węzłach i wskaźnikach, które Git wykorzystuje do śledzenia osi czasu zmian. Najlepszym sposobem zademonstrowania tych mechanizmów jest utworzenie zestawu zmian w repozytorium i prześledzenie go w obrębie tych trzech drzew.
Na początek utworzymy nowe repozytorium przy użyciu następujących poleceń:
$ mkdir git_reset_test
$ cd git_reset_test/
$ git init .
Initialized empty Git repository in /git_reset_test/.git/
$ touch reset_lifecycle_file
$ git add reset_lifecycle_file
$ git commit -m"initial commit"
[main (root-commit) d386d86] initial commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 reset_lifecycle_file
Powyższy przykładowy kod tworzy nowe repozytorium Git z jednym pustym plikiem o nazwie reset_lifecycle_file
. W tym momencie przykładowe repozytorium zawiera pojedynczy commit (d386d86
) związany z dodaniem pliku reset_lifecycle_file
.
materiały pokrewne
Git — ściągawka
POZNAJ ROZWIĄZANIE
Poznaj środowisko Git z rozwiązaniem Bitbucket Cloud
Katalog roboczy
Pierwszym drzewem, którym się zajmiemy, będzie „katalog roboczy”. To drzewo jest zsynchronizowane z lokalnym systemem plików i stanowi odzwierciedlenie bezpośrednich zmian wprowadzanych w zawartości plików i katalogów.
$ echo 'hello git reset' > reset_lifecycle_file
$ git status
On branch main
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
W naszym repozytorium demonstracyjnym zmodyfikujemy plik reset_lifecycle_file
, dodając do niego zawartość. Wywołanie polecenia git status
pokaże, że Git wie o zmianach wprowadzonych w tym pliku. Te zmiany są obecnie częścią pierwszego drzewa, czyli „katalogu roboczego”. Za pomocą polecenia git status
można wyświetlić zmiany w katalogu roboczym. Zostaną one wyświetlone na czerwono i opatrzone przedrostkiem „modified”.
Indeks przechowalni
Następne w kolejce jest drzewo „indeksu przechowalni”. To drzewo śledzi zmiany w katalogu roboczym, które za pomocą polecenia git add
zostały zmienione w zmiany do zapisania w następnym commicie. To drzewo stanowi złożony wewnętrzny mechanizm pamięci podręcznej. Git zasadniczo stara się ukrywać przed użytkownikiem szczegóły wdrożenia indeksu przechowalni.
Aby zobaczyć dokładnie stan indeksu przechowalni, musimy skorzystać z mniej znanego polecenia Git git ls-files
. Polecenie git ls-files
jest zasadniczo narzędziem debugowania przeznaczonym do sprawdzania stanu drzewa indeksu przechowalni.
git ls-files -s
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 reset_lifecycle_file
W tym przykładzie wykonaliśmy polecenie git ls-files
z opcją -s
lub --stage
. Bez opcji -s
polecenie git ls-files
zwróci po prostu listę nazw i ścieżek plików należących aktualnie do indeksu. Opcja -s
pozwala wyświetlić dodatkowe metadane plików w indeksie przechowalni. Takimi metadanymi są bity trybu zawartości w przechowalni, nazwa obiektu oraz numer w przechowalni. W tym przypadku interesuje nas nazwa obiektu, druga wartość (d7d77c1b04b5edd5acfc85de0b592449e5303770
). Jest to standardowy hash SHA-1 obiektu Git. Jest to hash zawartości plików. Historia commitów zapisuje własne hashe SHA obiektów do identyfikacji wskaźników commitów i referencji, a indeks przechowalni korzysta z własnych hashy SHA obiektów do śledzenia wersji plików w indeksie.
Następnie przeniesiemy zmodyfikowany plik reset_lifecycle_file
do indeksu przechowalni.
$ git add reset_lifecycle_file
$ git status
On branch main Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
W tym przypadku wywołaliśmy polecenie git add reset_lifecycle_file
, które powoduje dodanie pliku do indeksu przechowalni. Jeśli wywołamy teraz polecenie git status
w sekcji „Changes to be committed” (Zmiany do zatwierdzenia) zostanie wyświetlony na zielono plik reset_lifecycle_file
. Ważne, aby pamiętać, że polecenie git status
nie daje rzeczywistego obrazu indeksu przechowalni. Polecenie git status
zwraca zmiany między historią commitów a indeksem przechowalni. Przyjrzyjmy się zawartości indeksu przechowalni na tym etapie.
$ git ls-files -s 100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Widzimy, że hash SHA obiektu dla pliku reset_lifecycle_file
został zaktualizowany z e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
na d7d77c1b04b5edd5acfc85de0b592449e5303770
.
Historia commitów
Ostatnim drzewem jest historia commitów. Polecenie git commit
dodaje zmiany do trwałej migawki znajdującej się w historii commitów. Ta migawka zawiera również stan indeksu przechowalni w chwili wykonania commita.
$ git commit -am"update content of reset_lifecycle_file"
[main dc67808] update content of reset_lifecycle_file
1 file changed, 1 insertion(+)
$ git status
On branch main
nothing to commit, working tree clean
W tym przypadku utworzyliśmy nowy commit z komunikatem „update content of resetlifecyclefile”
. Zestaw zmian został dodany do historii commitów. Wywołanie polecenia git status
na tym etapie pokaże, że w żadnym z drzew nie ma obecnie żadnych oczekujących zmian. Wykonanie polecenia git log
spowoduje wyświetlenie historii commitów. Po prześledzeniu tego zestawu zmian w obrębie trzech drzew możemy przystąpić do korzystania z polecenia git reset
.
Jak to działa
Na pierwszy rzut oka polecenie git reset
działa podobnie, jak polecenie git checkout
. Podczas gdy polecenie git checkout
działa wyłącznie na wskaźnik HEAD
, polecenie git reset
spowoduje przeniesienie wskaźnika HEAD
oraz wskaźnika referencji bieżącej gałęzi. Aby lepiej zilustrować to działanie, rozważmy ten przykład:
Powyższy przykład przedstawia sekwencję commitów w gałęzi main
. Referencja HEAD
i referencja gałęzi main
wskazują obecnie na commit d. Teraz wykonajmy i porównajmy polecenia git checkout b
i git reset b
.
git checkout b
W przypadku polecenia git checkout
referencja main
wciąż wskazuje na d
. Referencja HEAD
została przeniesiona i teraz wskazuje na commit b
. Repozytorium będzie teraz w stanie „odłączonego wskaźnika HEAD
”.
git reset b
Dla porównania — polecenie git reset
przenosi zarówno referencję HEAD
, jak i gałęzi do określonego commita.
Oprócz aktualizacji wskaźników commita polecenie git reset
zmodyfikuje również stan trzech drzew. Modyfikacja wskaźnika zachodzi zawsze i jest aktualizacją trzeciego drzewa, czyli drzewa commitów. Argumenty wiersza polecenia --soft, --mixed
i --hard
określają sposób modyfikacji drzew indeksu przechowalni i katalogu roboczego.
Główne opcje
Domyślnie wywołanie polecenia git reset
ma niejawne argumenty --mixed
i HEAD
. Oznacza to, że wykonanie polecenia git reset
jest równoznaczne z wykonaniem polecenia git reset --mixed HEAD
. W tej formie HEAD
jest określonym commitem. Zamiast wskaźnika HEAD
można użyć dowolnego hasha SHA-1 commita Git.
'--hard
Jest to najbardziej bezpośrednia, NIEBEZPIECZNA i najczęściej używana opcja. Po przekazaniu opcji --hard
wskaźniki historii commitów są aktualizowane zgodnie z określonym commitem. Następnie indeks przechowalni i katalog roboczy są resetowane zgodnie z tym określonym commitem. Wszelkie oczekujące zmiany w indeksie przechowalni i katalogu roboczym są resetowane zgodnie ze stanem drzewa commitów. Oznacza to, że każda oczekująca praca znajdująca się w indeksie przechowalni i katalogu roboczym zostanie utracona.
Aby to zilustrować, posłużymy się trzema drzewami utworzonego wcześniej przykładowego repozytorium. Zacznijmy od wprowadzenia w nim pewnych zmian. Wykonajmy w repozytorium następujące polecenia:
$ echo 'new file content' > new_file
$ git add new_file
$ echo 'changed content' >> reset_lifecycle_file
Polecenia te spowodowały utworzenie nowego pliku o nazwie new_file
i dodanie go do repozytorium. Zmodyfikowana została też zawartość pliku reset_lifecycle_file
. Po wprowadzeniu tych zmian przyjrzyjmy się stanowi repozytorium, używając do tego polecenia git status
.
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: new_file
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
W tym przypadku wywołaliśmy polecenie git add reset_lifecycle_file
, które powoduje dodanie pliku do indeksu przechowalni. Jeśli wywołamy teraz polecenie git status
w sekcji „Changes to be committed” (Zmiany do zatwierdzenia) zostanie wyświetlony na zielono plik reset_lifecycle_file
. Ważne, aby pamiętać, że polecenie git status
nie daje rzeczywistego obrazu indeksu przechowalni. Polecenie git status
zwraca zmiany między historią commitów a indeksem przechowalni. Przyjrzyjmy się zawartości indeksu przechowalni na tym etapie.
$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
Widzimy, że plik new_file
został dodany do indeksu. Dokonaliśmy aktualizacji pliku reset_lifecycle_file
, ale hash SHA indeksu przechowalni (d7d77c1b04b5edd5acfc85de0b592449e5303770
) pozostał taki sam. Tak ma być, ponieważ nie użyliśmy polecenia git add
, żeby przenieść zmiany do indeksu przechowalni. Funkcjonują one w katalogu roboczym.
Wykonajmy teraz polecenie git reset --hard
i sprawdźmy nowy stan repozytorium.
$ git reset --hard
HEAD is now at dc67808 update content of reset_lifecycle_file
$ git status
On branch main
nothing to commit, working tree clean
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
W tym przypadku wykonaliśmy „twardy reset” przy użyciu opcji --hard
. Git wyświetla wynik informujący, że HEAD
wskazuje na najnowszy commit dc67808
. Następnie sprawdzamy stan repozytorium za pomocą polecenia git status
. Git wskazuje, że nie ma oczekujących zmian. Sprawdzamy również stan indeksu przechowalni i widzimy, że został on zresetowany do momentu sprzed dodania pliku new_file
. Usunięto nasze modyfikacje w pliku reset_lifecycle_file
oraz dodanie pliku new_file
. Tej utraty danych nie można cofnąć, o czym koniecznie trzeba pamiętać.
'--mixed
Jest to domyślny tryb działania. Wskaźniki są aktualizowane, indeks przechowalni jest resetowany do stanu określonego commita, a wszystkie zmiany cofnięte z indeksu przechowalni są przenoszone do katalogu roboczego. Kontynuujmy.
$ echo 'new file content' > new_file
$ git add new_file
$ echo 'append content' >> reset_lifecycle_file
$ git add reset_lifecycle_file
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: new_file
modified: reset_lifecycle_file
$ git ls-files -s
100644 8e66654a5477b1bf4765946147c49509a431f963 0 new_file
100644 7ab362db063f9e9426901092c00a3394b4bec53d 0 reset_lifecycle_file
W powyższym przykładzie wprowadziliśmy w repozytorium pewne modyfikacje. Ponownie dodaliśmy plik new_file
i zmodyfikowaliśmy zawartość pliku reset_lifecycle_file
. Te zmiany zostaną następnie zastosowane do indeksu przechowalni przy użyciu polecenia git add
. Po osiągnięciu tego stanu repozytorium wykonamy teraz reset.
$ git reset --mixed
$ git status
On branch main
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: reset_lifecycle_file
Untracked files:
(use "git add ..." to include in what will be committed)
new_file
no changes added to commit (use "git add" and/or "git commit -a")
$ git ls-files -s
100644 d7d77c1b04b5edd5acfc85de0b592449e5303770 0 reset_lifecycle_file
W tym przypadku wykonaliśmy „reset mieszany”. Dla przypomnienia, --mixed
jest trybem domyślnym i użycie tej opcji daje taki sam wynik jak wykonanie polecenia git reset
. Analizując wynik poleceń git status
i git ls-files
, widzimy, że indeks przechowalni został zresetowany do stanu, w którym jedynym plikiem w indeksie jest reset_lifecycle_file
. Przywrócona została również poprzednia wersja hasha SHA obiektu reset_lifecycle_file
.
W tym przypadku należy zwrócić uwagę przede wszystkim na to, że polecenie git status
pokazuje nam, że wprowadzono modyfikacje w pliku reset_lifecycle_file
oraz że istnieje nieśledzony plik new_file
. Jest to jawne działanie opcji --mixed
. Indeks przechowalni został zresetowany, a oczekujące zmiany przeniesiono do katalogu roboczego. Porównaj ten wynik z przypadkiem resetu --hard
, w którym zresetowany został zarówno indeks przechowalni, jak i katalog roboczy, co spowodowało utratę tych aktualizacji.
'--soft
Przekazanie argumentu --soft
powoduje zaktualizowanie wskaźników i zatrzymanie resetu. Indeks przechowalni i katalog roboczy pozostają nienaruszone. Ten sposób działania trudno wyraźnie przedstawić. Skorzystajmy raz jeszcze z naszego demonstracyjnego repozytorium i przygotujmy go do „miękkiego resetu”.
$ git add reset_lifecycle_file
$ git ls-files -s
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
Untracked files:
(use "git add ..." to include in what will be committed)
new_file
Tutaj ponownie użyliśmy polecenia git add
, aby przenieść zmodyfikowany plik reset_lifecycle_file
do indeksu przechowalni. Potwierdzamy, że indeks został zaktualizowany na podstawie wyniku polecenia git ls-files
. W wyniku uzyskanym po wykonaniu polecenia git status
zawartość sekcji „Changes to be commited” (Zmiany do zatwierdzenia) jest teraz wyświetlana na zielono. Plik new_file
z naszych poprzednich przykładów znajduje się teraz w katalogu roboczym jako plik nieśledzony. Wykonajmy szybko polecenie rm new-file
, aby usunąć plik, ponieważ nie będziemy go potrzebować w kolejnych przykładach.
Po osiągnięciu tego stanu repozytorium wykonujemy teraz „miękki reset”.
$ git reset --soft
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
$ git ls-files -s
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Wykonaliśmy „miękki reset”. Po przeanalizowaniu stanu repozytorium przy użyciu poleceń git status
i git ls-files
widzimy, że nic się nie zmieniło. Jest to oczekiwane zachowanie. Miękki reset powoduje zresetowanie jedynie historii commitów. Domyślnie polecenie git reset
wywoływane jest ze wskaźnikiem HEAD
jako commitem docelowym. Nasza historia commitów znajdowała się już pod wskaźnikiem HEAD
, dlatego po wykonaniu resetu do wskaźnika HEAD
właściwie nic się nie wydarzyło.
Aby lepiej zrozumieć sposób działania opcji --soft
i lepiej ją wykorzystać, potrzebujemy commita docelowego różnego od HEAD
. W indeksie przechowalni mamy oczekujący plik reset_lifecycle_file
. Utwórzmy nowy commit.
$ git commit -m"prepend content to reset_lifecycle_file"
Na tym etapie nasze repozytorium powinno mieć trzy commity. Będziemy cofać się w czasie do pierwszego commita. W tym celu potrzebujemy identyfikatora pierwszego commita. Można go znaleźć, wyświetlając wynik polecenia git log
.
$ git log
commit 62e793f6941c7e0d4ad9a1345a175fe8f45cb9df
Author: bitbucket
Date: Fri Dec 1 15:03:07 2017 -0800
prepend content to reset_lifecycle_file
commit dc67808a6da9f0dec51ed16d3d8823f28e1a72a
Author: bitbucket
Date: Fri Dec 1 10:21:57 2017 -0800
update content of reset_lifecycle_file
commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4
Author: bitbucket
Date: Thu Nov 30 16:50:39 2017 -0800
initial commit
Należy pamiętać, że identyfikatory historii commitów będą unikatowe dla każdego systemu. To oznacza, że identyfikatory commitów w tym przykładzie będą inne niż na Twoim komputerze. Identyfikator commita, który interesuje nas w tym przykładzie, to 780411da3b47117270c0e3a8d5dcfd11d28d04a4
. Jest to identyfikator odpowiadający „commitowi początkowemu” (initial commit). Po zlokalizowaniu tego identyfikatora użyjemy go jako commita docelowego dla naszego miękkiego resetu.
Zanim cofniemy się w czasie, najpierw sprawdźmy aktualny stan repozytorium.
$ git status && git ls-files -s
On branch main
nothing to commit, working tree clean
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Wykonujemy tutaj kombinację poleceń git status
i git ls-files -s
, która pokazuje nam, że w repozytorium znajdują się oczekujące zmiany, a wersja pliku reset_lifecycle_file
w indeksie przechowalni to 67cc52710639e5da6b515416fd779d0741e3762e
. Mając to na uwadze, możemy wykonać miękki reset w celu przywrócenia stanu do naszego pierwszego commita.
$git reset --soft 780411da3b47117270c0e3a8d5dcfd11d28d04a4
$ git status && git ls-files -s
On branch main
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: reset_lifecycle_file
100644 67cc52710639e5da6b515416fd779d0741e3762e 0 reset_lifecycle_file
Powyższy kod powoduje wykonanie „miękkiego resetu”, a także wywołanie kombinacji poleceń git status
i git ls-files
, która zwraca stan repozytorium. Możemy przeanalizować wynikowy stan repozytorium i zaobserwować coś ciekawego. Po pierwsze polecenie git status
wskazuje, że istnieją modyfikacje w pliku reset_lifecycle_file
i wyróżni je, sygnalizując, że istnieją zmiany w środowiska przejściowego do następnego commita. Po drugie wynik polecenia git ls-files
wskazuje, że indeks środowiska przejściowego nie uległ zmianie, i zachowany został wcześniejszy hash SHA 67cc52710639e5da6b515416fd779d0741e3762e.
Aby dokładniej wyjaśnić, co wydarzyło się w trakcie tego resetu, wykonajmy polecenie git log
:
$ git log commit 780411da3b47117270c0e3a8d5dcfd11d28d04a4 Author: bitbucket Date: Thu Nov 30 16:50:39 2017 -0800 initial commit
Wynik działania polecenia wskazuje teraz, że w historii commitów znajduje się jeden commit. To pomoże nam wyraźnie zilustrować, jaki efekt dało zastosowanie opcji --soft
. Podobnie jak w przypadku wszystkich wywołań polecenia git reset
pierwszym wykonywanym działaniem jest zresetowanie drzewa commitów. W naszych poprzednich przykładach z argumentami --hard
i --mixed
polecenie było wykonywane względem wskaźnika HEAD
i nie cofaliśmy w czasie historii commitów. W trakcie miękkiego resetu dzieje się tylko to.
Niejasne może się wydawać, dlaczego polecenie git status
wskazuje na istnienie zmodyfikowanych plików. Wykonanie polecenia z opcją --soft
wpływa na indeks przechowalni, dlatego aktualizacje w naszym indeksie przechowalni zostały cofnięte w czasie na podstawie historii commitów. Potwierdza to wynik polecenia git ls-files -s
, który wskazuje, że hash SHA pliku reset_lifecycle_file
nie uległ zmianie. Przypomnijmy, że polecenie git status
nie pokazuje stanu „trzech drzew”, tylko różnice między nimi. W tym przypadku wskazuje, że indeks przechowalni wyprzedza zmiany w historii commitów tak, jakbyśmy już dodali je do przechowalni.
Resetowanie a przywracanie
Jeśli użycie polecenia git revert potraktujemy jako „bezpieczny” sposób cofania zmian, polecenie git reset
należy uważać za metodę niebezpieczną. Użycie polecenia git reset
stwarza realne ryzyko utraty pracy. Polecenie git reset
nigdy nie usunie commita, jednak commity mogą zostać „osierocone”, co oznacza, że bezpośrednia ścieżka dostępu do nich z referencji przestanie istnieć. Takie osierocone commity można zazwyczaj znaleźć i przywrócić przy użyciu polecenia git reflog. Po uruchomieniu wewnętrznego narzędzia usuwania zbędnych elementów Git trwale usunie wszelkie osierocone commity. Domyślnie Git uruchamia narzędzie usuwania zbędnych elementów co 30 dni. Historia commitów jest jednym z „trzech drzew Git”, przy czym pozostałe dwa — indeks środowiska przejściowego i katalog roboczy — nie są tak trwałe jak commity. Podczas korzystania z tego narzędzia należy zachować ostrożność, ponieważ jest to jedyne polecenie Git, które potencjalnie może doprowadzić do utraty wykonanej pracy.
Podczas gdy operacja przywracania ma na celu bezpieczne cofanie publicznych commitów, celem polecenia git reset
jest cofanie lokalnych zmian w indeksie przechowalni i katalogu roboczym. Ze względu na odmienne cele te dwa polecenia są różnie implementowane: resetowanie całkowicie usuwa zestaw zmian, podczas gdy przywracanie zachowuje oryginalny zestaw zmian i wykorzystuje nowy commit do zastosowania operacji cofnięcia.
Nie resetuj historii publicznej
Nigdy nie należy używać polecenia git reset
, gdy jakiekolwiek migawki po
Usunięcie commita, nad którym inni członkowie zespołu kontynuowali prace, stwarza poważne problemy dla współpracy. Gdy takie osoby spróbują zsynchronizować się z Twoim repozytorium, efekt będzie taki, jakby fragment historii projektu nagle zniknął. Poniższa sekwencja ilustruje, co stanie się, gdy spróbujesz zresetować publiczny commit. Gałąź origin/main
jest wersją Twojej lokalnej gałęzi main
w centralnym repozytorium.
Gdy tylko dodasz nowe commity po wykonaniu resetu, Git pomyśli, że Twoja historia lokalna jest różna od gałęzi origin/main
, a commit scalenia wymagany do zsynchronizowania Twoich repozytoriów prawdopodobnie wprowadzi zamieszanie i frustrację wśród członków zespołu.
Musisz po prostu ograniczyć korzystanie z polecenia git reset
do nieudanych lokalnych eksperymentów i nie stosować go do opublikowanych zmian. Do naprawy commitów publicznych służy polecenie git revert
przeznaczone specjalnie do tego celu.
Przykłady
git reset <file>
Usuń określony plik z obszaru przechowalni, ale katalog roboczy pozostaw bez zmian. To polecenie powoduje usunięcie pliku z przechowalni bez zastępowania jakichkolwiek zmian.
git reset
Zresetuj obszar przechowalni, aby odpowiadał najnowszemu commitowi, ale pozostaw katalog roboczy bez zmian. To polecenie spowoduje usunięcie z przechowalni wszystkich plików bez zastępowania jakichkolwiek zmian, umożliwiając ponowne skompilowanie migawki w przechowalni od zera.
git reset --hard
Zresetuj obszar przechowalni i katalog roboczy, aby odpowiadały najnowszemu commitowi. Flaga --hard
jest dla Git instrukcją, aby oprócz usunięcia zmian z przechowalni zastąpić także wszystkie zmiany w katalogu roboczym. Innymi słowy, powoduje to pozbycie się wszystkich niezatwierdzonych zmian, dlatego przed użyciem tego polecenia musisz upewnić się, że faktycznie chcesz odrzucić wszystkie lokalne prace programistyczne.
git reset
Cofnij końcówkę bieżącej gałęzi do punktu wskazywanego przez commit
, zresetuj obszar środowiska przejściowego, aby odpowiadał temu stanowi, ale nie ruszaj katalogu roboczego. Wszystkie zmiany wprowadzone od momentu
będą się znajdować w katalogu roboczym, umożliwiając ponowne zatwierdzenie historii projektu z użyciem bardziej przejrzystych i szczegółowych migawek.
git reset --hard
Cofnij końcówkę bieżącej gałęzi do punktu wskazywanego przez
oraz zresetuj obszar środowiska przejściowego i katalog roboczy, aby odpowiadały temu stanowi. Spowoduje to usunięcie nie tylko niezatwierdzonych zmian, ale także wszystkich następujących po nich commitów.
Usuwanie pliku z przechowalni
Polecenie git reset
stosuje się często podczas przygotowywania migawek w przechowalni. Kolejny przykład zakłada, że mamy dwa pliki o nazwie hello.py
oraz main.py
, które zostały już dodane do repozytorium.
# Edit both hello.py and main.py
# Stage everything in the current directory
git add .
# Realize that the changes in hello.py and main.py
# should be committed in different snapshots
# Unstage main.py
git reset main.py
# Commit only hello.py
git commit -m "Make some changes to hello.py"
# Commit main.py in a separate snapshot
git add main.py
git commit -m "Edit main.py"
Jak można zauważyć, polecenie git reset
pomaga zachować wysoki poziom ukierunkowania commitów, umożliwiając wycofywanie z przechowalni zmian niezwiązanych z następnym commitem.
Usuwanie lokalnych commitów
Następny przykład ilustruje bardziej zaawansowany przypadek użycia. Pokazuje, co się dzieje, gdy przez jakiś czas pracujesz nad nowym eksperymentem, ale po zatwierdzeniu kilku migawek decydujesz się całkowicie go odrzucić.
# Create a new file called `foo.py` and add some code to it
# Commit it to the project history
git add foo.py
git commit -m "Start developing a crazy feature"
# Edit `foo.py` again and change some other tracked files, too
# Commit another snapshot
git commit -a -m "Continue my crazy feature"
# Decide to scrap the feature and remove the associated commits
git reset --hard HEAD~2
Polecenie git reset HEAD~2
cofa bieżącą gałąź o dwa commity, skutecznie usuwając z historii projektu dwie migawki, które właśnie utworzyliśmy. Pamiętaj, że tego rodzaju resetu powinno się używać tylko w odniesieniu do nieopublikowanych commitów. Nie wolno wykonywać powyższej operacji, jeśli commity zostały już wypchnięte do współdzielonego repozytorium.
Podsumowanie
Podsumowując, git reset
to zaawansowane polecenie przeznaczone do cofania lokalnych zmian stanu repozytorium Git. Polecenie git reset
działa w obrębie „trzech drzew Git”. Te drzewa to historia commitów (HEAD
), indeks środowiska przejściowego i katalog roboczy. Istnieją trzy opcje wiersza polecenia, które odpowiadają tym trzem drzewom. Opcje --soft, --mixed
i --hard
można przekazać do polecenia git reset
.
W tym artykule wykorzystaliśmy kilka innych poleceń Git, aby ułatwić zilustrowanie procesu resetowania. Więcej informacji na temat tych poleceń można znaleźć na stronach poświęconych każdemu z nich: git status, git log, git add, git checkout, git reflog i git revert.
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.