Rebase en Git

Está claro que Git es el control de versiones de moda, los tiempos de CVS quedan ya muy lejanos y la gente, poco a poco va migrando del sistema más utilizado, subversion, a sistemas distribuidos como puedan ser Git, Mercurial o Bazaar. 

Como Drupal hace un tiempo que está utilizando Git para sus repositorios oficiales, la mayoría de drupaleros hemos optado por esta opción y a la hora de controlar nuestro código, hay muchas alternativas de uso, siendo Gitflow una de las que más convence.

El término rebase en Git tiene dos acepciones muy diferenciadas que voy a intentar resumir aquí y que pueden resultar confusas.

Rebase utilizado como parámetro de pull

Cuando hacemos git pull, nos traemos los cambios del repositorio que elijamos o tengamos configurado, y uno de los parámetros que podemos utilizar es rebase:

git pull --rebase

¿Qué hace esto? Al realizar un pull estamos haciendo un fetch y justo después un merge, pasándole la opción rebase, git intentará traer todos los cambios y después aplicar nuestras modificaciones encima en lugar de intentar hacer un merge desde el punto en el que estábamos, esto hará que nuestro histórico tenga mucho mejor aspecto. Veámoslo en acción.

Para hacer la prueba me he hecho un fork del repositorio Spoon-Knife en github, cuyo propósito es hacer tests.

Clonamos y accedemos:

git clone git@github.com:pcambra/Spoon-Knife.git cd Spoon-Knife

Utilizando una herramienta de visualización o git log, podemos ver la situación actual. 

Para mostrar las diferencias entre git pull y git pull --rebase, voy a hacer un checkout de un cambio antiguo, crearé un fichero nuevo y le haré commit.

git checkout ed12290ba92e1b0a932f5250e06699a0abf47e84 
touch mifichero.txt
git add mifichero.txt
git commit -m "Commit inicial de un fichero vacío"

Con lo que el repositorio queda así:

Ahora ya que intencionadamente de alguna manera hemos forzado un conflicto, metiendo un commit en un estado "antiguo", si hacemos un pull (recordemos que es fetch + merge), va a hacer un merge automático de nuestros cambios a partir del estado en el que lo hemos hecho, quedando el árbol de nuestro repositorio un tanto lioso:

Ahora bien, si hacemos lo mismo: 

git checkout ed12290ba92e1b0a932f5250e06699a0abf47e84 
touch mifichero.txt
git add mifichero.txt
git commit -m "Commit inicial de un fichero vacío"

Pero en lugar de hacer simplemente pull, hacemos git pull --rebase, primero hará el fetch+merge y luego aplicará nuestros cambios encima, dejándonos una estructura más legible que seguro que agradecemos en el futuro.

Nota: Git no siempre va a poder hacer git pull --rebase, por descontado si hay conflictos, no sobreescribe el código sino que nos dejará editar y entonces forzará hacer un merge manual.

Podéis ver un ejemplo muy similar en este post y también es altamente recomendable esta explicación de Randy Fay.

Comando rebase

El comando rebase hace algo muy diferente; cuando tenemos una serie de commits que queremos asociar juntos, normalmente por la misma razón que el ejemplo anterior, mantener una historia de commits un poco más limpia, podemos utilizar git rebase.

git rebase -i BRANCH/HASH

Pongamos el ejemplo anterior

Y le añadimos un fichero, hacemos commit, pero por ejemplo nos damos cuenta que el fichero está vacío, o no nos acaba de gustar y hacemos múltiples commits, generando un histórico similar a este:

Para juntar todos estos commits en uno solo, podemos utilizar git rebase, el procedimiento sería localizar la rama o el hash anterior al primer commit que queremos agregar y usarlo como parámetro de git rebase:

git rebase -i bdd3996d38d885e18e5c5960df1c2c06e34d673f

Nota: El hash se puede averiguar usando git log o normalmente con cualquier herramienta gráfica de manejo de git.

Lo que nos llevará a una pantalla similar a esta, ya que le hemos indicado con -i que es interactivo. Este fichero lista todos los commits que hemos seleccionado para agregar para que elijamos qué queremos hacer con ellos.

Los commits que queramos conservar los dejamos como pick y los que queramos agregar los marcamos como squash:

Después guardamos y nos llevará a un segundo proceso, en el que nos muestra todos los mensajes de los commits a agregar.

Aquí deberemos escribir el comentario que queramos conservar para nuestro commit agregado.

Guardamos también este fichero y voilà, el resultado es un único commit con el mensaje que hayamos introducido en el paso final y todos los cambios juntos.

Git rebase en más detalle.

Comentarios

Como se guardan los cambios al momento de seleccionar los commit que quiero dejar?

Entré varias veces a mostrar o leer este post y nunca te lo he agradecido como dios manda!

Muy claro el artículo.
A la hora de hacer push puede ser necesario usar -f para que funcione.

Muy buen articulo, me sirvió mucho, gracias.

Muchas gracias por el post, no había encontrado ningún lugar que explicara tan claramente y paso a paso el proceso de rebase en Git. Se agradece por la buena lección didáctica y adelante que al compartir el conocimiento nos hacemos mejores. Saludos !!

Añadir nuevo comentario