Arreglando el histórico en git

Si usamos git en el día a día, probablemente nos habremos encontrado alguna vez con que olvidamos configurar nuestro user.name y/o user.email, el sistema coge valores por defecto y acabamos teniendo commits con autor o dirección de correo inválidos para el repositorio remoto (sea Github, Gerrit, BitBucket o cualquier otro). En tal caso hay que arreglar todos los commits incorrectos sí o sí.

Arreglar el último commit

Esta fiesta es la más sencilla. Para arreglar el último commit basta con hacer:

git commit --amend --author "New Author Name <email@address.com>"

Arreglar varios commits o un commit antiguo

Vale. La hemos cagado. Y ahora nos encontramos con miles de líneas de código que hemos ido comiteando sin hacer push, y de pronto tenemos a git rechazando el push porque el commiter es incorrecto para el repositorio remoto, es decir, su dirección de correo  y/o autor no los reconoce.

Podríamos decirnos que estamos jodidos y tratar de endosarle el marrón a otro, pero la verdad es que el problemón tiene solución. Es un poco pesada y usa algunas cosas de git que son peligrosas (puedes cambiar el orden de los commits de todo el repo jodiendo al resto de desarrolladores de tu equipo), pero siguiendo estas instrucciones con cuidado igual salvamos el culo…

En primer lugar hay que encontrar el commit más reciente en el repositorio local anterior a todos los que tienen mal el commiter. Por ejemplo, supón el siguiente histórico:

afffg2 patxi@gmail.com
cdfg34 jorge@gmail.com
3f55a4 patxi@gmail.com
345dg8 pepito@gmail.com
658dff patxi.gortazar@gmail.com
... a partir de aquí todo ok

Supongamos que hay que apañar el mail de patxi@gmail.com y cambiarlo por patxi.gortazar@gmail.com. El commit más reciente anterior a los que tienen el email patxi@gmail.com que hay que cambiar es el 345dg8 de pepito@gmail.com.

Ahora hacemos un git rebase con este commit como referencia:

git rebase -i -p 345dg8

Se abre un editor de texto que nos muestra todos los commits desde el 345dg8 en adelante. Cada commit está en una línea y comienza por «pick». Tenemos que ver cuáles queremos cambiar y sustituir «pick» por «edit» y guardar. La cosa debería quedar tal que así:

edit afffg2 patxi@gmail.com
pick cdfg34 jorge@gmail.com
edit 3f55a4 patxi@gmail.com
pick 345dg8 pepito@gmail.com

Guardamos y salimos. Git nos informa de cómo proceder, básicamente vamos a ir avanzando haciendo commits parando en cada uno de los commits que le hemos dicho que queremos editar. Estos commits los apañaremos y seguiremos adelante con el proceso hasta el final.

Vamos paso por paso. En primer lugar, git habrá avanzado por defecto hasta el primer commit: 3f55a4. Vamos a arreglar este commit. Para ello usamos «git commit –amend» indicando el nuevo autor y correo electrónico:

git commit --amend --author "Patxi Gortázar <patxi.gortazar@gmail.com>"

Se abre un editor por si queremos cambiar el mensaje del commit. No tocamos nada, simplemente guardamos y salimos.

Ahora avanzamos hasta el siguiente commit que queremos arreglar:

git rebase --continue

Git se para en el commit afffg2. Arreglar este commit de la misma forma:

git commit --amend --author "Patxi Gortázar <patxi.gortazar@gmail.com>"

Aunque hemos arreglado todos los commits, aún le tenemos que decir a git que avance hasta el final, dejando el repo con los mismos commits que tenía antes de comenzar:


git rebase --continue

Listo. Prueba a hacer un pull (por si acaso) y un push.

Probando git

Quizá algunos ya habréis tomado nota, por si acaso lo dejo dicho aquí: el otro día Alex Blewitt publicaba un fantástico post sobre git y egit (el plug-in de Eclipse que proporciona soporte integrado para git). En realidad el post versa casi exclusivamente sobre git. Merece la pena echarle cinco minutos para comprender cómo puede cambiar nuestra forma de desarrollar software con un sistema de control de versiones distribuido (DVCS – Distributed Version Control System).

