Hur man skriver ett Git Commit-meddelande

Innehåll: Introduktion | De sju reglerna | Tips

Inledning: Om du bläddrar igenom loggen för ett slumpmässigt Git-arkiv kommer du förmodligen att upptäcka att dess commit-meddelanden är mer eller mindre en enda röra. Ta till exempel en titt på de här pärlorna från mina tidiga dagar med att lägga in 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

Yikes. Jämför det med dessa nyare commits från samma arkiv:

$ 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

Vad vill du hellre läsa?

Det förstnämnda varierar i längd och form; det sistnämnda är kortfattat och konsekvent.
Det förstnämnda är vad som händer som standard; det sistnämnda händer aldrig av en slump.

Och även om många arkivs loggar ser ut som det förstnämnda, finns det undantag. Linuxkärnan och Git i sig är bra exempel. Titta på Spring Boot, eller något arkiv som förvaltas av Tim Pope.

De som bidrar till dessa arkiv vet att ett välskrivet Git-överlämningsmeddelande är det bästa sättet att kommunicera sammanhanget kring en ändring till andra utvecklare (och även till deras framtida jag). En diff talar om vad som har ändrats, men det är bara meddelandet om överlämning som på ett korrekt sätt kan tala om varför. Peter Hutterer tar upp detta på ett bra sätt:

Att återskapa kontexten för ett kodstycke är slösaktigt. Vi kan inte undvika det helt och hållet, så våra ansträngningar bör gå till att minska det så mycket som möjligt. Commit-meddelanden kan göra exakt det och som ett resultat visar ett commit-meddelande om en utvecklare är en bra samarbetspartner.

Om du inte har funderat särskilt mycket på vad som gör ett bra Git commit-meddelande kan det vara så att du inte har tillbringat mycket tid med att använda git log och relaterade verktyg. Det finns en ond cirkel här: eftersom commit-historiken är ostrukturerad och inkonsekvent spenderar man inte mycket tid på att använda eller ta hand om den. Och eftersom den inte används eller tas om hand förblir den ostrukturerad och inkonsekvent.

Men en välskött logg är en vacker och användbar sak. git blame, revert, rebase, log, shortlog och andra underkommandon kommer till liv. Att granska andras commits och pull requests blir något som är värt att göra och som plötsligt kan göras självständigt. Att förstå varför något hände för månader eller år sedan blir inte bara möjligt utan också effektivt.

Ett projekts långsiktiga framgång vilar (bland annat) på dess underhållbarhet, och en underhållare har få verktyg som är mer kraftfulla än sitt projekts logg. Det är värt att ta sig tid att lära sig hur man tar hand om en sådan på rätt sätt. Det som till en början kan vara besvärligt blir snart en vana, och så småningom en källa till stolthet och produktivitet för alla inblandade.

I det här inlägget tar jag bara upp det mest grundläggande elementet för att hålla en hälsosam commit-historik: hur man skriver ett individuellt commit-meddelande. Det finns andra viktiga metoder som commit squashing som jag inte tar upp här. Kanske gör jag det i ett senare inlägg.

De flesta programmeringsspråk har väletablerade konventioner för vad som utgör idiomatisk stil, dvs. namngivning, formatering och så vidare. Det finns naturligtvis variationer på dessa konventioner, men de flesta utvecklare är överens om att det är mycket bättre att välja en och hålla sig till den än det kaos som uppstår när alla gör sin egen grej.

En grupps inställning till sin commit log bör inte vara annorlunda. För att skapa en användbar revideringshistorik bör teamet först komma överens om en konvention för commit-meddelanden som definierar åtminstone följande tre saker:

Stil. Markeringssyntax, omslagsmarginaler, grammatik, kapitalisering, interpunktion. Skriv ut dessa saker, ta bort gissningar och gör det hela så enkelt som möjligt. Slutresultatet blir en anmärkningsvärt konsekvent loggbok som inte bara är ett nöje att läsa utan som faktiskt läses regelbundet.

Innehåll. Vilken typ av information ska commitmeddelandets huvuddel (om någon) innehålla? Vad ska den inte innehålla?

Metadata. Hur ska problemspårnings-ID, pull request-nummer etc. refereras?

Tyvärr finns det väletablerade konventioner för vad som utgör ett idiomatiskt Git-överföringsmeddelande. Många av dem förutsätts faktiskt i det sätt på vilket vissa Git-kommandon fungerar. Det finns inget du behöver uppfinna på nytt. Följ bara de sju reglerna nedan så är du på väg att göra en commit som ett proffs.

De sju reglerna för ett bra Git commit-meddelande

