Close

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.

Logo Git
materiały pokrewne

Git — ściągawka

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

4 węzły z węzłem „main” jako ostatnim

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

4 węzły ze wskaźnikiem main na ostatnim węźle i wskaźnikiem head na drugim węźle

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

2 zestawy po 2 węzły ze wskaźnikiem head/main na drugim węźle pierwszego zestawu

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.

diagram zakresu poleceń git reset

'--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 zostały wypchnięte do publicznego repozytorium. Po opublikowaniu commita musisz założyć, że inni programiści opierają się na nim.

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.

Schemat przedstawiający działania po zresetowaniu: w rezultacie powstają dwie główne gałęzie

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.

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