Come scrivere un messaggio di commit di Git

Contenuti: Introduzione | Le sette regole | Suggerimenti

Introduzione: Perché i buoni messaggi di commit sono importanti

Se sfogli il log di un qualsiasi repository Git, probabilmente troverai che i suoi messaggi di commit sono più o meno un casino. Per esempio, date un’occhiata a queste gemme dei miei primi giorni di commit per Spring:

$ git log --oneline -5 --author cbeams --before "Fri Mar 26 2009"e5f4b49 Re-adding ConfigurationPostProcessorTests after its brief removal in r814. @Ignore-ing the testCglibClassesAreLoadedJustInTimeForEnhancement() method as it turns out this was one of the culprits in the recent build breakage. The classloader hacking causes subtle downstream effects, breaking unrelated tests. The test method is still useful, but should only be run on a manual basis to ensure CGLIB is not prematurely classloaded, and should not be run as part of the automated build.2db0f12 fixed two build-breaking issues: + reverted ClassMetadataReadingVisitor to revision 794 + eliminated ConfigurationPostProcessorTests until further investigation determines why it causes downstream tests to fail (such as the seemingly unrelated ClassPathXmlApplicationContextTests)147709f Tweaks to package-info.java files22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils7f96f57 polishing

Che schifo. Confrontalo con questi commit più recenti dallo stesso repository:

$ git log --oneline -5 --author pwebb --before "Sat Aug 30 2014"5ba3db6 Fix failing CompositePropertySourceTests84564a0 Rework @PropertySource early parsing logice142fd1 Add tests for ImportSelector meta-data887815f Update docbook dependency and generate epubac8326d Polish mockito usage

Cosa preferiresti leggere?

Il primo varia in lunghezza e forma; il secondo è conciso e coerente.
Il primo è ciò che accade per default; il secondo non accade mai per caso.

Mentre i log di molti repository sembrano i primi, ci sono eccezioni. Il kernel Linux e Git stesso sono grandi esempi. Guardate Spring Boot, o qualsiasi repository gestito da Tim Pope.

I collaboratori di questi repository sanno che un messaggio di commit di Git ben fatto è il modo migliore per comunicare il contesto di un cambiamento agli altri sviluppatori (e anche a loro stessi in futuro). Un diff ti dirà cosa è cambiato, ma solo il messaggio di commit può dirti correttamente perché. Peter Hutterer fa bene questo punto:

Ristabilire il contesto di un pezzo di codice è uno spreco. Non possiamo evitarlo completamente, quindi i nostri sforzi dovrebbero andare a ridurlo il più possibile. I messaggi di commit possono fare esattamente questo e come risultato, un messaggio di commit mostra se uno sviluppatore è un buon collaboratore.

Se non hai pensato molto a cosa rende un grande messaggio di commit di Git, potrebbe essere il caso che tu non abbia passato molto tempo ad usare git log e strumenti correlati. C’è un circolo vizioso qui: poiché la cronologia dei commit è destrutturata e incoerente, non si passa molto tempo ad usarla o a prendersene cura. E poiché non viene usata o curata, rimane non strutturata e incoerente.

Ma un log ben curato è una cosa bella e utile. git blame, revert, rebase, log, shortlog e altri sottocomandi prendono vita. Rivedere i commit e le richieste di pull degli altri diventa qualcosa che vale la pena fare, e improvvisamente può essere fatto in modo indipendente. Capire perché qualcosa è successo mesi o anni fa diventa non solo possibile ma efficiente.

Il successo a lungo termine di un progetto si basa (tra le altre cose) sulla sua manutenibilità, e un manutentore ha pochi strumenti più potenti del log del suo progetto. Vale la pena di prendersi del tempo per imparare a curarne uno in modo appropriato. Quello che può essere una seccatura all’inizio diventa presto un’abitudine, e alla fine una fonte di orgoglio e produttività per tutte le persone coinvolte.

In questo post, sto affrontando solo l’elemento più basilare per mantenere una storia di commit sana: come scrivere un messaggio di commit individuale. Ci sono altre pratiche importanti come il commit squashing che non sto affrontando qui. Forse lo farò in un post successivo.

La maggior parte dei linguaggi di programmazione hanno convenzioni ben stabilite su ciò che costituisce uno stile idiomatico, cioè la denominazione, la formattazione e così via. Ci sono variazioni su queste convenzioni, naturalmente, ma la maggior parte degli sviluppatori concorda sul fatto che sceglierne una e attenersi ad essa è molto meglio del caos che ne deriva quando ognuno fa a modo suo.

