Jak powinien wyglądać dobry commit?

Dzisiejszy wpis będzie skierowany do osób zaczynających swoją przygodę z systemami kontroli wersji (VCS). Mimo że porady będą bazowane na systemie git, większość z nich można z powodzeniem zastosować także dla jego alternatyw.

Rozmiar commitu

Jest to rzecz, z którą - według moich obserwacji - początkujący mają największy problem. Nie istnieje bowiem idealna ilość zmian w jednym commicie, którą możemy się kierować za każdym razem. Myślę, że commit o dobrym rozmiarze najprościej jest zdefiniować słowem samodzielny. Co takiego mam przez to na myśli?

  • zmiany w commicie powinny stanowić działąjącą całość, nie powinno się commitować kodu, który generuje na przykład błąd parsera
  • commit powinien obejmować jedną funkcjonalność. Ponownie, nie da rady zdefiniować jak dużo obejmuje jedna "funkcjonalność". Spróbuj jednak postąpić w ten sposób - wyobraź sobie, że commit, który masz zaraz wyślesz, będzie musiał zostać cofnięty (np. komendą git revert). Czy robiąc to, pozbędziesz się tylko jednego kroku, czy też będziesz musiał w osobnym commicie powtórzyć część z dokonanych wcześniej zmian (które właśnie hipotetycznie cofnąłeś). Jeżeli zachodzi druga sytuacja, to commit jest za duży.

Opis commitu

Bądźmy szczerzy, każdy lubi czasem popuścić wodze fantacji wpisując wiadomość commitu. Najczęściej jest to danie upustu frustracji, która towarzyszyła tworzeniu danego kodu. Powstały nawet specjalne strony jak whatthecommit.com. Warto jednak poświęcić kilka sekund więcej na napisanie wiadomości commitu, aby w przyszłości zaoszczędzić swój czas.

Opis commitu jest wykorzystywany w bardzo wielu sytuacjach - komendach git log, git rebase, git blame, czy ich odpowiednikach w serwisach typu GitHub. Traktuj opis commitu tak, jakbyś musiał go przeczytać i szybko zrozumieć kilka dni później. Staraj się być zwięzłym i rzeczowym - jeżeli nie ze względu na siebie, to na współpracowników.

Kolejną sprawą jest możliwość podzielenia commitu na krótki opis główny i dłuższą (opcjonalną!), bardziej szczegółową część, która nie jest pokazywana w niektórych sytuacjach. Wspiera to oryginalny klient gita (np. komenda git log --abbrev-commit) i np. GitHub (w widoku listy commitów pokazywana jest tylko krótka wiadomość, a reszta rozwija się po kliknięciu). Git oddziela streszczony opis commitu od jego pełnej treści za pomocą jednej pustej linii.

Oto skrótowy, kontretny opis Twojego commitu

Tutaj możesz zawrzeć długi opis, w którym przykładowo omówisz przyczyny
wprowadzenia danej zmiany i jej konsekwencje dla projektu. Może być to
przydatne szczególnie dla Twoich współpracowników.

Na koniec kilka dodatkowych (częściowo subiektywnych, lecz popartych obserwacjami) zaleceń dotyczących formy samej wiadomości:

  • żadna z linii w opisie commita nie powinna przekraczać 72 znaków (konsola ma domyślnie 80 znaków szerokości)
  • rozpocznij opis od wielkiej litery
  • na końcu pierwszej linii commitu nie stawiaj kropki
  • użyj trybu rozkazującego (zwłaszcza w języku angielskim; powód - git używa takiego samego trybu dla wiadomości generowanych przez siebie, na przykład "Merge a from remote b")

Weryfikacja commitu

Przed zatwierdzeniem dowolnego commitu, przejrzyj go - najlepiej dwa razy. Służy do tego oczywiście diff, czyli wykaz wszystkich zmian poczynionych w ramach danego commmitu. Generalnie do jego wyświetlenia służy komenda git diff (trudne, prawda?), ale oczywiście jest haczyk. Domyślnie nie pokazuje ona zawartości plików właśnie dodanych do repozytorium, a jedynie zmiany w przestrzeni roboczej (przed wykonaniem git add).

Metodą uniwersalną jest wydanie komendy git add . (lub git add . --all dla gita 1.x - wszystko, aby uwzględnić też pliki właśnie usuwane z repozytorium) a następnie git diff --cached. Po tych dwóch poleceniach dostaniemy pełną listę zmian, zarówno w dodawanych, zmienianych, jak i usuwanych plikach - pozwala to zaoszczędzić naprawdę wielu pomyłek w kodzie.

Łączenie (squashowanie) commitów

Łączenie kilku commitów w jeden, zwłaszcza po ich wysłaniu do zdalnego repozytorium (remote), zaburza poniekąd historię zmian i może prowadzić do tego, że system kontroli wersji nie będzie spełniał swojej roli. Zachodzą jednak sytuacje, w których może być to przydatne, a nawet wymagane przez innych developerów. Najczęściej dzieje się tak w wypadku Pull Requestów lub ich odpowiedników z serwisów innych niż GitHub.

Powodem, dla którego w tej sytuacji możemy użyć squashowania (dosł. zgniatania) commitów jest zachodzący workflow. Zmiany są wykonywane w obrębie naszego własnego repozytorium (forka) i wysyłane jako propozycja zmian dla jakiegoś projektu. Bardzo często ludzie zarządzający tym projektem będą mieli do nas pewne uwagi i wskazówki, w związku z czym do Pull Requesta dołączają kolejne commity. Warto je na końcu połączyć, bo Pull Request z założenia powinien obejmować jedną funkcjonalność, a więc często być też jednym commitem (jak pisałem na początku artykułu). Kilka różnych metod squashowania commitów znajdziecie w tym wątku na SO.

Zakończenie

Mimo, że opisałem tutaj całkiem sporo założeń i może się wydawać, że teraz wysłanie dobrego commita miałoby zajmować wieczność i oznaczać sprawdzanie każdego punktu z listy, to nie ma czym się przejmować. Większość z wniosków wyciągniętych w artykule jest bardzo logiczna i nie powinna być dla Was niczym nowym, a reszta szybko wejdzie w nawyk. Należy brać poprawkę na dozę subiektywizmu zawartą w tym tekście.