miércoles, febrero 24, 2016

Mercurial y control de versiones

En cada proyecto de desarrollo debemos contar con una herramienta para el control de versiones. Mercurial es actualmente mi favorito, hace años fue cvs y luego svn.

Haremos un simple ejercicio para mostrar funciones básicas de uso frecuente con ésta herramienta.  No he probado como funcionarían éstos ejemplos en Windows, pero si funcionan en sistemas POSIX.  Unix, Linux y OS X.


Uso básico de Mercurial (hg)

1 $
2 $
3 $
4 $
5 $
mkdir -p ~/workspace/hgtests
cd $_
echo "prueba a" > a.txt
echo "esta es otra prueba a" > b.txt
echo "debería llamarse 'prueba b'" >> b.txt

En la línea 1 y 2 se crea un directorio de pruebas y nos movemos dentro de él.
Líneas 3, 4 y 5 agregamos contenido a dos archivos de texto.

 1 $
 2 $
 3
 4
 5 $
 6
 7
 8 $
 9
10
hg init
hg st
? a.txt
? b.txt
hg add
adding a.txt
adding b.txt
hg ci -m"Init commit"
abort: no username supplied
(use "hg config --edit" to set your username)

Línea 1, iniciamos el repositorio.
Línea 2, pedimos el estatus (st) del repositorio.  Ahí muestra dos archivos que no reconoce '?'
Línea 5, agregamos todos los archivos desconocidos al repositorio
Línea 8, hacemos commit, que guarda el estado actual de los archivos en el repositorio

Si es la primera vez que usas mercurial te pedirá que agregues el nombre del usuario que está haciendo el commit.  Recomiendo hacerlo en la configuración global ~/.hgrc

~/.hgrc
1
2
[ui]
user = Jupabeans <jupabeans@gmail.com>

Nuevamente hacemos commit

1 $
2 $
3 $
4
5
6
7
8
hg ci -m"Init commit"
hg st
hg log
changeset:   0:39c7ddc9da96
tag:         tip
user:        Jupabeans <jupabeans@gmail.com>
date:        Wed Feb 24 11:11:56 2016 -0600
summary:     Init commit

Línea 2, vemos que el estatus es 'nada'
Línea 3, pedimos la bitácora de cambios (changesets)

El primer changeset tiene como consecutivo 0 y el hash del nodo del commit.

Hagamos cambios en los contenidos de los archivos

 1 $
 2 $
 3
 4
 5 $
 6
 7
 8
 9
10
11
12
13 $
14 $
15
sed -i.bak 's/esta/ésta/g' b.txt
hg st
M b.txt
? b.txt.bak
hg diff
diff -r 39c7ddc9da96 b.txt
--- a/b.txt Wed Feb 24 11:11:56 2016 -0600
+++ b/b.txt Wed Feb 24 13:32:24 2016 -0600
@@ -1,2 +1,2 @@
-esta es otra prueba a
+ésta es otra prueba a
 debería llamarse 'prueba b'
hg ci -m"QF: Agregando acento"
hg st
? b.txt.bak

Línea 1, usando sed reemplazamos esta -> ésta en b.txt
Línea 2, pedimos el estatus del repositorio y ahora observamos que b.txt está marcado como Modified y el archivo de respaldo generado por sed está como desconocido '?'
Línea 5, pedimos la diferencia del estado actual del proyecto respecto al que tenemos almacenado en el repositorio.
Línea 10 y 11, muestra la diferencia entre antes y después
Línea 13, hacemos nuevo commit.  Notar que no estamos agregando el nuevo archivo de respaldo al repositorio
Línea 14, pedimos el nuevo estatus y ahora sólo muestra el archivo desconocido

No tenemos por qué guardar archivos de respaldo en el repositorio ya que para eso mismo lo estamos utilizando y aunque me gusta conservarlos por un tiempo en el directorio de trabajo, no quiero que me estén apareciendo cada vez que pido el estatus.

Para evitar que se muestren, podemos agregar una lista de extensiones que queremos ignorar agregando el archivo .hgignore en la raíz del directorio de trabajo.

.hgignore
# use regexp syntax
syntax: regexp
.bak$

Con una expresión regular le estamos diciendo que todos los archivos que terminen en '.bak' sean ignorados.

1 $
2
hg st
? .hgignore

