Conventional Commits, to standard który określa format commit message w jasny sposób. W taki, który jest czytelny zarówno dla człowieka jak i maszyn. Czytelny dla człowieka, bo łatwo prześledzić historię zmian w repozytorium. Dla maszyn, bo taki ustandaryzowany format można oprogramować.
Mógłbym rzecz, że polecenie git commit, to jedna z niepozornych komend git. Dzięki tej krótkiej, ale za to jak ważnej wiadomości można dużo się dowiedzieć. Między innymi to, co autor miał na myśli.
Niestety, realia są, jakie są i w większości projektów ta wiadomość nic nie mówi. Czasami odnoszę wrażenie, że ludzie muszą tam wpisać cokolwiek, bo to jedyny wymóg by opublikować kawałek swojej pracy. W niektórych projektach jedynym wymogiem jest umieszczenie numeru ticketa. Commity o treści refactor, added XXX czy cr fixes, to smutna i nudna rzeczywistość.
A co gdyby można było to poprawić? Gdyby można było czytać historię zmian w naszym kodzie tylko za pomocą historii commitów – bez zaglądania w szczegóły? Gdyby trzeba było zrobić revert kodu na produkcji – wiedziałbyś który commit trzeba usunąć bez zaglądania w szczegóły?
Okazuje się, że jest pewien standard, który porządkuje te wiadomość w bardzo prosty prosty sposób. Cytując pewną znaną scenę, z polskiego kultowego filmu: i teraz wchodzę ja, cały na biało.
Conventional Commits
Conventional Commits określa listę typów commitów odpowiadających za faktyczne zmiany w kodzie. Commit message wygląda następująco:
type(scope): description
Conventional Commits – type
Type, jak sama nazwa określna rodzaj zmian w kodzie. Czy wprowadzone zmiany to nowa funkcjonalność a może jakaś poprawka? Spójrz na poniższą listę typów wiadomości jakie można użyć:
feat– nowa funkcjonalnośćfix– poprawka do istniejącej funkcjonalnościdocs– zmiany tylko w dokumentacjichore– zmiany które nie mają wpływu z zawartość kodu źródłowego czy testów (np. aktualizacja paczek)refactor– zmiany, które nie są zarówno poprawkami jak i nowymi funkcjonalnościamitests– wszystko co związane z testami (dodawanie, edycja)perf– zmiany w kodzie usprawniające wydajność,styles– wszelkiego rodzaju formatorania kodu, białe znaki, przecinki czy brakujące średnikici– zmiany na potrzeby CI (konfigi, skrypty),build– zmiany wpływające na proces builda,revert– revert ostanich zmian
Conventional Commits – scope
W dokumentacji jest informacja, że powinien być to rzeczownik określający miejsce w kodzie np. validator, form, itd. Z doświadczenia wiem (ilość i złożoność projektów), że nie zawsze jest idealne rozwiązanie. Ta „sugestia” sprawdza się bardzo dobrze w małych projektach gdzie funkcjonalność nie jest skomplikowana.
Problem pojawia się wtedy gdy nad projektem pracuje duża liczba osób. W takim przypadkach określenie scope jest problematyczne.
Dlatego w zespołach w których pracuję używany numeru ticketa jako scope. Dodatkową zaletą tego podejścia jest to, że gdy korzystasz z takiego systemu jak JIRA, to taki numer jest automatycznie linkowany. Podczas generowania CHANGELOGu taki dodatek jest bardzo fajny.
scope jest opcjonalny.
Conventional Commits – description
Najistotniejsza część. Trzeba się na prawdę postarać by napisać dobry komentarz. Na szczęście tylko na początku. Im lepiej (epicko) opiszesz swoje zmiany, tym lepiej dla wszystkich.
Najlepszy przykład z jakim sam się spotkasz to commity o wdzięcznej treści jak: cr fixes. Ile razy to widzisz? Wiesz co kryje się za tym magicznym zwrotem? A gdyby można było napisać, że zmieniłeś sposób wywołania funkcji lub poprawiłeś literówkę w nazwie zmiennej albo usunąłeś wywałania razem z definicją jakieś metody? Czy takie wiadomości nie są czytelniejsze?
Idealna wiadomość to powinna:
- Zwięźle opisywać zmiany jakie zaszły w kodzie.
- Być w trybie rozkazującym w czasie teraźniejszym (ang. present tense). Oznacza to, że nie powinno być czegoś takiego jak
added polish translationslubadding polish translationstylkoadd polish translations.
BREAKING CHANGE (łamiące zmiany)
Warto jeszcze wspomnieć, że nasze niektóre zmiany mogą być łamiące (np. w API). Dlatego warto jeszcze wspomnieć o footerze commita. Jest to idealne miejsce, w którym możemy oznaczyć „łamiące zmiany”. Footer wiadomości powinien być oddzielny nową linią i praktyce wygląda następująco:
feat: allow provided config object to extend other configs BREAKING CHANGE: `extends` key in config file is now used for extending other config files
Kilka przykładów z wykorzystujących Conventional Commits:
docs: add short circuit to hook example (#665) docs: fix typo in code inside README.md (#662) fix(deps): update dependency cz-conventional-changelog to v3 (#654) fix(deps): update dependency lodash to v4.17.15 (#652) chore: update mocha, other dev deps (#653) fix(node): remove node 6 and 8 support (#649) chore(deps): update dependency semantic-release to v15.13.18 (#644) fix: update dependencies for security (#645) fix(deps): update dependency lodash to v4.17.14 [security] (#641) docs: highlight pre-requisties and bubble up related sections (#613) fix(cli): allow argv to be overridden in bootstrap (#621) feat(cli): implement --hook option for git hooks integration (#615) tests(ED-439): add tests for Modal component styles: format code with new prettier config
Ale po co to wszystko?!
- Conventional Commits wprowadza porządek w commitach.
- Wbrew pozorom poprawia komunikację w projekcie (zespole).
- Pozwala wygenerować CHANGELOG (historię zmian).
- Jest możliwość sprawdzania czy commit message jest poprawny (git hook pre-commit).
- W bardzo łatwy sposób można określić które zmiany w kodzie mogę być opublikowane jako major, minor czy patch (jeśli korzysta się z Semantic Versioning –
major.minor.patch).
W Conventional Commits jest jeden problem – Ty!
Oczywiście jest jeden problem w tym wszystkim. Jest to oczywiście czynnik ludzki. Developerzy przede wszystkim muszą chcieć dawać lepsze opisy i rozumieć jakiego rodzaju wprowadzili zmiany w kodzie. Tak więc przed każdym commitem trzeba się dodatkowo „wysilić”. Dobra wiadomość jest taka, że szybko to wchodzi w krew.
Przydatne linki:
- Conventional Commits – oficjalna dokumentacja
- commitlint (nodejs) – pozwala sprawdzić czy commit jest poprawny
- semantic release (nodejs) – automatyzuje proces publikowania i release’owania paczek
- generate-changelog (nodejs) – generuje changelog na podstawie commitów
- commitizen (nodejs) – pomaga w tworzeniu commitów poprzez prompty w konsoli
- Conventional commit for php – libka znajomego do sprawdzania poprawności commit message (WIP)