Ten artykuł nie jest listą egzotycznych flag do zapamiętania, lecz praktycznym zestawem komend Git, które warto znać, gdy repozytorium przestaje zachowywać się jak spokojna sekwencja pull → commit → push.
Zanim użyjesz komendy ratunkowej: cztery warstwy Git
Słowo „cofnąć” jest nieprecyzyjne. Ta sama zmiana może znajdować się w czterech miejscach, a pomylenie ich jest częstą przyczyną utraty danych:
| Warstwa | Co zawiera | Bezpieczny pierwszy krok |
|---|---|---|
| commit wskazywany przez branch | historia zapisana jako obiekty Git | git log --oneline --decorate -n 10 |
| staging area, czyli index | zmiany przygotowane do następnego commita | git diff --staged |
| working tree | zmiany w śledzonych plikach poza stagingiem | git diff |
| pliki untracked lub ignored | pliki, których commit może w ogóle nie obejmować | git status --short --ignored |
Zacznij od git status, a przed operacją zmieniającą historię utwórz referencję do obecnego commita:
Taki branch chroni aktualny commit, ale nie zapisuje zmian roboczych ani plików nieśledzonych. Jeżeli to one są najcenniejsze, nie uruchamiaj reset --hard, restore, clean ani git gc. Najpierw zrób kopię katalogu poza repozytorium lub bezpieczny commit ratunkowy na osobnym branchu. Sprawdź, czy kopia obejmuje także pliki ukryte i ignorowane.
1. git stash: tymczasowy schowek na zmiany
git stash push zapisuje stan śledzonych plików w osobnym wpisie i cofa te zmiany w katalogu roboczym. Domyślnie nie obejmuje plików nieśledzonych ani ignorowanych, dlatego czysty wynik zależy od użytych flag. To przydatna opcja, gdy musisz szybko przełączyć branch lub zrobić hotfix bez mieszania go z aktualną pracą.
Jeżeli chcesz odłożyć tylko wybrane pliki, podaj ścieżkę na końcu:
Jeśli git stash pop zakończy się konfliktem, wpis pozostaje na liście. Rozwiąż konflikt i usuń go ręcznie dopiero po sprawdzeniu rezultatu. git stash branch bywa bezpieczniejszy, gdy stash powstał dawno temu i aktualny branch znacznie się zmienił.
stash jest wygodny, ale łatwo zamienić go w mały śmietnik. Jeśli praca ma potrwać dłużej niż kilka godzin, zwykle lepszy będzie mały commit roboczy na osobnym branchu, najlepiej wypchnięty do prywatnego brancha zdalnego.
2. git cherry-pick: jak przenieść konkretny commit?
git cherry-pick kopiuje zmianę z wybranego commita na aktualny branch i nie przenosi całej historii brancha, ale tworzy nowy commit z tym samym diffem.
Gdy pojawi się konflikt, Git zatrzyma operację i poczeka na decyzję:
cherry-pick najlepiej działa dla małych, niezależnych commitów: poprawki buga, drobnej zmiany konfiguracyjnej, jednego refaktoru. Jeśli przenosisz w ten sposób połowę brancha, prawdopodobnie lepszy będzie merge albo rebase.
3. git reflog: jak odzyskać zgubiony commit?
Wskaźnik zmienia się przy commitach, resetach, przełączeniach branchy i
rebase. git reflog zapisuje te ruchy lokalnie, dlatego często pozwala
odzyskać commity, które zniknęły z normalnego git log.
Typowy scenariusz: robisz reset --hard, orientujesz się, że to był błąd, a potem szukasz poprzedniego stanu w reflogu.
Reflog zapisuje ruchy referencji, a nie każdą wersję pliku. Może odzyskać commit sprzed reset --hard, ale nie gwarantuje odzyskania niezacommitowanej pracy nadpisanej w working tree. W takim przypadku sprawdź lokalną historię IDE, snapshoty systemu plików i backupy; kolejne operacje zapisu mogą zmniejszać szanse narzędzi odzyskujących dane.
Reflog pomaga też przy usuniętym branchu:
4. git reset i git revert: dwa sposoby cofania commita
git reset przesuwa aktualny branch na inny commit i w zależności od trybu może zostawić zmiany w staging area, w working directory albo usunąć je z plików roboczych.
Najprostsze rozróżnienie wygląda tak:
| Komenda | Co robi | Kiedy używać |
|---|---|---|
git reset --soft | cofa commit, zostawia zmiany w staging area | gdy chcesz poprawić ostatni commit |
git reset --mixed | cofa commit, zostawia zmiany w working directory | gdy chcesz ponownie ułożyć staging |
git reset --hard | ustawia branch, index i pliki zgodnie z celem | tylko po zabezpieczeniu lokalnej pracy |
git revert <hash> | dodaje nowy commit cofający wcześniejszą zmianę | gdy historia jest współdzielona |
git revert nie przepisuje historii. Dodaje nowy commit, który odwraca konkretną zmianę:
reset --hard nie ogranicza się do „cofniętego commita”: nadpisuje także inne lokalne zmiany w śledzonych plikach, a kolidujące pliki nieśledzone mogą zostać usunięte. Przed jego użyciem sprawdź wszystkie cztery warstwy i utwórz kopię. Jeżeli chcesz tylko usunąć plik ze staging area, użyj git restore --staged -- plik; jeżeli chcesz porzucić jego zmiany robocze, użyj git restore --worktree -- plik dopiero po obejrzeniu git diff.
Historię lokalną możesz sprzątać agresywnie, ale już historię współdzieloną traktuj jak kontrakt z innymi osobami pracującymi na repozytorium.
Jeśli chcesz cofnąć zmianę, która trafiła już na branch używany przez zespół albo , zacznij od revert. O pracy z automatyzacją po stronie repozytorium więcej piszę w artykule o CI/CD dla Next.js.
5. git bisect: znajdź commit, który zepsuł projekt
git bisect prowadzi Cię przez historię metodą wyszukiwania binarnego. Zamiast ręcznie sprawdzać 40 commitów po kolei, oznaczasz znany dobry i znany zły punkt, a Git sam wybiera kolejne commity do testowania.
Jeśli masz test, który jednoznacznie wykrywa problem, możesz zautomatyzować proces:
Komenda uruchamiana przez git bisect run musi zwracać 0 dla commita dobrego, kod 1–127 z wyjątkiem 125 dla złego oraz 125, jeśli rewizji nie da się przetestować i należy ją pominąć. Pozostałe kody przerywają bisect. Test powinien być deterministyczny; niestabilny test albo błąd instalacji zależności może wskazać fałszywego winowajcę.
6. git rebase -i: uporządkuj lokalną historię
Interaktywny rebase pozwala zmieniać kolejność commitów, poprawiać opisy, łączyć małe commity i usuwać niepotrzebne zmiany przed review.
Git otworzy edytor z listą commitów:
Typowe sprzątanie wygląda tak:
Interaktywny rebase ma największy sens przed wypchnięciem brancha albo przed otwarciem pull requesta. Gdy commit jest już używany przez inne osoby, rebase zaczyna generować koszt synchronizacji.
7. git log: szukanie w historii z precyzją
git log jest bardziej elastyczny niż prosta lista commitów, ponieważ pozwala zawężać historię po autorze, czasie, pliku, opisie albo nawet po fragmencie kodu.
Najbardziej praktyczne warianty:
git log --graph --decorate --oneline --allpokazuje rozjazd branchy w czytelnej formie.git log -- path/to/file.tszawęża historię do jednego pliku.git log -S "nazwaFunkcji"szuka commitów zmieniających liczbę wystąpień dokładnego tekstu.git log -G "wyrażenie"szuka diffów, których dodane lub usunięte linie pasują do wyrażenia regularnego.git log -ppokazuje diff każdego commita.
Jeśli debugujesz zmianę zachowania aplikacji, połącz git log -S z testami i narzędziami diagnostycznymi właściwymi dla aplikacji.
8. git diff: zobacz, co naprawdę się zmieniło
git diff pokazuje różnice między working directory, staging area, commitami i branchami. To komenda, którą warto uruchamiać przed commitem częściej niż po fakcie.
Dwie i trzy kropki odpowiadają na inne pytania. main..feature/login porównuje końcowe drzewa obu commitów, a main...feature/login porównuje feature z punktem wspólnego przodka, co zwykle lepiej odpowiada zmianom widocznym w pull requeście.
Najczęstszy workflow przed commitem:
git diff chroni przed przypadkowym dorzuceniem debug logów, lokalnych zmian konfiguracyjnych albo fragmentu pracy z innego zadania. To szczególnie ważne, gdy pracujesz na dużym branchu i staging robisz partiami.
9. git clean: jak usunąć nieśledzone pliki?
git clean usuwa pliki, których Git nie śledzi. To przydatne po eksperymentach, buildach, generowanych plikach albo zmianach zależności, ale jest też jedną z bardziej ryzykownych komend z tej listy.
Różnice są istotne:
| Komenda | Efekt |
|---|---|
git clean -n | symuluje usunięcie nieśledzonych plików |
git clean -nd | symuluje usunięcie nieśledzonych katalogów |
git clean -ndx | symuluje także usunięcie plików ignorowanych |
git clean -f | usuwa nieśledzone pliki |
git clean -fd | usuwa nieśledzone pliki i katalogi |
git clean -fdx | usuwa także pliki ignorowane przez .gitignore |
git clean -i | uruchamia tryb interaktywny |
git clean usuwa pliki, których Git z definicji nie śledzi. Po wykonaniu operacji reflog zwykle nie ma czego odzyskiwać. Pozostają historia lokalna edytora, snapshot systemu plików, kosz systemowy lub backup, dlatego dokładny dry run jest tu ważniejszy niż przy operacjach na commitach.
Jeśli nie masz pewności, użyj git clean -i. Tryb interaktywny jest wolniejszy, ale daje dodatkową kontrolę nad tym, co faktycznie zniknie.
10. git worktree: kilka branchy bez ciągłego przełączania
git worktree pozwala mieć kilka katalogów roboczych podpiętych do tego samego repozytorium. Każdy katalog może pracować na innym branchu.
Pierwsze dwie komendy są wariantami, nie sekwencją do wykonania jedna po drugiej. Git nie pozwala zwykle podpiąć tego samego brancha do dwóch worktree jednocześnie, co chroni przed równoległą edycją tej samej linii pracy w kilku katalogach.
To rozwiązuje typowy problem. Wyobraź sobie sytuację: jesteś w połowie większej zmiany, a nagle trzeba zrobić review albo hotfix. Bez worktree kończy się to stashowaniem, przełączaniem brancha i późniejszym odtwarzaniem kontekstu. Z worktree otwierasz drugi katalog i pracujesz równolegle.
Ratunkowe scenariusze Git krok po kroku
Poniższe przykłady to skrót decyzyjny — zakładają, że przed wykonaniem komendy sprawdzasz stan repozytorium przez git status.
Jak przerwać niedokończony merge, rebase albo cherry-pick?
Nie zgaduj, która operacja trwa. git status pokaże stan sekwencera i właściwą komendę. Jeżeli chcesz wrócić do punktu sprzed rozpoczęcia operacji, użyj odpowiadającego jej --abort:
Uruchom tylko komendę pasującą do bieżącej operacji. --quit ma inne znaczenie: zapomina stan sekwencera, ale nie musi odtworzyć indexu i working tree, dlatego nie jest zamiennikiem --abort.
Jak cofnąć ostatni lokalny commit, ale zostawić zmiany?
Użyj tego, gdy commit jest lokalny i chcesz poprawić treść commita, dodać brakujący plik albo inaczej podzielić zmiany.
Co zrobić po commicie na złym branchu?
Jeśli commit jest ostatni, nie został wypchnięty, a working tree jest czysty, najpierw utwórz poprawny branch wskazujący ten commit, a dopiero później przesuń branch błędny:
Branch correct-branch zabezpiecza commit przed resetem. Jeśli na working tree są dodatkowe zmiany, najpierw je skopiuj lub zapisz, zamiast wykonywać --hard. Gdy commit trafił już na współdzielony remote, bezpieczniejszy wariant to git revert na złym branchu i git cherry-pick na właściwym.
Jak zmienić opis ostatniego commita?
amend zmienia hash commita. Lokalnie to normalne narzędzie. Na własnym branchu po pushu uzgodnij przepisanie historii i użyj ochrony --force-with-lease; na branchu współdzielonym dodaj kolejny commit zamiast nadpisywać istniejący.
Jak dodać plik do ostatniego commita?
To dobre rozwiązanie, gdy commit nie został jeszcze wypchnięty albo pracujesz na prywatnym branchu.
Jak cofnąć zmianę wypchniętą na remote?
To najczytelniejsza ścieżka na branchach współdzielonych. Historia pokazuje zarówno pierwotną zmianę, jak i commit cofający.
Jak cofnąć merge commit?
-m 1 wskazuje pierwszego rodzica jako główną linię historii, ale nie każdy merge należy cofać względem rodzica numer 1. Najpierw sprawdź rodziców przez git show --no-patch --pretty=%P HEAD i potwierdź właściwą linię. Cofnięcie merge commita wpływa również na zachowanie przyszłych scaleń, dlatego w złożonym przypadku przećwicz operację na branchu testowym.
Jak odzyskać usunięty śledzony plik?
Jeżeli usunięcie nie trafiło do staging area, przywróć wersję z indexu:
Jeżeli usunięcie zostało już dodane do staging area, najpierw odtwórz wpis indexu z HEAD, a następnie plik roboczy:
Jeśli chcesz pobrać wersję z określonego commita, wskaż źródło jawnie:
Komendy zapisujące do working tree mogą nadpisać lokalną wersję pliku. Przed wykonaniem sprawdź git diff -- path/to/file.ts, aby nie usunąć innych zmian w tym samym miejscu.
Jak pobrać jeden plik z innego brancha?
To czytelniejsza, nowocześniejsza alternatywa dla starszych wariantów opartych o git checkout --.
Kiedy wolno użyć push --force-with-lease?
Po rebase lub amend na własnym branchu pull requesta zwykły push nie jest fast-forward. --force-with-lease odmawia nadpisania, jeśli zdalna referencja nie ma oczekiwanej wartości, dlatego jest bezpieczniejsze od --force, ale nie stanowi blokady absolutnej. Automatyczny fetch może zaktualizować remote-tracking branch i osłabić domyślną kontrolę.
Nie stosuj tego mechanizmu do main ani brancha współdzielonego bez jawnego uzgodnienia i ochrony po stronie serwera.
Jak sprawdzić, kto zmienił konkretną linię?
Wbrew nazwie git blame nie służy do szukania winnych — najlepiej traktować go jako narzędzie do znalezienia kontekstu: commita, pull requesta, decyzji technicznej albo osoby, którą warto zapytać o szczegóły.
Git cheat sheet: którą komendę wybrać?
Na koniec krótka, praktyczna ściągawka.
| Problem | Komenda |
|---|---|
| Muszę tymczasowo odłożyć zmiany | git stash |
| Chcę przenieść jeden commit | git cherry-pick <hash> |
| Zgubiłem commit po resecie | git reflog |
| Chcę poprawić lokalny ostatni commit | git reset --soft HEAD~1 |
| Chcę cofnąć commit na remote | git revert <hash> |
| Szukam commita z błędem | git bisect |
| Sprzątam lokalną historię | git rebase -i |
| Szukam zmiany w historii | git log -S "fragment" |
| Sprawdzam, co trafi do commita | git diff i git diff --staged |
| Usuwam pliki i katalogi nieśledzone | git clean -nd, potem -fd |
| Usuwam także pliki ignorowane | git clean -ndx, potem -fdx |
| Pracuję na kilku branchach naraz | git worktree |