Al pedir el estatus sólo marca que no reconoce .hgignore, conviene agregarlo.

 1 $
 2
 3 $
 4 $
 5 $
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
hg add
adding .hgignore
hg ci -m"Agregando .hgignore"
hg st
hg log
changeset:   2:a56e1bbd4035
tag:         tip
user:        Jupabeans <jupabeans@gmail.com>
date:        Wed Feb 24 13:53:44 2016 -0600
summary:     Agregando .hgignore

changeset:   1:c1bfab6436ef
user:        Jupabeans <jupabeans@gmail.com>
date:        Wed Feb 24 13:33:02 2016 -0600
summary:     QF: Agregando acento

changeset:   0:39c7ddc9da96
user:        Jupabeans <jupabeans@gmail.com>
date:        Wed Feb 24 11:11:56 2016 -0600
summary:     Init commit

Línea 1, agregamos archivos desconocidos al repositorio
Línea 3, hacemos commit
Línea 4, pedimos el estatus, nos los muestra sin cambios
Línea 5, pedimos la bitácora.  Observamos la conveniencia de tener comentarios descriptivos en cada commit que hagamos.

Modificamos un archivo y agregamos otro

 1 $
 2 $
 3 $
 4
 5
 6 $
 7
 8
 9
10
11
12
13 $
14
15 $
16 $
echo "el tercer archivo" > c.txt
vim a.txt
hg st
M a.txt
? c.txt
hg diff
diff -r a56e1bbd4035 a.txt
--- a/a.txt Wed Feb 24 13:53:44 2016 -0600
+++ b/a.txt Wed Feb 24 15:08:16 2016 -0600
@@ -1,1 +1,2 @@
 prueba a
+¡exitosa!
hg add
adding c.txt
hg ci -m"Agregando tercer archivo y calificando prueba a"
hg st

Línea 1, agregamos un tercer archivo, c.txt
Línea 2, modificamos el contenido de a.txt
Línea 3, al pedir el estatus, nos muestra que hemos modificado a.txt y que no conoce c.txt
Línea 6, pedimos la diferencia y observamos ahora que sólo agrega una línea (12)
Línea 13, agregamos los archivos desconocidos, c.txt
Línea 15, hacemos commit
Línea 16, comprobamos el nuevo estatus del repositorio

Así podemos trabajar modificando nuestros archivos y agregando nuevos como vayamos creciendo nuestro proyecto.

¿Qué pasa cuando tenemos un error y queremos regresar a un estado anterior?


1 $
2
3 $
4
5 $
6
7 $
8
ls
a.txt     b.txt     b.txt.bak c.txt
hg update -r 2
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
ls
a.txt     b.txt     b.txt.bak
cat a.txt
prueba a

Línea 1, pido la lista de archivos en el directorio
Línea 2, me muestra cuatro archivos, a.txt, b.txt, c.txt y b.txt.bak que aunque no esté en el repositorio, sigue en el directorio de trabajo
Línea 3, pido que 'actualice' mi directorio de trabajo a la revisión (changeset) 2
Línea 4, muestra que un archivo ha sido actualizado y otro a sido removido
Línea 5, pido nuevamente la lista
Línea 6, me muestra 3 archivos, a.txt, b.txt y b.txt.bak, c.txt no estaba presente en ésta revisión por lo que ha sido eliminado del directorio de trabajo
Línea 7, pido los contenidos de a.txt
Línea 8, sólo muestra la línea original de nuestro archivo

Por ahora sólo veremos el estado pero no realizaremos ninguna modificación, pues ello crearía una rama anónima (branch) y esas las veremos después.

Regresemos a la última revisión


1 $
2
3 $
4
5 $
6
7
hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
ls
a.txt     b.txt     b.txt.bak c.txt
cat a.txt
prueba a
¡exitosa!

Línea 1, pido actualización de mi directorio de trabajo y me muestra que dos archivos han sido actualizados
Línea 3, pido la lista de archivos en el directorio
Línea 4, me muestra que c.txt ha regresado al directorio de trabajo
Línea 5, pido el contenido de a.txt y ahora muestra la línea que agregamos anteriormente

Ahora podemos seguir trabajando con nuestros archivos.  Haciendo commit cada vez que realicemos cambios relevantes tendremos siempre la oportunidad de revisar o regresar a un estado anterior ya conocido.

Los sistemas de control de versiones  son una herramienta que todo programador debe de conocer, hay varios sabores, Mercurial es el que me gusta,  git goza de mucha popularidad, es muy bueno y muy parecido.

No hay comentarios.: