Como escrever uma mensagem de compromisso Git

Conteúdo: Introdução | As Sete Regras | Dicas

Introdução: Porque as boas mensagens de commit são importantes

Se você navegar pelo log de qualquer repositório Git aleatório, você provavelmente descobrirá que suas mensagens de commit são mais ou menos uma bagunça. Por exemplo, dê uma olhada nestas gemas dos meus primeiros dias de submissão para 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. Compara isso com estes commits mais recentes do mesmo repositório:

$ 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

Que preferes ler?

A primeira varia em comprimento e forma; a segunda é concisa e consistente.
A primeira é o que acontece por defeito; a segunda nunca acontece por acidente.

Embora muitos logs de repositórios se pareçam com os primeiros, existem excepções. O kernel do Linux e o próprio Git são grandes exemplos. Veja o Spring Boot, ou qualquer repositório gerenciado por Tim Pope.

Os contribuidores desses repositórios sabem que uma mensagem de commit do Git bem elaborada é a melhor maneira de comunicar o contexto sobre uma mudança aos colegas desenvolvedores (e de fato aos seus futuros “eus”). Um diff lhe dirá o que mudou, mas somente a mensagem de commit pode lhe dizer apropriadamente o porquê. Peter Hutterer faz bem este ponto:

Restabelecer novamente o contexto de um pedaço de código é um desperdício. Não podemos evitá-lo completamente, por isso os nossos esforços devem ir no sentido de o reduzir o mais possível. Mensagens de commit podem fazer exatamente isso e como resultado, uma mensagem de commit mostra se um desenvolvedor é um bom colaborador.

Se você não deu muita atenção ao que faz uma grande mensagem de commit do Git, pode ser que você não tenha gasto muito tempo usando git log e ferramentas relacionadas. Há um ciclo vicioso aqui: como o histórico de commit é não-estruturado e inconsistente, não se gasta muito tempo usando ou cuidando dele. E porque não é usado ou cuidado, ele permanece não-estruturado e inconsistente.

Mas um bem cuidado com o log é uma coisa bonita e útil. git blame, revert, rebase, log, shortlog e outros sub-mandos ganham vida. Rever os compromissos dos outros e puxar pedidos torna-se algo que vale a pena fazer, e de repente pode ser feito de forma independente. Entender porque algo aconteceu há meses ou anos se torna não apenas possível, mas eficiente.

O sucesso de um projeto a longo prazo repousa (entre outras coisas) na sua capacidade de manutenção, e um mantenedor tem poucas ferramentas mais poderosas do que o log do seu projeto. Vale a pena dedicar algum tempo para aprender a cuidar de um de forma adequada. O que pode ser um incômodo no início logo se torna hábito, e eventualmente uma fonte de orgulho e produtividade para todos os envolvidos.

Neste post, estou me dirigindo apenas ao elemento mais básico para manter um histórico de comprometimento saudável: como escrever uma mensagem de comprometimento individual. Existem outras práticas importantes, como a de “commit squashing”, que eu não estou abordando aqui. Talvez eu faça isso num post posterior.

As principais linguagens de programação têm convenções bem estabelecidas sobre o que constitui estilo idiomático, ou seja, nomear, formatar e assim por diante. Há variações nestas convenções, claro, mas a maioria dos desenvolvedores concorda que escolher uma e aderir a ela é muito melhor do que o caos que acontece quando cada um faz a sua própria coisa.

A abordagem da equipe ao seu registro de commit não deve ser diferente. A fim de criar um histórico de revisões útil, as equipes devem primeiro concordar em uma convenção de mensagens de commit que defina pelo menos as três coisas seguintes:

Style. Sintaxe de Markup, margens de wrap, gramática, capitalização, pontuação. Soletrar estas coisas, remover a adivinhação, e tornar tudo tão simples quanto possível. O resultado final será um log notavelmente consistente que não é apenas um prazer de ler, mas que de facto é lido regularmente.

Conteúdo. Que tipo de informação deve conter o corpo da mensagem de compromisso (se houver)? O que não deve conter?

Metadata. Como deve ser referenciada a emissão de IDs de rastreamento, números de pedido, etc.?

Felizmente, existem convenções bem estabelecidas sobre o que faz uma mensagem de commit Git idiomática. De fato, muitas delas são assumidas na forma como certos comandos Git funcionam. Não há nada que você precise reinventar. Apenas siga as sete regras abaixo e você está a caminho de fazer commit como um pro.

As sete regras de uma grande mensagem de commit de Git

