Después de sumarme al desarrollo del plugin de Trac para RPC (gracias a Odd Simon Simonsen ;o) y trabajar en unas mejoras, he decidido migrar mis proyectos anteriores desde Subversion hacia Mercurial. Aquí describiré una alternativa para realizarla. Por el momento no soy un experto, por lo que el análisis de las ventajas y puntos débiles de esta variante quedará pospuesto hasta que pruebe otras alternativas (si es que eso ocurre ...). Sin embargo, Usted puede formar parte de la búsqueda de la solución más adecuada y opinar u ofrecer sugerencias. Si desea saber acerca de cómo compartir código y colaborar utilizando repositorios de Mercurial, le sugiero que consulte un artículo previo sobre el uso de MQ con Bitbucket. Es así cómo se desarrolla el plugin que les mencioné anteriormente, utilizando su repositorio de parches.
Estado inicial : Subversion
Subversion ha sido por mucho tiempo la opción más difundida para desarrollar proyectos de software libre. De hecho este es el sistema de control de versiones que fue ofrecido en primer lugar por Sourceforge, Google Code y casi todos los otros grandes patrocinadores del open source. Sin embargo el surgimiento primero de GitHub, luego de Bitbucket y otros sitios similares basados en Bazaar, han hecho que los sistemas distribuidos de control de versiones ganen una fuerza tremenda. En gran parte esto se debe a la inmensa gama de modelos de colaboración que son posibles cuando no se tiene un repositorio centralizado. De hecho, desde que comencé a utilizar SVN la sugerencia que nos hacia mi profesor, Alí, era que le echáramos un vistazo a SVK.
En este momento se gestó el proyecto FLiOOPS con una idea original de Kyrie (a.k.a. Luis Alberto Zarrabeitia) para concebir una implementación de la programación por contratos en Python. Lo que una vez fue una tarea de curso, con el tiempo fue creciendo y se incorporaron nuevas ideas. Entre todas ellas se destacó el módulo dutest, que se utilizó para escribir las pruebas de unidad. El aumento en la complejidad, implicó la existencia de varias ramas de desarrollo y otros elementos a los que Subversion no les da una solución. Por ejemplo acá les muestro la estructura actual del repositorio original :
/ java trunk branches tags py trunk branches add_opts mdl_inv pep302 test_pycontract guarded_synch ochk_arch pysimrec tags dutest 0_1_0 0_1_1 0_1_2 0_2_1 0_2_2
La buena noticia es que Sourceforge permite también el uso de (múltiples) repositorios de Mercurial y Git. Inicialmente esto hace posible tener un repositorio para cada lenguaje de programación, o para cada librería que se desarrolle en el marco del proyecto. Sin embargo hay que trasladar todas las versiones ya existentes hacia su nuevo destino.
Opciones para migrar
Después de buscar información e indagar un poco, encontré varias herramientas, pero parece que tres se distinguen del resto:
- HgSubversion es una librería externa cuyo objetivo es lograr una integración completa con Subversion.
- El comando convert permite migrar repositorios de distintos tipos (incluso otros repositorios hechos con Mercurial) hacia un repositorio de Mercurial. Como permite filtrar y renombrar los contenidos que serán incorporados, también resulta útil para filtrar repositorios Mercurial completos y obtener un subconjunto de los cambios que resultan de interés. Entre los tipos de herramientas soportadas se encuentran :
- Hgsvn es un projrcto que surgió a partir de la comunidad de desarollo de
Python para dar una respuesta a la
migración de los repositorios de la librería estándar. Varias de sus
características son:
- Los datos de las versiones se solicitan por lotes.
- El proceso es incremental, por lo que es posible interrumpirlo y resumirlo posteriormente.
- Preserva los meta-datos (e.g. nombres y fechas de los cambios, registros de copia y de cambio de nombres de ficheros).
- Crea marcas (a.k.a. tags) locales para cada versión en el repositorio original.
- Preserva los nombres y las relaciones entre las ramas (a.k.a. branches).
En todos los casos parece que el punto débil resulta ser la posibilidad de hacer commits en el repositorio SVN. Sin embargo, como ya les expliqué, este no era mi objetivo en este caso. Finalmente me decidí a utilizar Hgsvn.
Clonando el repositorio con Hgsvn
Hgsvn solo clona una rama a la vez. Considerando la estructura del repositorio original, el primer paso es clonar el trunk. Para ello se ejecuta el comando hgimportsvn de la siguiente manera:
$ pwd /home/olemis/repos/ $ mkdir flioops $ cd flioops $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/trunk > ./trunk.log $ cd trunk && hgpullsvn >> ../trunk.log
En el ejemplo se redirecciona la salida hacia el fichero ./trunk.log para su posterior análisis. Les recomiendo observarlo, puesto a que conocerán los cambios que se van incorporando y los datos que se van recuperando del repositorio original. La lista puede ser larga.
En este caso se crea un repositorio dentro de la carpeta trunk. En general el nombre se corresponde con el último tramo de la URL que se especifica como parámetro. Podemos comprobar que todo está en orden después de observar el log del nuevo repositorio.
$ pwd /home/olemis/repos/flioops/trunk $ hg log | more changeset: 49:39cfb58b31b6 branch: trunk tag: tip tag: svn.91 user: olemis date: Wed Sep 17 10:34:32 2008 -0500 summary: [svn r91] - Bug fixed... The class PackageTestLoader loaded the tests defined in changeset: 48:5595c9f190ca branch: trunk tag: svn.88 user: olemis date: Thu Sep 11 14:38:56 2008 -0500 summary: [svn r88] Starting dutest release 0.2.1. changeset: 47:ac470dd94852 branch: trunk tag: svn.86 user: olemis date: Thu Sep 11 12:10:25 2008 -0500 summary: [svn r86] - PackageTestLoader class added to oop.utils.dutest module. It loads $ hg branches trunk 49:39cfb58b31b6 $ hg tags -v | more tip 49:39cfb58b31b6 svn.91 49:39cfb58b31b6 local svn.88 48:5595c9f190ca local svn.86 47:ac470dd94852 local svn.83 46:0f8df4313c98 local svn.82 45:287eef1166bf local svn.80 44:7accc993aad9 local svn.78 43:9ab9af3bbbfd local svn.77 42:4678ec09ef71 local $ hg log -r svn.88 -v changeset: 48:5595c9f190ca branch: trunk tag: svn.88 user: olemis date: Thu Sep 11 14:38:56 2008 -0500 files: utils/dutest.py description: [svn r88] Starting dutest release 0.2.1.
Como se puede observar solo obtenemos una sola rama llamada trunk. Su nombre se selecciona de manera análoga a partir de la URL. También se crea una marca local para cada revisión que se encuentra en el repositorio original. De esta forma es más fácil relacionar los cambios en un repositorio y otro. Por ejemplo la versión 88 en el repositorio central se corresponde con la versión 48 localmente, cuyo identificador global sería 5595c9f190ca. Además, si se nos olvidan estos detalles (de más está decir que yo me los sé de memoria :P ) pero conocemos la versión en el repositorio de Subversion, entonces es posible hacer referencia a su contra-parte en el repositorio de Mercurial utilizando el nombre (i.e. marca) svn.88.
Como se puede observar se mantiene el nombre del autor de los cambios y la fecha. Quizás le puedan quedar dudas ... pues ¡aclarémoslas!.
$ svn info Path: . URL: https://flioops.svn.sourceforge.net/svnroot/flioops/py/trunk Repository Root: https://flioops.svn.sourceforge.net/svnroot/flioops Repository UUID: 878b2ac5-cb47-0410-a52a-c42bf53788ce Revision: 91 Node Kind: directory Schedule: normal Last Changed Author: olemis Last Changed Rev: 91 Last Changed Date: 2008-09-17 10:34:32 -0500 (Wed, 17 Sep 2008)
Pues bien, en un mismo espacio coexisten ambos repositorios. Compare esta fecha con la que reporta Mercurial y verá que son idénticas. Después de satisfacer su curiosidad inicial, es hora entonces de clonar el resto de los cambios.
Clonando las ramas con Hgsvn
Como hemos visto antes, Hgsvn solo clona una rama a la vez. El proceso para recuperar los cambios restantes es análogo.
$ cd .. $ pwd /home/olemis/repos/flioops $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/add_opts > ./add_opts.log $ cd add_opts && hgpullsvn >> ../add_opts.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/mdl_inv > ./mdl_inv.log $ cd mdl_inv && hgpullsvn >> ../mdl_inv.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/pep302 > ./pep302.log $ cd pep302 && hgpullsvn >> ../pep302.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/test_pycontract > ./test_pycontract.log $ cd test_pycontract && hgpullsvn >> ../test_pycontract.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/guarded_synch > ./guarded_synch.log $ cd guarded_synch && hgpullsvn >> ../guarded_synch.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/ochk_arch > ./ochk_arch.log $ cd ochk_arch && hgpullsvn >> ../ochk_arch.log && cd .. $ hgimportsvn https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/pysimrec > ./pysimrec.log $ cd pysimrec && hgpullsvn >> ../pysimrec.log && cd .. $ ls add_opts mdl_inv pep302 test_pycontract guarded_synch ochk_arch pysimrec trunk add_opts.log mdl_inv.log pep302.log test_pycontract.log guarded_synch.log ochk_arch.log pysimrec.log trunk.log
Ahora todos esperamos efectos similares (i.e. marcas locales, una sola rama, fechas consistentes y dos repositorios en fraternal coexistencia).
$ cd add_opts $ hg log | more changeset: 36:660d2a6c5f05 branch: add_opts tag: tip tag: svn.54 user: olemis date: Thu Aug 07 09:42:48 2008 -0500 summary: [svn r54] The error messages can be formatted now by specifying the templates changeset: 35:0243dc200611 branch: add_opts tag: svn.53 user: olemis date: Thu Aug 07 09:36:52 2008 -0500 summary: [svn r53] The whole assertion checking infrastructure is prepared for changeset: 34:84a28bf08590 branch: add_opts tag: svn.52 user: olemis date: Thu Aug 07 09:22:20 2008 -0500 summary: [svn r52] The function oop.utils.NormalizeException is prepared for assuming $ svn info Path: . URL: https://flioops.svn.sourceforge.net/svnroot/flioops/py/branches/add_opts Repository Root: https://flioops.svn.sourceforge.net/svnroot/flioops Repository UUID: 878b2ac5-cb47-0410-a52a-c42bf53788ce Revision: 54 Node Kind: directory Schedule: normal Last Changed Author: olemis Last Changed Rev: 54 Last Changed Date: 2008-08-07 09:42:48 -0500 (Thu, 07 Aug 2008) $ hg branches add_opts 36:660d2a6c5f05 trunk 29:536184bf7e16 (inactive) $ hg heads | grep changeset changeset: 36:660d2a6c5f05 $ hg tags -v tip 36:660d2a6c5f05 svn.54 36:660d2a6c5f05 local svn.53 35:0243dc200611 local svn.52 34:84a28bf08590 local svn.50 33:7e5b743efd37 local svn.49 32:901f61877293 local svn.48 31:51e7d78f26d8 local svn.47 30:336637e2a0f1 local $ hg log -b trunk | grep changeset changeset: 29:536184bf7e16 changeset: 28:ec176d410993 changeset: 27:d6ddd117926d changeset: 26:1f89e8573172 changeset: 25:7b0847a67a80 changeset: 24:23571cc5686b changeset: 23:cad71e6a4e34 changeset: 22:2d9e43109e87 changeset: 21:2a77c9c5e6ad changeset: 20:c177d00b5a4e changeset: 19:64eb1c7cdd91 changeset: 18:7c941992dbe5 changeset: 17:4dd286854034 changeset: 16:548c66a002c2 changeset: 15:7ad0521ce2b4 changeset: 14:f0087f96d466 changeset: 13:2540d610c198 changeset: 12:ee7e670e9b88 changeset: 11:8ba039171b0a changeset: 10:af39f33a1776 changeset: 9:c95d926d676d changeset: 8:28535deb9fc9 changeset: 7:1ddfa139931c changeset: 6:84e23e55166e changeset: 5:e1d36b91fb4a changeset: 4:80257d1256a9 changeset: 3:6c81277c9392 changeset: 2:82714a5663d1 changeset: 1:89d45b024c67 changeset: 0:51750d40d056 $ cd ..\trunk $ hg log -r 29:25 | grep changeset changeset: 29:536184bf7e16 changeset: 28:ec176d410993 changeset: 27:d6ddd117926d changeset: 26:1f89e8573172 changeset: 25:7b0847a67a80
Si bien hay varias semejanzas con respecto a los meta-datos, las diferencias son notables. Por lo visto hay menos revisiones y ... ¿dos ramas? ¿De dónde salió trunk si clonamos a add_opts? ¿Qué se puede deducir después de ejecutar los comandos anteriores? Primeramente, como las revisiones tienen el mismo identificador en ambos repositorios (trunk y add_opts) se puede deducir que se clonó el primer repositorio que teníamos localmente hasta la revisión 29:536184bf7e16. De ahi en adelante hgsvn detectó que en el repositorio SVN se había hecho una copia de los contenidos de la carpeta trunk y por tanto interpreta este hecho como la creación de una nueva rama y lo refleja localmente. En lo sucesivo solo se añaden las versiones en la rama add_opts. Es por esto que solo se crea una cabeza (a.k.a. head). La misma situación se repite para los otros repositorios.
Si Usted prefiere tener varias ramas en un solo repositorio (como yo ;o) solo resta unir todas las ramas.
$ pwd /home/olemis/repos/flioops/trunk $ find ../* -type d -print0 | xargs -0 -I repos hg pull repos $ hg branches ochk_arch 76:6ac33679d5c3 guarded_synch 75:386b8f866218 test_pycontract 73:d80a5ccc6153 mdl_inv 69:a9b380084029 pep302 62:00b7244f3642 add_opts 60:660d2a6c5f05 pysimrec 53:93e9562bfb41 trunk 49:39cfb58b31b6 $ hg heads | grep changeset changeset: 76:6ac33679d5c3 changeset: 75:386b8f866218 changeset: 73:d80a5ccc6153 changeset: 69:a9b380084029 changeset: 62:00b7244f3642 changeset: 60:660d2a6c5f05 changeset: 53:93e9562bfb41 changeset: 49:39cfb58b31b6
Ya tenemos el repositorio con todas las de la ley. Lo único que es de resaltar es que desaparecen las marcas que indicaban los números de revisión SVN para las ramas, pues son locales a los repositorios clonados.
Conclusiones
Hgsvn solo clona una rama a la vez. Subversion no tiene implementada una noción de ramas, por lo tanto el comando hgimportsvn considera como nombre el último tramo de la URL especificada. La herramienta se destaca por mantener la mayor fidelidad posible con respecto al repositorio SVN original y recuperar la mayor cantidad posible de meta-datos para incorporarlos en la copia local hecha con Mercurial. Hgsvn analiza las operaciones realizadas en el repositorio SVN original e infiere las relaciones entre las ramas para reflejarlas en el clon que se crea localmente.
Actualizado 23/04/2010 13:43:09 con hgpullsvn
ResponderEliminar