O kurczę, Git!?!

Git jest trudny: łatwo coś popsuć, a rozgryzienie tego, jak naprawić błędy jest niemożliwe. Dokumentacja Gita to taki paradoks kury i jajka: nie jesteś w stanie znaleźć instrukcji, jak wygrzebać się z bałanu, jeżeli nie wiesz, jak nazywa się to coś, o czym powinieneś wiedzieć, żeby rozwiązać Twój problem.

Oto niektóre złe sytuacje, w które zdarzyło mi się wpakować oraz instrukcje jak z nich wyjść, wyłożone prostym językiem.

O kurczę, coś poszło bardzo nie tak, powiedz że Git ma jakiś magiczny wehikuł czasu!?!

git reflog
# zobaczysz listę wszystkich rzeczy, które
# zrobiłeś(aś) w gicie, we wszystkich branchach!
# każda z nich ma indeks HEAD@{index}
# znajdź tą sprzed momentu, kiedy wszystko się popsuło
git reset HEAD@{index}
# magiczny wehikuł czasu

Możesz tego użyć do odzyskania rzeczy, które przez przypadek usunąłeś(aś), lub do usunięcia czegoś, co próbowałeś(aś) zrobić i zepsuło repozytorium, lub do przywrócenia normalności po nieudanym merge'u, albo po prostu żeby wrócić do momentu, kiedy wszystko było dobrze. Używam komendy reflog BARDZO CZĘSTO. Czapki z głów dla wielu, wielu, wielu, wielu, wielu ludzi, którzy zasugerowali dodanie jej!

O kurczę, zrobiłem(am) commit i zaraz potem zauważyłem(am), że muszę dodać jedną małą zmianę!

# dokonaj zmiany
git add . # lub dodaj pojedyncze pliki
git commit --amend --no-edit
# teraz Twój ostatni commit zawiera tę zmianę!
# UWAGA: nigdy nie poprawiaj publicznych commitów

Zazwyczaj zdarza mi się tak, gdy zacommituję, uruchomię testy/lintery i... no tak, zapomniałam postawić spację po znaku równości. Możesz także wykonać zmianę jako nowy commit i użyć rebase -i w celu scalenia obu commitów w jeden, ale ta metoda jest milion razy szybsza.

Uwaga: Nigdy nie poprawiaj commitów, które zostały wypushowane do publicznego/współdzielonego brancha! Poprawiaj tylko te commity, które istnieją jedynie w Twojej kopii lokalnej, no chyba że chcesz popsuć sobie dzień.

O kurczę, muszę zmienić opis mojego ostatniego commitu!

git commit --amend
# postępuj według instrukcji, aby wyedytować treść opisu

Głupie wytyczne formatowania opisów w commitach.

O kurczę, niechcący zacommitowałem(am) do mastera coś, co powinno być w nowym branchu!

# stwórz nowego brancha z aktualnym stanem z mastera
git branch jakas-nazwa-nowego-brancha
# usuń ostatni commit z brancha master
git reset HEAD~ --hard
git checkout jakas-nazwa-nowego-brancha
# Twój commit jest teraz w tym branchu :)

Ważne: to nie zadziała, jeżeli wypushowałeś(aś) już commita do publicznego/współdzielonego brancha, a jeżeli próbowałeś(aś) wcześniej innych sposobów, to prawdopodobnie będziesz musiał(a) wykonać git reset HEAD@{liczba-commitów-wstecz} zamiast HEAD~. Smutek, żal i zgrzytanie zębów. Aha, wielu, wielu, wielu ludzi zasugerowało nieznany mi wcześniej genialny sposób jak to zapisać krócej. Wszystkim Wam dziękuję!

O kurczę, niechcący zacommitowałem(am) do nie tego brancha co trzeba!

# wycofaj ostatni commit, ale zachowaj dokonane zmiany
git reset HEAD~ --soft
git stash
# przejdź do prawidłowego brancha
git checkout nazwa-prawidlowego-brancha
git stash pop
git add . # lub dodaj pojedyncze pliki
git commit -m "Twój opis commitu";
# teraz Twoje zmiany są w prawidłowym branchu

Wielu ludzi sugerowało również użycie w tej sytuacji komendy cherry-pick. Wybierz tę metodę, która wydaje Ci się sensowniejsza!