Calme-se em mente: Isto já foi tudo dito antes.

  1. Separar assunto do corpo com uma linha em branco
  2. Limite a linha do assunto a 50 caracteres
  3. Capitalizar a linha do assunto
  4. Não termine a linha do assunto com um ponto
  5. Utilize o humor imperativo na linha do assunto
  6. Embrulhe o corpo com 72 caracteres
  7. Utilize o corpo para explicar o quê e porquê vs. how

Por exemplo:

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

Separate subject from body with a blank line

From the git commit manpage:

Embora não seja necessário, é uma boa idéia começar a mensagem de commit com uma única linha curta (menos de 50 caracteres) resumindo a mudança, seguida por uma linha em branco e depois uma descrição mais completa. O texto até à primeira linha em branco de uma mensagem de submissão é tratado como o título da submissão, e esse título é usado em todo o Git. Por exemplo, Git-format-patch(1) transforma uma submissão em email, e usa o título na linha Assunto e o resto da submissão no corpo.

Primeiro, nem todas as submissões requerem tanto um assunto como um corpo. Às vezes uma única linha está bem, especialmente quando a mudança é tão simples que não é necessário mais nenhum contexto. Por exemplo:

Fix typo in introduction to user guide

Nada mais precisa ser dita; se o leitor se pergunta qual foi a digitação, ela pode simplesmente dar uma olhada na própria mudança, ou seja, usar git show ou git diff ou git log -p.

Se você está submetendo algo assim na linha de comando, é fácil usar a opção -m para git commit:

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

No entanto, quando uma submissão merece um pouco de explicação e contexto, você precisa escrever um corpo. Por exemplo:

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.

Comprometer mensagens com corpos não são tão fáceis de escrever com a opção -m. É melhor escrever a mensagem em um editor de texto apropriado. Se você ainda não tem um editor configurado para uso com Git na linha de comando, leia esta seção do Pro Git.

Em qualquer caso, a separação de assunto do corpo compensa ao navegar no log. Aqui está a entrada de log completa:

$ 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 agora git log --oneline, que imprime apenas a linha de assunto:

$ git log --oneline42e769 Derezz the master control program

Or, git shortlog, que agrupa commits por usuário, mostrando novamente apenas a linha de assunto para concisão:

$ 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

Existem vários outros contextos no Git onde a distinção entre linha de assunto e corpo é feita – mas nenhum deles funciona corretamente sem a linha em branco no meio.

Limite a linha de assunto a 50 caracteres

50 caracteres não é um limite difícil, apenas uma regra de ouro. Manter linhas de assunto com este comprimento garante que elas sejam legíveis, e força o autor a pensar por um momento sobre a maneira mais concisa de explicar o que está acontecendo.

Tip: Se você está tendo dificuldades para resumir, você pode estar cometendo muitas mudanças ao mesmo tempo. Procure por commits atômicos (um tópico para um post separado).

GitHub’s UI está totalmente ciente destas convenções. Ele irá avisá-lo se você ultrapassar o limite de 50 caracteres:

E irá truncar qualquer linha de assunto com mais de 72 caracteres com uma elipse:

Então dispare para 50 caracteres, mas considere 72 o limite difícil.

Capitalizar a linha de assunto

Isto é tão simples quanto parece. Comece todas as linhas de assunto com uma letra maiúscula.

Por exemplo:

  • Acelerar para 88 milhas por hora

Em vez de:

  • acelerar para 88 milhas por hora

Não termine a linha de assunto com um ponto

Pontuação de percurso é desnecessária nas linhas de assunto. Além disso, o espaço é precioso quando você está tentando mantê-los a 50 caracteres ou menos.

Exemplo:

  • Abra as portas do compartimento da cápsula

Em vez de:

  • Abra as portas do compartimento da cápsula.

Utilizar o humor imperativo na linha de assunto

O humor imperativo significa apenas “falado ou escrito como se estivesse a dar um comando ou instrução”. Alguns exemplos:

  • Limpe o seu quarto
  • Feche a porta
  • Tire o lixo para fora

Cada uma das sete regras que você está lendo neste momento estão escritas no imperativo (“Embrulhe o corpo em 72 caracteres”, etc.).

O imperativo pode soar um pouco rude; é por isso que não o usamos frequentemente. Mas é perfeito para as linhas de assunto do Git commit. Uma razão para isso é que o próprio Git usa o imperativo sempre que ele cria um commit em seu nome.

Por exemplo, a mensagem padrão criada quando usando git merge lê:

Merge branch 'myfeature'

E quando usando git revert:

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

Or quando clicando no botão “Merge” em um GitHub, puxa o pedido:

Merge pull request #123 from someuser/somebranch

Então quando você escreve suas mensagens de commit no imperativo, você está seguindo as próprias convenções embutidas do Git. Por exemplo:

  • Subsistema de reefator X para legibilidade
  • Atualizar documentação de início
  • Remover métodos depreciados
  • Largar versão 1.0.0

