Recetas Git: Deshacer el último commit

Cuando necesitemos deshacer el último commit que hayamos realizado (y que no haya sido pusheado al servidor) debemos hacer uso del commando reset para dejar las cosas tal y como estaban justo antes de hacer el git commit. Para ello no tenemos más que hacer:

git reset --soft HEAD^

Después de este comando tendremos nuestro working directory exactamente como lo teníamos justo antes de realizar el commit, con todos los ficheros añadidos que formaban parte del commit, etc.

Recetas Git: Log de commits locales

A veces, cuando necesitamos ver el log de commits que de nuestro repositorio, nos interesa conocer qué commits son locales, es decir, cuales de ellos no están en el servidor remoto. ¿Por qué necesitamos saberlo? Porque son esos commits locales los que podemos modificar sin temor alguno, ya que no han sido subidos al servidor remoto y no hay peligro de que provoquemos conflictos con otra persona que se los haya podido bajar a su máquina local.

Para conseguir un log con los commits locales no tenemos más que especificárselo a nuestro comando git log:

git log master --not --remotes=*/master

Con eso conseguimos ver los commits de la rama local master que no están en ninguna de las ramas master de los remotes configurados en el repositorio.

Actualización 17/05/2011 – 14:46:

También puede conseguirse con:

git log origin/master..master

Recetas Git: Mi log de commits

Una de las cosas buenas que tiene Git es su capacidad de ser configurado al gusto del usuario. Como ejemplo os dejo el alias que he creado en mi sistema para mostrar el log de commits de una manera personalizada.

Para añadirlo debemos editar nuestro fichero .gitconfig global, situado en el directorio raiz de nuestro usuario. Dentro de ese fichero tendremos una sección [alias] (si no existe no hay más que crearla) y le añadimos nuestro alias personalizado, en este caso lg:

[alias]
    lg = log --pretty=format:'%C(white)[%C(yellow)%h%C(white)] %C(blue)(%ad) %C(white)%s %C(green)%cn' --date=local

Gracias a esto, cuando hagamos git lg veremos un log personalizado:

jghurtado@macbook:~/Sites/sample-project
> git lg
[9ac7d19] (Mon May 9 09:44:54 2011) Adds a sample product page Juan G. Hurtado
[c8be333] (Fri May 6 12:38:38 2011) Adds sample listing products page Juan G. Hurtado
[2c49ce1] (Thu May 5 12:02:51 2011) Adds breadcrumbs styles Juan G. Hurtado
[697d682] (Thu May 5 11:55:34 2011) Adds styles for horizontal separators on boxes Juan G. Hurtado

Recetas Git: Modificar un commit antiguo

Cuando estamos trabajando en un proyecto versionado con Git a veces nos encontramos con que necesitamos modificar un commit que hicimos hace algún tiempo, pero que aún no hemos enviado al servidor (esto es importante, ya que no debemos modificar commits a los que ya hemos hecho push). ¿Cómo podemos hacerlo? Con un rebase interactivo.

Para empezar debemos averiguar el hash del commit anterior al que queremos modificar, para ello no hay más que hacer un git log y copiar el hash que nos interese:

> git log
[9ff028c] (Thu May 5 12:02:51 2011) Adds breadcrumbs styles -  Juan G. Hurtado
[61b5536] (Thu May 5 11:55:34 2011) Adds styles for horizontal separators on boxes - Juan G. Hurtado
[ac44e68] (Thu May 5 09:47:08 2011) Adds server messages styles - Juan G. Hurtado
[ff9c592] (Thu May 5 09:37:26 2011) Adds boxes to sample page - Juan G. Hurtado

Por ejemplo, si queremos modificar el commit
“Adds styles for horizontal separators on boxes”

, cuyo hash es
“61b5536″

, debemos quedarnos con el hash del commit anterior, que es el
“ac44e68″

. Una vez que lo tenemos ya podemos comenzar el rebase interactivo:

> git rebase -i ac44e68

Este comando nos abrirá el editor para Git que tengamos definido en el sistema con un texto parecido a este:

pick 61b5536 Adds styles for horizontal separators on boxes
pick 9ff028c Adds breadcrumbs styles

# Rebase ac44e68..9ff028c onto ac44e68
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

La primera parte del texto es un listado de los commits que van a formar parte del rebase, y lo que quieres hacer con ellos. Por defecto todos los commits tienen pick, que significa que no se hará nada con ellos, se quedarán igual. La segunda parte del texto es una ayuda que indica lo que puedes hacer con los commits: pick, reword, edit, squash, fixup y exec. En nuestro caso necesitamos editar un commit, así que usaremos edit. Editamos el texto, modificando la linea de nuestro commit para que quede tal que:

edit 61b5536 Adds styles for horizontal separators on boxes

Cuando salvemos y cerremos el editor con el texto modificado comenzará el rebase con las opciones que le hayamos indicado, en este caso editar un commit. Cuando editamos un commit lo que hace Git es movernos en el tiempo y llevarnos al proyecto en el estado que estaba justo después de hacer el commit que queremos editar. Estando en este momento temporal podemos hacer los cambios que necesitemos y añadirlos a nuestro commit como se hace normalmente:

