¡Aprende la mentalidad y el proceso detrás de Trunk Based Dev! A través de mi negocio de consultoría, equipo a las organizaciones de tecnología con las últimas y mejores herramientas de DevOps para aumentar la velocidad de sus equipos de desarrollo. Parte de esto significa abandonar flujos de trabajo obsoletos como "GitFlow" y reemplazarlos con procesos más optimizados: desarrollo basado en troncos e implementación continua. Pronto hablaremos más sobre "Desarrollo basado en troncales", pero es importante que esta conversación defina qué es "troncal", ya que también he visto que se usa mal. El "tronco" es el o rama de su repositorio. El maletero es una constante. En el desarrollo basado en troncales, usted se compromete con la rama troncal o realiza ramas y solicitudes de extracción contra la rama troncal. No hay ramas alternativas de larga duración contra las que fusionarse, como . main master development He visto equipos crear una nueva sucursal y llamarla el nuevo "tronco" cada pocas semanas. Aunque aplaudo el esfuerzo, no dio en el blanco por mucho. Veremos más sobre cómo se ve este proceso de principio a fin más adelante... Primero, permítanme dedicar un minuto a respaldar mis afirmaciones de que GitFlow está desactualizado y explicar los objetivos de pasar a un enfoque diferente. Algunos de los mejores consejos de programación que jamás recibirá provienen de la lectura obligatoria de Eric Evan, . Domain Driven Design, para los no iniciados, es una metodología para modelar dominios comerciales en código. El consejo es el siguiente: Domain Driven Design “Hacer explícitos los conceptos implícitos” - Eric Evans, Diseño impulsado por el dominio ¿Qué significa esto exactamente? Además, ¿cómo se aplica esto a Git o GitFlow? Primero, definamos qué significa un “concepto implícito”. Un concepto implícito es algo que ocurre como efecto secundario de otra cosa. Por ejemplo, un efecto secundario de comprometerse con el el uso de GitFlow es a menudo vincularlo con la implementación de una versión de ese servicio en un entorno como el de ensayo. development La acción explícita realizada es comprometerse con una rama. La acción implícita causada por esa acción está ejecutando un proceso de implementación. No cambiamos explícitamente nuestro entorno de ensayo: cambiamos el código base de nuestra aplicación en una rama específica y actualizó el entorno como efecto secundario. Luego, a menudo vemos lo mismo cometiendo contra el "tronco" ( ) rama. La acción explícita es comprometerse a - la acción implícita vincula algún tipo de proceso de implementación a esta acción explícita. Expanda eso a varios repositorios y ahora tendrá un verdadero dolor de cabeza. main/master master Entonces, pregúntese: ¿cómo podemos hacer explícitos los conceptos actualmente implícitos de lanzar nuevas versiones de nuestro software? La respuesta: haciendo que esos conceptos formen parte de nuestro modelo. La razón de la incomodidad que sentimos es que el modelo en realidad no modela nuestros entornos. Nuestros entornos en este modelo son completamente implícitos, y como puede imaginar, cosas como los entornos de producción son piezas bastante importantes, que deben modelarse explícitamente, sin duda. Para ello, podemos crear repositorios que modelen explícitamente cada uno de nuestros entornos. Es decir, en lugar de vincular el concepto implícito de publicar e implementar código a nuestras confirmaciones, podemos definir explícitamente los requisitos de dicha publicación e implementación en una base de código. por ejemplo, nuestro El repositorio del entorno podría verse así: staging dependencies: - name: content repository: http://bucketrepo/bucketrepo/charts/ version: 0.0 .75 - name: mongodb-replicaset repository: https://kubernetes-charts.storage.googleapis.com/ version: 3.15 .1 - name: person-model repository: http://bucketrepo/bucketrepo/charts/ version: 0.0 .20 - name: www repository: http://bucketrepo/bucketrepo/charts/ version: 0.0 .55 ¿Qué versión de cada aplicación está en preparación? La respuesta ya no es “cualquier cosa que haya en el sucursal para cada proyecto”. Es , , , y . Se define explícitamente en . development 0.0.75 3.15.1 0.0.20 0.0.55 un repositorio de entorno Mientras tanto, su entorno de producción podría estar encendido versión y en . También se define explícitamente en el código. www 0.0.50 content 0.0.74 Mucho mejor que “¿lo que hay en el baúl de cada proyecto? 🤷♂️” Para lograr esto, necesitaremos abordar las cosas de una manera un poco diferente a la que probablemente esté acostumbrado. Todavía querremos vincular algunas acciones a las confirmaciones, pero no tantos conceptos diferentes: solo queremos hacer cosas que estén relacionadas con el código base en el que estamos trabajando. En cambio, podemos limitar esa acción a lanzar un nuevo artefacto versionado del proyecto en el que estamos trabajando. Más explícitamente, esto significa que la primera confirmación de la rama troncal lanzará la versión de ese proyecto, agrupado y listo para funcionar, finalizado e inmutable. El segundo compromiso con el tronco dará como resultado la liberación , y el tercero , etc. También podemos ejecutar algunas pruebas automatizadas como parte de ese proceso de lanzamiento, por lo que si la confirmación cuatro falla en esas pruebas, por ejemplo, no se lanzará hasta la quinta confirmación, lo que hace que las pruebas vuelvan a pasar. 0.0.1 0.0.2 0.0.3 0.0.4 Lo que son exactamente esos artefactos versionados variará según el tipo de proyecto en el que esté trabajando, pero generalmente son imágenes de Docker, repositorios de gráficos o varios paquetes específicos de lenguaje como NPM o lanzamientos de Maven. A continuación, podemos conectarnos al proceso de lanzamiento y con cada nuevo lanzamiento, causado por cada confirmación verificada en Trunk, para . actualizar explícitamente el entorno de ensayo Luego, una vez que se verifique la combinación explícita de artefactos versionados en el entorno de prueba en ejecución, todos se pueden mover a producción en . una sola confirmación explícita Así que, en pocas palabras, eso es un desarrollo basado en troncales bastante moderno. No está tan mal, ¿verdad? Puede usar una herramienta como Jenkins X para configurarlo de inmediato. Usar JX puede ser un poco abrumador, ya que se basa en Kubernetes y en varias herramientas del ecosistema de Kubernetes. En mi curso, , analizo la configuración de un sistema como este de principio a fin, además de otros temas como SSO, pruebas de carga y más. ¡Échale un vistazo si estás interesado! DevOps Bliss ¿Qué hay exactamente en ¿historia? git Con nuestro flujo de trabajo de desarrollo basado en troncales definido, vale la pena tomarse un momento para explicar cómo pensar en lo que su proyecto la historia realmente lo es. git El historial de tu repositorio es una serie de confirmaciones en una lista ordenada. Para moverse a diferentes puntos en ese historial, se utilizan punteros. Estos punteros se llaman y . branches tags Echa un vistazo a esta imagen extraída de la documentación de . Git - Branches in a Nutshell Es útil recordar esto mientras trabaja con Git. Toda esta estructura, las confirmaciones, los punteros, etc., se copian en su máquina cuando clona un repositorio. Sin embargo, no se actualizan automáticamente. Probablemente estés acostumbrado a usar en este punto para obtener los cambios de la . git pull origin En cambio, quiero mostrarle algunos comandos alternativos para ayudarlo con su flujo de trabajo de desarrollo basado en troncales. Flujo de trabajo general En la siguiente sección, quiero capturar la toma de decisiones del "día en la vida" y los flujos de trabajo de git que deberá emplear cuando trabaje en un proyecto que sigue el desarrollo basado en troncos. En primer lugar, ya sea que esté trabajando directamente en la rama troncal (sí, esto está bien), o en una rama que se fusionará con la rama troncal, necesitará saber qué está sucediendo en Github, información como, tener otros desarrolladores cambiaron el código desde la última vez que revisé? Supongo que está familiarizado con el flujo de trabajo básico de git de , , , y , y aquí es donde usted podría pensar . Quiero darte una forma más granular de pensar al respecto. add commit push pull pull Para hacer eso, vamos a empezar con un . git fetch git fetch A medida que trabaja en su próxima solución o función, ya sea directamente en la rama troncal o en una rama de corta duración que eventualmente se fusionará con la rama troncal, también lo hacen otros desarrolladores. Están empujando y tirando de la misma en Github llamado como tu eres. Agregar nuevas confirmaciones al registro y nuevos punteros en forma de ramas y etiquetas. remote origin El comando simplemente obtiene la información sobre estos cambios. No actualiza tu copia local con los cambios remotos, simplemente hace consciente de que esos cambios existen. git fetch git Entonces, mientras esté ocupado trabajando, de vez en cuando, ejecute: git fetch --all -p los flag le dice a git que obtenga de cualquier has añadido, o de otro modo. los la bandera representa lo que le dice a git que puede limpiar los punteros de rama eliminados, etc. --all remote origin -p prune Entonces sabrá si hay alguna actualización que necesite traer a su copia local. Aquí hay un ejemplo de cómo se ve: > git fetch --all -p Fetching origin From github.com:servicebus/kafkabus - [deleted] (none) -> origin/renovate/mocha-7.x remote: Enumerating objects: 150, . remote: Counting objects: 100% (150/150), . remote: Compressing objects: 100% (58/58), . remote: Total 198 (delta 125), reused 97 (delta 92), pack-reused 48 Receiving objects: 100% (198/198), 362.90 KiB | 1.31 MiB/s, . Resolving deltas: 100% (144/144), completed with 6 objects. 98169c3..6b4ca35 master -> origin/master * [new branch] renovate/commitizen-4.x -> origin/renovate/commitizen-4.x * [new branch] renovate/kafkajs-1.x -> origin/renovate/kafkajs-1.x * [new tag] v2.0.1 -> v2.0.1 * [new tag] v2.0.2 -> v2.0.2 * [new tag] v2.0.3 -> v2.0.3 * [new tag] v2.0.4 -> v2.0.4 done done done done local En respuesta a la llama, puedo ver eso ha sido actualizado el en comparación con mi versión local de la historia de git: . También hay algunas ramas y etiquetas nuevas. Correr verificará que todavía estoy en el comprometerse localmente. git fetch master origin 98169c3..6b4ca35 git log 98169c3 > git commit 98169c3fabf3052dd89fe0c6900bc3c11a0252a4 (HEAD -> master, tag: v2.0.3) Author: Patrick Lee Scott <pat@patscott.io> Date: Sat May 23 13:48:00 2020 -0400 fix: high throughput already uses no transactions log test # ... more commits ... Entre paréntesis podemos ver algunos indicadores para este compromiso: , , y . HEAD master tag: v2.0.3 Con esa información en mente, querremos nuestros cambios en la versión de la historia señalada por , que fue lo último, , de la información sobre la recuperación ( ). Es decir, los cambios en Github llegaron primero a Github, antes de nuestros cambios, por lo que nuestros cambios deben trasladarse encima. rebase origin/master 6b4ca35 98169c3..6b4ca35 Dependiendo de nuestra situación, querremos usar una reorganización regular o interactiva. Trabajando en una solución directamente contra la rama Trunk Si está trabajando en una solución que se puede hacer fácilmente, no hay problema, en mi opinión, con comprometerse directamente con la rama troncal. He trabajado en equipos con 20 ingenieros haciendo esto, y les prometo que está bien, siempre y cuando tenga un proceso de CI/CD sólido. Si este proceso falla, lo peor que pasa es nada. La compilación se marca como fallida, no se publica ni promociona nada. La única regla es que si rompes la construcción, ahora es tu trabajo arreglarlo, ¡y esa es la máxima prioridad! Si tiene miedo de esto, está bien usar una rama, y cubriré ese flujo de trabajo a continuación. Entonces, cuando está trabajando directamente en el tronco, ha obtenido información de Github y hay nuevas confirmaciones para incorporar, ahora es el momento de . rebase git rebase A nuestros cambios además de ejecutamos el comando: rebase origin/master git rebase origin/master Esto solo funciona DESPUÉS de una búsqueda; de lo contrario, git no conoce la información sobre nuevas confirmaciones y ramas del nombrada . ¿Tener sentido? Usar para obtener información y para luego usar esa información. remote origin fetch rebase Todas las confirmaciones que haya realizado después de la confirmación inicial, que es en este caso, se recogerá, se colocará a un lado y luego se actualizará su historial para que coincida . Esto significa que su registro de historial coincidirá con el versión de la historia - terminando en en nuestro ejemplo. Luego, sus confirmaciones que se colocaron a un lado, se colocarán al final del registro. 98169c3 origin/master origin 6b4ca35 > git rebase origin/master First, rewinding head to replay your work on top of it... Fast-forwarded master to origin/master. Ahora, cuando corro verá que la última confirmación se ha actualizado a la última confirmación identificada por más temprano ( ). git log fetch 98169c3..6b4ca35 > git commit 6b4ca353091a7d6a9eeba8ee5b1978112a81cabf (HEAD -> master, origin/master, origin/HEAD) Author: Renovate Bot <bot@renovateapp.com> Date: Wed Nov 4 06:34:00 2020 +0000 chore(deps): update dependency jest to v26.6.3 log # ... your commits ... Si no hay conflictos, este será un comando bastante simple. Si hay conflictos, deben resolverse primero. Para resolver conflictos, en su IDE, solucione los conflictos y, cuando esté listo, guarde los cambios, agréguelos al área de preparación de git y luego ejecute . git rebase --continue Para aprender más sobre , asegúrese de leer los documentos de Git - . rebase Git - Rebasing Para obtener más información sobre cómo resolver conflictos, recomiendo aprender la herramienta VSCode Merge: . Control de versiones en Visual Studio Code Trabajando en una sucursal que se fusionará con Trunk a través de una solicitud de extracción Si está haciendo algo más que una solución rápida, como trabajar en una nueva función, es probable que desee trabajar en una rama. git rebase -i origin/master Lo anterior funciona bien cuando estamos en trabajando en una sola confirmación para una solución. Cuando estamos en una rama, preparándonos para un cambio que se fusionará en a través de una solicitud de extracción, a menudo se espera que hagamos las cosas bien y limpias. Los mantenedores de proyectos de código abierto le pedirán que "aplaste y rebase" sus confirmaciones en una sola confirmación para fusionarlas. git rebase master master Esto mantiene limpio el historial de la rama maestra y la reversión es fácil de realizar. La gente debatirá sobre los méritos de hacer esto versus no hacerlo, pero dejemos de lado todas esas razones por ahora, y concentrémonos en cómo hacerlo. Si especifica el bandera o cuando ejecuta el comando de reorganización, en lugar de una reorganización regular, realizará una "reorganización de la base interactiva". -i --interactive Esto te permite comete en su pequeño segmento de la historia, o varias confirmaciones en una sola confirmación. reword squash Hacerlo te lleva a un editor en tu Terminal llamado , y por esta razón, se requiere conocer los conceptos básicos de VIM para poder realizar el rebase interactivo. (Presione A para ingresar al modo de edición, edítelo, presione Esc, luego escriba y presione Entrar). Si no está familiarizado con VIM, tómese un tiempo para leer los conceptos básicos. vim :wq Aquí hay un ejemplo de la documentación de git ( ): Git - Historial de reescritura pick f7f3f6d Change my name a bit pick 310154e Update README formatting and add blame pick a5f4a0d Add cat-file # Rebase 710f0f8..a5f4a0d onto 710f0f8 # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c <commit> to reword the commit message. # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out Para aplastar todas las confirmaciones, use VIM para editar los mensajes que dicen decir o para los compromisos 2 y 3: pick s squash pick f7f3f6d Change my name a bit squash 310154e Update README formatting and add blame s a5f4a0d Add cat-file Esto dará como resultado una sola confirmación, , que consta de los cambios de las tres confirmaciones. Change my name a bit Digamos que también desea cambiar el mensaje de confirmación, en su lugar, puede editar las confirmaciones de esta manera: r f7f3f6d Change my name a bit s 310154e Update README formatting and add blame s a5f4a0d Add cat-file Si guarda este cambio, aparecerá otra ventana de VIM que le permitirá escribir un nuevo mensaje de confirmación para el o bandera. r reword git push origin/<branch> --force Después de , esencialmente ha modificado el historial de confirmaciones en el registro. Si intenta enviar estos cambios a obtendrá un error! Esto se espera. rebase origin Debe decirle a git que tiene la intención de cambiar el historial usando el bandera. Esto reemplazará el registro de confirmaciones del origen con su nuevo registro de confirmaciones reorganizado. --force ADVERTENCIA: ¡No debes forzar a empujar al maestro, solo a las ramas! Rebasando cuando tienes cambios sin guardar ¿Necesita reorganizar pero tiene cambios no confirmados? Si intenta hacer esto, recibirá un error que le indicará que debe confirmar o guardar sus cambios para volver a establecer la base. > git rebase origin/master error: cannot rebase: You have unstaged changes. error: Please commit or stash them. Si está listo para cometerlos, adelante y hágalo. De lo contrario, puede guardarlos, realizar la reorganización y luego recuperar los cambios del alijo nuevamente. git stash Para poner todos sus cambios a un lado en un espacio temporal, ejecute: git stash Luego, puede completar la reorganización: git fetch --all -p git rebase origin/master Una vez que se completa la reorganización, puede recuperar todos los cambios en los que estaba trabajando desde su alijo: git stash pop --autostash El flujo de trabajo anterior es lo suficientemente común como para que rebase realmente admita una bandera, que combina todos los pasos en uno! --autostash git rebase origin/master --autostash Conclusión Aunque pueden ser más pasos que , a veces extraer resultados en confirmaciones de fusión inesperadas - usando , y le brinda un control más explícito cuando trabaja con git, ¡lo cual es muy útil cuando trabaja con el desarrollo basado en troncos! git pull fetch stash rebase ¡Espero que comprenda los beneficios de hacer explícitas nuestras operaciones previamente implícitas y cómo puede usar algunos comandos nuevos de git para facilitar ese proceso! Si desea implementar la implementación continua en su organización y necesita ayuda, comuníquese conmigo a través de mi sitio web . Si eres un ingeniero del tipo DIY como yo, ¡mira mi curso en su lugar! patscott.io DevOps Bliss Publicado anteriormente en https://www.cloudnativeentrepreneur.com/blog/a-guide-to-git-with-trunk-based-development