L’approccio di un team al suo registro dei commit non dovrebbe essere diverso. Per creare una storia di revisione utile, i team dovrebbero prima concordare una convenzione per i messaggi di commit che definisca almeno le seguenti tre cose:

Stile. Sintassi del markup, margini di avvolgimento, grammatica, capitalizzazione, punteggiatura. Spiegare queste cose, rimuovere le congetture e rendere tutto il più semplice possibile. Il risultato finale sarà un registro notevolmente coerente che non solo è piacevole da leggere, ma che viene effettivamente letto regolarmente.

Contenuto. Che tipo di informazioni dovrebbe contenere il corpo del messaggio di commit (se c’è)? Cosa non dovrebbe contenere?

Metadati. Come dovrebbero essere referenziati gli ID di tracciamento dei problemi, i numeri delle richieste di pull, etc.?

Fortunatamente, ci sono convenzioni ben stabilite su cosa rende un messaggio di commit Git idiomatico. Infatti, molte di esse sono assunte nel modo in cui certi comandi Git funzionano. Non c’è niente da reinventare. Basta seguire le sette regole qui sotto e sei sulla strada per fare il commit come un professionista.

Le sette regole di un grande messaggio di commit di Git

Tieni a mente: Tutto questo è già stato detto prima.

  1. Separare l’oggetto dal corpo con una riga vuota
  2. Limitare la riga dell’oggetto a 50 caratteri
  3. Mettere in maiuscolo la riga dell’oggetto
  4. Non terminare la riga dell’oggetto con un punto
  5. Utilizzare l’imperativo nella riga dell’oggetto
  6. Avvolgere il corpo a 72 caratteri
  7. Utilizzare il corpo per spiegare cosa e perché vs. come

Per esempio:

Summarize changes in around 50 characters or lessMore detailed explanatory text, if necessary. Wrap it to about 72characters or so. In some contexts, the first line is treated as thesubject of the commit and the rest of the text as the body. Theblank line separating the summary from the body is critical (unlessyou omit the body entirely); various tools like `log`, `shortlog`and `rebase` can get confused if you run the two together.Explain the problem that this commit is solving. Focus on why youare making this change as opposed to how (the code explains that).Are there side effects or other unintuitive consequences of thischange? Here's the place to explain them.Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary hereIf you use an issue tracker, put references to them at the bottom,like this:Resolves: #123See also: #456, #789

Separare il soggetto dal corpo con una riga vuota

Dalla git commit manpage:

Anche se non è richiesto, è una buona idea iniziare il messaggio di commit con una singola riga breve (meno di 50 caratteri) che riassume il cambiamento, seguita da una riga vuota e poi da una descrizione più approfondita. Il testo fino alla prima riga vuota in un messaggio di commit è trattato come il titolo del commit, e quel titolo è usato in tutto Git. Per esempio, Git-format-patch(1) trasforma un commit in una email, e usa il titolo nella riga dell’oggetto e il resto del commit nel corpo.

In primo luogo, non tutti i commit richiedono sia un oggetto che un corpo. A volte una singola linea va bene, specialmente quando il cambiamento è così semplice che non è necessario un ulteriore contesto. Per esempio:

Fix typo in introduction to user guide

Non c’è bisogno di dire altro; se il lettore si chiede quale fosse il refuso, può semplicemente dare un’occhiata al cambiamento stesso, cioè usare git show o git diff o git log -p.

Se state facendo un commit come questo sulla linea di comando, è facile usare l’opzione -m per git commit:

$ git commit -m"Fix typo in introduction to user guide"

Tuttavia, quando un commit merita un po’ di spiegazione e contesto, dovete scrivere un corpo. Per esempio:

Derezz the master control programMCP turned out to be evil and had become intent on world domination.This commit throws Tron's disc into MCP (causing its deresolution)and turns it back into a chess game.

I messaggi di commit con corpo non sono così facili da scrivere con l’opzione -m. È meglio scrivere il messaggio in un editor di testo appropriato. Se non hai già un editor impostato per l’uso con Git a linea di comando, leggi questa sezione di Pro Git.

In ogni caso, la separazione dell’oggetto dal corpo ripaga quando si scorre il log. Ecco la voce completa del log:

