git rebase

En este documento analizaremos en profundidad el comando git rebase. El comando de fusión mediante cambio de base también se ha explicado en las páginas Configuración de un repositorio y Reescritura del historial. En esta página analizaremos con más detalle la configuración y la ejecución de git rebase. Examinaremos también los casos de uso y peligros habituales de la fusión mediante cambio de base.

La fusión mediante cambio de base es una de las dos utilidades de Git que se especializa en integrar cambios de una rama a otra. La otra utilidad de integración de cambios es git merge. La fusión es siempre un registro de cambios de avance. En cambio, la fusión mediante cambio de base presenta potentes funciones de reescritura del historial. Para ver una comparativa detallada entre la función de fusión y la de fusión mediante cambio de base, consulta nuestra guía Fusión frente a fusión mediante cambio de base. La propia fusión mediante cambio de base tiene 2 modos principales: el modo "manual" y el "interactivo". Analizaremos los diferentes modos de fusión mediante cambio de base más detalladamente a continuación.

¿Qué es git rebase?

La reorganización es el proceso de mover o combinar una secuencia de confirmaciones en una nueva confirmación base. La reorganización es muy útil y se visualiza fácilmente en el contexto de un flujo de trabajo de ramas de funciones. El proceso general se puede visualizar de la siguiente manera:

Tutorial de Git: Git rebase

Desde una perspectiva del contenido, la reorganización consiste en cambiar la base de tu rama de una confirmación a otra haciendo que parezca que has creado la rama desde una confirmación diferente. Internamente, Git lo hace creando nuevas confirmaciones y aplicándolas a la base especificada. Es muy importante entender que, aunque la rama parece la misma, se compone de nuevas confirmaciones por completo.

Uso

El motivo principal por el que llevar a cabo una fusión mediante cambio de base es para mantener un historial del proyecto lineal. Por ejemplo, piensa en una situación en la que la rama principal haya progresado desde que empezaste a trabajar en una rama de función. Quieres incorporar las últimas actualizaciones de la rama principal a tu rama de función, pero quieres mantener el historial de la rama limpio para que parezca que has estado trabajando a partir de la rama principal más reciente. Esto proporciona el beneficio posterior de una fusión limpia de la rama de función de nuevo a la rama principal. ¿Por qué queremos mantener un "historial limpio"? Los beneficios de tener un historial limpio se vuelven evidentes cuando se realizan operaciones de Git para investigar la introducción de una regresión. Una situación más realista sería la siguiente:

  1. Se identifica un error en la rama principal. Una función que funcionaba perfectamente ahora falla.
  2. Un desarrollador examina el historial de la rama principal usando git log. Dado que el historial está "limpio", el desarrollador puede deducir rápidamente qué ha ocurrido en el historial del proyecto.
  3. El desarrollador no puede identificar mediante git log cuándo se introdujo el error, por lo que ejecuta un comando git bisect.
  4. Dado que el historial de Git está limpio, git bisect tiene un conjunto perfeccionado de confirmaciones para comparar cuando se busca la regresión. El desarrollador encuentra rápidamente la confirmación que introdujo el error y puede actuar en consecuencia.

Encontrarás más información sobre git log y git bisect en sus respectivas páginas individuales, en las que se explica como usarlos.

Tienes dos opciones para integrar tu función en la rama principal: fusionar directamente o cambiar de base y después fusionar. La primera opción da como resultado una fusión de tres vías y una confirmación de fusión, mientras que la segunda resulta en una fusión con avance rápido y una historia perfectamente lineal. El siguiente diagrama demuestra cómo cambiar de base en la rama principal facilita una fusión con avance rápido.

Git rebase: rama en la maestra

Reorganizar es una forma habitual de integrar cambios de nivel superior en tu repositorio local. Si se incorporan cambios de nivel superior con Git merge, se produce una confirmación de fusión superflua cada vez que quieres ver cómo ha progresado el proyecto. Por otra parte, reorganizar es como decir “Quiero basar mis cambios en lo que ya han hecho todos”.

No reorganices el historial público

Como hemos explicado anteriormente en Reescritura del historial, nunca debes realizar una fusión mediante cambio de base en las confirmaciones una vez que se hayan enviado a un repositorio público. La fusión mediante cambio de base sustituiría las confirmaciones antiguas por las nuevas y parecerá que esa parte del historial de tu proyecto se ha desvanecido de repente.

Git Rebase estándar frente Git Rebase interactivo

git rebase interactive es cuando git rebase acepta un argumento -- i, que significa "interactivo". Sin ningún argumento, el comando se ejecuta en el modo estándar. En ambos casos, vamos a presuponer que hemos creado una rama de función aparte.