Escrever desta forma pode ser um pouco estranho no início. Estamos mais acostumados a falar no humor indicativo, que é tudo sobre relatar fatos. É por isso que as mensagens de commit frequentemente acabam por ler assim:

  • Bug fixo com Y
  • Comportamento de X

E por vezes as mensagens de commit são escritas como uma descrição do seu conteúdo:

  • Mais correcções para coisas quebradas
  • Passar novos métodos API

Para remover qualquer confusão, aqui está uma regra simples para acertar sempre.

Uma linha de assunto Git commit devidamente formada deve ser sempre capaz de completar a seguinte frase:

  • Se aplicada, esta commit irá sua linha de assunto aqui

Por exemplo:

  • Se aplicado, esta submissão irá refactorar o subsistema X para legibilidade
  • Se aplicado, esta submissão irá actualizar a documentação de início
  • Se aplicado, esta submissão irá remover métodos obsoletos
  • Se aplicado, esta submissão irá lançar a versão 1.0.0
  • Se aplicada, esta submissão irá fundir a requisição #123 do usuário/filial

Note como isto não funciona para as outras formas não-imperativas:

  • Se aplicado, esta submissão irá corrigir um bug com Y
  • Se aplicado, esta submissão irá mudar o comportamento do X
  • Se aplicado, esta submissão irá corrigir mais coisas quebradas
  • Se aplicado, esta submissão irá adoçar novos métodos da API

Remembrar: O uso do imperativo é importante apenas na linha de assunto. Você pode relaxar esta restrição quando estiver escrevendo o corpo.

Brulhe o corpo com 72 caracteres

Git nunca embrulha o texto automaticamente. Quando você escreve o corpo de uma mensagem de commit, você deve ter em mente sua margem direita, e embrulhar o texto manualmente.

A recomendação é fazer isso com 72 caracteres, para que Git tenha bastante espaço para recuar o texto enquanto ainda mantém tudo abaixo de 80 caracteres no total.

Um bom editor de texto pode ajudar aqui. É fácil de configurar o Vim, por exemplo, para embrulhar o texto a 72 caracteres quando você está escrevendo um commit do Git. Tradicionalmente, no entanto, IDEs têm sido terríveis em fornecer suporte inteligente para embrulhar texto em mensagens de commit (embora em versões recentes, IntelliJ IDEA finalmente ficou melhor sobre isso).

Utilizar o corpo para explicar o quê e por quê vs. how

Este commit do Bitcoin Core é um grande exemplo de explicação do que mudou e porquê:

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.

Dê uma vista de olhos na diff completa e pensa no tempo que o autor está a poupar aos teus colegas e futuros committers, tirando o tempo necessário para fornecer este contexto aqui e agora. Se ele não o fizesse, provavelmente estaria perdido para sempre.

Na maioria dos casos, você pode deixar de fora detalhes sobre como uma mudança foi feita. O código é geralmente auto-explicativo a este respeito (e se o código é tão complexo que precisa de ser explicado em prosa, é para isso que servem os comentários da fonte). Basta focar em deixar claro as razões pelas quais você fez a mudança em primeiro lugar – a maneira como as coisas funcionavam antes da mudança (e o que estava errado com isso), a maneira como funcionam agora, e porque você decidiu resolvê-la da maneira que você fez.

O futuro mantenedor que agradece a você mesmo!

Dicas

Aprenda a amar a linha de comando. Deixe o IDE para trás.

Por tantas razões quantos os subcomandos do Git, é sábio abraçar a linha de comando. Git é insanamente poderoso; IDEs também são, mas cada um de maneiras diferentes. Eu uso uma IDE todos os dias (IntelliJ IDEA) e tenho usado outras extensivamente (Eclipse), mas eu nunca vi integração IDE para Git que pudesse começar a combinar a facilidade e potência da linha de comando (uma vez que você saiba).

Certas funções IDE relacionadas a Git são inestimáveis, como chamar git rm quando você apaga um arquivo, e fazer as coisas certas com git quando você renomeia um. Onde tudo se desmorona é quando você começa a tentar fazer commit, merge, rebase, ou fazer análise sofisticada do histórico através do IDE.

Quando se trata de empunhar todo o poder do Git, é a linha de comando até o fim.

Lembrar que se você usa Bash ou Zsh ou Powershell, existem scripts de preenchimento de abas que tiram muito da dor de lembrar os subcomandos e switches.

Ler Pro Git

O livro Pro Git está disponível online de graça, e é fantástico. Aproveite!

Crédito da imagem do cabeçalho: xkcd

Deixe uma resposta

O seu endereço de email não será publicado.