Håll dig i minnet: Allt detta har sagts förut.

  1. Separera ämnesraden från kroppen med en tom rad
  2. Begränsar ämnesraden till 50 tecken
  3. Gör ämnesraden stor
  4. Avslut inte ämnesraden med ett punktstreck
  5. Använd imperativstämning i ämnesraden
  6. Begränsar kroppen till 72 tecken
  7. Använd kroppen för att förklara vad och varför vs. how

Till exempel:

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

Separera ämne från kropp med en tom rad

Från git commit manpage:

Trots att det inte är ett krav är det en bra idé att börja commit-meddelandet med en enda kort (mindre än 50 tecken) rad som sammanfattar ändringen, följt av en tom rad och sedan en mer ingående beskrivning. Texten fram till den första tomma raden i ett commitmeddelande behandlas som committiteln, och den titeln används i hela Git. Git-format-patch(1) förvandlar till exempel en commit till ett e-postmeddelande och använder titeln på ämnesraden och resten av commit i brödtexten.

För det första kräver inte alla commit både ett ämne och en brödtext. Ibland räcker det med en enda rad, särskilt när ändringen är så enkel att ingen ytterligare kontext behövs. Till exempel:

Fix typo in introduction to user guide

Inget mer behöver sägas; om läsaren undrar vad felskrivningen var kan hon helt enkelt ta en titt på själva ändringen, dvs. använda git show eller git diff eller git log -p.

Om du bekräftar något sådant här på kommandoraden är det enkelt att använda -m-alternativet till git commit:

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

Men när en bekräftelse förtjänar lite mer förklaring och sammanhang behöver du skriva en body. Till exempel:

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.

Commit-meddelanden med body är inte så lätta att skriva med alternativet -m. Det är bättre att skriva meddelandet i en riktig textredigerare. Om du inte redan har en editor inställd för användning med Git på kommandoraden kan du läsa det här avsnittet i Pro Git.

I vilket fall som helst lönar sig separationen av ämne och kropp när du bläddrar i loggen. Här är den fullständiga loggposten:

$ 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.

Och nu git log --oneline, som bara skriver ut ämnesraden:

$ git log --oneline42e769 Derezz the master control program

Och, git shortlog, som grupperar commits efter användare, och som återigen bara visar ämnesraden för att göra det mer kortfattat:

$ 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

Det finns ett antal andra sammanhang i Git där distinktionen mellan ämnesraden och kroppen spelar in – men ingen av dem fungerar som den ska utan den tomma raden emellan.

Begränsar ämnesraden till 50 tecken

50 tecken är ingen hård gräns, bara en tumregel. Att hålla ämnesraderna på denna längd säkerställer att de är läsbara och tvingar författaren att tänka en stund på det mest koncisa sättet att förklara vad som händer.

Tip: Om du har svårt att sammanfatta kan det hända att du gör för många ändringar på en gång. Sträva efter atomic commits (ett ämne för ett separat inlägg).

GitHubs användargränssnitt är fullt medveten om dessa konventioner. Den varnar dig om du överskrider gränsen på 50 tecken:

Och förkortar alla ämnesrader som är längre än 72 tecken med en ellips:

Så sikta på 50 tecken, men betrakta 72 som den hårda gränsen.

Gör ämnesraden stor

Det här är lika enkelt som det låter. Börja alla ämnesrader med en stor bokstav.

Till exempel:

  • Accelerera till 88 miles per timme

Istället för:

  • accelerera till 88 miles per timme

Avslut inte ämnesraden med ett punktstreck

Spårande skiljetecken är onödiga i ämnesrader. Dessutom är utrymmet dyrbart när du försöker hålla dem till 50 tecken eller mindre.

Exempel:

  • Öppna dörrarna till kapselutrymmet

Istället för:

  • Öppna dörrarna till kapselutrymmet.

Använd imperativstämning i ämnesraden

Imperativstämning betyder bara ”talat eller skrivet som om man gav ett kommando eller en instruktion”. Några exempel:

  • Rengör ditt rum
  • Stäng dörren
  • Ta ut soporna

Var och en av de sju regler som du läser om just nu är skrivna i imperativ (”Slå in kroppen vid 72 tecken” osv.).

Impedativet kan låta lite ohövligt; det är därför vi inte ofta använder det. Men det är perfekt för ämnesrader för Git commit. En anledning till detta är att Git självt använder imperativet när det skapar en commit för din räkning.

Till exempel lyder standardmeddelandet som skapas när du använder git merge:

Merge branch 'myfeature'

Och när du använder git revert:

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

Och när du klickar på knappen ”Merge” (sammanfoga) på en GitHub pull request:

Merge pull request #123 from someuser/somebranch

Så när du skriver dina commit-meddelanden i imperativ följer du Gits egna inbyggda konventioner. Till exempel:

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

