() translation by (you can also view the original English article)
En el flujo fundamental de Git, tu desarrollas una nueva característica en
una rama dedicada a eso, luego la mergeas a la rama de producción una vez que
está finalizada. Eso hace de git merge
una herramienta integral para combinar ramas. Sin embargo, no es lo único que ofrece Git.



Como una alternativa al escenario de arriba, pudieras combinar las ramas con
el comando git rebase
. En lugar de enlazar las ramas con un commit
de merge, el hacer rebase a una rama mueve completamente la rama con la nueva
característica hacia la punta de master
como se muestra abajo.



Esto sirve para el mismo propósito que git merge
, la integración de commits de diferentes ramas. Pero hay dos razones por las que quisiéramos optar por un rebase sobre un merge:
- Resulta en una historia lineal del proyecto.
- Nos da la oportunidad de limpiar commits locales.
En este tutorial, exploraremos estas dos casos comunes de git
rebase
. Desafortunadamente, los beneficios de git
rebase
vienen con una transigencia. Cuando es usado incorrectamente, puede ser una de las operaciones más peligrosas que se pueden realizar en un repositorio Git. Así que, también estaremos tomando una mirada cuidadosa a los peligros de hacer rebase.
Prerrequisitos
Este tutorial asume que estás familiarizado con los comandos básicos de Git y flujos de colaboración. Debes sentirte cómodo enviando tus cambios al área de preparación (staging), commiteando instantáneas, desarrollando características en ramas aisladas, mergeando ramas, y empujando/jalando ramas hacia/desde repositorios remotos.
1. Haciendo Rebase para una historia lineal
El primer caso de uso que exploraremos envuelve una historia de proyecto divergente. Considera un repositorio donde tu rama de producción se ha movido hacia adelante mientras se está desarrollando una nueva característica:



Para hacer un rebase en la rama feature
de la
rama master
, correrías los siguientes comandos:
1 |
git checkout feature |
2 |
git rebase master |
Esto trasplanta la rama feature
desde su locación actual hacia la punta de la rama master
:



Hay dos escenarios en los que querrías hacer esto. Primero, si la nueva
característica depende de nuevos commits en master
, ahora tendría
acceso a ellos. Segundo, si la nueva característica fue completada, ahora sería
preparada para un merge de avance rápido (fast-forward) hacia
master
. En ambos casos, hacer rebase resulta en una historia
lineal, donde git merge
resultaría en commits de merge innecesarios.
Por ejemplo, considera que pasaría si se integrarán los commits más recientes con un merge en lugar de hacer un rebase:
1 |
git checkout feature |
2 |
git merge master |
Esto nos daría un commit de merge extra en la rama feature
. Más aún, esto pasaría cada vez que quisiéramos incorporar commits más recientes en la rama de la nueva característica. Eventualmente, la historia del proyecto estaría llena de commits de merge sin significado.



Este mismo beneficio se puede ver cuando se mergea en la otra dirección. Sin
hacer un rebase, integrar la rama feature
terminada en
master
requiere un commit de merge. Mientras este es un commit de merge con significado (en el sentido de que representa una nueva característica completada), la historia resultante está llena de bifurcaciones:



Cuando haces un rebase antes de mergear, Git puede hacer un avance rápido de
master
hacia la punta de feature
. Encontrarás una
historia lineal de cómo tu proyecto ha progresado en la salida de git
log
—los commits en feature
están pulcramente agrupados
encima de los commits en master
. Esto no es necesariamente el caso cuando las ramas son enlazadas con un commit de merge.



Resolviendo Conflictos
Cuando corres git rebase
, , Git toma cada commit en la rama y los mueve, uno por uno, hacia la nueva base. Si uno de estos commits alteran las misma líneas de código que los commits más recientes, resultará en un conflicto.



El comando git merge
te deja resolver todos los conflictos de
la rama al final del merge, el cual es uno de los propósitos primarios de un
commit de merge. Sin embargo, funciona un poco diferente cuando haces un
rebase. Los conflictos son resueltos a base de commits individuales. Así que,
si git rebase
encuentra un conflicto, detendrá el procedimiento de rebase y mostrará un mensaje de advertencia:
1 |
Auto-merging readme.txt |
2 |
CONFLICT (content): Merge conflict in readme.txt |
3 |
Failed to merge in the changes.
|
4 |
.... |
5 |
When you have resolved this problem, run "git rebase --continue". |
6 |
If you prefer to skip this patch, run "git rebase --skip" instead.
|
7 |
To check out the original branch and stop rebasing, run "git rebase --abort". |
Visualmente, así es como se ve la historia de tu proyecto cuando git
rebase
encuentra un conflicto:



