Czysta historia zmian w GIT – to proste!
💡

Czysta historia zmian w GIT – to proste!

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:

image

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:

image

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!