Algo que me ha llamado bastante la atención es lo ágil que es trabajar con git. En primer lugar, git utiliza repositorios locales. Podemos trabajar con repositorios remotos, pero normalmente tenemos una copia local de éstos.
La forma de trabajar con git es diferente a como hacemos normalmente con un sistema de control del código centralizado como Subversion. En git cada desarrollador tiene una copia del repositorio, y puede evolucionarla como quiera. A priori esto puede parece un caos, pero es realmente ágil y versátil. Debido a la forma en que git encadena los commits (échale un vistazo al post de Blewitt para más detalles, es muy gráfico), es posible hacer merges mucho más sofisticados, por lo que esta evolución paralela realmente no supone un problema.

En sidelab llevamos ya muchos meses discutiendo acerca de la necesidad de migrar a git uno de nuestros proyectos software. El proyecto en concreto es JMH, actualmente alojado en un Subversion en Sourceforge. JMH es un framework Java que comenzó a desarrollar Micael y que posteriormente hemos ido evolucionando conjuntamente desde que me incorporé a la línea de investigación en la que él estaba trabajando.

Para que se entienda nuestra necesidad de cambiarnos de svn a git comentaré un poco cuál es la casuística que tenemos ahora mismo entre manos. Actualmente, ambos trabajamos en la misma línea de investigación: nuestra tarea consiste en tratar de obtener soluciones de buena calidad en problemas que son computacionalmente complejos. Este tipo de problemas no suele ser posible resolverlos de manera exacta, obteniendo el mejor valor posible (o valor óptimo). Por tanto, se intenta diseñar algoritmos aproximados que sean capaces de dar soluciones de calidad en tiempos razonables.

En general, tanto Mica como yo trabajamos en problemas diferentes, pero ambos nos apoyamos en el framework JMH, porque hay muchas cosas ya hechas. Por ejemplo, tenemos el framework de experimentación ya montado. También tenemos un conjunto de clases que nos ayudan a construir análisis de los resultados. ¿Cuál es el problema? Que con bastante frecuencia ambos nos encontramos con la necesidad de cambiar esto o aquello del framework, y normalmente los cambios o bien afectan al otro, o bien afectan a alguno de los problemas en los que hemos trabajado en el pasado (y que nos gustaría que siguieran funcionando en el futuro). Necesitamos tener la posibilidad de evolucionar jmh por separado.

En este sentido git es un sistema muy adecuado. En git no existe el concepto de repositorio central (aunque podría haberlo en algún sitio) y es muy habitual que alguien clone el repositorio público de otro para evolucionar el código como le apetezca. La ventaja es que si yo quiero beneficiarme de los cambios de otro, esto también es muy sencillo. Basta que apunte mi git a la url del git del otro desarrollador para que introduzca sus cambios en mi código.

Yo, por lo pronto ya tengo Egit instalado en mi Eclipse. En la página del proyecto tienen un manual muy completo para empezar. He seguido los pasos al pie de la letra y ya tengo mi repositorio git, y mi repositorio remoto en github donde puedes encontrar un esbozo de API (de momento pruebas, nada más) para consultar un Bugzilla mediante la API de servicios web que nos ofrece. Puedes hacer pull desde este repositorio utilizando esta URI: git://github.com/gortazar/es.sidelab.api.bugzilla.git. Por cierto que github tiene un importador de Subversion.

Mi problema es que me he acostumbrado a utilizar Mylyn-Bugzilla-Subversive y me gustaría tener una asociación similar Mylyn-Bugzilla-Git. Echando un ojo por ahí, me he encontrado con este conector de Mylyn para github. También podría seguir utilizando Sourceforge, sin embargo, su lentitud es desesperante. Y la buena noticia es que parece que en el momento en que egit termine de madurar, van a proporcionar soporte en Mylyn para gestionar los changesets como hacen con Subversive.

Ahora bien, miedo me da poder evolucionar mi repositorio tanto como quiera. Me conozco y haré pocos push a mi repositorio remoto. Habrá que volver a preocuparse por las copias de seguridad. No será la primera vez que perdemos un disco duro.