> git add .
> git commit --amend

Cuando ya tenemos modificado el commit a nuestro gusto, continuamos con el rebase:

> git rebase --continue

Y ya ha quedado modificado el commit sin haber alterado su orden, sin haber hecho un commit nuevo, etc. No obstante es importante recordar que esto sólo podemos hacer con commits que aún no se hayan enviado al servidor (a los que no se haya hecho push).

Git y los sub-módulos

A pesar de que en la oficina usamos SVN, llevo un tiempo usando Git para el control de versiones de mis chorradillas (gracias a la bendita ayuda de Antonio) y debo decir que estoy más que contento. Es todo un gustazo utilizarlo, incluso sin interfaz gráfica, directamente desde la línea de comandos (con ZSH + oh-my-zsh, por supuesto).

Una de las cosas que estoy haciendo con Git es utilizar su sistema de sub-módulos para integrar proyectos dentro de proyectos. Es decir, tener un proyecto versionado con Git dentro de otro proyecto también versionado (à la Inception).

Os pondré como ejemplo el theme de este blog, que lo tengo versionado con Git. En él hago uso de Flaminwork (un framework del que ya os hablaré en otro post) que es un proyecto Git en si mismo. ¿No sería estupendo poder obtener las últimas novedades en el Flaminwork del theme directamente desde el repositorio central? Es muy sencillo conseguirlo con los sub-módulos de Git:

# Entramos en el directorio de nuestro proyecto ya versionado con Git
> cd coloresefimeros/

# Añadimos nuestro sub-módulo desde el repositorio remoto,
# metiendo su contenido en el directorio "flaminwork"
> git submodule add git@github.com:juanghurtado/flaminwork.git flaminwork

# Si accedemos al directorio "flaminwork" nos encontraremos en el
# repositorio de Flaminwork, no en el del theme
> cd flaminwork/

# Y cada vez que queramos actualizarlo, no tendremos más que hacer un pull
> git pull

Este sistema es genial para poder actualizar de forma sencilla proyectos como Flaminwork que, por su naturaleza, se utilizan siempre dentro de otros proyectos.

Pero aún hay más: Flaminwork, al tratarse de un framework front-end, no tiene forma de ser testeado salvo en proyectos reales (como es el caso del theme de este blog), así que si durante su uso en el theme encuentro algo que solucionar en Flaminwork no necesito abrir el proyecto, editarlo, subir los cambios y bajármelos en la copia de Flaminwork que tengo en el theme… Lo puedo hacer directamente en el sub-módulo que he creado, porque ese ya es el proyecto en si. Tan sencillo como realizar los cambios y hacer push desde el submódulo:

# Accedo al directorio del submódulo dentro del theme
> cd coloresefimeros/flaminwork/

# Realizo los cambios
> ....

# Y los subo al repositorio central en GitHub
> git push

# Como también quiero que el theme guarde los cambios
# realizados en el sub-módulo, también hago commit del
# proyecto padre
> cd ..
> git add .
> git commit -m 'Update Flaminwork to v0.1-RC1'
> git push

¿Fácil, verdad? Si hasta yo que no soy más que un maquetador web puede hacer esto, cualquiera puede hacerlo y beneficiarse de todas las ventajas que ofrece Git, que no son pocas. Ya os iré contando más cositas conforme vaya aprendiendo, mientras tanto os recomiendo que le echéis un ojo al libro Pro Git, de Scott Chacon, disponible on-line de forma gratuita.

Gestionar tus tareas con todo.txt

En mi búsqueda de organización personal en lo relativo a las cosas que tengo que hacer (que no son muchas, la verdad sea dicha) he probado todo tipo de sistemas, desde Evernote hasta Remember de Milk, pasando por ficheros de texto plano gestionados desde TextMate con un bundle llamado Tasks. Desgraciadamente ninguno de estos me convenció.

Afortunadamente la red está constantemente sorprendiéndome, y el otro día no recuerdo cómo me topé con Todo.txt, el proyectito personal de Gina Trapani, la editora de LifeHacker (el mayor blog sobre tips y productividad que conozco).

Todo.txt es un gestor de tareas pero destinado a su uso desde la terminal del sistema. Vamos, lo que vienen siendo comandos de toda la vida. Por ejemplo, una vez instalado todo siguiendo las instrucciones disponibles en su wiki, no tenemos más que empezar a añadir tareas a nuestro fichero:

t add "Escribir un post sobre todo.txt #blog @casa"

Tan sencillo como veis. Y además gracias al uso de etiquetas en nuestras tareas podemos más tarde listarlas de manera filtrada de la siguiente manera:

t ls #blog

Lo que hace especial a este sistema, aparte de su sencillez si estás acostumbrado a teclear ordenes a tu ordenador en lugar de a usar el ratón, es que las tareas acaban guardándose en un fichero de texto plano con un formato determinado.

Es esta última feature la que me llama la atención, ya que me permite guardar el fichero con todas las tareas en un directorio sincronizado con Dropbox, pudiendo así estar al alcance de todas las máquinas desde las que trabajo.

Podéis aprender más sobre este sistema echándole un ojo a su web oficial, o directamente en su repositorio en GitHub.