cracks for soft
cracksmachine
cracks serials db
crackssearchdb
download rapidshare files
downloadcracksdb
free cracks 4 all
download free serials
dola dll warez
keygen machine
Bisecando con Mercurial (o encontrando changesets con bugs) « il libero

Het downloaden van klingeltöne, Download von klingeltöne, Het downloaden van klingeltöne, Descargar tonos, Téléchargez des sonneries, scarica suonerie, Beltonen downloaden, Nedlasting av ringetoner, Download ringtones

Bisecando con Mercurial (o encontrando changesets con bugs)

Hace un tiempo vengo leyendo y estudiando Mercurial, un sistema de control de versiones distribuido. Sin embargo todo lo que se es teoría, ya que mis amigos (con los que podría probarlo) no quieren dejar Subversion. Pero bueno… sin decir quiénes son, y al saber que ellos leen mi blog, sólos se darán cuenta que están en el error :)

A grandes rasgos (y según lo que yo entendí y veo más notorio), la gran diferencia entre los sistemas de control de versiones centralizados como Subversion o CVS y los distribuidos como Git, Mercurial y Bazaar, es que los primeros imponen más restricciones que sus pares distribuidos. En Subversion por ejemplo, si accedo a mi repositorio vía Internet, y en algún momento no tengo conexión, entonces no puedo hacer commits. Si quiero ver los logs de algunas revisiones o realizar otras operaciones que no sean updates o commits, necesito acceder al repositorio central, lo que es más lento.

En los sistemas centralizados hay un servidor central, y varias copias de trabajo, una por cada desarrollador. En los sistemas distribuidos puede o no haber un servidor central con el repositorio, pero la diferencia es que todos los desarrolladores tienen el repositorio completo, no una copia de trabajo. Es decir que cada persona posee la versión actual más toda la historia del proyecto en su computadora, lo que permite operar en modo offline. Esto puede parecer ineficiente, pero de hecho no lo es: los grandes proyectos de Software Libre como Linux, NetBeans, OpenJDK, Mozilla, y en un futuro GNOME también, usan estos sistemas, ya que ofrecen grandes beneficios (especialmente para los proyectos Open Source). Imagínense hacer commits, crear branches, etc, sin necesidad de conexión. Y cuando termino mi trabajo, luego de revisarlo bien, ahí recién puedo subir todo, o sino decirle a mi colega desarrollador que actualice su repositorio desde el mío.

En definitiva, con un sistema distribuido puedo trabajar con menos restricciones que con uno centralizado.


Estos días estaba leyendo el capítulo 9 de un libro de Mercurial. Uno de los subcapítulos está dedicado a la búsqueda de revisiones o changesets que han introducido bugs. Realizar esto con Mercurial es fácil, ya que tengo todo el repositorio disponible para trabajar offline, por lo que las búsquedas entre changesets son mucho más rápidas.

Imagínense que encontramos un error en nuestro proyecto, y queremos ver en qué revisión fue introducido para realizar un backout del mismo. Para esto, en Mercurial, podemos utilizar el comando “bisect”. Hagamos el ejemplo que se muestra en el libro para ser breves y correctos. Ejecutamos:

$ hg init test
$ cd test

Con un script bash creo automáticamente algunos changesets, e introduzco un bug en el commit 22:

#!/bin/bash

buggy_change=22
for (( i = 0; i < 35; i++ )); do
	if [[ $i = $buggy_change ]]; then
		echo 'i have a gub' > myfile$i
		hg commit -q -A -m 'buggy changeset'
	else
		echo 'nothing to see here, move along' > myfile$i
		hg commit -q -A -m 'normal changeset'
	fi
done

Ejecuto el script en el directorio del repositorio. Esperamos unos momentos a que finalice. Encontrarán varios archivos myfile*. Actualmente estamos, obviamente, en la revisión tip (o HEAD en Subversion):

$ hg parent
changeset:   34:a1c784850dca
tag:         tip
user:        Milton Pividori <miltondp@gmail.com>
date:        Wed Sep 03 10:53:58 2008 -0300
summary:     normal changeset

$

Para saber si en la revisión actual se encuentra el bug, simplemente ejecutamos esto:

$ grep 'i have a gub' myfile*
myfile22:i have a gub
$

Para comenzar las pruebas, ejecutamos:

$ hg bisect --reset

Le indicamos a Mercurial que la revisión tip (o sea, la 34 en nuestro caso, y la actual de la copia de trabajo), es “bad”, ya que contiene el bug (o mejor dicho, posee esa característica de la cual queremos saber en qué changeset se introdujo. Digo esto, porque podríamos querer buscar qué changeset solucionó determinado bug, con lo cual la revisión “bad” sería la que no tiene el error):

$ hg bisect --bad
$

