Git
Chapters ▾ 2nd Edition

2.4 Podstawy Gita - Cofanie zmian

Cofanie zmian

W każdym momencie może zajść potrzeba cofnięcia jakiejś zmiany. Poniżej przyjrzymy się kilku podstawowym funkcjom cofającym modyfikacje. Bądź ostrożny, ponieważ nie wszystkie cofnięcia można odwrócić. Jest to jedno z niewielu miejsc Gita, w których należy być naprawdę ostrożnym, gdyż można stracić bezpowrotnie część pracy.

Jeden z częstych przypadków to zbyt pochopne wykonanie rewizji i pominięcie w niej części plików, lub też pomyłka w notce do zmian. Jeśli chcesz poprawić wcześniejszą, błędną rewizję, wystarczy uruchomić git commit raz jeszcze, tym razem, z opcją --amend (popraw):

$ git commit --amend

Polecenie bierze zawartość poczekalni i zatwierdza jako dodatkowe zmiany. Jeśli niczego nie zmieniłeś od ostatniej rewizji (np. uruchomiłeś polecenie zaraz po poprzednim zatwierdzeniu zmian) wówczas twoja migawka się nie zmieni ale będziesz miał możliwość modyfikacji notki.

Jak zwykle zostanie uruchomiony edytor z załadowaną treścią poprzedniego komentarza. Edycja przebiega dokładnie tak samo jak zawsze, z tą różnicą, że na końcu zostanie nadpisana oryginalna treść notki.

Czas na przykład. Zatwierdziłeś zmiany a następnie zdałeś sobie sprawę, że zapomniałeś dodać do poczekalni pliku, który chciałeś oryginalnie umieścić w wykonanej rewizji. Wystarczy, że wykonasz następujące polecenie:

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

Wszystkie trzy polecenia zakończą się jedną rewizją - druga operacja commit zastąpi wynik pierwszej.

Usuwanie pliku z poczekalni

Następne dwie sekcje pokazują jak zarządzać poczekalnią i zmianami w katalogu roboczym. Dobra wiadomość jest taka, że polecenie używane do określenia stanu obu obszarów przypomina samo jak cofnąć wprowadzone w nich zmiany. Na przykład, powiedzmy, że zmieniłeś dwa pliki i chcesz teraz zatwierdzić je jako dwie osobne rewizje, ale odruchowo wpisałeś git add * co spowodowało umieszczenie obu plików w poczekalni. Jak w takiej sytuacji usunąć stamtąd jeden z nich? Polecenie git status przypomni ci, że:

$ git add .
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

Tekst znajdujący się zaraz pod nagłówkiem “Changes to be committed” mówi "użyj git reset HEAD <plik>... żeby usunąć plik z poczekalni. Nie pozostaje więc nic innego jak zastosować się do porady i zastosować ją na pliku CONTRIBUTING.md:

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Polecenie wygląda odrobinę dziwacznie, ale działa. Plik CONTRIBUTING.md ciągle zawiera wprowadzone modyfikacje ale nie znajduje się już w poczekalni.

Uwaga

W chwili gdy wywołasz polecenie git reset z opcją --hard może być ono niebezpieczne, ale w tym przypadku plik z katalogu roboczego pozostaje nietknięty. Wywołanie git reset bez opcji jest bezpieczne - dotyczy tylko poczekalni.

W tej chwili ta "magiczna inwokacja" jest wszystkim co powineneś wiedzieć na temat polecenia git reset. Zagłębimy się w szczegóły co robi polecenie reset oraz jak używać go do robienia naprawdę ciekawych rzeczy w rozdziale Reset Demystified.

Cofanie zmian w zmodyfikowanym pliku

Co jeśli okaże się, że nie chcesz jednak zatrzymać zmian wykonanych w pliku CONTRIBUTING.md? W jaki sposób łatwo cofnąć wprowadzone modyfikacje czyli przywrócić plik do stanu w jakim był po ostatniej rewizji (lub początkowym sklonowaniu, lub jakkolwiek dostał się do katalogu roboczego)? Z pomocą przybywa raz jeszcze polecenie git status. W ostatnim przykładzie, pliki będące poza poczekalnią wyglądają następująco:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   CONTRIBUTING.md

Git konkretnie wskazuje jak pozbyć się dokonanych zmian. Zróbmy zatem co każe Git:

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

Możesz teraz przeczytać, że zmiany zostały cofnięte.

Ważne

Powinieneś sobie już także zdawać sprawę, że polecenie git checkout -- [file] jest dość niebezpieczne: wszelkie zmiany jakie wykonałeś w pliku przepadają - w rzeczy samej został on nadpisany poprzednią wersją. Nigdy nie używaj tego polecenia dopóki nie jesteś absolutnie pewny, że nie chcesz i nie potrzebujesz już danego pliku.

Jeśli jedynie chcesz się go chwilowo pozbyć przyjrzymy się specjalnemu poleceniu schowka (stash) oraz gałęziom w Gałęzie Gita - są to generalnie znacznie lepsze sposoby.

Pamiętaj, że wszystko co zatwierdzasz do repozytorium Gita może zostać w niemalże dowolnym momencie odtworzone. Nawet rewizje, które znajdowały się w usuniętych gałęziach, albo rewizje nadpisane zatwierdzeniem poprawiającym --amend mogą być odtworzone (odzyskiwanie danych opisujemy w Data Recovery). Jednakże, cokolwiek utraciłeś a nie było to nigdy wcześniej zatwierdzane do repozytorium, prawdopodobnie odeszło na zawsze.