# Create a feature branch based off of main 
git checkout -b feature_branch main
# Edit files 
git commit -a -m "Adds new feature" 

Git rebase en modo estándar tomará automáticamente las confirmaciones de tu rama de trabajo actual y las aplicará al encabezado de la rama anterior.

git rebase <base>

Esta acción fusiona mediante cambio de base automáticamente la rama actual en , que puede ser cualquier tipo de referencia de confirmación (por ejemplo, un ID, un nombre de rama, una etiqueta o una referencia relativa a HEAD).

Ejecutar git rebase con la marca -i inicia una sesión de fusión mediante cambio de base interactiva. En vez de mover a ciegas todas las confirmaciones a la nueva base, la fusión mediante cambio de base interactiva te da la oportunidad de alterar las confirmaciones individuales en el proceso, lo que te permite limpiar el historial eliminando, dividiendo y alterando una serie existente de confirmaciones. Es como git commit --amend, pero a lo bestia.

git rebase --interactive <base>

Esta acción fusiona mediante cambio de base la rama actual en , pero utiliza una sesión de fusión mediante cambio de base interactiva. Se abre un editor en el que puedes introducir comandos (descritos a continuación) para cada confirmación que se fusiona mediante cambio de base. Estos comandos determinan cómo se transferirán las confirmaciones individuales a la nueva base. Puedes reordenar también el listado de confirmaciones para cambiar el orden de las propias confirmaciones. En cuanto hayas especificado comandos para cada confirmación en la fusión mediante cambio de base, Git empezará a fusionar mediante cambio de base confirmaciones aplicando los comandos de fusión mediante cambio de base. Los comandos de edición de fusión mediante cambio de base son los siguientes:




pick 2231360 some old commit
pick ee2adc2 Adds new feature


# Rebase 2cf755d..ee2adc2 onto 2cf755d (9 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

Comandos de reorganización adicionales

Tal y como se explica detalladamente en la página Reescritura del historial, la fusión mediante cambio de base se puede usar para cambiar múltiples confirmaciones antiguas, los archivos confirmados y varios mensajes. Aunque estas son las aplicaciones más habituales, git rebase también tiene opciones de comando adicionales que pueden ser útiles en aplicaciones más complejas.

  • git rebase -- d significa que, durante la fusión mediante cambio de base, la confirmación se descartará del bloque de confirmaciones combinado final.
  • git rebase -- p deja la confirmación como está. No modificará el mensaje ni el contenido de la confirmación y seguirá siendo una confirmación individual en el historial de ramas.
  • git rebase -- x durante la fusión mediante cambio de base ejecuta un script de shell de línea de comandos en todas las confirmaciones marcadas. Esto puede ser útil, por ejemplo, al ejecutar el conjunto de pruebas de tu base de código en confirmaciones específicas, lo que podría ayudar a identificar regresiones durante una fusión mediante cambio de base.

Resumen

La reorganización interactiva te da control completo sobre el aspecto del historial de tu proyecto. Esto les confiere mucha libertad a los desarrolladores, porque les permite confirmar un historial "desordenado" mientras se centran en escribir código para después volver y limpiarlo tras eso.

A la mayoría de desarrolladores les gusta usar una reorganización interactiva para pulir una rama de función antes de fusionarla en la base de código principal. Esto les da la oportunidad de combinar confirmaciones insignificantes, eliminar las obsoletas y asegurarse de que todo lo demás está en orden antes de confirmar el historial del proyecto "oficial". Para todos los demás, parecerá como si toda la función se hubiera desarrollado en una única serie de confirmaciones bien planificadas.

El poder real del cambio de base interactivo se puede observar en el historial de la rama principal resultante. Los demás creerán que eres un desarrollador brillante que ha implementado la nueva función con la cantidad perfecta de confirmaciones la primera vez. Así es como un cambio de base interactivo puede mantener el historial de un proyecto limpio y solo con la información relevante.

Opciones de configuración

Hay algunas propiedades de la fusión mediante cambio de base que pueden configurarse mediante git config. Estas opciones alterarán el aspecto del resultado de git rebase.

  • rebase.stat: Un booleano que está definido como falso de forma predeterminada. Esta opción activa y desactiva la visualización del contenido diffstat visual que muestra qué ha cambiado desde la última fusión mediante cambio de base.
  • rebase.autoSquash: Un valor booleano que activa y desactiva el comportamiento --autosquash.
  • rebase.missingCommitsCheck: Se puede definir con varios valores que cambian el comportamiento de la fusión mediante cambio de base acerca de las confirmaciones que faltan.
warn Imprime el resultado de advertencia en modo interactivo que advierte de las confirmaciones eliminadas.