git checkout nazwa-prawidlowego-brancha
# wybierz ostatni commit do mastera
git cherry-pick master
# usuń go z mastera
git checkout master
git reset HEAD~ --hard

O kurczę, próbowałem(am) zrobić diff, ale nic się nie wyświetliło?!

Jeżeli masz pewność, że dokonałeś(aś) zmian w plikach, a mimo to diff świeci pustkami, to prawdopodobnie dodałeś(aś) swoje pliki do stagingu i musisz użyć specjalnej flagi.

git diff --staged

Plik wyświetli się poniżej ¯\(ツ)/¯ (tak, wiem że to feature, a nie bug, ale to jest zaskakujące i nieoczywiste za pierwszym razem, kiedy Ci się to wydarzy!)

O kurczę, muszę przywrócić commit dokonany jakieś 5 commitów temu!

# znajdź commit, który chesz przywrócić
git log
# użyj klawiszy ze strzałkami aby poruszać się w górę i w dół po historii
# gdy znajdziesz swój commit, zanotuj jego hash
git revert [zapisany hash]
# git stworzy nowego commita, który przywraca tamten commit
# postępuj zgodnie z instrukcjami, aby uzupełnić opis commitu
# albo po prostu zapisz i zacommituj

Okazuje się, że wcale nie musisz przywracać zmian poprzez ręczne wyszukiwanie i robienie kopiuj-wklej starej zawartości do istniejącego pliku! Jeżeli zacommitowałeś(aś) buga, możesz przywrócić cały commit za jednym zamachem za pomocą revert.

Możesz także przywrócić pojedynczy plik zamiast całego commitu! Ale oczywiście jak na gita przystało, to zupełnie inny zestaw komend...

O kurczę, muszę wycofać moje zmiany z pliku!

# znajdź hash commitu sprzed momentu, w którym plik został zmieniony
git log
# użyj klawiszy ze strzałkami aby poruszać się w górę i w dół po historii
# gdy znajdziesz swój commit, zanotuj jego hash
git checkout [zanotowany hash] -- sciezka/do/pliku
# stara wersja pliku będzie teraz w Twoim indexie
git commit -m "Wow, nie musisz wycofywać zmian metodą kopiuj-wklej!"

Kiedy wreszcie to ogarnęłam, to było MEGA. MEGA. M-E-G-A. A tak serio, na której planecie checkout -- brzmi sensownie jako najlepsza metoda wycofania zmian w pliku? :wygraża-pięściami-w-stronę-linusa-torvaldsa:

Olać to, poddaję się

cd ..
sudo rm -r glupi-katalog-z-gitowym-repo
git clone https://jakis.adres.github/glupi-katalog-z-gitowym-repo.git
cd glupi-katalog-z-gitowym-repo

Dzięki dla Erica V. za podesłanie. Ewentualne pretensje dotyczące użycia sudo w tym żarcie proszę kierować do niego.

Ale tak na poważnie, jeżeli w Twoim branchu jest już taki bałagan, że musisz zsynchronizować stan swojego repozytorium ze zdalnym w sposób "zalecany przez Gita", spróbuj tego poniżej. Ale pamiętaj, że te czynności są destrukcyjne i nieodwracalne!

# pobierz najnowszy stan origina
git fetch origin
git checkout master
git reset --hard origin/master
# usuń wszystkie nieśledzone pliki i foldery
git clean -d --force
# powtórz checkout/reset/clean dla każdego skopanego brancha

*Disclaimer: Niniejsza strona nie ma na celu bycia wyczerpującym poradnikiem. Owszem, da się zrobić te same rzeczy z większą dozą teoretycznej czystości czy czegoś tam, ale wypracowałam te kroki metodą prób i błędów, licznych przekleństw i przewracania stolików. A potem wpadłam na szalony pomysł, żeby udostępnić je światu opakowane w zdrową dozę beztroski. Bierzesz albo nie, Twój wybór!

Wielkie dzięki dla wszystkich, którzy przetłumaczyli tę stronę na inne języki, jesteście super! Moritz Stückler (de) · Daniil Golubev (ru) · Łukasz Wójcik (pl) · fedemcmac (it) · Michel (fr)

Jeśli chcesz pomóc w dodaniu tłumaczenia na swój język, prześlij PR na GitHub