$ git logcommit 42e769bdf4894310333942ffc5a15151222a87beAuthor: Kevin Flynn <[email protected]>Date: Fri Jan 01 00:00:00 1982 -0200 Derezz the master control program MCP turned out to be evil and had become intent on world domination. This commit throws Tron's disc into MCP (causing its deresolution) and turns it back into a chess game.

E ora git log --oneline, che stampa solo l’oggetto:

$ git log --oneline42e769 Derezz the master control program

Oppure, git shortlog, che raggruppa i commit per utente, mostrando di nuovo solo l’oggetto per concisione:

$ git shortlogKevin Flynn (1): Derezz the master control programAlan Bradley (1): Introduce security program "Tron"Ed Dillinger (3): Rename chess program to "MCP" Modify chess program Upgrade chess programWalter Gibbs (1): Introduce protoype chess program

Ci sono molti altri contesti in Git dove la distinzione tra oggetto e corpo entra in gioco, ma nessuno di questi funziona correttamente senza la linea bianca in mezzo.

Limitare la riga dell’oggetto a 50 caratteri

50 caratteri non è un limite rigido, solo una regola empirica. Mantenere le righe dell’oggetto a questa lunghezza assicura che siano leggibili, e costringe l’autore a pensare per un momento al modo più conciso di spiegare cosa sta succedendo.

Suggerimento: Se stai avendo difficoltà a riassumere, potresti aver commesso troppi cambiamenti in una volta. Sforzati di fare commit atomici (un argomento per un post separato).

L’interfaccia utente di GitHub è pienamente consapevole di queste convenzioni. Ti avvertirà se superi il limite di 50 caratteri:

E troncherà qualsiasi oggetto più lungo di 72 caratteri con un ellissi:

Quindi spara per 50 caratteri, ma considera 72 il limite massimo.

Capitalizzare l’oggetto

Questo è semplice come sembra. Inizia tutte le righe dell’oggetto con una lettera maiuscola.

Per esempio:

  • Accelerare a 88 miglia all’ora

Invece di:

  • accelerare a 88 miglia all’ora

Non finire la riga dell’oggetto con un punto

La punteggiatura finale non è necessaria nelle righe dell’oggetto. Inoltre, lo spazio è prezioso quando stai cercando di tenerli a 50 caratteri o meno.

Esempio:

  • Aprire le porte dell’alloggiamento della capsula

Invece di:

  • Aprire le porte dell’alloggiamento della capsula.

Utilizza l’imperativo nell’oggetto

L’imperativo significa semplicemente “detto o scritto come se desse un comando o un’istruzione”. Alcuni esempi:

  • Pulisci la tua stanza
  • Chiudi la porta
  • Porta fuori la spazzatura

Ognuna delle sette regole che stai leggendo ora è scritta all’imperativo (“Avvolgi il corpo a 72 caratteri”, ecc.).

L’imperativo può suonare un po’ rude; ecco perché non lo usiamo spesso. Ma è perfetto per l’oggetto dei commit di Git. Una ragione è che Git stesso usa l’imperativo ogni volta che crea un commit per tuo conto.

Per esempio, il messaggio di default creato quando si usa git merge recita:

Merge branch 'myfeature'

E quando si usa git revert:

Revert "Add the thing with the stuff"This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.

O quando si clicca il pulsante “Merge” su una richiesta di pull di GitHub:

Merge pull request #123 from someuser/somebranch

Quindi quando scrivi i tuoi messaggi di commit in imperativo, stai seguendo le convenzioni incorporate di Git. Per esempio:

  • Refactor subsystem X for readability
  • Update getting started documentation
  • Remove deprecated methods
  • Release version 1.0.0

Scrivere in questo modo può essere un po’ imbarazzante all’inizio. Siamo più abituati a parlare nell’umore indicativo, che è tutto per riportare i fatti. Ecco perché i messaggi di commit spesso finiscono per leggere così:

  • Fissato bug con Y
  • Modifica del comportamento di X

E a volte i messaggi di commit vengono scritti come una descrizione del loro contenuto:

  • Più correzioni per cose rotte
  • Dolci nuovi metodi API

Per eliminare ogni confusione, ecco una semplice regola per fare le cose bene ogni volta.

Una linea dell’oggetto di un commit Git formata correttamente dovrebbe sempre essere in grado di completare la seguente frase:

  • Se applicato, questo commit avrà il tuo oggetto qui

Per esempio:

  • Se applicato, questo commit rifattorizzerà il sottosistema X per la leggibilità
  • Se applicato, questo commit aggiornerà la documentazione iniziale
  • Se applicato, questo commit rimuoverà i metodi deprecati
  • Se applicato, questo commit rilascerà la versione 1.0.0
  • Se applicato, questo commit fonderà la richiesta di pull #123 dall’utente/branch

