Reorganizar el Trabajo Realizado

Rebase

Reorganizar el Trabajo Realizado

En Git hay dos formas de integrar cambios de una rama en otra: merge y rebase. En esta seccion veremos que es la reorganizacion (rebase), como hacerla, por que es una herramienta bastante util, y en que casos no conviene usarla.


Reorganizacion Basica

Volvamos al ejemplo de la seccion de fusion. Tenemos dos ramas divergentes: master y experiment. La forma mas sencilla de integrar las ramas es merge, que realiza una fusion entre las dos ultimas instantaneas de cada rama (C3 y C4) y su ancestro comun (C2), generando una nueva confirmacion con dos padres.

Historial divergente entre master y experiment
Fusion mediante merge

Sin embargo, tambien puedes tomar los cambios introducidos en C4 y reaplicarlos encima de C3. En Git, esto se llama reorganizar (rebase). Con el comando git rebase, puedes tomar todos los cambios confirmados en una rama y reaplicarlos sobre otra:

git checkout experiment
git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
Rebase de experiment sobre master

Lo que hace es ir al ancestro comun de las dos ramas (experiment y master), calcular las diferencias introducidas por cada confirmacion de experiment, guardar esas diferencias en archivos temporales, actualizar la rama experiment al mismo punto que master, y finalmente aplicar ordenadamente cada diferencia.


Ahora solo queda volver a master y hacer un fast-forward:

git checkout master
git merge experiment

El resultado final es identico al de la fusion, pero el historial queda mas limpio: parece que todo el trabajo se hizo en serie, aunque en realidad se hizo en paralelo.

Fast-forward de master tras el rebase

El rebase reproduce confirmaciones en el mismo orden en que fueron creadas, mientras que la fusion toma los puntos finales y los une.


Reorganizaciones mas Interesantes

El rebase puede aplicarse sobre una rama que no sea su base directa. Supongamos un historial como este: una rama server creada a partir de master para agregar funcionalidades del lado del servidor, y una rama client creada a partir de server para cambios del lado del cliente.

Historial con rama client sobre server sobre master

Quieres integrar los cambios de client en master para publicarlos, pero dejar los cambios de server aparte hasta que esten mejor probados. Puedes usar git rebase --onto para tomar los commits de client que no estan en server y reaplicarlos sobre master:

git rebase --onto master server client

Esto significa: “toma la rama client, averigua los cambios desde que divergio de server, y aplicalos sobre master.

Rebase de client sobre master usando --onto

Ahora puedes hacer fast-forward de master:

git checkout master
git merge client
master tras hacer fast-forward con client

Cuando los cambios de server esten listos, puedes reorganizarlos sobre master sin tener que hacer checkout antes:

git rebase master server

Esto significa: “toma la rama server, averigua los cambios desde que divergio de master, y aplicalos sobre master.

Rebase de server sobre master

Y finalmente integrar y limpiar:

git checkout master
git merge server
git branch -d client
git branch -d server
Rebase de server sobre master
Historial final limpio

Ejercicio 01

Rebase en accion

  • Descargar targz
  • Descomprimir con tar -xzf 10-rebase-onto.tar.gz
  • Entrar a la carpeta con cd 10-rebase-onto/
  • Abre tu editor de codigo (ej: code .) y lee el README.md

Los Peligros del Rebase

El rebase tiene un coste: nunca reorganices confirmaciones que ya hayas enviado a un repositorio publico.

Si reorganizas commits que otros ya tienen como base de su trabajo, les estaras causando problemas serios. Cuando reorganizas, Git descarta las confirmaciones originales y crea otras nuevas que son similares pero distintas. Si alguien tenia trabajo basado en esas confirmaciones originales, tendra que refusionar su trabajo, y el historial se volvera un caos.

Ejemplo

Clonas desde un servidor central y haces algo de trabajo local. Alguien mas hace trabajo, incluyendo una fusion, y lo envia al servidor. Haces git fetch y obtienes esas confirmaciones. Luego fusionas ese trabajo en tu rama local.

Clonas un repositorio y haces trabajo local
Otro colaborador sube trabajo fusionado

Pero entonces esa persona decide reorganizar su trabajo en vez de fusionar, y hace git push --force sobreescribiendo el historial en el servidor. Cuando vuelves a hacer git fetch, obtienes las nuevas confirmaciones reorganizadas. Ahora ambos estan en un lio. Si haces git pull, crearas una fusion confirmada que incluye ambas lineas del historial, y el repositorio quedara con confirmaciones duplicadas (el trabajo original y su version reorganizada), lo cual genera confusion.

Fusionas el trabajo remoto en tu rama
El colaborador sobreescribe el historial con rebase

Reorganizar una Reorganizacion

Si te encuentras en esta situacion, Git tiene algunos recursos que pueden ayudarte. Ademas del checksum SHA-1 del commit, Git calcula un checksum basado en el parche que introduce cada confirmacion — el llamado patch-id.


Si te traes el trabajo sobreescrito y lo reorganizas sobre las nuevas confirmaciones de tu companero con git rebase teamone/master, Git puede identificar automaticamente que parte correspondia a tu trabajo y aplicarla de vuelta. En vez de una nueva fusion confusa, el resultado sera un historial mas limpio.

Historial duplicado tras fusionar trabajo reorganizado

Esto solo funciona si los parches de tu companero son muy similares a los originales. Tambien puedes simplificar el proceso usando git pull --rebase en vez del git pull tradicional, o hacerlo manualmente con git fetch seguido de git rebase teamone/master.


Si sueles usar git pull y quieres que --rebase este activado por defecto:

git config --global pull.rebase true

Si solo reorganizas confirmaciones que nunca han estado disponibles publicamente, estaras bien. Si reorganizas confirmaciones sobre las que otros han basado su trabajo, prepárate para problemas.


Rebase vs Merge

Ahora que has visto ambas herramientas, te preguntaras cual es mejor. Para responder, vale la pena pensar en que representa el historial.


Para algunos, el historial de un repositorio es un registro de todo lo que ha pasado: un documento historico valioso por si mismo que no deberia alterarse. Cambiar el historial de confirmaciones es casi como falsificar la historia.


Para otros, el historial es la historia de como se hizo el proyecto: algo que se edita y refina antes de publicar, igual que un borrador de un libro o un manual de instrucciones.


No hay una respuesta unica correcta. Git es una herramienta poderosa y cada equipo decide segun su contexto.

En la practica

Reorganiza libremente en ramas locales o en ramas puntuales que solo tu estes trabajando. Evita el rebase en ramas de largo recorrido compartidas (master, develop, etc.) o en cualquier rama sobre la que alguien mas haya basado su trabajo.


Ejercicio 02

Rebase: conceptos y peligros

Entrar al kahoot y esperar el codigo de la sala, o presionar la opcion “Aprende” para responder de forma individual.


Recursos