Git mínimo viable para el desarrollo basado en troncos

Nov 30 2022
Menos es más cuando se trata de una herramienta poderosa y demasiado complicada como Git Eli es cofundador y codirector ejecutivo de trunk.io.

Menos es más cuando se trata de una herramienta poderosa y demasiado complicada como Git

Eli es cofundador y codirector ejecutivo de trunk.io . Ha ocupado puestos de liderazgo en ingeniería en Microsoft, Uber y Google (que adquirió la startup que cofundó). Su enfoque principal es ayudar a los equipos a crear un mejor software, más rápido.

El infierno de Git de Dante

Desarrollar en un entorno colaborativo es una operación complicada que implica el seguimiento y la coordinación del trabajo de varios desarrolladores. Muchos de nosotros usamos Git para el control de versiones para controlar todos estos cambios. Pero en las propias palabras de Linus , Git es "el administrador de información del infierno". Ofrece tantas opciones y estados extraños que puede encontrarse fácilmente en un mal lugar hasta el punto en que necesita volver a clonar desde cero, incluso si no hizo nada malo.

Muchos de nosotros sabemos que un desarrollador que estudió la especificación de referencia de Git y el árbol de Merkle subyacente en el que se basa, pero ser un experto en Git probablemente no esté en la descripción de su trabajo. Puedo conducir un automóvil perfectamente bien sin entender cómo funciona realmente la transmisión y, al final del día, Git es solo un medio para un fin.

Para aprovechar al máximo Git, debe usarlo la menor cantidad posible cuando se trata de desarrollo basado en troncos. Limite los comandos que usa, mantenga su rama de funciones actualizada correctamente y estandarice el uso como equipo. Individualmente, puede disfrutar de la libertad de hacer lo que quiera. Pero como equipo, el precio de la libertad se paga con fricción.

Limite sus acciones de Git

La práctica más fácil de implementar para lograr la máxima eficiencia de Git es ceñirse a un subconjunto de comandos. Git puede hacer muchas cosas, pero la gran mayoría de su poder solo existe para ayudarte a salir de situaciones terribles. La cantidad de comandos que permite Git en comparación con la cantidad de comandos que realmente deberías invocar es bastante diferente.

Una vez, un CTO me pidió que diera una clase de git avanzado a su equipo de ingeniería. Mi respuesta fue sencilla:

para cuando necesite git avanzado, las ruedas ya se habrán desprendido del automóvil. centrémonos en cambio en usar core git correctamente.

Aquí están los 10 comandos de git que uso para hacer mi trabajo y minimizar la cantidad de veces que aterrizo en el infierno de Git:

Gestión de Sucursales

git checkout -t -b {branch-name}
Cree una rama, establezca el flujo ascendente en la rama actual y cambie a ella. También puede hacer esto a través del comando `git branch`, pero requiere varios comandos. Dado que siempre quiero cambiar a una rama cuando la creo, este comando es más breve. Crea y revisa la sucursal de una sola vez. Por lo general, nombro todas mis sucursales eli/{work-being-done}.

git checkout {branch-name}
Cambiar a una sucursal.

git branch -vv
Vea sus sucursales actuales y sus upstreams.

git branch -D {branch-name}
Elimine una rama local, generalmente para limpiar las ramas obsoletas/fusionadas en la principal.

empujando y tirando

git fetch origin main:main
Acerque el control remoto maina su local main, ¡incluso cuando no se encuentre actualmente en la mainsucursal! Ese es el ; muy útil cuando intenta fusionar lo último mainen una sucursal de relaciones públicas.

git push origin
Empuje mi código al control remoto; probablemente activando muchas máquinas en CI para verificar mi trabajo.

git pull
Actualizar mi sucursal local con el control remoto del mismo nombre.

cometer

git add -A .
Agregar todo en lo que estoy trabajando (archivos nuevos y editados).

git commit -am "work"
Vea aquí para una inmersión profunda en mis pensamientos sobre los mensajes de compromiso locales

git merge main
Mucho más sobre esto a continuación. Soy pragmático y no un rebaser y estoy feliz de enturbiar mi sucursal local con los tejemanejes de main.

Mantenga su rama de funciones actualizada correctamente