Att skriva på det här sättet kan vara lite besvärligt i början. Vi är mer vana vid att tala i indikativstämning, som handlar om att rapportera fakta. Det är därför som commit-meddelanden ofta slutar så här:

  • Rättade fel med Y
  • Ändrade beteende för X

Och ibland skrivs commit-meddelanden som en beskrivning av deras innehåll:

  • Mer rättelser för trasiga saker
  • Söta nya API-metoder

För att undanröja all förvirring finns här en enkel regel för att göra rätt varje gång.

En korrekt utformad Git commit-ämnesrad bör alltid kunna komplettera följande mening:

  • Om den tillämpas kommer denna commit att din ämnesrad här

Till exempel:

  • Om det tillämpas kommer denna commit att refaktorisera delsystem X för att öka läsbarheten
  • Om det tillämpas kommer denna commit att uppdatera dokumentationen för att komma igång
  • Om det tillämpas kommer denna commit att ta bort föråldrade metoder
  • Om det tillämpas kommer denna commit att släppa version 1.0.0
  • Om det tillämpas kommer denna commit att slå ihop pull request #123 från user/branch

Bemärk hur detta inte fungerar för de andra icke-imperativa formerna:

  • Om det tillämpas kommer detta commit att fixa fel med Y
  • Om det tillämpas kommer detta commit att ändra beteende för X
  • Om det tillämpas kommer detta commit att fixa fler trasiga saker
  • Om det tillämpas kommer detta commit att ge nya API-metoder

Håll dig till minnet: Användning av imperativ är viktigt endast i ämnesraden. Du kan lätta på denna begränsning när du skriver kroppen.

Varva kroppen vid 72 tecken

Git vänder aldrig om text automatiskt. När du skriver kroppen av ett commitmeddelande måste du tänka på dess högra marginal och omsluta texten manuellt.

Rekommendationen är att göra detta vid 72 tecken, så att Git har gott om utrymme för att ringa in text och samtidigt hålla allt under 80 tecken totalt.

En bra textredigerare kan vara till hjälp här. Det är lätt att konfigurera Vim, till exempel, så att text omsluts med 72 tecken när du skriver en Git-överföring. Traditionellt sett har IDE:er dock varit usla på att ge smart stöd för textomslag i commit-meddelanden (även om IntelliJ IDEA i de senaste versionerna äntligen har blivit bättre på detta).

Använd kroppen för att förklara vad och varför jämfört med. how

Denna commit från Bitcoin Core är ett bra exempel på att förklara vad som ändrades och varför:

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.

Ta en titt på hela diff-filen och tänk på hur mycket tid författaren sparar för sina kollegor och framtida committenter genom att ta sig tid att ge detta sammanhang här och nu. Om han inte gjorde det skulle det förmodligen vara förlorat för alltid.

I de flesta fall kan man utelämna detaljer om hur en ändring har gjorts. Kod är i allmänhet självförklarande i detta avseende (och om koden är så komplex att den måste förklaras i prosa är det vad källkommentarer är till för). Fokusera bara på att klargöra skälen till varför du gjorde ändringen från början – hur saker och ting fungerade före ändringen (och vad som var fel med det), hur de fungerar nu och varför du bestämde dig för att lösa det på det sätt du gjorde.

Den framtida underhållare som tackar dig kan vara du själv!

Tipsen

Lär dig att älska kommandoraden. Lämna IDE bakom dig.

För lika många anledningar som det finns Git-underkommandon är det klokt att omfamna kommandoraden. Git är vansinnigt kraftfullt; IDE:er är det också, men var och en på olika sätt. Jag använder ett IDE varje dag (IntelliJ IDEA) och har använt andra i stor utsträckning (Eclipse), men jag har aldrig sett någon IDE-integration för Git som kan börja matcha kommandoradens lätthet och kraft (när du väl kan den).

Vissa Git-relaterade IDE-funktioner är ovärderliga, som att anropa git rm när du raderar en fil och att göra rätt saker med git när du byter namn på en. Där allt faller isär är när du börjar försöka lägga in, sammanfoga, återbasera eller göra sofistikerade historikanalyser med hjälp av IDE.

När det gäller att använda Git fullt ut är det kommandoraden som gäller hela vägen.

Håll i minnet att oavsett om du använder Bash, Zsh eller Powershell så finns det tabkompletteringsskript som tar bort en stor del av smärtan med att komma ihåg underkommandon och växlar.

Läs Pro Git

Boken Pro Git finns tillgänglig gratis på nätet, och den är fantastisk. Utnyttja den!

Header image credit: xkcd

Lämna ett svar

Din e-postadress kommer inte publiceras.