Rebase interactivo
El rebase interactivo es una de las características más poderosas y, potencialmente, complejas de Git. Te permite reescribir la historia de tus commits de manera muy flexible. Puedes reordenar commits, combinar (squash) commits, editar mensajes de commit o incluso eliminar commits por completo. Es como tener un editor detallado para la historia de tus commits.
Advertencia: El rebase interactivo, especialmente en ramas compartidas, puede ser riesgoso porque reescribe la historia. Nunca realices un rebase de commits que ya se hayan enviado a un repositorio compartido a menos que comprendas completamente las implicaciones y hayas coordinado con tu equipo. El rebase es generalmente más seguro y adecuado para limpiar tus commits locales antes de compartirlos.
Vamos a crear una serie de commits con los que trabajar para ver el rebase interactivo en acción:
echo "First change" >> hello.txt
git commit -am "First change"
echo "Second change" >> hello.txt
git commit -am "Second change"
echo "Third change" >> hello.txt
git commit -am "Third change"
Estos comandos crean tres nuevos commits seguidos rápidamente, cada uno agregando una línea a nuestro archivo "hello.txt". La abreviatura -am
en git commit -am
combina la preparación (staging) de todos los archivos modificados y eliminados (-a
) con el commit (-m
). Es un atajo conveniente cuando ya estás siguiendo archivos y solo quieres hacer un commit de los cambios en ellos.
Ahora, digamos que queremos limpiar estos tres commits. Quizás "First change" y "Second change" en realidad formaban parte del mismo cambio lógico y queremos combinarlos en un solo commit. Y quizás queremos mejorar el mensaje de commit de "Third change". El rebase interactivo es perfecto para esto.
Inicia una sesión de rebase interactivo usando:
git rebase -i HEAD~3
Entendamos este comando:
git rebase -i
: git rebase
es el comando para realizar un rebase. La bandera -i
significa "interactivo", lo que le dice a Git que queremos realizar un rebase interactivo.
HEAD~3
: Esto especifica el rango de commits que queremos rebasar. HEAD
se refiere al commit actual. ~3
significa "retroceder tres commits desde HEAD". Entonces, HEAD~3
selecciona los últimos tres commits. También podrías especificar un hash de commit en lugar de HEAD~3
para comenzar el rebase desde un punto específico de la historia.
Cuando ejecutas este comando, Git abrirá tu editor de texto predeterminado con una lista de los últimos tres commits. La interfaz del editor es donde le indicas a Git cómo modificar tu historia. Verás algo como esto (los hashes de commit serán diferentes):
1 pick 63c95db First change
2 pick 68e7909 Second change
3 pick 5371424 Third change
4
5 ## Rebase 3bf348d..5371424 onto 3bf348d (3 commands)
6 #
7 ## Commands:
8 ## p, pick <commit> = use commit
9 ## r, reword <commit> = use commit, but edit the commit message
10 ## e, edit <commit> = use commit, but stop for amending
11 ## s, squash <commit> = use commit, but meld into previous commit
12 ## f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
13 ## commit's log message, unless -C is used, in which case
14 ## keep only this commit's message; -c is same as -C but
15 ## opens the editor
16 ## x, exec <command> = run command (the rest of the line) using shell
17 ## b, break = stop here (continue rebase later with 'git rebase --continue')
18 ## d, drop <commit> = remove commit
19 ## l, label <label> = label current HEAD with a name
20 ## t, reset <label> = reset HEAD to a label
21 ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
22 #. create a merge commit using the original merge commit's
23 #. message (or the oneline, if no original merge commit was
24 #. specified); use -c <commit> to reword the commit message
25 #
26 ## These lines can be re-ordered; they are executed from top to bottom.
27 #
28 ## If you remove a line here THAT COMMIT WILL BE LOST.
29 #
30 ## However, if you remove everything, the rebase will be aborted.
Entendiendo el editor de rebase interactivo:
- Lista de commits: La parte superior del archivo lista los commits que forman parte de la operación de rebase. Por defecto, se listan en orden cronológico inverso (el más antiguo en la parte superior, el más reciente en la parte inferior).
- Comandos: Debajo de la lista de commits, Git proporciona una lista de comandos disponibles que puedes usar para modificar cada commit. Los más comunes son:
pick
(o p
): Utiliza el commit tal como está. Este es el valor predeterminado.
reword
(o r
): Utiliza el commit, pero te permite cambiar el mensaje de commit.
edit
(o e
): Utiliza el commit, pero detiene el proceso de rebase en este commit para que puedas hacer más cambios (por ejemplo, modificar archivos, agregar más cambios).
squash
(o s
): Combina este commit con el commit anterior en la lista. El mensaje de commit de este commit se agregará al mensaje del commit anterior (luego podrás editar el mensaje combinado).
fixup
(o f
): Similar a squash
, pero descarta el mensaje de commit de este commit y solo utiliza el mensaje del commit anterior.
drop
(o d
): Elimina este commit por completo de la historia.
Nuestro plan de rebase interactivo:
Queremos:
- Combinar "Second change" con "First change".
- Cambiar el mensaje de "Third change" por uno mejor.
Para lograr esto, modifica el archivo del editor para que se vea así:
pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change
No cambies los hashes de commit en el archivo del editor.
Instrucciones para editar en Vim (si es tu editor):
- Entrar en modo de inserción: Presiona la tecla
i
. Esto te permite escribir y editar el archivo.
- Hacer cambios: Utiliza las flechas de tu teclado para navegar a las líneas y cambiar "pick" por "squash" y "reword" como se muestra arriba.
- Salir del modo de inserción: Presiona la tecla
Esc
.
- Guardar y salir: Escribe
:wq
y presiona Enter
.
Después de guardar y cerrar el archivo del editor, Git comenzará a realizar las operaciones de rebase según tus instrucciones.
Primero, combinar commits:
Git procesará primero el comando "squash". Combinará los cambios de "Second change" con "First change". Luego, abrirá tu editor de texto nuevamente, esta vez para que puedas editar el mensaje de commit combinado para el commit combinado. Verás algo como esto en el editor:
## This is a combination of 2 commits.
## This is the 1st commit message:
First change
## This is the 2nd commit message:
Second change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:30:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (2 commands done):
## pick abc1234 First change
## squash def5678 Second change
## Next command to do (1 remaining command):
## reword ghi9101 Third change
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
Este editor muestra los mensajes de commit originales de los commits que se están combinando. Ahora puedes editar este texto para crear un solo mensaje de commit coherente para el commit combinado. Por ejemplo, podrías cambiarlo a:
Combined first and second changes: Initial setup of hello.txt
Después de editar el mensaje, guarda y cierra el editor.
A continuación, cambiar el mensaje de un commit:
Luego, Git pasará al comando "reword" para "Third change". Abrirá el editor nuevamente, esta vez para que puedas editar el mensaje de commit de "Third change". Verás el mensaje original:
Third change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
#
## Date: Tue Oct 24 10:35:00 2023 +0000
#
## interactive rebase in progress; onto <base_commit_hash>
#
## Last commands done (3 commands done):
## pick abc1234 First change
## squash def5678 Second change
## reword ghi9101 Third change
## Next command to do (0 remaining commands):
## (finish)
## You are currently rebasing branch 'master' on '<base_commit_hash>'.
Cambia el mensaje a algo más descriptivo, como:
Improved third change: Added a more descriptive line to hello.txt
Guarda y cierra el editor.
Finalización del rebase:
Después de procesar todos los comandos de tus instrucciones de rebase interactivo, Git terminará el proceso de rebase. Por lo general, verás un mensaje como "Successfully rebased and updated refs/heads/master."
Verificando la historia rebasada:
Ahora, vuelve a comprobar tu registro de commits:
git log --oneline
Deberías ver una historia que se vea algo así (los hashes de commit serán diferentes):
<commit_hash_third_revised> Improved third change: Added a more descriptive line to hello.txt
<commit_hash_combined> Combined first and second changes: Initial setup of hello.txt
<commit_hash_cherry_pick> Add new feature
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original_reverted> Add line to be reverted
<commit_hash_initial_amended> Initial commit with important note
Observa que:
- Ahora tienes menos commits que antes (hemos combinado dos commits en uno).
- El mensaje de commit de lo que era "First change" y "Second change" ahora está combinado y es más descriptivo.
- El mensaje de commit de lo que era "Third change" se ha modificado.
- Los hashes de commit de los commits rebasados han cambiado porque hemos reescrito la historia.
Recordatorios importantes sobre el rebase interactivo:
- Herramienta para la historia local: El rebase interactivo es principalmente una herramienta para limpiar la historia de tus commits locales.
- Evita el rebase en ramas compartidas: No realices un rebase de ramas que se hayan enviado a un repositorio compartido a menos que estés absolutamente seguro de lo que estás haciendo y hayas coordinado con tu equipo. Reescribir la historia compartida puede causar serios problemas para los colaboradores.
git rebase --abort
: Si cometemos un error durante un rebase interactivo, o si te confundes o encuentras conflictos que no puedes resolver, siempre puedes usar git rebase --abort
para cancelar la operación de rebase y devolver tu rama al estado en el que se encontraba antes de comenzar el rebase. Esta es una forma segura de salir de un rebase si algo sale mal.