Poradniki Programowanie

Conventional Commits czyli jak tworzyć epickie commity w git

Pinterest LinkedIn Tumblr

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ści
  • docs – zmiany tylko w dokumentacji
  • chore – 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ściami
  • tests – 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 średniki
  • ci – 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 translations lub adding polish translations tylko add 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?!

  1. Conventional Commits wprowadza porządek w commitach.
  2. Wbrew pozorom poprawia komunikację w projekcie (zespole).
  3. Pozwala wygenerować CHANGELOG (historię zmian).
  4. Jest możliwość sprawdzania czy commit message jest poprawny (git hook pre-commit).
  5. 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:

Z tym „epickie” commity, to był trochę taki clickbait, ale w słusznej sprawie :).

Od ponad 10 lat jestem zaangażowany w świat elektroniki użytkowej, zdobywając szeroką wiedzę i doświadczenie w testowaniu oraz recenzowaniu najnowszych technologii. Moja kariera obejmuje pracę w wiodących firmach technologicznych, gdzie specjalizowałem się w rozwiązywaniu złożonych problemów technicznych oraz doradzaniu w kwestiach wyboru sprzętu. Na moim blogu publikuję dokładne poradniki oraz recenzje urządzeń takich jak smartfony, routery i słuchawki, oferując czytelnikom rzetelne informacje oparte na wieloletnim doświadczeniu i skrupulatnych testach. Moim celem jest dostarczanie treści, które pomagają w podejmowaniu świadomych decyzji zakupowych oraz pełnym wykorzystaniu możliwości nowoczesnej elektroniki.

komentarze 3

  1. Podawanie numeru zlecenia w commicie jest bardzo dobre i przydatne, ale nie jako scope. To zabija tę funkcję, która właśnie w dużych projektach pokazuje swoją siłę, bo łatwo się zorientować jakiego obszaru dotyka commit. Świetnym przykładem jest niemały przecież Angular: https://github.com/angular/angular/blob/master/CHANGELOG.md
    fix(forms) lub perf(compiler) mówią znacznie wiecej niż fix(jira-997) i dodatkowo pozwalają grupować i grepować 😉 po zakresach zmian.

    • Piotr Cichosz Odpowiedz

      Każdy merge z konfliktami wrzucam z flagą `–no-verify`

Skomentuj