Guía para principiantes del rebase interactivo

Publicado originalmente por Stride el 16 de enero de 2018 52,731 lecturas

Foto de perfil de Stride Hacker Noon

A principios de este año hice por primera vez un rebase interactivo y me impresionó lo que uno puede hacer con él. También me pareció un poco complejo al principio. Espero que esta guía ayude a eliminar algunas incertidumbres en torno a ella.

También, ya que es tan poderoso y se puede esencialmente reescribir la historia, una pequeña advertencia antes de empezar: Hay muchas escuelas de pensamiento sobre Git y si rebasar es una buena idea o no. Este post no se adentrará en esas discusiones y sólo pretende mostrar los fundamentos del uso del rebasado interactivo.

TL;DR

  • El rebasado interactivo se puede utilizar para cambiar los commits de muchas maneras, tales como editar, borrar y aplastar.
  • Para indicarle a Git dónde empezar el rebase interactivo, utiliza el SHA-1 o el índice del commit que precede inmediatamente al commit que quieres modificar.
  • Durante un rebase interactivo, cuando Git hace una pausa en un commit que has etiquetado para editar, el flujo de trabajo no es diferente al de un proceso de commit normal: escenificas los archivos y luego los envías. La única diferencia es que utilizas el comando git commit --amend en lugar de git commit.
  • El rebase interactivo creará nuevos SHA-1, por lo que es mejor utilizar el rebase interactivo en los commits que no has empujado a una rama remota.

El Problema

En este ejemplo, vamos a trabajar en una situación en la que hemos estado trabajando en una rama de características y tenemos un par de commits que nos gustaría cambiar o eliminar. Este es el aspecto de nuestro registro git:

Desde el registro git anterior, estos son los dos commits que queremos cambiar:
4a4d705 – En este commit cometimos accidentalmente un conflicto de fusión
6c01350 – En este commit eliminamos el conflicto de fusión

Lo que nos gustaría hacer es retroceder en el tiempo hasta 4a4d705, eliminar el conflicto de fusión en el commit, y luego eliminar 6c01350 ya que el conflicto de fusión está resuelto y ya no necesitamos este commit. Esto será una mejora para nuestro historial de commits por dos razones:

  1. No tendremos commits rotos (conflictos de fusión)
  2. Sólo tendremos commits significativos, sin commits relacionados únicamente con la fijación de un conflicto de fusión perdido

La solución

Esta situación es un buen candidato para el rebasado interactivo. Scott Chacon, en su libro Pro Git, describe el rebasado interactivo de la siguiente manera: «A veces la cosa arreglada… no puede ser enmendada al commit no del todo perfecto que arregla, porque ese commit está enterrado profundamente en una serie de parches. Eso es exactamente para lo que sirve el rebase interactivo: usarlo después de un montón de , reordenando y editando confirmaciones, y aplastando múltiples confirmaciones en una.»

¿Qué confirmaciones queremos modificar?

Para iniciar un rebase interactivo, tenemos que decirle a Git qué confirmaciones queremos modificar. Hacemos esto haciendo referencia a la confirmación inmediatamente anterior a la primera confirmación que queremos modificar. O, en pocas palabras, referenciamos «el último commit que queremos mantener tal cual», según Scott Chacon.

Veamos nuestro ejemplo para entenderlo mejor. Hay dos formas de referenciar este commit:
Por SHA-1 – El último commit que queremos retener como está tiene un SHA-1 de 528f82epor lo que podemos pasarlo a nuestro comando rebase interactivo.
Por índice – La última confirmación que queremos mantener como está tiene una posición de índice de 3 (Git utiliza la indexación basada en cero) por lo que podemos pasar HEAD~3 a nuestro comando rebase interactivo.

Nota – Si sólo tienes unos pocos commits en los que rebase interactivo, el uso del índice es probablemente más fácil para la mayoría de la gente. Sin embargo, si tienes muchos commits, usar el SHA-1 es probablemente más fácil para no tener que contar todo el camino hasta el registro de git.

Iniciar el rebasado interactivo

En base a nuestro ejemplo, ejecutaremos o bien:

$ git rebase -i 528f82e

O bien

$ git rebase -i HEAD~3

Lo que abre esta ventana en Vim:

Nota que los commits están en el orden inverso al del git log. En el registro de git la confirmación más reciente está en la parte superior. En esta vista, la confirmación más reciente está en la parte inferior. También observa que los comentarios de abajo dan una lista útil de los comandos válidos que podemos usar en cada confirmación.

Si no sabes Vim, simplemente haz clic en cada palabra pick que quieras editar y luego pulsa la tecla <i> (para el modo de inserción). Una vez que haya terminado de escribir pulse la tecla <esc> para salir del modo de inserción.

En nuestro ejemplo, hemos cambiado el comando a edit para el commit que queremos modificar y hemos cambiado el comando a drop para el commit que queremos borrar. Luego ejecutamos :wq para guardar y salimos de la ventana de Vim.

Modificar el commit

De vuelta en el terminal vemos este mensaje:

Esto tiene sentido porque estamos detenidos en 4a4d705. Este es el commit más antiguo de la serie de commits que queremos modificar. Comenzaremos con este commit y trabajaremos a través de cada commit hasta el más reciente.

Como recordatorio, 4a4d705 fue el commit con el conflicto de fusión que cometimos accidentalmente. Cuando abrimos nuestro editor vemos el conflicto de fusión allí:

Así que arreglamos el conflicto de fusión en el archivo pero ¿qué hacemos ahora? En caso de duda, git status:

¡Cool! Esto es realmente útil. Vemos que actualmente estamos editando 4a4d705, y vemos los dos siguientes commits sobre los que hay que actuar después de éste.

El resto del mensaje nos está explicando un flujo de trabajo conocido. Git nos dice que si queremos modificar el commit ejecutamos git commit --amend. Esto actuará esencialmente como nuestro típico git commit que usamos en un flujo de trabajo normal. Al final de este mensaje vemos que nuestro archivo fue modificado reflejando los cambios que acabamos de hacer para eliminar el conflicto de fusión. Tenemos que poner en escena el archivo antes de confirmar. Esto no es diferente de un flujo de trabajo normal.

Todo lo que hacemos es git add tempChanger.js para preparar el archivo editado y luego git commit --amend para confirmar el archivo preparado. Esto abrirá de nuevo una ventana de Vim con el mensaje de confirmación:

Podemos editar el mensaje de confirmación o dejarlo como está. Vamos a elegir mantener el mensaje de confirmación igual y vamos a escribir :wq para guardar y salir de la ventana.

Ahora hemos editado nuestra antigua confirmación. ¿Y ahora qué? Ejecutamos git status:

Continuar rebase interactivo

¡No tenemos nada más que cambiar en el commit así que continuemos!

Ejecutamos git rebase --continue y vemos el siguiente mensaje:

¿Ya hemos terminado? ¿Pero qué pasa con los otros dos commits? Bueno, el siguiente commit sobre el que había que actuar era 6c01350. Este commit lo marcamos para borrarlo (drop) cuando iniciamos el rebase interactivo. Git lo borró automáticamente y pasó al siguiente commit, 41aa9d2. Este nunca fue modificado en el rebase interactivo inicial. Su comando por defecto era pick, lo que significa que se utilizará el commit. Git aplicó ese commit y como ese era el último commit, el rebase interactivo se completó.

Nota, si tuviéramos más commits que editar, simplemente nos habríamos movido al siguiente commit y habríamos empezado el proceso de modificación tal y como hicimos arriba. El ciclo continúa hasta que no quedan commits.

El botón de expulsión

Cabe destacar que si en algún momento de nuestro rebase interactivo metemos la pata y no sabemos cómo arreglarlo, siempre podemos abortar. En cualquier momento podemos ejecutar git rebase --abort en la terminal y el rebase interactivo se abortará sin guardar los cambios. Entonces tendríamos que volver a empezar el rebase interactivo.

Después del rebase interactivo

Nuestro registro de git ahora tiene el siguiente aspecto:

Notarás que algunas cosas han cambiado desde antes de empezar el rebase interactivo:

  • Ya no tenemos el commit 6c01350 con el mensaje de commit «Remove merge conflict». Este es el commit que eliminamos en nuestro rebase interactivo.
  • Nuestro commit que editamos, 4a4d705, tiene un SHA-1 diferente, 2b7443d.
  • Nuestro commit más reciente de nuestro git log original, 41aa9d2, también tiene un nuevo SHA-1, 2b95e92. Este commit no se modificó, sino que simplemente se aplicó al commit anterior 2b7443d.

Efectos secundarios del rebasado interactivo

Para los dos commits más recientes de nuestro git log, al tener nuevos SHA-1, Git los ve como commits completamente nuevos. Esto es incluso cierto para nuestro último commit, 2b95e92, donde ni el mensaje de commit ni los archivos cambiaron en absoluto. Esto trae a colación un punto importante con el rebasamiento interactivo: Si modificas un commit, ese commit y todos los sucesivos tendrán nuevos SHA-1.

Esto no afectará a nada si los commits que has modificado no han sido empujados a una rama remota. Sin embargo, si usted completó un rebase interactivo en los commits que ya fueron empujados a una rama remota y luego empujó su rama de nuevo vería:

Técnicamente, usted podría evitar esto usando git push --force pero eso es muy peligroso. Si la rama remota tiene commits de otras personas pero tu rama local no tiene esos commits todavía, borrarás efectivamente sus commits.

Otra solución es usar git push --force-with-lease que sólo modificará tus commits pero no los commits que pertenecen a otros, aunque esto también puede ser problemático. Por ejemplo, si otro desarrollador ya tiene esos commits que recibieron nuevos SHA-1’s en su rama local, cuando jalen la rama remota, tendrán conflictos de fusión con cada uno de estos commits.

Cuándo usar --force-with-lease está más allá del alcance de esta entrada del blog, pero sería mejor consultar a otros miembros de su equipo antes de hacerlo. Usted puede leer más acerca de git push --force-with-lease aquí.

La clave para llevar de esta sección es que es mucho más fácil y más seguro utilizar rebasing interactivo en los compromisos que aún no han sido empujados a una rama remota.

Escrito por Blake DeBoer. Publicado originalmente en Dev.to

¿Senior, Lead, o Principal developer en NYC? ¡Stride está contratando! ¿Quieres mejorar tu equipo tecnológico? Vea cómo lo hacemos nosotros! www.stridenyc.com

Etiquetas

Únete a Hacker Noon

Crea tu cuenta gratuita para desbloquear tu experiencia de lectura personalizada.

Leave a Reply