Los conflictos pueden ser inspeccionados corriendo git status
.
La salida del comando se ve muy similar a un conflicto de merge.
1 |
Unmerged paths: |
2 |
(use "git reset HEAD <file>..." to unstage) |
3 |
(use "git add <file>..." to mark resolution) |
4 |
|
5 |
both modified: readme.txt |
6 |
|
7 |
no changes added to commit (use "git add" and/or "git commit -a") |
Para resolver el conflicto, abre el archivo con conflicto (reader.txt en el ejemplo de arriba), encuentra las líneas afectadas, y manualmente editalas para obtener el resultado deseado. Luego, dile a Git que el conflicto está resuelto enviando el archivo al área de espera (staging):
1 |
git add readme.txt |
Nota que esta es la misma manera en la que marcas un conflicto con git
merge
como resuelto. Pero recuerda que estas en la mitad de un proceso de rebase —no querrás olvidarte del resto de los commits que necesitan ser movidos. El último paso es decirle a Gt que termine el rebase con la opción --continue
:
1 |
git rebase --continue
|
Esto moverá el resto de los commits, uno por uno, y si algún otro conflicto surge, tendrás que repetir este proceso de nuevo.
Si no quieres resolver conflictos, puedes optar por cualquiera de las
banderas --skip
ó --abort
. Esta última es particularmente útil si no tienes idea que esta pasando y solo quieres regresar a lo seguridad.
1 |
# Ignora el commit que causó el conflicto
|
2 |
git rebase --skip
|
3 |
|
4 |
# Aborta completamente el rebase y vuelve al estado inicial
|
5 |
git rebase --abort
|
2. Haciendo rebase para limpiar commits locales
Hasta ahora, solo hemos usado git rebase
para mover ramas, pero
es mucho más poderoso que eso. Pasándole la bandera -i
, puedes
empezar a hacer una sesión de rebase interactiva. El rebase interactivo te deja
definir precisamente como cada commit será movido hacia la nueva base. Esto te
da la oportunidad de limpiar la historia de una rama de característica nueva
antes de compartirla con otros desarrolladores.
Por ejemplo, digamos que terminaste de trabajar en tu rama
feature
y estas listo para integrarla a master
. Para comenzar una sesión de rebase interactiva, corre el siguiente comando:
1 |
git checkout feature |
2 |
git rebase -i master
|
Esto abrirá un editor conteniendo todos los commits en
feature
que están a punto de ser movidos:
1 |
pick 5c43c2b [Descripción para el commit más viejo]
|
2 |
pick b8f3240 [Descripción para el segundo commit más viejo]
|
3 |
pick c069f4a [Descripción para el commit más reciente]
|
Esta lista define cómo es que la rama feature
va a verse después del rebase.
Cada línea representa un commit y el comando pick
antes de cada hash de commit define que es lo que va a pasar durante el rebase. Noda que los commits están listados desde el más viejo al más reciente. Alterando este listado, ganas control completo sobre la historia del proyecto.
Si quieres cambiar el orden de los commits, simplemente reordena las líneas.
Si quieres cambiar el mensaje de un commit, usa el comando reword
.
Si quieres combinar dos commits, cambia el comando pick
por
squash
. Esto desplegará todos los cambios en ese commit en el
commit de arriba. Por ejemplo, si aplicaste un squash a el segundo commit en la
lista de arriba, la rama feature
se verá como la siguiente después de guardar y cerrar el editor:



El comando edit
es particularmente poderoso. Cuando alcanza el
commit especificado, Git pausará el procedimiento de rebase, muy parecido a
como cuando encuentra un conflicto. Esto te da la oportunidad de alterar el
contenido del commit con git commit --amend
o incluso agregar más
commit con los comandos estándar git add
/git commit
.
Cualquier commit nuevo que agregues será parte de la nueva rama.
Hacer un rebases interactivos puede tener un impacto profundo en tu flujo de
trabajo. En lugar de preocuparte por romper cambios encapsulados en commits,
puedes enfocarte en escribir código. Si terminas commiteando algo que debería
ser un solo cambio en diferentes instantáneas, entonces ya no es un problema
—reescribe la historia con git rebase -i
y aplicales un squash a todos para tener un commit más significativo.
3. Peligros de hacer rebase
Ahora que tienes un entendimiento de git rebase
, podemos hablar de cuándo no usarlo. Internamente, hacer un rebase no mueve los commits en una nueva rama. En su lugar, crea nuevos commits que contienen los cambios deseados. Con esto en mente, hacer un rebase se puede visualizar mejor como lo siguiente:



Después de hacer un rebase, los commits en feature
tendrán diferentes hashes. Esto significa que no solo posicionamos una rama —literalmente, reescribimos la historia del proyecto. Esto es un efecto secundario muy importante de git rebase
.
Cuando estás trabajando solo en un proyecto, reescribir la historia no es
asunto importante. Sin embargo, tan pronto como empiezas a trabajar en un
ambiente colaborativo, puede convertirse en algo peligroso. Si reescribes
commits que otros desarrolladores están usando (e.g commits en la rama
master
), se verá como si esos commits hayan desaparecido la próxima vez que ellos traten de jalar tu trabajo. Esto resulta en un escenario confuso del cual es difícil recuperarse.
Con esto en mente, nunca deberías hacer un rebase de commits que han sido empujados a un repositorio público a menos que estés seguro que nadie ha basado su trabajo en ellos.
Conclusión
Este tutorial introdujo los dos usos más comunes de git rebase
.
Hablamos mucho acerca de mover ramas, pero ten en cuenta que hacer rebase realmente se trata de controlar la historia de tu proyecto. El poder de reescribir commits después de los hechos te libera para enfocarte en tus tareas de desarrollo en lugar de separar tu trabajo en instantáneas.
Nota que hacer rebase es totalmente un agregado opcional a tu caja de
herramientas de Git. Puedes hacer todo lo que necesitas con comandos git
merge
. De hecho, esto es más seguro, ya que evita la posibilidad de
reescribir la historia pública. Sin embargo, si entiendes los riesgos,
git rebase
puede ser una forma mucha más limpia de integrar ramas comparado con commits de merge.
¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!