error

Detiene la fusión mediante cambio de base e imprime los mensajes de advertencia de confirmación eliminados.

ignore

Establecido de forma predeterminada, ignora todas las advertencias de ausencia de confirmación.
  • rebase.instructionFormat: Una cadena con formato git log que se utilizará para formatear la visualización de la fusión mediante cambio de base interactiva.

Aplicación de reorganización avanzada

El argumento de la línea de comandos --onto puede usarse en git rebase. En el modo --onto de git rebase, el comando se expande a:

 git rebase --onto <newbase> <oldbase>

El comando --onto posibilita un tipo de fusión mediante cambio de base más eficaz que permite pasar referencias específicas para que estén en los extremos de una fusión mediante cambio de base.
Pongamos que tenemos un repositorio de ejemplo con ramas como las siguientes:


   o---o---o---o---o  main
        \
         o---o---o---o---o  featureA
              \
               o---o---o  featureB

featureB se basa en featureA. Sin embargo, nos damos cuenta de que featureB no depende de ninguno de los cambios de featureA y podría simplemente separarse de la rama principal.

 git rebase --onto main featureA featureB

featureA es la < oldbase >. main se convierte en la < newbase > y featureB es la referencia a la que apuntará el HEAD de la < newbase >. Entonces, los resultados son:

 
                      o---o---o  featureB
                     /
    o---o---o---o---o  main
     \
      o---o---o---o---o  featureA
                           

Comprensión de los peligros de la reorganización

Una advertencia que hay que tener en cuenta al trabajar con Git Rebase es que los conflictos de fusión pueden llegar a ser más frecuentes durante un flujo de trabajo de fusión mediante cambio de base. Esto ocurre si tienes una rama longeva que se ha desviado de la principal. En algún punto querrás fusionar mediante cambio de base con respecto a la rama principal y, en ese momento, esta podría contener muchas confirmaciones nuevas con las que podrían entrar en conflicto tus cambios en la rama. Esto se soluciona fácilmente fusionando a menudo, mediante cambio de base, tu rama con respecto a la principal y realizando confirmaciones con más frecuencia. Los argumentos de la línea de comandos --continue y --abort pueden pasarse a git rebase para hacer avanzar o restablecer la fusión mediante cambio de base a la hora de abordar conflictos.

Conviene hacer una advertencia más seria sobre la fusión mediante cambio de base en lo referente a las confirmaciones perdidas resultantes de la reescritura interactiva del historial. Si se ejecuta una fusión mediante cambio de base en modo interactivo y se ejecutan subcomandos como squash o drop, se eliminarán las confirmaciones del registro inmediato de tu rama. A primera vista puede parecer que las confirmaciones han desaparecido para siempre. Sin embargo, mediante git reflog estas confirmaciones pueden restaurarse y toda la fusión mediante cambio de base puede deshacerse. Para obtener más información sobre cómo usar git reflog para encontrar confirmaciones perdidas, visita nuestra página de documentación de git reflog.

Git Rebase en sí mismo no es muy peligroso. Los casos peligrosos de verdad se producen cuando se ejecuta una reorganización interactiva de reescritura del historial y se envían de forma forzada los resultados a una rama remota que está compartida con otros usuarios. Este patrón debería evitarse, porque tiene la capacidad de sobrescribir el trabajo de otros usuarios remotos cuando incorporan cambios.

Recuperación de una reorganización de nivel superior

Si otro usuario ha realizado una fusión mediante cambio de base y ha realizado un envío forzado a la rama a la que estás confirmando, un git pull sobrescribirá cualquier confirmación que hayas basado en la rama anterior con el extremo que se envió de forma forzada. Afortunadamente, usando git reflog puedes obtener el registro de referencias de la rama remota. En el registro de referencias de la rama remota, puedes encontrar una referencia anterior a la fusión mediante cambio de base. De este modo, puedes realizar una fusión mediante cambio de base de tu rama con respecto a esa referencia remota utilizando la opción --onto tal y como se ha explicado antes en la sección "Aplicación de la fusión mediante cambio de base avanzada".

Resumen

En este artículo hemos analizado cómo usar git rebase. Hemos explicado casos prácticos básicos y avanzados, así como ejemplos más avanzados. Algunos puntos de discusión clave son los siguientes:

  • El modo estándar de git rebase frente al interactivo
  • Las opciones de configuración de git rebase
  • git rebase --onto
  • Las confirmaciones perdidas de git rebase

Hemos analizado el uso de git rebase con otras herramientas como git reflog, git fetch y git push. Visita las páginas correspondientes para obtener más información.

¿Quieres aprender a usar Git?

Prueba este tutorial interactivo.

Comienza ahora