Odpowiednio prowadzona historia w repozytorium pomaga zorientować się w postępie zmian i metodach rozwiązywania problemów przez innych programistów w projekcie. Dla wielu z nas czysta historia zmian w git brzmi tak samo abstrakcyjnie jak 100% pokrycie kodu testami. Kto z nas nie spotkał repozytorium, którego historia po wykonaniu git log
wygląda tak:
Obraz jest fatalny. Mam dla Ciebie prosty sposób, który pomoże uniknąć takiego bałaganu.
Sądząc po powyższym obrazku, programista dodał do widoku formularz. Zakomitował tę zmianę.
Następnie:
- Poprawił typo w nazwie przycisku. Zrobił commit.
- Poprawił typo w placeholderze w polu z tytułem. Zrobił commit.
- Dodał na widoku span, w którym wyświetlił całą wartość obiektu. Zrobił commit.
- Dodał jeszcze jedno pole do formularza. Zrobił commit.
- Zmienił typ dodanego pola. Zrobił commit.
- Usunął puste linie. Zrobił commit.
- Poprawił literówkę. Zrobił commit.
- Usunął niepotrzebny span. Zrobił commit.
W powyższej implementacji było kilka nieoczekiwanych zmian, jak poprawy literówek, wylogowanie sobie do widoku jakichś pomocniczych wartości, formatowanie kodu. Można też wnioskować, że dodanie kolejnego pola wynikało z niedopatrzenia przy przenoszeniu wymagań biznesowych do kodu.
Efektem był taki kod:
Wszystkie te zmiany powinny się całkiem logicznie znaleźć w zakresie pierwszego commita: „Add task form to the task edit partial”. Tymczasem mamy tutaj totalnie spapraną historię, która o niczym zupełnie nie mówi, no chyba tylko o niechlujności autora takiego kodu.
git commit –amend
Aby uniknąć takiego bałaganu Git daje nam komendę commit --amend
. Po poprawieniu literówki, wystarczy zaindeksować zmiany przez git add --all
, a następnie dodać te zmiany do poprzedniego commita za pomocą git commit --amend
. Otworzy się domyślny edytor dla commit messages, gdzie mamy też możliwość zmiany tresci wiadomości poprzedniego commita.
Przy większej ilości zmian tego typu polecenia, każdorazowe otwieranie edytora, zamykanie (jeśli to VIM vielu może mieć problemy), może być zwyczajnie upierdliwe.
Dlatego mamy coś takiego:
git commit –amend –no-edit
Dodajemy zmiany: git add --all
, a następnie git commit --amend --no-edit
. I już. Zmiany dorzucone do poprzedniego commita. Edytor się nie otwiera, bo flaga --no-edit
nie daje możliwości edycji commit message.
Zmiana historii
Musisz pamiętać, że git commit --amend
oraz git commit --amend --no-edit
nadpisuje poprzedni commit zmieniając jego indentyfikator SHA. Jakie to ma konsekwencje? Ano takie, że jeśli ten commit jest już wysłany do repo, przy git push
dostaniemy w twarz komunikatem, że historia jest różna i najpierw trzeba pobrać zmiany z repo. Mimo, że commit message jest ten sam, to te commity różnią się, git traktuje je jako dwie różne zmiany.
Żeby wypchnąć nowe zmiany do repozytorium musisz wykonać komendę, której dawno temu zakazano Ci wykonywać git push --force
. Niech pierwszy rzuci kamień ten, kto zrobił pusha z forcem i nadpisał zmiany, a potem gryzł klawiaturę z nerwów. W takiej sytuacji jest ryzyko, że wypychając zmiany do repo możemy nadpisać jakieś commity, których nie mamy lokalnie u siebie np. pracę, którą wypchnął ktoś z naszego zespołu.
git push –force-with-lease
Dlatego, rekomenduję, żeby na zawsze zapomnieć, że jest coś takiego jak git push --force
, a zawsze używać git push --force-with-lease
.
Oto różnica między nimi:
force
nadpisuje branch w repozytorium zmianami z naszego lokalnego brancha.
-force-with-lease
– jest bezpieczniejszą opcją, która nie nadpisuje żadnej zmiany na zdalnym branchu, jeśli zostały tam dodane nowe commity (przez innego członka zespołu albo kontrybutora). Zapewnia, że nie nadpiszesz czyjejś pracy przez force pusha.
Zapamiętaj!
Zawsze używaj git push --force-with-lease
.
Aliasy
Klepanie tych wszystkich komend może być uciążliwe i robione w pośpiechu może być powodem frustracji ze względu na łatwe do zrobienia literówki. Mój sposób na ten problem, to aliasy lub skróty dodane do powłoki.
Mam dla Ciebie wpisy gotowe do skopiowania do .gitconfig
aa = add --all
cam = commit --amend
came = commit --amend --no-edit
puf = push --force-with-lease
Potem wywołujemy:
git aa
– dla git add --all
git cam
– dla git commit --amend
git came
– dla git commit --amend --no-edit
git puf
– dla git push --force-with-lease
Bonus!
Jeśli jeszcze nie znasz, to koniecznie poznaj fish shell. https://fishshell.com/
Ma bardzo prosty mechanizm dodawania skrótów od powłoki. Ja uwielbiam fish. Najczęściej używam skrótów ustawionych właśnie w nim. Do powyższych komend dodałem własne:
abbr -a -U -- gaa 'git add --all'
abbr -a -U -- gam 'git commit --amend'
abbr -a -U -- game 'git commit --amend --no-edit'
abbr -a -U -- gpuf 'git push --force-with-lease'
Nie trzeba pisać przedrostka git 🙂
Wystarczy:
gaa
– dla git add --all
gam
– dla git commit --amend
game
– dla git commit --amend --no-edit
gpuf
– dla git push --force-with-lease
Pożegnaj się z zaśmieconą historią! Czysta historia zmian w Git – to takie proste!
Zobacz też: Artykuł o przechowywaniu kluczy w React Native