Nota come questo non funzioni per le altre forme non imperative:

  • Se applicato, questo commit sistemerà il bug con Y
  • Se applicato, questo commit cambierà il comportamento di X
  • Se applicato, questo commit sistemerà più cose rotte
  • Se applicato, questo commit addolcirà i nuovi metodi API

Ricordo: L’uso dell’imperativo è importante solo nell’oggetto. Puoi allentare questa restrizione quando stai scrivendo il corpo.

Wrap il corpo a 72 caratteri

Git non avvolge mai il testo automaticamente. Quando scrivi il corpo di un messaggio di commit, devi badare al suo margine destro, e avvolgere il testo manualmente.

La raccomandazione è di farlo a 72 caratteri, in modo che Git abbia molto spazio per far rientrare il testo pur mantenendo tutto sotto gli 80 caratteri complessivi.

Un buon editor di testo può aiutare. È facile configurare Vim, per esempio, per avvolgere il testo a 72 caratteri quando stai scrivendo un commit di Git. Tradizionalmente, comunque, gli IDE sono stati terribili nel fornire un supporto intelligente per il text wrapping nei messaggi di commit (anche se nelle ultime versioni, IntelliJ IDEA è finalmente migliorato su questo).

Usa il corpo per spiegare cosa e perché vs. Come

Questo commit da Bitcoin Core è un grande esempio di spiegazione di cosa è cambiato e perché:

commit eb0b56b19017ab5c16c745e6da39c53126924ed6Author: Pieter Wuille <[email protected]>Date: Fri Aug 1 22:57:55 2014 +0200 Simplify serialize.h's exception handling Remove the 'state' and 'exceptmask' from serialize.h's stream implementations, as well as related methods. As exceptmask always included 'failbit', and setstate was always called with bits = failbit, all it did was immediately raise an exception. Get rid of those variables, and replace the setstate with direct exception throwing (which also removes some dead code). As a result, good() is never reached after a failure (there are only 2 calls, one of which is in tests), and can just be replaced by !eof(). fail(), clear(n) and exceptions() are just never called. Delete them.

Date un’occhiata al diff completo e pensate a quanto tempo l’autore sta risparmiando ai colleghi e ai futuri committers prendendo il tempo di fornire questo contesto qui e ora. Se non lo facesse, probabilmente sarebbe perso per sempre.

Nella maggior parte dei casi, si possono tralasciare i dettagli su come è stato fatto un cambiamento. Il codice è generalmente auto-esplicativo in questo senso (e se il codice è così complesso da dover essere spiegato in prosa, è a questo che servono i commenti ai sorgenti). Concentratevi solo sul rendere chiare le ragioni per cui avete fatto il cambiamento in primo luogo – il modo in cui le cose funzionavano prima del cambiamento (e cosa c’era di sbagliato), il modo in cui funzionano ora, e perché avete deciso di risolvere il problema in quel modo.

Il futuro manutentore che vi ringrazia potrebbe essere voi stesso!

Tips

Imparate ad amare la riga di comando. Lasciati l’IDE alle spalle.

Per tante ragioni quanti sono i sottocomandi di Git, è saggio abbracciare la linea di comando. Git è follemente potente; anche gli IDE lo sono, ma ognuno in modi diversi. Io uso un IDE ogni giorno (IntelliJ IDEA) e ne ho usati altri estensivamente (Eclipse), ma non ho mai visto un’integrazione IDE per Git che potesse iniziare ad eguagliare la facilità e la potenza della linea di comando (una volta che la conosci).

Alcune funzioni IDE legate a Git sono inestimabili, come chiamare git rm quando cancelli un file, e fare le cose giuste con git quando ne rinomini uno. Dove tutto cade a pezzi è quando si inizia a cercare di fare commit, merge, rebase, o fare sofisticate analisi della storia attraverso l’IDE.

Quando si tratta di esercitare tutta la potenza di Git, è la linea di comando tutto il percorso.

Ricorda che se usi Bash o Zsh o Powershell, ci sono script di completamento delle schede che ti tolgono la fatica di ricordare i sottocomandi e gli interruttori.

Leggi Pro Git

Il libro Pro Git è disponibile online gratuitamente, ed è fantastico. Approfittatene!

Credito immagine intestazione: xkcd

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.