En mi último artículo , discutí cómo administrar el código de aterrizaje en main. Pero a menudo, durante el desarrollo, y ciertamente antes de la fusión, debe estar actualizado con los últimos cambios mainporque:

  • Necesita alguna función que haya aterrizado mainpara hacer su trabajo de función.
  • Algo maincambió y si no lo recoge, lo romperá cuando intente obtener su código.
  • Solo quieres mantenerte actualizado porque eres ingeniero y te encantan las cosas nuevas y brillantes.

Puerta 1: git merge

Esta es mi estrategia de fusión preferida. Es simple, confiable y sin lujos. Un básico git mergetoma todos los cambios que ocurrieron mainy los fusiona como confirmaciones junto con sus cambios. Puedes leer hasta la saciedad acerca de las desventajas de esta solución sencilla, pero honestamente me encojo de hombros y digo "No me importa eso". Si fusionas tus ramas de trabajo main, todas esas tonterías sobre la contaminación de git-log se convierten en eso.

Honestamente, realmente no importa cómo/por qué/qué entró en mi rama de trabajo. Lo que importa es: ¿se compila, funciona y el código hace lo que quiero que haga? El resto es académico.

En la práctica, y soy un ingeniero muy práctico, git mergele permite manejar los conflictos de fusión de manera limpia y volver a construir. Resuelve cualquier conflicto de fusión una vez, y estás listo para comenzar. Esta es la mejor opción para principiantes, usuarios avanzados ocupados y aquellos de ustedes que no sueñan con convertirse en gurús de Git.

Puerta 2: git rebase

Esta es la peor opción con diferencia. Está bien, lo sé; prácticamente hay toda una generación que ve rebase vs merge algo como tabulaciones vs espacios. Pero en proyectos "reales", en los que uno o varios equipos se fusionan en un repositorio diariamente, la reorganización requiere resolver conflictos de combinación en lugar de uno con una reorganización de combinación o aplastamiento (más información a continuación).

Además, cualquier cambio de base vuelve a escribir el historial de Git, y eso significa que si su rama tiene una contraparte remota, debe git push --forcepisotear su historial con el nuevo historial de cambio de base de su rama. Esto está bien en teoría, pero tiene el potencial de eliminar accidentalmente su trabajo anterior de una manera que es extremadamente difícil de recuperar. Simplemente es mucho más peligroso que fusionarse y, sinceramente, me da dolor de cabeza solo de pensarlo.

A los efectos de este artículo, intenté cambiar la base solo para recordarme lo mucho que me desagrada este flujo de trabajo. Mira lo que arroja un simple comando de rebase en mi terminal:

los buenos flujos de trabajo no deberían requerir sugerencias para funcionar

Hay tanto mal con ese terminal. Recibo notas sobre hashes de confirmación; no quiero verlas. Obtengo cuatro líneas de pistas amarillas para recordarme cómo funciona todo esto. Y ahora estoy en un flujo de trabajo solo para obtener el código de mainmi sucursal. El árbol de decisiones es ridículamente complicado; git rebase --skip- ¡¿qué es eso?! ¿Por qué estaría bien omitir una confirmación? ¿Sabes que? No me digas porque tengo trabajo que hacer.

Puerta 3: git squash rebase

Definitivamente no quieres lo que hay detrás de la puerta #2, pero algunas personas son rebaseers intransigentes. Mi cofundador, el codirector ejecutivo David Apirian, me ha mostrado su entrada secreta tras bambalinas para reorganizar y es increíble. Llamaremos a esto el squash rebase .

He reiterado la importancia de la simplicidad de Git, pero con este enfoque, puedes tener tu rebase y comerlo también. Obtienes las capas mágicas de un rebase sin la locura de un flujo de rebase estándar. Con una rebase de squash, puedes:

  • Vea una diferencia de sus confirmaciones locales antes de enviar a GitHub.
  • Deshaz fácilmente tu última confirmación.
  • Evite contaminar la vista de GitHub de su solicitud de extracción con un montón de pequeñas confirmaciones locales.

Paso 1: aplasta todas tus confirmaciones locales:

git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message

git rebase main

Puede poner todo esto junto en un solo súper comando para extraer el último principal, aplastar todas sus confirmaciones y volver a basar en el último principal:

git reset --soft $(git merge-base HEAD main) &&
git commit -am "" --allow-empty-message &&
git fetch origin main:main &&
git rebase main

