Este post es una traducción del post How to Write a Git Commit Message, de @cbeams. Este trabajo se realizo en colaboración con @may_cabrera con permiso del autor original. Gracias a ambos!
Teniendo como excusa el formato y uso de comentarios en Git, el post nos muestra diversas formas de uso de este SCV, cuestiones metodológicas y de uso cotidiano de Git. Es muy interesante en muchos sentidos, así que les recomiendo tomarse un tiempo para su lectura.
Introducción | Las Siete Reglas | Consejos
Introducción: ¿Por qué es importante un buen mensaje de commit?
Si navegas el registro de un repositorio Git al azar probablemente encuentres que sus mensajes de commit son mas o menos un lío. Por ejemplo, echemos un vistazo a estas gemas de mis primeros días desarrollando 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 files 22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils 7f96f57 polishing
Uff. Compara eso con estos commits más recientes del mismo repositorio:
$ git log --oneline -5 --author pwebb --before "Sat Aug 30 2014" 5ba3db6 Fix failing CompositePropertySourceTests 84564a0 Rework @PropertySource early parsing logic e142fd1 Add tests for ImportSelector meta-data 887815f Update docbook dependency and generate epub ac8326d Polish mockito usage
¿Qué prefieres leer?
El primero cambia mucho en forma y tamaño; el último es conciso y consistente. El primero es lo que ocurre por defecto; el segundo nunca ocurre por accidente.
Mientras muchos logs de repositorio se ven como el primero, hay excepciones. El kernel de Linux y git en sí son grandes ejemplos. Mira Spring Boot, o cualquier repositorio gestionado por Tim Pope .
Los contribuidores de estos repositorios saben que un mensaje de commit git bien elaborado es la mejor manera de comunicar el contexto sobre un cambio al resto de los colegas desarrolladores (y ciertamente a sí mismos en el futuro). Un diff dirá lo que ha cambiado, pero sólo el mensaje de commit puede decir correctamente por qué. Peter Hutterer señala este punto así:
Restablecer el contexto de una pieza de código es un desperdicio. No podemos evitarlo por completo, entonces nuestros esfuerzos deberían estar en reducirlo [tanto] como sea posible. Los mensajes de commits pueden hacer exactamente eso, y como resultado, el mensaje muestra si un desarrollador es un buen colaborador.
Si no te has puesto a pensar en como generar un buen mensaje de commit de git, puede ser que no hayas gastado suficiente tiempo usando git log
y herramientas relacionadas. Existe un circulo vicioso aquí: Debido a que el historial de commit carece de estructura y consistencia, uno no gasta demasiado tiempo usándolo o teniéndolo en cuenta. Y debido a que no es usado o tenido en cuenta, este se mantiene sin estructura e inconsistente
Sin embargo un log cuidado es algo hermoso y útil. git blame
, revert
, rebase
, log
, shortlog
y otros subcomandos cobran vida. Revisar commits y pull requests de otros se convierte en algo digno de hacerse, y de repente se puede hacer de forma independiente. Entender porque algo sucedió hace meses o años se convierte en algo no solo posible, sino también eficiente.
El éxito a largo plazo de un proyecto descansa (entre otras cosas) en su facilidad de mantenimiento, y un desarrollador tiene pocas herramientas más poderosas que el log del proyecto. Vale la pena tomarse el tiempo para aprender cómo cargarlo de una forma adecuada. Lo que puede ser una molestia en un principio pronto se convierte en hábito, y, finalmente, un motivo de orgullo y productividad para todos los involucrados.
En este artículo, me refiero sólo al elemento más básico para mantener un historial saludable de commits: cómo escribir un mensaje de commit particular. Hay otras prácticas importantes como commit squashing que no serán tratadas aquí. Tal vez lo haré en un post posterior.
La mayoría de los lenguajes de programación tienen convenciones bien establecidas de lo que conforma el estilo idiomático, es decir, de nombres y formato y cosas así. Hay variaciones en estas convenciones, por supuesto, pero la mayoría de los desarrolladores están de acuerdo en que escoger una y ajustarse a ella es mucho mejor que el caos que se produce cuando todo el mundo lo hace a su manera.
El enfoque de un equipo con su log de commits no debería ser diferente. Con el fin de crear un historial de revisiones útiles, los equipos primero deben ponerse de acuerdo sobre una convención en mensajes de commits que defina al menos las siguientes tres cosas:
Estilo. La sintaxis, los límites de márgenes, la gramática, la capitalización, la puntuación. Explica claramente estos puntos, elimina la ambigüedad y hazlo lo más claro posible. El resultado final será un log muy coherente que no sólo será fácil de leer, sino que en realidad será leído de forma regular.
Contenido. ¿Qué tipo de información debe contener el cuerpo del mensaje de commit (si lo hay)? ¿Que no debería contener?
Metadatos. ¿Cómo se debe marcar el identificador de un issue, el numero de pull request, etc. ?
Afortunadamente, hay convenciones bien establecidas sobre lo que hace a un commit un mensaje idiomático De hecho, muchas de estas convenciones se asumen como la forma determinada en ciertos comandos git. No hay nada que necesite ser reinventado. Sólo tienes que seguir las siete reglas descritas abajo y ya estarás en camino para hacer commits como un profesional.
Las siete reglas de un gran mensaje de commit de git
- Separa el título del cuerpo con una línea en blanco
- Limita título a 50 caracteres
- Capitaliza el título
- No termines el título con un punto
- Utiliza el modo imperativo en el título
- Limita el cuerpo a 72 caracteres
- Utiliza el cuerpo para explicar qué y por qué vs cómo
Por ejemplo:
Resumir los cambios en aproximadamente 50 caracteres o menos Mas texto descriptivo, si es necesario. Límita aproximadamente a 72 caracteres o menos. En algunos contextos, la primera línea se establece como el asunto del commit y el resto es el cuerpo. La línea en blanco que separa el asunto del cuerpo es fundamental (a menos que se omita el cuerpo por completo); diversas herramientas como `log`,` y `shortlog` y `rebase` pueden confundirse si ejecuta los dos juntos. Explique el problema que el commit está resolviendo. Concéntrese en por qué usted está haciendo este cambio en comparación de cómo (pues el código explica esa parte). ¿Hay efectos secundarios u otros consecuencias poco intuitivas en este cambio? Este es el lugar para explicarlos. Otros párrafos vienen después de líneas en blanco. - Las viñetas o listas son aceptables también - Normalmente, un guión o asterisco se utiliza para la viñeta, precedido por un solo espacio, con líneas en blanco en el medio, pero hay diferentes convenciones Si utiliza un administrador de "issues", coloque referencias a ellos en la parte inferior, así: Resuelve: # 123 Consulte también: # 456, # 789
1. Separa el título del cuerpo del mensaje con una linea en blanco
Desde la página de manual de git commit
:
Aunque no es necesario, es una buena idea iniciar el mensaje de commit con una sola linea corta (menos de 50 caracteres) que resuma el cambio, seguida de una línea en blanco y, a continuación una descripción más completa. El texto sobre la primera línea en blanco en el mensaje del commit es tratado como el título del commit, y este título se utiliza en todo Git. Por ejemplo, git-format-patch (1) convierte un commit en un correo electrónico, y utiliza el título como asunto y el resto del commit en el cuerpo.
En primer lugar, no todo commit requiere tanto un título como un cuerpo. A veces una sola línea está bien, sobre todo cuando el cambio es tan simple que agregar más contexto no es necesario. Por ejemplo:
Fix typo in introduction to user guide
No es necesario decir nada más; si el lector se pregunta cuál fue el error tipográfico, simplemente puede echar un vistazo al cambio, mediante el uso de git show
o git diff
o git log -p
.
Si estás realizando un commit de este tipo en la línea de comandos, es fácil de usar el parametro -m
para git commit
:
$ git commit -m "Fix typo in introduction to user guide"
Sin embargo, cuando un commit merece un poco de explicación y contexto, es necesario escribir un cuerpo. Por ejemplo:
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.
Esto no es tan fácil de hacer con el parametro -m
, realmente necesitas de un editor adecuado. Si aún no dispones de un editor configurado para usar con git en la línea de comandos, lee esta sección de Pro Git .
En cualquier caso, la separación del titulo y cuerpo vale la pena cuando se navega por el log. Aquí está la entrada del log completo:
$ git log commit 42e769bdf4894310333942ffc5a15151222a87be Author: Kevin Flynn <kevin@flynnsarcade.com> 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.
Y ahora git log --oneline
, que imprime sólo la línea de título:
$ git log --oneline 42e769 Derezz the master control program
O, git shortlog
, que agrupa commits por usuario, de nuevo mostrando sólo la línea de titulo para ser conciso:
$ git shortlog Kevin Flynn (1): Derezz the master control program Alan Bradley (1): Introduce security program "Tron" Ed Dillinger (3): Rename chess program to "MCP" Modify chess program Upgrade chess program Walter Gibbs (1): Introduce protoype chess program
Hay numerosos contextos en git donde la distinción entre titulo y cuerpo entran en acción, pero ninguno de ellos funciona correctamente sin la línea en blanco en el medio.
2. Limita la línea de título a 50 caracteres
50 caracteres no es un límite estricto, sólo una regla práctica. Mantener las líneas de título en esta longitud asegura que sean legibles, y obliga al autor a pensar por un momento acerca de la forma más concisa de explicar lo que está enviando.
Consejo: Si estás teniendo dificultades para resumir, podrías estar realizando un commit con demasiados cambios de una sola vez. Intenta realizar commits atómicos (un tema para un post aparte).
La Interfaz de GitHub es plenamente consciente de estas convenciones. Te avisará si te pasas del límite de 50 caracteres:
Y truncará cualquier título de más de 69 caracteres con puntos suspensivos:
Así que intenta con 50 caracteres, pero considera como limite máximo 69.
3. Capitaliza la línea de título
Esto es tan simple como suena. Comienza todas las líneas de título con una letra mayúscula.
Por ejemplo:
- Acelerar a 88 millas por hora
En lugar de:
- acelerar a 88 millas por hora
4. No termines la línea de título con un punto
Usar puntuación es innecesario en las líneas de título. Además, el espacio es muy valioso cuando se está tratando de mantener en 50 caracteres o menos .
Ejemplo:
- Abrir la ranura de la puerta de la bodega
En lugar de:
- Abrir la ranura de la puerta de la bodega.
5. Utiliza el modo imperativo en la línea de título
Modo imperativo simplemente significa “hablar o escribir como si dieras una orden o instrucción”. Algunos ejemplos:
- Limpia tu habitación
- Cierra la puerta
- Saca la basura
Cada una de las siete reglas que estás leyendo ahora mismo están escritas en imperativo (“Limita el cuerpo a 72 caracteres”, etc).
El imperativo puede sonar un poco grosero; es por eso que a menudo no lo usamos. Pero es perfecto para el título de un commit. Una razón para esto es que git mismo utiliza el imperativo cada vez que crea un commit con tu nombre.
Por ejemplo, el mensaje predeterminado creado al utilizar git merge
es:
Merge branch 'myfeature'
Y al usar git revert
:
Revert "Add the thing with the stuff" This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.
O cuando se hace clic en el botón “Merge” en un pull request de Github
Merge pull request #123 from someuser / somebranch
Así que cuando escribas mensajes de commit en forma imperativa, realmente estás siguiendo las mismas convenciones incorporadas por git. Por ejemplo:
- Refactor subsystem X for readability
- Update getting started documentation
- Remove deprecated methods
- Release version 1.0.0
Escribir de esta manera puede ser un poco incómodo al principio. Estamos más acostumbrados a hablar en el modo indicativo, que está más relacionado para informar hechos. Es por eso que los mensajes de commits terminan leyéndose así:
- Fixed bug with Y
- Changing behaviour of X
Y a veces los mensajes se escriben como una descripción de su contenido:
- More fixes broken stuff
- Sweet new API methods
Para eliminar cualquier confusión, aquí hay simple regla para hacerlo bien cada vez.
Un commit de git formado adecuadamente siempre debe ser capaz de completar la siguiente frase:
- If applied, this commit will your subject line here
Por ejemplo:
- If applied, this commit will refactor subsystem X for readability
- If applied, this commit will update getting started documentation
- If applied, this commit will remove deprecated methods
- If applied, this commit will release version 1.0.0
- If applied, this commit will merge pull request #123 from user/branch
Observa cómo esto no funciona para las otras formas no imperativas:
- If applied, this commit will fixed bug with Y
- If applied, this commit will changing behaviour of X
- If applied, this commit will more fixes for broken stuff
- If applied, this commit will sweet new API methods
Recuerda: El uso del imperativo es importante sólo en el título. Puedes omitir esta restricción cuando estés escribiendo el cuerpo
6. Ajusta el cuerpo a 72 caracteres
Git nunca ajusta el texto automáticamente. Cuando escribes el cuerpo de un mensaje de commit, debes recordar su margen derecho, y ajustar el texto manualmente.
La recomendación es hacer esto en 72 caracteres, por lo que git tiene mucho espacio para indentar texto mientras se mantiene todo debajo de 80 caracteres en general.
Un buen editor de texto puede ayudar aquí. Es fácil de configurar en Vim, por ejemplo, para ajustar el texto a 72 caracteres cuando se está escribiendo un git commit. Tradicionalmente, sin embargo, los IDEs han sido desastrosos para proveer un apoyo inteligente en el ajuste de texto en los mensajes de commit (aunque en las versiones recientes, IntelliJ IDEA ha conseguido finalmente mejorar sobre esto).
7. Utilizar el cuerpo para explicar qué y porqué en lugar de como
Este commit del repositorio Bitcoin Core es un gran ejemplo de sobre explicar lo que ha cambiado y por qué:
commit eb0b56b19017ab5c16c745e6da39c53126924ed6 Author: Pieter Wuille <pieter.wuille@gmail.com> 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.
Echa una mirada al diff completo y sólo piensa en la cantidad de tiempo que el autor le ahorro a los colegas y futuros desarrolladores por tomarse el tiempo para proporcionar este contexto, aquí y ahora. Si él no lo hubiera hecho, esto probablemente se perdería para siempre.
En la mayoría de los casos, puedes ignorar los detalles sobre cómo se ha hecho un cambio. El código es generalmente auto-explicativo en este sentido (y si el código es tan complejo como para necesitar ser explicados en prosa, esa es la función de los comentarios en el código fuente). Sólo céntrate en aclarar las razones por las que has realizado el cambio, como funcionaban las cosas antes del cambio (y que había de malo con eso), la forma en que funciona ahora, y por qué decidiste resolverlo de la forma en la que lo hiciste.
¡El futuro programador agradecido puedes ser tú mismo!
Consejos
Aprendé a amar la línea de comandos. Deja el IDE atrás.
Debido a la gran cantidad de subcomandos git que hay, es prudente adoptar la línea de comandos. Git es increíblemente poderoso; los IDEs lo son tambien, pero cada uno a su manera. Yo uso un IDE todos los días (IntelliJ IDEA) y he utilizado otros extensivamente (Eclipse), pero nunca he visto una integración del IDE con git que coincida con la facilidad y el poder de la línea de comandos (una vez que lo sepas hacer).
Ciertas funciones de los IDE relacionadas con git son invaluables, como llamar a git rm
cuando eliminamos un archivo, y hacer las cosas bien con git
al renombrarlo. Todo se cae cuando se comienza a intentar hacer commits, merge, rebase, o hacer un análisis sofisticado de la historia a través del IDE.
Cuando se trata de utilizar todo el poder de git, la línea de comandos es la manera.
Recuerda que si usas Bash o Z shell, hay scripts de autocompletado que alivian gran parte del dolor de recordar los subcomandos.
Lee Pro Git
El libro Pro Git está disponible en línea de forma gratuita, y es fantástico. ¡Aprovechalo!
Buen post! Si tuviera puntines te llevarías los diez. Yo he cometido innumerables veces las barbaridades que se mencionan en el artículo y lentamente (creo que) voy mejorando.
Puntualmente, la regla 7 es fundamental. La 4 tiene sentido, pero aún me cuesta acostumbrarme. Y para la 5, me cuesta pensarlo en español: el imperativo me suena mal…
de aplicarse, este commit… “Remover métodos obsoletos”?
de aplicarse, este commit… “Mezclar el pull request #123”?
Las siguientes quedan un poco mejor,
de aplicarse, este commit… “Removerá métodos obsoletos”?
de aplicarse, este commit… “Mezclará el pull request #123”?
o bien
al aplicar este commit… “Se remueven métodos obsoletos”?
al aplicar este commit… “Se mezcla el pull request #123”?
pero no son órdenes. Alguna alternativa más fina?
La regla 7 expresa claramente que dejemos de hacer nuestros algoritmos totalmente faltos de expresividad. Por ese lado, estuvo haciendo algunas lecturas de Clean Code, donde llevan al extremo la idea de que “El código en si mismo es la documentación”. De esta forma, no hay motivos para explicar el como en otro lado (Ni en comentarios dentro del código, ni mucho menos en el repositorio ni en el issue tracker).
Cuando necesitamos explicar el como en el commit, es porque la deuda técnica es mayor a lo tolerable.
Sobre la 5, me cuesta mucho, pero pienso que es lo que propones o agregar adelante un “Debe remover…”, “Debe mezclar…”, pero me suena mas forzado. La idea del “Se…” tampoco me gusta. Si tuviera que quedarme con una, usaría la primera, aunque no sean ordenes, son consecuencias de una acción. La otra:
de aplicarse, este commit… “Remueve métodos obsoletos”
de aplicarse, este commit… “Mezcla el pull request #123”
Pero las veo como alternativas, me parece que lo importante es “ “.
Genial. Compro esta última forma que mencionas, que además asocia las reglas 5 y 7 en una sola. Entonces la pregunta para armar el título de un cambio pasa a ser la siguiente: “qué hace este commit?”
qué hace este commit? este commit… Remueve métodos obsoletos
qué hace este commit? este commit… Mezcla el pull request #123
qué hace este commit? este commit… Agrega el soporte para proxy servers
eventualmente, el “por qué se hace?” queda para la descripción, y el “cómo lo hace?” es el código mismo.
Abrazo!