paint-brush
Una guía para Git con desarrollo basado en troncospor@patrickleet
18,193 lecturas
18,193 lecturas

Una guía para Git con desarrollo basado en troncos

por Patrick Lee Scott13m2021/03/25
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Una guía para el desarrollo basado en Git con Trunk, Patrick Lee Scott explica los objetivos de pasar a un enfoque diferente. El maletero es una constante. En el desarrollo basado en troncos, te comprometes con el. rama troncal, o hacer bifurcaciones y solicitudes de extracción contra la rama troncal. No hay ramas alternativas de larga duración para fusionarse, como la rama "troncal". 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.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Una guía para Git con desarrollo basado en troncos
Patrick Lee Scott HackerNoon profile picture

¡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

 main
o
 master
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
 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 . Domain Driven Design, para los no iniciados, es una metodología para modelar dominios comerciales en código. El consejo es el siguiente:

“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

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

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" (

 main/master
) rama. La acción explícita es comprometerse a
 master
- 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.

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

 staging
El repositorio del entorno podría verse así:

 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

 development
sucursal para cada proyecto”. Es
 0.0.75
,
 3.15.1
,
 0.0.20
, y
 0.0.55
. Se define explícitamente en un repositorio de entorno .

Mientras tanto, su entorno de producción podría estar encendido

 www
versión
 0.0.50
y
 content
en
 0.0.74
. También se define explícitamente en el código.

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

 0.0.1
de ese proyecto, agrupado y listo para funcionar, finalizado e inmutable. El segundo compromiso con el tronco dará como resultado la liberación
 0.0.2
, y el tercero
 0.0.3
, 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,
 0.0.4
no se lanzará hasta la quinta confirmación, lo que hace que las pruebas vuelvan a pasar.

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 enuna 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, DevOps Bliss , 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!

¿Qué hay exactamente en
 git
¿historia?

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

 git
la historia realmente lo es.

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

 branches
y
 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

 git pull
en este punto para obtener los cambios de la
 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

 add
,
 commit
,
 push
, y
 pull
, y aquí es donde usted podría pensar
 pull
. Quiero darte una forma más granular de pensar al respecto.

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

 remote
en Github llamado
 origin
como tu eres. Agregar nuevas confirmaciones al registro y nuevos punteros en forma de ramas y etiquetas.

El comando

 git fetch
simplemente obtiene la información sobre estos cambios. No actualiza tu copia local con los cambios remotos, simplemente hace
 git
consciente de que esos cambios existen.

Entonces, mientras esté ocupado trabajando, de vez en cuando, ejecute:

 git fetch --all -p
los
 --all
flag le dice a git que obtenga de cualquier
 remote
has añadido,
 origin
o de otro modo. los
 -p
la bandera representa
 prune
lo que le dice a git que puede limpiar los punteros de rama eliminados, etc.

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, done . remote: Counting objects: 100% (150/150), done . remote: Compressing objects: 100% (58/58), done . remote: Total 198 (delta 125), reused 97 (delta 92), pack-reused 48 Receiving objects: 100% (198/198), 362.90 KiB | 1.31 MiB/s, done . Resolving deltas: 100% (144/144), completed with 6 local 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

En respuesta a la

 git fetch
llama, puedo ver eso
 master
ha sido actualizado el
 origin
en comparación con mi versión local de la historia de git:
 98169c3..6b4ca35
. También hay algunas ramas y etiquetas nuevas. Correr
 git log
verificará que todavía estoy en el
 98169c3
comprometerse localmente.

 > git log commit 98169c3fabf3052dd89fe0c6900bc3c11a0252a4 (HEAD -> master, tag: v2.0.3) Author: Patrick Lee Scott <[email protected]> Date: Sat May 23 13:48:00 2020 -0400 fix: high throughput test already uses no transactions # ... more commits ...

Entre paréntesis podemos ver algunos indicadores para este compromiso:

 HEAD
,
 master
, y
 tag: v2.0.3
.

Con esa información en mente, querremos

 rebase
nuestros cambios en la versión de la historia señalada por
 origin/master
, que fue lo último,
 6b4ca35
, de la información sobre la recuperación (
 98169c3..6b4ca35
). Es decir, los cambios en Github llegaron primero a Github, antes de nuestros cambios, por lo que nuestros cambios deben trasladarse encima.

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

 rebase
nuestros cambios además de
 origin/master
ejecutamos el comando:

 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

 remote
nombrada
 origin
. ¿Tener sentido? Usar
 fetch
para obtener información y
 rebase
para luego usar esa información.

Todas las confirmaciones que haya realizado después de la confirmación inicial, que es

 98169c3
en este caso, se recogerá, se colocará a un lado y luego se actualizará su historial para que coincida
 origin/master
. Esto significa que su registro de historial coincidirá con el
 origin
versión de la historia - terminando en
 6b4ca35
en nuestro ejemplo. Luego, sus confirmaciones que se colocaron a un lado, se colocarán al final del registro.

 > git rebase origin/master First, rewinding head to replay your work on top of it... Fast-forwarded master to origin/master.

Ahora, cuando corro

 git log
verá que la última confirmación se ha actualizado a la última confirmación identificada por
 fetch
más temprano (
 98169c3..6b4ca35
).

 > git log # ... your commits ... commit 6b4ca353091a7d6a9eeba8ee5b1978112a81cabf (HEAD -> master, origin/master, origin/HEAD) Author: Renovate Bot <[email protected]> Date: Wed Nov 4 06:34:00 2020 +0000 chore(deps): update dependency jest to v26.6.3

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

 rebase
, asegúrese de leer los documentos de Git - 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

 git rebase
funciona bien cuando estamos en
 master
trabajando en una sola confirmación para una solución. Cuando estamos en una rama, preparándonos para un cambio que se fusionará en
 master
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.

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

 -i
bandera o
 --interactive
cuando ejecuta el comando de reorganización, en lugar de una reorganización regular, realizará una "reorganización de la base interactiva".

Esto te permite

 reword
comete en su pequeño segmento de la historia, o
 squash
varias confirmaciones en una sola confirmación.

Hacerlo te lleva a un editor en tu Terminal llamado

 vim
, 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
 :wq
y presione Entrar). Si no está familiarizado con VIM, tómese un tiempo para leer los conceptos básicos.

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

 pick
decir
 s
o
 squash
para los compromisos 2 y 3:

 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,

 Change my name a bit
, que consta de los cambios de las tres confirmaciones.

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

 r
o
 reword
bandera.

 git push origin/<branch> --force

Después de

 rebase
, esencialmente ha modificado el historial de confirmaciones en el registro. Si intenta enviar estos cambios a
 origin
obtendrá un error! Esto se espera.

Debe decirle a git que tiene la intención de cambiar el historial usando el

 --force
bandera. Esto reemplazará el registro de confirmaciones del origen con su nuevo registro de confirmaciones reorganizado.

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,

 --autostash
que combina todos los pasos en uno!

 git rebase origin/master --autostash

Conclusión

Aunque pueden ser más pasos que

 git pull
, a veces extraer resultados en confirmaciones de fusión inesperadas - usando
 fetch
,
 stash
y
 rebase
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!

¡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 patscott.io . Si eres un ingeniero del tipo DIY como yo, ¡mira mi curso DevOps Bliss en su lugar!

Publicado anteriormente en https://www.cloudnativeentrepreneur.com/blog/a-guide-to-git-with-trunk-based-development