DanLevy.net

Pelea: Git Rebase vs. Merge

Una pregunta eterna...

Hero image for Pelea: Git Rebase vs. Merge

Pelea: ¡Git Rebase vs. Merge (Squash)!

¿Debo hacer Rebase? ¿O Squash Merge?

¿Por qué este tema evoca fervor religioso?

Algunos ingenieros usan el conocimiento de git (y la terminal) como una señal de su nivel de habilidad relativo. Y cualquier práctica ligada a nuestra identidad/ego puede ser imposible de analizar con imparcialidad, y mucho menos cambiar.

Otros factores incluyen la familiaridad y el sesgo de supervivencia, que pueden enturpiar aún más nuestra propia evaluación y suposiciones.

Pregunta clave: ¿Cuál es el propósito de un commit de git?

  1. ¿Haces commit temprano y a menudo? ¿Usando una mentalidad de “punto de control” o respaldo?
    • ¿Donde todo queda registrado, incluso los falsos comienzos y experimentos? (p. ej. git commit -am "Updated deps" && git push, repetir regularmente)
    • ¿Quizás los mensajes de commit son menos importantes que el código para ti?
  2. O, ¿tus commits son una obra de arte cuidadosamente curada y esculpida?
    • ¿Cada commit es una unidad de trabajo atómica y autocontenida? (p. ej. git add package.json && git commit -m "Updated deps")
    • ¿O simplemente no puedes soportar registros de commit “desordenados”?
    • ¿Tus revisiones de PR a menudo implican revisar commit por commit?

| 💡 ¿Qué otro(s) modelo(s) mental(es) definen cómo ves los commits? ¡Avísame @justsml!

¿Estás pensando en git de una manera que proporciona el mayor valor para ti, tu equipo y tu organización?

Dado que existen mentalidades muy diferentes en torno a la estrategia de commits, no es de extrañar que haya tanta confusión sobre la forma “correcta” de usar git.

Escenario: Crear una etiqueta de revisión para un lanzamiento

Comparemos el proceso de crear una etiqueta de lanzamiento excluyendo algunos commits recientes en main.

Lanzamiento con Git Tag desde main con 2 ramas de características

La forma Rebase

Modelo mental: “Quiero crear una versión alternativa de un historial existente. (p. ej. cometí un error hace 16 merges y puedo necesitar un control de grano fino para corregirlo. Además, podría quedar atrapado en un ciclo aparentemente interminable de conflictos y --continue.)”

  1. Obtener lo último: git checkout main && git pull
  2. Crear nueva rama: git checkout -b release/hot-newness-and-stuff
  3. Iniciar rebase interactivo e incluir la referencia git a la que quieres volver en el tiempo. git rebase -i HEAD~6 (Nota: HEAD~6 es un ‘git ref’ abreviado para ‘hace 6 commits’)
  4. Elimina los commits deseados cambiando su prefijo a drop. Guarda y cierra el editor.
  5. Resuelve conflictos de merge, git add . && git rebase --continue (NO hagas git commit).
  6. Repite el paso anterior hasta completar.
  7. Etiqueta/envía usando el proceso actual. Ejemplo git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Pros

Contras

La forma (Squash) Merge

Modelo mental: “Quiero un lanzamiento personalizado, comenzando en un punto dado e incluyendo las ramas deseadas.”

  1. Obtener lo último: git checkout main && git pull
  2. Crear nueva rama: git checkout -b release/hot-newness-and-stuff
  3. Mergea las ramas y/o commits deseados: git merge --no-ff feature/hot-newness bug/fix-123 (usa la bandera --no-ff siempre que sea posible.)
  4. Resuelve cualquier conflicto de merge (si surge).
  5. Etiqueta/envía usando el proceso actual. Ejemplo git tag -a v1.2.3 -m 'Release v1.2.3' && git push --tags

Pros

Contras

Conclusión

Al final del día, un proceso más simple con menos riesgo debería ganar.

Aunque los rebasers tienen formas de resolver (o evitar) sus problemas, el hecho permanece: eventualmente necesitarás un cinturón negro en git fu. (p. ej. Incluso un humilde git push puede enfrentar complejidad adicional: ¿fue git push --force o git push --force-with-lease? ¿Por qué lidiar con eso en absoluto?)

Hay otra razón por la que el rebasing para crear un historial revisado siempre estará en desventaja comparado con git merge .... Un git merge permite que el CLI de git aplique algoritmos avanzados para evitar conflictos analizando el HEAD de cada rama.

Esto puede ser más inteligente porque cada merge solo se preocupa por el estado más reciente de cada rama deseada, mientras que el rebasing debe reproducir (o eliminar) el historial de commits en la secuencia especificada. Esto limita la capacidad de git para optimizar el merge ya que solo compara 2 commits a la vez.

En última instancia, el rebasing significa que ocasionalmente te encontrarás reviviendo commits y conflictos antiguos irrelevantes, incluso si sabes que desde entonces han sido eliminados o resueltos.

Resumen