[alias]
 smartcommit = !git add -A . && git commit -am "" --allow-empty-message
 squash = !git reset --soft $(git merge-base HEAD main) && git commit -am "" --allow-empty-message
 squashrebase = !git squash && git fetch origin main:main && git rebase main
 smart = !git smartcommit && git squashrebase

Uno de los aspectos más geniales de siempre aplastar tus compromisos locales es que en la parte superior git logestá todo el trabajo que has hecho en la rama. Tu trabajo local ahora se asemeja más a cómo se verá el PR fusionado de squash cuando aterrice en main.

En cualquier momento, puede sacar la última confirmación de HEAD con su sucursal local y ver todo el trabajo que ha hecho fuera de main. Esto es similar a la vista detallada que GitHub le brinda de lo que cambió, pero le permite permanecer en su terminal y evitar alternar confusamente entre dos UI diferentes.

Debo admitir que la primera vez que David me mostró este flujo de trabajo quedé impresionado. En su mayoría, había matado al rebasedragón y la capacidad de ver localmente todos los archivos que ha cambiado en su rama es genial.

Si bien el reajuste de squash lo ayuda a mantener un mejor estado de flujo, a la gran mayoría de los desarrolladores les conviene simplemente fusionarse. El uso incorrecto de rebase o squash rebase da como resultado una fricción que acaba con la productividad.

Dificultades de la ayuda de la rama de GitHub

estas casillas de verificación en GitHub parecen útiles... pero no lo son

GitHub ofrece esta configuración ☝️ para sugerir fusionarse mainautomáticamente con tu rama si no está actualizada. Esta configuración suele ser contraproducente. Quiero que mi sucursal local sea la única fuente de verdad para mis relaciones públicas, y nadie más (incluido GitHub) debería presionarlo. Trabajar con GitHub en mi rama personal debería ser una calle de sentido único. Codifico localmente y empujo.

Si configura GitHub para comenzar a enviar actualizaciones a su rama de trabajo, está solicitando que el tráfico comience a dirigirse en la dirección incorrecta. Y esa es una receta para el dolor.

Este problema también surge cuando los revisores de código te dejan pequeñas sugerencias de compromiso en tu solicitud de extracción y tú las aplicas alegremente. Si luego hace algún trabajo en su sucursal local antes de hacer esos cambios... ha abierto una pequeña bolsa de dolor.

A menudo descubro que accidentalmente me hice esto a mí mismo cuando trato de enviar mis actualizaciones al control remoto y git me dice que no estoy sincronizado. En ese momento tengo dos opciones:

  1. git push --forcey elimine cualquier cosa en el control remoto que no esté en su sucursal local. Generalmente cualquier cosa con la palabra fuerza es destructiva y desalentadora.
  2. git merge origin/{my-work-branch}para fusionar los cambios remotos en la vista local de la sucursal.

Esta configuración de GitHub ️☝️ requiere que los PR estén actualizados mainantes de fusionarse con ella. Esto generalmente "resuelve" la posibilidad de que se rompa main; sin embargo, solo funciona con repositorios de muy baja velocidad. Una vez que un puñado de desarrolladores intentan fusionar múltiples relaciones públicas por día, terminan en una carrera para fusionarse maino, de lo contrario, se encuentran constantemente realizando una fusión/reorganización mainen su rama para estar "actualizados" nuevamente, y básicamente compiten con sus compañeros de trabajo. ser el primero en fusionarse. Doloroso. Por lo general, si desea esta característica pero no es un desarrollador independiente, probablemente desee una cola de combinación en su lugar, que es la forma escalable de lograr un sistema principal sin interrupciones. Esto es algo que consideramos lo suficientemente importante como para producirlo con Trunk Merge .

Minimizar Git para obtener el valor máximo

Al mantener su uso de Git lo más simple posible, les da a los desarrolladores menos recursos para colgarse y más tiempo para hacer el trabajo de desarrollo que realmente importa. Haz que Git juegue un papel secundario y no se convierta en el protagonista. Para mantener un entorno de desarrollo sano y colaborativo, recuerde:

  • Elija el área de superficie más pequeña de Git para usar y quédese con ella.
  • Administre siempre sus cambios ascendentes de la misma manera.
  • Estandarice las prácticas en todo su equipo para que sus compañeros tampoco se descarrilen.