De alguna forma, sabemos que la revisión 10 no tiene el bug, así que la marcamos como “good”. En este caso y a los fines del ejemplo saber esto es sencillo, pero digo “de alguna forma” porque en la práctica debería ser la revisión más tardía sin el bug, lo cual podría llevarnos un tiempo.

$ hg bisect --good 10
Testing changeset 22:3103b305bc80 (24 changesets remaining, ~4 tests)
0 files updated, 0 files merged, 12 files removed, 0 files unresolved
$

Vean que Mercurial nos está diciendo cuántos changesets va a procesar, y la cantidad aproximada de tests que vamos a tener que realizar. Como utiliza búsqueda binaria o dicotómica, la cantidad de comparaciones o pruebas es logarítmica, lo cual obviamente reduce mucho el proceso de búsqueda.

Al ejecutar el comando anterior, la copia de trabajo se actualiza a la revisión 22 ( (34 + 10) / 2 = 22 ). Ahora realizamos el test en esta revisión y le decimos a Mercurial si la misma es “bad” o “good”:

$ grep 'i have a gub' myfile*
myfile22:i have a gub
$

Lo anterior nos indica que es “bad”. Claro que esto se puede automatizar tanto como queramos. Podemos crear un script que haga casi todo:

#!/bin/bash

if ( grep -q 'i have a gub' myfile* )
then
	result=bad
else
	result=good
fi
echo this revision is $result
hg bisect --$result

Lo ejecutamos:

$ ./test.sh
this revision is bad
Testing changeset 16:2deb07b463cf (12 changesets remaining, ~3 tests)
0 files updated, 0 files merged, 6 files removed, 0 files unresolved
$

Ahora Mercurial nos sitúa en la revisión 16 ( (10 + 22) / 2 = 16 ). Faltan unos 3 tests aún. Volvemos a correr nuestro script:

$ ./test.sh
this revision is good
Testing changeset 19:dc33070521ae (6 changesets remaining, ~2 tests)
3 files updated, 0 files merged, 0 files removed, 0 files unresolved

$ ./test.sh
this revision is good
Testing changeset 20:98953c61831c (3 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

$ ./test.sh
this revision is good
Testing changeset 21:d1616a476608 (2 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

$ ./test.sh
this revision is good
The first bad revision is:
changeset:   22:3103b305bc80
user:        Milton Pividori <miltondp@gmail.com>
date:        Wed Sep 03 10:53:56 2008 -0300
summary:     buggy changeset

La revisión que introdujo el bug que estamos buscando es la 22. Con algún tipo de software que automatice la práctica de “Integración continua”, podríamos indicar que seamos notificados cuando alguna revisión o changeset (independientemente del sistema de control de versiones utilizado) no pasa las pruebas o no compila por ejemplo. Esto nos habilitaría a detectar exactamente qué commit introdujo errores o bugs.

Sin embargo, puede pasar que no haya una prueba determinada que me indique que cierta revisión tiene un error, sino que nos demos cuenta de eso más tarde, y nos interese saber en qué changeset se introdujo el mismo para revertirlo (obviamente que esto no es tan sencillo. Habría que estudiar ese changeset para saber dónde está el error). Luego de introducir la prueba de unidad que verifica ese error que estamos buscando, podemos utilizarla para encontrar la revisión que introdujo el problema con algún script y el comando “bisect” que vimos anteriormente, automatizando la operación. Luego, en caso de ser los cambios de dicha revisión simples como el ejemplo, podemos revertirlo de esta forma en Mercurial (primero vuelvo a la revisión tip con “hg update”):

$ hg update
$ hg backout -m "Elimino el bug" 22
removing myfile22
created new head
changeset 35:37d16077cdac backs out changeset 22:a7281f19f281
the backout changeset is a new head - do not forget to merge
(use "backout --merge" if you want to auto-merge)

Ahora hay dos “heads” en mi repositorio (un “head” es un changeset que no tiene hijos), por lo tanto debemos, como nos está indicando Mercurial, hacer un merge entre ambos (podríamos haber utilizado la opción –merge del comando backout directamente).

Con el comando “hg view” podemos ver esto gráficamente:

Notar que el “padre” de la nueva revisión 35 (que soluciona el bug) es el changeset 22. O sea que este cambio no introduce todos los cambios entre ésta revisión y la 34. Por lo tanto debemos hacer, como ya nos había indicado Mercurial, un merge:

$ hg merge
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg status
R myfile22
$ hg commit -m "Merge hecho"
$

Otra vez, ejecuto “hg view” para ver todo esto gráficamente:

Notar que los changeset de la nueva revisión tip 36 son los commits 34 y 35.

Si bien nunca hice estas cosas con Subversion, por lo que me acuerdo de haber leído, en este sistema de control de versiones hacer esto no es tan sencillo. Justamente una de las críticas a Subversion es que es fácil hacer branches, pero difícil fusionarlos.

Google
 

  • César, te olvidaste del link a la página de TortoiseHg. Ya lo corregí.

    TortoiseHg no sólo está para Windows, se encuentra disponible para Nautilus :) Gracias a una búsqueda de otra cosa me di cuenta recién. Acá están las instrucciones para usarlo, y hay screenshots también. Lo instalé, funciona perfecto, y parece que está muy muy bueno.

    Yo personalmente creo que vale la pena aprender a usarlo, simplemente porque es una herramienta superior, y está desplazando a los sistemas centralizados. Por supuesto que para hacer los TPs basta con algo como Subversion, pero pienso que hasta ahí nomás.

    Lo hablamos más personalmente :)
  • Comienzo igual que Nacho: gracias por considerarme tu amigo! :)
    (No voy a escribir demasiado, ya lo hicieron ustedes (no se porqué aviso esto :P )) La verdad que "desde lejos" parece estar muy bueno Mercurial y esto de los sistemas de control de versiones distribuidos. Por alguna buena razón, supongo, los grandes proyectos de software libre están migrando a ellos. Y quizás de esa ultima oración surja una gran pregunta: ¿Se justifica usar estos sistemas para proyectos pequeños como los que, hasta ahora, estamos acostumbrados a hacer? Mas allá de cual sea la respuesta, confío en el instinto tecnológico (por decirlo de alguna manera) de Milton, hemos incluido muchas herramientas nuevas con éxito durante los últimos tiempos. No queda mas que probarlo. Ah! TortoiseHg puede ayudarnos a conocer las funcionalidades de Mercurial mas fácil, lastima que solo esta para Windows.
  • (Me salió largo el comentario...)

    Aclaro que la introducción era un chiste :) No estoy diciendo que tengamos que cambiar ya a Mercurial, simplemente me parece superior (o sea, la arquitectura es superior), y como vos decís, Subversion nos alcanza y sobra para lo que hacemos. Pero se podrían presentar algunas situaciones (¿te acordas cuando preparábamos el paper para las JAIIO en la facu, que no teníamos acceso al repositorio SVN?) en las que las restricciones del modelo de Subversion/CVS pueden joder un poco.

    Más allá de los commits locales, todas las operaciones en general son locales, o sea más rápidas, incluso si comparás Mercurial con el método "ra_local" de Subversion, que es el más rápido que tiene. Hay ventajas en lo que respecta al manejo de archivos renombrados por ejemplo (aunque en Subversion 1.5 ya lo incluyeron, pero no se qué tan maduro está). Dependes menos del buen funcionamiento de la red/Internet. Es más seguro: el repositorio se encuentra en todos los desarrolladores (si el server SVN muere y no hiciste backups, perdiste toda la historia de tu proyecto).

    Con respecto a los proyectos de Software Libre, representan una gran, gran ventaja. Imaginate que queres bajarte el código de algún proyecto y queres hacer unos cambios. Con un sistema distribuido como Mercurial, clonas el repositorio (un checkout digamos), haces cambios, commiteas, y todas las operaciones que quieras (ya que tenes ahí el repositorio). Si queres compartir los cambios, le decis al mantenedor que pullee de tu repositorio (es super sencillo esto) para obtener tus cambios (algo así es el modo de colaboración entre los desarrolladores de Linux). En un sistema centralizado, haces un checkout, y no podes commitear, a menos que tengas permisos en el servidor. Esto es bastante desventajoso.

    Los sistemas distribuidos son muy, muy escalables, por su arquitectura. Un sistema centralizado pierde feo en este punto... justamente, es centralizado. Si tenés un proyecto gigantesco (notar que me estoy yendo de nuestra situación de hacer trabajos prácticos obviamente, ya lo aclaré arriba), se puede complicar bastante con un solo servidor. En un DVCS no existe este problema.

    No me acuerdo bien como es el proceso de merge en Subversion, pero lo poco que recuerdo del manual es que si bien lo haces con un comando (o algunos más), es un poco complicado. Esto lo recuerdo borrosamente digamos. De todas formas hay desventajas mas importantes: cuando haces un merge, Subversion no maneja la historia de ese branch. O sea que no podes saber qué commits (o changesets) fueron fusionados. El merge es como un solo commit más, con un log, digamos, tipo "fusiono los cambios del branch X". Si bien esto empieza a cambiar con la versión 1.5, se ha implementado lo básico (obviamente, está un [o unos] pasos atrás que los sistemas distribuidos).

    Fijate en los screenshots que puse en el post (con "hg view"), especialmente el último: si la operación de encontrar el commit que introdujo el bug hubiera sido más compleja y me hubiera demandado más commits, podría haber hecho un branch, trabajar tranquilo, y después fusionarlo. En realidad ésta hubiese sido la forma correcta de hacerlo. Y al fusionarlo en trunk (digamos), vería todos los commits hechos en el branch. Es más, esto de hacer un branch aparte para trabajar tranquilo y en aislamiento es lo que se recomienda hacer en Mercurial. Es la forma común de trabajar: hacer branches. En Subversion esto siempre se trata de evitar, porque es más complejo.

    ¿Por qué estoy tan seguro de que es mejor el proceso de branch/merge en los sistemas distribuidos que en Subversion o centralizados? Porque así lo afirman los desarrolladores de grandes proyectos de Software Libre, donde tienen que lidiar con esto de hacer branches/merges muchísimo. Mirá este comentario (que podés encontrar acá, donde habla de las ventajas de Git): "Merging branches is very pleasant. CVS and SVN always made this hard."

    Algunos quotes más (muchas cosas se aplican a todos los sistemas distribuidos, no exclusivamente a los nombrados):

    <ul>
    <li>Git - SVN Crash Course: "Git supports merging between branches much better than Subversion"</li>
    <li>Git's Major Features Over Subversion: "Branches in Git are a core concept used everyday by every user. In Subversion they are almost an afterthought and tend to be avoided unless absolutely necessary."</li>
    <li>Bazaar vs Subversion: "Better merging - fully integrated and works across distributed repositories "</li>
    <li>Mercurial versus Subversion (Mark J. Wielaard) (acá dice cosas más allá del tema branch/merge): "The benefits are really huge. Not only is making diffs between any two versions of any files instant once you have a local mercurial clone, it also gives you an easy way to experiment with your local patches and have them under version control, creating, merging and generating meaningful diffs between branches is much nicer than with subversion, and it is much, much more space efficient than subversion. A checkout of 1 revision of openjdk with subversion is 1.2GB, the whole mercurial repo, which includes all revisions, takes just 740MB disk space. Amazing."</li>
    </ul>

    Fijate en ese último comentario algo que te había dicho una vez: todo el repositorio Mercurial es mucho más eficiente con respecto al espacio en disco requerido que una copia de trabajo Subversion (o sea, estamos hablando de cosas totalmente distintas... por lo menos si estaríamos comparando con el repositorio de Subversion).

    Mirá este post en VivaLinux. Los mismos desarrolladores ven el concepto de Subversion ya agotado, si bien eso no significa que se deje de utilizar, sino que, en resumen, el futuro será para los sistemas distribuidos.

    Sin embargo, Subversion es mejor para grandes archivos binarios, debido a su arquitectura (en este caso en particular es una ventaja). En esta situación, Subversion permite también hacer un lock de los archivos (cosa que estuvimos usando con los poster... bueno... "usando"). Hacer eso, un lock, en un DVCS no tiene sentido. SVN está más soportado en general, sin embargo esto va a ir cambiando gradualmente. Y con respecto a la documentación, los dos (Mercurial y Subversion) me parece están muy bien.

    Los comandos de Mercurial son muy parecidos a los de Subversion, por lo que aprender a usarlo sería muy fácil. No veo desventajas en usar, en este caso, una bomba atómica para matar un mosquito: es un sistema mejor, se perfila como la arquitectura del futuro, su uso prácticamente implica ventajas en todos los sentidos, y sobre todo: aprender a usarlo es fácil (y más si sabes Subversion), etc. Si fuese algo totalmente raro y distinto sería otra cosa...
  • Agrego algo: veo que muchas de las funcionalidades ofrecidas con Mercurial son las que se obtienen de Subversion y la integración con alguna aplicación de gestión de proyectos como Trac (que tiene para la gestión de reportes, tickets de tareas, defectos, mejoras, etc).

    Me gustaría que me plantearas un caso donde se complica el merge un un branch con el trunk (hacia o desde) y donde Mercurial lo maneje mejor (podríamos resolver uno cada uno).
  • jajaja Milton, gracias por considerarme tu amigo! (igual el link dice quién soy no?)

    Veo que es muy potente la herramienta, pero sin embargo para el uso que le damos creo que Subversion nos sobra. Si bien podríamos utilizar Mercurial como estamos utilizando Subversion ahora, cambiando apenas la sintaxis de uso, creo que sería matar un mosquito con un cañon.

    Al margen de esto creo que sería útil en un grupo de trabajo grande, pero donde todos conozcan bien la herramienta. No veo (capaz porque no se me presentó el problema y no lo veo acá) las ventajas aparte de la de poder hacer commits locales (con lo cual podría hacer más atómicas mis modificaciones).

    Voy a buscar más info y si encuentro algo interesante te lo pego acá debajo y vemos como se resolvería.

    Buen post :-D
blog comments powered by Disqus