mercurial

204
Control Distribuido de Revisiones con Mercurial Bryan O’Sullivan Copyright c 2006, 2007 Bryan O’Sullivan. Este material puede distribuirse ´ unicamente bajo los t´ erminos y condiciones establecidos en la versi´ on 1.0 de la Licencia de Publicaci ´ on Abierta (OPL). Refi´ erase por favor al ap´ endice D para encontrar el texto de la licencia. Este libro fue preparado a partir de rev fc4daaad6415, fechado 2009-02-10 00:28 -0500, usando Mercurial rev Mercurial Distributed SCM (version 1.0.1) .

Upload: claudia-nieves

Post on 01-Nov-2015

208 views

Category:

Documents


15 download

TRANSCRIPT

Page 1: Mercurial

Control Distribuido de Revisiones con Mercurial

Bryan O’Sullivan

Copyright c© 2006, 2007 Bryan O’Sullivan.Este material puede distribuirse unicamente bajo los terminos y condiciones establecidos en la

version 1.0 de la Licencia de Publicacion Abierta (OPL). Refierase por favor al apendice D paraencontrar el texto de la licencia.

Este libro fue preparado a partir de rev fc4daaad6415, fechado 2009-02-10 00:28 -0500, usandoMercurial rev Mercurial Distributed SCM (version 1.0.1) .

Page 2: Mercurial

Indice general

Indice general I

Prefacio 20.1. Este libro es un trabajo en progreso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20.2. Acerca de los ejemplos en este libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20.3. Colofon—este libro es Libre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1. Introduccion 31.1. Acerca del control de revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

1.1.1. ¿Por que usar control de revisiones? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.1.2. La cantidad de nombres del control de revisiones . . . . . . . . . . . . . . . . . . . . . . . . 4

1.2. Historia resumida del control de revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.3. Tendencias en el control de revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.4. Algunas ventajas del control distribuido de revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . 5

1.4.1. Ventajas para proyectos de codigo abierto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.4.2. Ventajas para proyectos comerciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

1.5. ¿Por que elegir Mercurial? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.6. Comparacion de Mercurial con otras herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

1.6.1. Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.6.2. Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.6.3. CVS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.6.4. Herramientas comerciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.6.5. Elegir una herramienta de control de revisiones . . . . . . . . . . . . . . . . . . . . . . . . . 10

1.7. Migrar de otra herramienta hacia Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2. Una gira de Mercurial: lo basico 112.1. Instalar Mercurial en su sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

2.1.1. Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.1.2. Solaris . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.1.3. Mac OS X . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.1.4. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.2. Arrancando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.2.1. Ayuda integrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2.3. Trabajar con un repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.3.1. Hacer una copia local de un repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.3.2. Que hay en un repositorio? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.4. Vistazo rapido al historial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.4.1. Conjuntos de cambios, revisiones, y comunicandose con otras personas . . . . . . . . . . . . 152.4.2. Ver revisiones especıficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.4.3. Informacion mas detallada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

I

Page 3: Mercurial

2.5. Todo acerca de las opciones para comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.6. Hacer y repasar cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.7. Grabar cambios en un nuevo conjunto de cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

2.7.1. Definir un nombre de usuario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.7.2. Escribir un mensaje de consignacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.7.3. Escribir un buen mensaje de consignacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.7.4. Cancelar una consignacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.7.5. Admirar nuestro trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

2.8. Compartir cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222.8.1. Jalar cambios desde otro repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222.8.2. Actualizar el directorio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.8.3. Empujar cambios a otro repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242.8.4. Compartir cambios a traves de una red . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

3. Una gira de Mercurial: fusionar trabajo 263.1. Fusionar lıneas de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

3.1.1. Conjuntos de cambios de frentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.1.2. Hacer la fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293.1.3. Consignar los resultados de la fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

3.2. Fusionar cambios con conflictos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.2.1. Usar una herramienta grafica para fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.2.2. Un ejemplo real . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

3.3. Simplificar el ciclo jalar-fusionar-consignar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

4. Tras bambalinas 364.1. Registro del historial de Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

4.1.1. Seguir el historial de un unico fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.1.2. Administracion de ficheros monitoreados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.1.3. Registro de informacion del conjunto de cambios . . . . . . . . . . . . . . . . . . . . . . . . 364.1.4. Relaciones entre revisiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

4.2. Almacenamiento seguro y eficiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384.2.1. Almacenamiento eficiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384.2.2. Operacion segura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384.2.3. Recuperacion rapida de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384.2.4. Identificacion e integridad fuerte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

4.3. Historial de revisiones, ramas y fusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404.4. El directorio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

4.4.1. Que pasa en una consignacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404.4.2. Creacion de un nuevo frente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414.4.3. Fusion de frentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

4.5. Otras caracterısticas de diseno interesantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424.5.1. Compresion ingeniosa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424.5.2. Reordenado de lectura/escritura y atomicidad . . . . . . . . . . . . . . . . . . . . . . . . . . 424.5.3. Acceso concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.5.4. Evitar movimientos de brazo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.5.5. Otros contenidos del estado de directorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

5. Mercurial dıa a dıa 495.1. Como indicarle a Mercurial que ficheros seguir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

5.1.1. Nombramiento explıcito e implıcito de ficheros . . . . . . . . . . . . . . . . . . . . . . . . . 495.1.2. Nota al margen: Mercurial trata ficheros, no directorios . . . . . . . . . . . . . . . . . . . . . 50

5.2. Como dejar de hacer seguimiento a un fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

II

Page 4: Mercurial

5.2.1. Al eliminar un fichero no se afecta su historial . . . . . . . . . . . . . . . . . . . . . . . . . . 515.2.2. Ficheros perdidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515.2.3. Nota al margen: ¿Por que decirle explıcitamente a Mercurial que elimine un fichero? . . . . . 525.2.4. Atajo util—agregar y eliminar ficheros en un solo paso . . . . . . . . . . . . . . . . . . . . . 52

5.3. Copiar ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525.3.1. Resultados de copiar un fichero durante una fusion . . . . . . . . . . . . . . . . . . . . . . . 535.3.2. ¿Por que los cambios se reflejan en las copias? . . . . . . . . . . . . . . . . . . . . . . . . . 545.3.3. Como hacer que los cambios no sigan a la copia? . . . . . . . . . . . . . . . . . . . . . . . . 545.3.4. Comportamiento de la orden “hg copy” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

5.4. Renombrar ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555.4.1. Renombrar ficheros y fusionar cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565.4.2. Cambios de nombre divergentes y fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565.4.3. Cambios de nombre convergentes y fusion . . . . . . . . . . . . . . . . . . . . . . . . . . . 575.4.4. Otros casos lımite relacionados con renombramientos . . . . . . . . . . . . . . . . . . . . . . 57

5.5. Recuperarse de equivocaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

6. Colaborar con otros 596.1. La interfaz web de Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596.2. Modelos de colaboracion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

6.2.1. Factores a tener en cuenta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606.2.2. Anarquıa informal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606.2.3. Un repositorio central unico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606.2.4. Trabajo con muchas ramas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616.2.5. Ramas de caracterısticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636.2.6. El tren de publicacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636.2.7. El modelo del kernel Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636.2.8. Solamente jalar frente a colaboracion publica . . . . . . . . . . . . . . . . . . . . . . . . . . 646.2.9. Cuando la colaboracion encuentra la administracion ramificada . . . . . . . . . . . . . . . . 64

6.3. Aspectos tecnicos de la colaboracion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656.4. Compartir informalmente con “hg serve” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

6.4.1. Cuestiones adicionales para tener en cuenta . . . . . . . . . . . . . . . . . . . . . . . . . . . 656.5. Uso del protocolo Secure Shell (ssh) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

6.5.1. Como leer y escribir URLs de ssh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666.5.2. Encontrar un cliente ssh para su sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666.5.3. Generar un par de llaves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676.5.4. Uso de un agente de autenticacion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676.5.5. Configurar el lado del servidor apropiadamente . . . . . . . . . . . . . . . . . . . . . . . . . 676.5.6. Compresion con ssh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69

6.6. Servir sobre HTTP usando CGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696.6.1. Lista de chequeo de la configuracion del servidor web . . . . . . . . . . . . . . . . . . . . . 696.6.2. Configuracion basica de CGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706.6.3. Compartir varios repositorios con un guion CGI . . . . . . . . . . . . . . . . . . . . . . . . . 726.6.4. Descarga de ficheros fuente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736.6.5. Opciones de configuracion en Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

7. Nombres de ficheros y asociacion de patrones 757.1. Nombrado de ficheros simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757.2. Ejecucion de comandos sin ningun nombre de fichero . . . . . . . . . . . . . . . . . . . . . . . . . . 757.3. Reportar que esta pasando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777.4. Uso de patrones para identificar ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77

7.4.1. Patrones glob estilo interprete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777.4.2. Asociacion con patrones de expresiones regulares re . . . . . . . . . . . . . . . . . . . . . . 78

III

Page 5: Mercurial

7.5. Filtrado de ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797.6. Ignorar ficheros y directorios no deseados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797.7. Sensibilidad a mayusculas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

7.7.1. Almacenamiento portable y seguro de repositorios . . . . . . . . . . . . . . . . . . . . . . . 807.7.2. Deteccion de conflictos de mayusculas/minusculas . . . . . . . . . . . . . . . . . . . . . . . 807.7.3. Arreglar un conflicto de mayusculas/minusculas . . . . . . . . . . . . . . . . . . . . . . . . 80

8. Administracion de versiones y desarrollo ramificado 818.1. Dar un nombre persistente a una revision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

8.1.1. Manejo de conflictos entre etiquetas durante una fusion . . . . . . . . . . . . . . . . . . . . . 838.1.2. Etiquetas y clonado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848.1.3. Cuando las etiquetas permanentes son demasiado . . . . . . . . . . . . . . . . . . . . . . . . 84

8.2. El flujo de cambios—El gran cuadro vs. el pequeno . . . . . . . . . . . . . . . . . . . . . . . . . . . 848.3. Administrar ramas en repositorios estilo gran cuadro . . . . . . . . . . . . . . . . . . . . . . . . . . 848.4. No repita trabajo: fusion entre ramas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 858.5. Nombrar ramas dentro de un repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 868.6. Tratamiento de varias ramas nombradas en un repositorio . . . . . . . . . . . . . . . . . . . . . . . . 888.7. Nombres de ramas y fusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 908.8. Normalmente es util nombrar ramas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90

9. Encontrar y arreglar sus equivocaciones 919.1. Borrar el historial local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91

9.1.1. La consignacion accidental . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919.1.2. Hacer rollback una transaccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919.1.3. Erroneamente jalado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929.1.4. Despues de publicar, un roll back es futil . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929.1.5. Solamente hay un roll back . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

9.2. Revertir un cambio equivocado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939.2.1. Errores al administrar ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

9.3. Tratar cambios consignados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.3.1. Retroceder un conjunto de cambios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.3.2. Retroceder el conjunto de cambios punta . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969.3.3. Retroceso de un cambio que no es la punta . . . . . . . . . . . . . . . . . . . . . . . . . . . 969.3.4. Mas control sobre el proceso de retroceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989.3.5. Por que “hg backout” hace lo que hace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

9.4. Cambios que nunca debieron ocurrir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029.4.1. Como protegerse de cambios que han “escapado” . . . . . . . . . . . . . . . . . . . . . . . . 102

9.5. Al encuentro de la fuente de un fallo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1029.5.1. Uso de la orden “hg bisect” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039.5.2. Limpieza despues de la busqueda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

9.6. Consejos para encontrar fallos efectivamente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079.6.1. Dar una entrada consistente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079.6.2. Automatizar tanto como se pueda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079.6.3. Verificar los resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079.6.4. Tener en cuenta la interferencia entre fallos . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079.6.5. Acotar la busqueda perezosamente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

10. Manejo de eventos en repositorios mediante ganchos 10910.1. Vistazo general de ganchos en Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10910.2. Ganchos y seguridad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

10.2.1. Los ganchos se ejecutan con sus privilegios de usuario . . . . . . . . . . . . . . . . . . . . . 11010.2.2. Los ganchos no se propagan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

IV

Page 6: Mercurial

10.2.3. Es posible hacer caso omiso de los ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . 11010.2.4. Asegurarse de que ganchos crıticos sean ejecutados . . . . . . . . . . . . . . . . . . . . . . . 111

10.3. Precauciones con ganchos pretxn en un repositorio de acceso compartido . . . . . . . . . . . . . . . 11110.3.1. Ilustracion del problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

10.4. Tutorial corto de uso de ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11210.4.1. Llevar a cabo varias acciones por evento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11210.4.2. Controlar cuando puede llevarse a cabo una actividad . . . . . . . . . . . . . . . . . . . . . . 113

10.5. Escribir sus propios ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11310.5.1. Escoger como debe ejecutarse su gancho . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11310.5.2. Parametros para ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11410.5.3. Valores de retorno de ganchos y control de actividades . . . . . . . . . . . . . . . . . . . . . 11410.5.4. Escribir un gancho externo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11410.5.5. Indicar a Mercurial que use un gancho interno . . . . . . . . . . . . . . . . . . . . . . . . . . 11510.5.6. Escribir un gancho interno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115

10.6. Ejemplos de ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11510.6.1. Escribir mensajes de consignacion significativos . . . . . . . . . . . . . . . . . . . . . . . . 11510.6.2. Comprobar espacios en blanco finales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

10.7. Ganchos adicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11710.7.1. acl—control de acceso a partes de un repositorio . . . . . . . . . . . . . . . . . . . . . . . . 11710.7.2. bugzilla—integracion con Bugzilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11910.7.3. notify—enviar notificaciones de correo electronico . . . . . . . . . . . . . . . . . . . . . . 122

10.8. Informacion para escritores de ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12410.8.1. Ejecucion de ganchos internos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12410.8.2. Ejecucion de ganchos externos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12410.8.3. Averiguar de donde vienen los conjuntos de cambios . . . . . . . . . . . . . . . . . . . . . . 125

10.9. Referencia de ganchos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12510.9.1. changegroup—luego de anadir conjuntos de cambios remotos . . . . . . . . . . . . . . . . . 12510.9.2. commit—luego de la creacion de un nuevo conjunto de cambios . . . . . . . . . . . . . . . . 12610.9.3. incoming—luego de que un conjunto de cambios remoto es anadido . . . . . . . . . . . . . 12610.9.4. outgoing—luego de la propagacion de los conjuntos de cambios . . . . . . . . . . . . . . . 12610.9.5. prechangegroup—antes de empezar la adicion de conjuntos de cambios remotos . . . . . . 12710.9.6. precommit—antes de iniciar la consignacion de un conjunto de cambios . . . . . . . . . . . 12710.9.7. preoutgoing—antes de empezar la propagacion de conjuntos de cambios . . . . . . . . . . 12710.9.8. pretag—antes de etiquetar un conjunto de cambios . . . . . . . . . . . . . . . . . . . . . . 12810.9.9. pretxnchangegroup—antes de completar la adicion de conjuntos de cambios remotos . . . . 12810.9.10.pretxncommit—antes de completar la consignacion de un nuevo conjunto de cambios . . . . 12810.9.11.preupdate—antes de actualizar o fusionar el directorio de trabajo . . . . . . . . . . . . . . . 12910.9.12.tag—luego de etiquetar un conjunto de cambios . . . . . . . . . . . . . . . . . . . . . . . . 12910.9.13.update—luego de actualizar o fusionar el directorio de trabajo . . . . . . . . . . . . . . . . . 129

11. Personalizar los mensajes de Mercurial 13011.1. Usar estilos que vienen con Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

11.1.1. Especificar un estilo predeterminado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13111.2. Ordenes que soportan estilos y plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13111.3. Cuestiones basicas de plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13111.4. Palabras claves mas comunes en las plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13211.5. Secuencias de Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13311.6. Uso de filtros con palabras claves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

11.6.1. Combinar filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13511.7. De plantillas a estilos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

11.7.1. Los ficheros de estilo mas sencillos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13711.7.2. Sintaxis de ficheros de estilo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

V

Page 7: Mercurial

11.8. Ejemplos de ficheros de estilos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13711.8.1. Identificar equivocaciones en ficheros de estilo . . . . . . . . . . . . . . . . . . . . . . . . . 13711.8.2. Identificar de forma unica un repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13811.8.3. Mostrando salida parecida a Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139

12. Administracion de cambios con Colas de Mercurial 14012.1. El problema de la administracion de parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14012.2. La prehistoria de las Colas de Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

12.2.1. Trabajar parches con quilt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14112.2.2. Pasar de trabajo con parches con Quilt hacia Colas de Mercurial . . . . . . . . . . . . . . . . 141

12.3. La gran ventaja de MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14112.4. Entender los parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14212.5. Comenzar a usar Colas de Mercurial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

12.5.1. Crear un nuevo parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14312.5.2. Refrescar un parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14412.5.3. Aplicar un parche tras otro y dar seguimiento . . . . . . . . . . . . . . . . . . . . . . . . . . 14512.5.4. Manipular la pila de parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14512.5.5. Introducir y sustraer muchos parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14612.5.6. Medidas de seguridad y como saltarlas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14612.5.7. Trabajar con varios parches a la vez . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

12.6. Mas acerca de parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14812.6.1. La cantidad de franjas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14812.6.2. Estrategias para aplicar parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14912.6.3. Algunos detalles de la representacion de parches . . . . . . . . . . . . . . . . . . . . . . . . 15012.6.4. Cuidado con los difusos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15012.6.5. Manejo de descartes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151

12.7. maximizar el rendimiento de MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15112.8. Actualiar los parches cuando el codigo cambia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15212.9. Identificar parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15212.10.Otra informacion util . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15312.11.Administrar parches en un repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

12.11.1.Soporte de MQ para repositorios de parches . . . . . . . . . . . . . . . . . . . . . . . . . . . 15412.11.2.Detalles a tener en cuenta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

12.12.Otras herramientas para trabajar con parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15512.13.Buenas practicas de trabajo con parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15512.14.Recetas de MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

12.14.1.Administrar parches “triviales” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15612.14.2.Combinar parches completos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15812.14.3.Fusionar una porcion de un parche dentro de otro . . . . . . . . . . . . . . . . . . . . . . . . 158

12.15.Diferencias entre quilt y MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

13. Usos avanzados de las Colas de Mercurial 16013.1. El problema de multiples objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

13.1.1. Aproximaciones tentadoras que no funcionan adecuadamente . . . . . . . . . . . . . . . . . 16013.2. Aplicar parches condicionalmente mediante guardias . . . . . . . . . . . . . . . . . . . . . . . . . . 16113.3. Controlar los guardias de un parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16113.4. Selecccionar los guardias a usar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16213.5. Reglas de MQ para aplicar parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16313.6. Podar el entorno de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16313.7. Dividir el fichero series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16413.8. Mantener la serie de parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

13.8.1. El arte de escribir parches de backport . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

VI

Page 8: Mercurial

13.9. Consejos utiles para hacer desarrollo con MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16513.9.1. Organizar parches en directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16513.9.2. Ver el historial de un parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

14. Anadir funcionalidad con extensiones 16814.1. Mejorar el desempeno con la extension inotify . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16814.2. Soporte flexible de diff con la extension extdiff . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

14.2.1. Definicion de alias de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17214.3. Uso de la extension transplant para seleccionar . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17214.4. Enviar cambios vıa correo electronico con la extension patchbomb . . . . . . . . . . . . . . . . . . . 172

14.4.1. Cambiar el comportamiento de las bombas de parches . . . . . . . . . . . . . . . . . . . . . 173

A. Referencia de Ordenes 175A.1. “hg add”—Anade ficheros en la proxima consignacion . . . . . . . . . . . . . . . . . . . . . . . . . 175A.2. “hg diff”—imprime los cambios en el historial o el directorio actual . . . . . . . . . . . . . . . . . 175

A.2.1. Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175A.3. “hg version”—imprime la informacion de version y derechos de reproduccion . . . . . . . . . . . . 177

A.3.1. Consejos y trucos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

B. Referencia de las Colas de Mercurial 179B.1. Referencia de ordenes MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179

B.1.1. “hg qapplied”—imprimir los parches aplicados . . . . . . . . . . . . . . . . . . . . . . . . 179B.1.2. “hg qcommit”—consignar cambios en la cola del repositorio . . . . . . . . . . . . . . . . . 179B.1.3. “hg qdelete”—eliminar un parche del fichero series . . . . . . . . . . . . . . . . . . . . 179B.1.4. “hg qdiff”—imprimir la diferencia del ultimo parche aplicado . . . . . . . . . . . . . . . . 179B.1.5. “hg qfold”—fusionar (“integrar”) varios parches en uno solo . . . . . . . . . . . . . . . . . 179B.1.6. “hg qheader”—desplegar el encabezado/descripcion de un parche . . . . . . . . . . . . . . 180B.1.7. “hg qimport”—importar el parche de un tercero en la cola . . . . . . . . . . . . . . . . . . 180B.1.8. “hg qinit”—preparar un repositorio para trabajar con MQ . . . . . . . . . . . . . . . . . . 180B.1.9. “hg qnew”—crear un parche nuevo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180B.1.10. “hg qnext”—imprimir el nombre del proximo parche . . . . . . . . . . . . . . . . . . . . . 180B.1.11. “hg qpop”—sustraer parches de la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181B.1.12. “hg qprev”—imprimir el nombre del parche anterior . . . . . . . . . . . . . . . . . . . . . 181B.1.13. “hg qpush”—introducir parches a la pila . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181B.1.14. “hg qrefresh”—actualiza el ultimo parche aplicado . . . . . . . . . . . . . . . . . . . . . . 182B.1.15. “hg qrename”—renombrar un parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182B.1.16. “hg qrestore”—restaurar el estado almacenado de la cola . . . . . . . . . . . . . . . . . . 182B.1.17. “hg qsave”—almacena el estado actual de la cola . . . . . . . . . . . . . . . . . . . . . . . 182B.1.18. “hg qseries”—imprime la serie completa de parches . . . . . . . . . . . . . . . . . . . . . 182B.1.19. “hg qtop”—imprime el nombre del parche actual . . . . . . . . . . . . . . . . . . . . . . . 183B.1.20. “hg qunapplied”—imprimir los parches que aun no se han aplicado . . . . . . . . . . . . . 183B.1.21. “hg strip”—remover una revision y sus descendientes . . . . . . . . . . . . . . . . . . . . 183

B.2. Referencia de ficheros de MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183B.2.1. El fichero series . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183B.2.2. El fichero status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

C. Instalar Mercurial desde las fuentes 184C.1. En un sistema tipo Unix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184C.2. En Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184

VII

Page 9: Mercurial

D. Licencia de Publicacion Abierta 185D.1. Requerimientos en versiones modificadas y no modificadas . . . . . . . . . . . . . . . . . . . . . . . 185D.2. Derechos de reproduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185D.3. Alcance de la licencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185D.4. Requerimientos sobre trabajos modificados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186D.5. Recomendaciones de buenas practicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186D.6. Opciones de licencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186

Bibliografıa 188

Indice alfabetico 188

VIII

Page 10: Mercurial

Indice de figuras

2.1. Historial grafico del repositorio hello . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.1. Historial reciente divergente de los repositorios my-hello y my-new-hello . . . . . . . . . . . . . . 273.2. Contenidos del repositorio despues de jalar my-hello a my-new-hello . . . . . . . . . . . . . . . . 283.3. Directorio de trabajo y repositorio durante la fusion, y consignacion consecuente . . . . . . . . . . . 293.4. Cambios con conflictos a un documento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313.5. Usando kdiff3 para fusionar versiones de un fichero . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.1. Relacion entre ficheros en el directorio de trabajo y ficheros de registro en el repositorio . . . . . . . . 374.2. Relaciones entre metadatos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374.3. Instantanea de un revlog, con deltas incrementales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454.5. El directorio de trabajo puede tener dos padres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464.6. El directorio de trabajo obtiene nuevos padres luego de una consignacion . . . . . . . . . . . . . . . 464.7. El directorio de trabajo, actualizado a un conjunto de cambios anterior . . . . . . . . . . . . . . . . . 474.8. Despues de una consignacion hecha mientras se usaba un conjunto de cambios anterior . . . . . . . . 474.9. Fusion de dos frentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5.1. Simular un directorio vacıo con un fichero oculto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

6.1. Ramas de Caracterısticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

9.1. Retroceso de un cambio con la orden “hg backout” . . . . . . . . . . . . . . . . . . . . . . . . . . 979.2. Retroceso automatizado de un cambio a algo que no es la punta con la orden “hg backout” . . . . . 989.3. Retroceso usando la orden “hg backout” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009.4. Fusion manual de un retroceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

10.1. Un gancho simple que se ejecuta al hacer la consignacion de un conjunto de cambios . . . . . . . . . 11210.2. Definicion de un segundo gancho commit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11310.3. Uso del gancho pretxncommit para controlar consignaciones . . . . . . . . . . . . . . . . . . . . . 11410.4. Un gancho que prohıbe mensajes de consignacion demasiado cortos . . . . . . . . . . . . . . . . . . 11610.5. Un gancho simple que revisa si hay espacios en blanco finales . . . . . . . . . . . . . . . . . . . . . 11610.6. Un mejor gancho para espacios en blanco finales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

11.1. Template keywords in use . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13311.2. Filtros de plantilla en accion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136

12.1. Uso sencillo de las ordenes diff y patch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14212.2. Lıneas a anadir en ˜/.hgrc para habilitar la extension MQ . . . . . . . . . . . . . . . . . . . . . . . 14312.3. Como verificar que MQ esta habilitado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14312.4. Preparar un repositorio para usar MQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14412.5. Crear un nuevo parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

IX

Page 11: Mercurial

12.6. Refrescar un parche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14512.7. Refrescar un parche muchas veces para acumular cambios . . . . . . . . . . . . . . . . . . . . . . . 14612.8. Aplicar un parche despues del primero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14712.9. Entender la pila de parches con “hg qseries” y “hg qapplied” . . . . . . . . . . . . . . . . . . . 14812.10.Parches aplicados y no aplicados en la pila de parches de MQ . . . . . . . . . . . . . . . . . . . . . . 14812.11.Modificar la pila de parches aplicados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14912.12.Pushing all unapplied patches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14912.13.Crear un parche a la fuerza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15012.14.Uso de las caracterısticas de etiquetamiento al trabajar con MQ . . . . . . . . . . . . . . . . . . . . . 15412.15.Las ordenes diffstat, filterdiff, y lsdiff . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

1

Page 12: Mercurial

Prefacio

El control distribuido de revisiones es un territorio relativamente nuevo, y ha crecido hasta ahora gracias a a lavoluntad que tiene la gente de salir y explorar territorios desconocidos.

Estoy escribiendo este libro acerca de control de revisiones distribuido porque creo que es un tema importanteque merece una guıa de campo. Escogı escribir acerca de Mercurial porque es la herramienta mas facil para explorarel terreno, y sin embargo escala a las demandas de retadores ambientes reales donde muchas otras herramientas decontrol de revisiones fallan.

0.1. Este libro es un trabajo en progresoEstoy liberando este libro mientras lo sigo escribiendo, con la esperanza de que pueda ser util a otros. Tambien

espero que los lectores contribuiran como consideren adecuado.

0.2. Acerca de los ejemplos en este libroEste libro toma un enfoque inusual hacia las muestras de codigo. Cada ejemplo esta “en directo”—cada uno es

realmente el resultado de un script de shell que ejecuta los comandos de Mercurial que usted ve. Cada vez que unacopia del libro es construida desde su codigo fuente, todos los scripts de ejemplo son ejecutados automaticamente, ysus resultados actuales son comparados contra los resultados esperados.

La ventaja de este enfoque es que los ejemplos siempre son precisos; ellos describen exactamente el compor-tamiento de la version de Mercurial que es mencionada en la portada del libro. Si yo actualizo la version de Mercurialque estoy documentando, y la salida de algun comando cambia, la construccion falla.

Hay una pequena desventaja de este enfoque, que las fechas y horas que usted vera en los ejemplos tienden aestar “aplastadas” juntas de una forma que no serıa posible si los mismos comandos fueran escritos por un humano.Donde un humano puede emitir no mas de un comando cada pocos segundos, con cualquier marca de tiempo resultantecorrespondientemente separada, mis scripts automatizados de ejemplos ejecutan muchos comandos en un segundo.

Como un ejemplo de esto, varios commits consecutivos en un ejemplo pueden aparecer como habiendo ocurridodurante el mismo segundo. Usted puede ver esto en el ejemplo bisect en la seccion 9.5, por ejemplo.

Ası que cuando usted lea los ejemplos, no le de mucha importancia a las fechas o horas que vea en las salidas delos comandos. Pero tenga confianza en que el comportamiento que esta viendo es consistente y reproducible.

0.3. Colofon—este libro es LibreEste libro esta licenciado bajo la Licencia de Publicacion Abierta, y es producido en su totalidad usando herramien-

tas de Software Libre. Es compuesto con LATEX; las ilustraciones son dibujadas y generadas con Inkscape.El codigo fuente completo para este libro es publicado como un repositorio Mercurial, en http://hg.serpentine.

com/mercurial/book.

2

Page 13: Mercurial

Capıtulo 1

Introduccion

1.1. Acerca del control de revisionesEl control de revisiones es el proceso de administrar diferentes versiones de una pieza de informacion. En su forma

mas simple es algo que la mayorıa de gente hace a mano: cada vez que usted modifica un fichero, lo graba con unnuevo nombre que contiene un numero, cada uno mayor que el anterior.

Administrar manualmente muchas versiones de incluso solo un fichero es una tarea propensa a errores, a pesar deque hace bastante tiempo hay herramientas que ayudan en este proceso. Las primeras herramientas para automatizarel control de revisiones fueron pensadas para que un usuario administrara un solo fichero. En las decadas pasadas, elalcance de las herramientas de control de revisiones ha ido aumentando considerablemente; ahora manejan muchosficheros y facilitan el trabajo en conjunto de varias personas. Las mejores herramientas de control de revisiones de laactualidad no tienen problema con miles de personas trabajando en proyectos que consisten de cientos de miles deficheros.

1.1.1. ¿Por que usar control de revisiones?Hay muchas razones por las cuales usted o su equipo desearıa usar una herramienta automatica de control de

revisiones para un proyecto.

Llevar registro del historial y la evolucion de su proyecto, para evitar hacer la tarea manualmente. Por cadacambio, tendra una bitacora de quien lo hizo; por que se hizo; cuando se hizo; y de que se trataba el cambio.

Cuando trabaja con mas personas, los programas de control de revisiones facilitan la colaboracion. Por ejemplo,cuando varias personas hacen cambios potencialmente incompatibles de forma casi simultanea, el programa leayudara a identificar y resolver tales conflictos.

Puede ayudarle a recuperarse de equivocaciones. Si aplica un cambio que posteriormente se evidencia comoun error, puede revertirlo a una version previa a uno o muchos ficheros. De hecho, una herramienta realmentebuena, incluso puede ayudarle efectivamente a darse cuenta exactamente cuando se introdujo el error (para masdetalles ver la seccion 9.5).

Le ayudara a trabajar simultaneamente, y a manejar las diferencias entre multiples versiones de su proyecto.

La mayorıa de estas razones son igualmente validas —por lo menos en teorıa— ası este trabajando en un proyectosolo usted, o con mucha gente.

Algo fundamental acerca de lo practico de un sistema de control de revisiones en estas dos escalas (“un hackersolitario” y “un equipo gigantesco”) es como se comparan los beneficios con los costos. Una herramienta de controlde revisiones que sea difıcil de entender o usar impondra un costo alto.

3

Page 14: Mercurial

Un proyecto de quinientas personas es muy propenso a colapsar solamente con su peso inmediatamente sin unaherramienta y un proceso de control de versiones. En este caso, el costo de usar control de revisiones ni siquiera setiene en cuenta, puesto que sin el, el fracaso esta casi garantizado.

Por otra parte, un “arreglo rapido” de una sola persona, excluirıa la necesidad de usar una herramienta de controlde revisiones, porque casi seguramente, el costo de usar una estarıa cerca del costo del proyecto. ¿No es ası?

Mercurial soporta ambas escalas de de desarrollo de manera unica. Puede aprender lo basico en pocos minutos, ydado su bajo sobrecosto, puede aplicar el control de revisiones al proyecto mas pequeno con facilidad. Su simplicidadsignifica que no tendra que preocuparse por conceptos obtusos o secuencias de ordenes compitiendo por espaciomental con lo que sea que realmente este tratando de hacer. Al mismo tiempo, Mercurial tiene alto desempeno y sunaturaleza peer-to-peer le permite escalar indoloramente para manejar grandes proyectos.

Ninguna herramienta de control de revisiones puede salvar un proyecto mal administrado, pero la eleccion deherramientas puede hacer una gran diferencia en la fluidez con la cual usted puede trabajar en un proyecto.

1.1.2. La cantidad de nombres del control de revisionesEl control de revisiones es un campo amplio, tan amplio que no hay un acronimo o nombre unico. A continuacion

presentamos un listado de nombres comunes y acronimos que se podrıan encontrar:

Control de revisiones (RCS)

Manejo de Configuraciones de Programas (SCM), o administracon de configuraciones

Administracion de codigo fuente

Control de Codigo Fuente, o Control de Fuentes

Control de Versiones (VCS)

Algunas personas aducen que estos terminos tienen significados diversos, pero en la practica se sobreponen tanto queno hay una forma acordada o incluso adecuada de separarlos.

1.2. Historia resumida del control de revisionesLa herramienta de control de revisiones mas antigua conocida es SCCS (Sistema de Control de Codigo), escrito

por Marc Rochkind en Bell Labs, a comienzos de los setentas (1970s). SCCS operaba sobre ficheros individuales, yrequerıa que cada persona que trabajara en el proyecto tuviera acceso a un espacio compartido en un solo sistema.Solamente una persona podıa modificar un fichero en un momento dado; el arbitramiento del acceso a los ficherosse hacıa con candados. Era comun que la gente pusiera los candados a los ficheros, y que posteriormente olvidaraquitarlos, impidiendo que otro pudiera modificar los ficheros en cuestion sin la intervencion del administrador.

Walter Tichy desarrollo una alternativa gratuita a SCCS a comienzos de los ochentas (1980s); llamo a su programaRCS (Sistema de Control de Revisiones). Al igual que SCCS, RCS requerıa que los desarrolladores trabajaran enun unico espacio compartido y colocaran candados a los ficheros para evitar que varias personas los modificaransimultaneamente.

Despues en los ochenta, Dick Grune uso RCS como un bloque de construccion para un conjunto de guiones delınea de comando, que inicialmente llamo cmt, pero que renombro a CVS (Sistema Concurrente de Versiones). Lagran innovacion de CVS era que permitıa a los desarrolladores trabajar simultaneamente de una forma mas o menosindependiente en sus propios espacios de trabajo. Los espacios de trabajo personales impedıan que los desarrolladoresse pisaran las mangueras todo el tiempo, situacion comun con SCCS y RCS. Cada desarrollador tenıa una copia detodos los ficheros del proyecto y podıa modificar sus copias independientemente, Tenıan que fusionar sus edicionesantes de consignar los cambios al repositorio central.

Brian Berliner tomo los scripts originales de Grune y los reescribio en C, publicando en 1989 el codigo sobre elcual se ha desarrollado la version moderna de CVS. CVS adquirio posteriormente la habilidad de operar sobre unaconexion de red, dotandolo de una arquitectura, cliente/servidor. La arquitectura de CVS es centralizada; el historialdel proyecto esta unicamente en el repositorio central. Los espacios de trabajo de los clientes contienen unicamente

4

Page 15: Mercurial

copias recientes de las versiones de los ficheros, y pocos metadatos para indicar donde esta el servidor. CVS ha tenidoun exito enorme; Es probablemente el sistema de control de revisiones mas extendido del planeta.

A comienzos de los noventa (1990s), Sun MicroSystems desarrollo un temprano sistema distribuido de controlde revisiones llamado TeamWare. Un espacio de trabajo TeamWare contiene una copia completa del historial delproyecto. TeamWare no tiene la nocion de repositorio central. (CVS se basaba en RCS para el almacenamiento de suhistorial; TeamWare usaba SCCS.)

A medida que avanzaba la decada de los noventa, se empezo a evidenciar los problemas de CVS. Almacenacambios simultaneos a muchos ficheros de forma individual, en lugar de agruparlos como una operacion unica yatomica logicamente. No maneja bien su jerarquıa de ficheros; es facil desordenar un repositorio al renombrar ficherosy directorios. Peor aun, su codigo fuente es difıcil de leer y mantener, lo que hizo que su “umbral de dolor” paraarreglar sus problemas arquitecturales fuera algo prohibitivo.

En 2001, Jim Blandy y Karl Fogel, dos desarrolladores que habıan trabajado en CVS, comenzaron un proyectopara reemplazarlo con una herramienta con mejor arquitectura y codigo mas limpio. El resultado, Subversion, nose separo del modelo centralizado cliente/servidor de CVS, pero anadio consignaciones atomicas de varios ficheros,mejor manejo de espacios de nombres , y otras caracterısticas que lo hacen mejor que CVS. Desde su version inicial,ha ido creciendo en popularidad rapidamente.

Mas o menos en forma simultanea Graydon Hoare comenzo a trabajar en un ambicioso sistema distribuido decontrol de versiones que llamo Monotone. Mientras que Monotone se enfocaba a evitar algunas fallas de diseno deCVS con una arquitectura peer-to-peer, fue mucho mas alla de las herramientas anteriores (y posteriores) de control derevisiones en varios aspectos innovadores. Usa hashes criptograficos como identificadores, y tiene una nocion integralde “confianza” para codigo de diversas fuentes.

Mercurial nacio en el 2005. Algunos de sus aspectos de de diseno fueron influenciados por Monotone, pero Mer-curial se enfoca en la facilidad de uso, gran rendimiento y escalabilidad para proyectos muy grandes.

1.3. Tendencias en el control de revisionesHa habido una tendencia inconfundible en el desarrollo y uso de las herramientas de control de revisiones en las

cuatro decadas pasadas, mientras la gente se ha hecho familiar con las capacidades de sus herramientas y se ha vistorestringida por sus limitaciones.

La primera generacion comenzo administrando ficheros individuales en computadores por persona. A pesar de quetales herramientas representaron un avance importante frente al control de revisiones manual, su modelo de candadosy la dependencia a un solo computador los limito a equipos de trabajo pequenos y acoplados.

La segunda generacion dejo atras esas limitaciones moviendose a arquitecturas centradas en redes, y administrandoproyectos completos a la vez. A medida que los proyectos crecıan, nacieron nuevos problemas. Con la necesidad decomunicacion frecuente con los servidores, escalar estas maquinas se convirtio en un problema en proyectos realmentegrandes. Las redes con poca estabilidad podrıan impedir que usuarios remotos se conectaran al servidor. A medida quelos proyectos de codigo abierto comenzaron a ofrecer acceso de solo lectura de forma anonima a cualquiera, la gentesin permiso para consignar vio que no podıan usar tales herramientas para interactuar en un proyecto de forma natural,puesto que no podıan guardar sus cambios.

La generacion actual de herramientas de control de revisiones es peer-to-peer por naturaleza. Todos estos sistemashan eliminado la dependencia de un unico servidor central, y han permitido que la gente distribuya sus datos de controlde revisiones donde realmente se necesita. La colaboracion a traves de Internet ha cambiado las limitantes tecnologicaspor la cuestion de eleccion y consenso. Las herramientas modernas pueden operar sin conexion indefinidamente yautonomamente, necesitando una conexion de red solamente para sincronizar los cambios con otro repositorio.

1.4. Algunas ventajas del control distribuido de revisionesA pesar de que las herramientas para el control distribuido de revisiones lleva varios anos siendo tan robustas y

usables como la generacion previa de sus contrapartes, algunas personas que usan las herramientas mas antiguas nose han percatado de sus ventajas. Hay gran cantidad de situaciones en las cuales las herramientas distribuidas brillanfrente a las centralizadas.

5

Page 16: Mercurial

Para un desarrollador individual, las herramientas distribuidas casi siempre son mas rapidas que las centralizadas.Por una razon sencilla: Una herramienta centralizada necesita comunicarse por red para las operaciones mas usuales,debido a que los metadatos se almacenan en una sola copia en el servidor central. Una herramienta distribuida almacenatodos sus metadatos localmente. Con todo lo demas de la misma forma, comunicarse por red tiene un sobrecosto enuna herramienta centralizada. No subestime el valor de una herramienta de respuesta rapida: Usted empleara muchotiempo interactuando con su programa de control de revisiones.

Las herramientas distribuidas son indiferentes a los caprichos de su infraestructura de servidores, de nuevo, debidoa la replicacion de metadatos en tantos lugares. Si usa un sistema centralizado y su servidor explota, ojala los mediosfısicos de su copia de seguridad sean confiables, y que su ultima copia sea reciente y ademas funcione. Con unaherramienta distribuida tiene tantas copias de seguridad disponibles como computadores de contribuidores.

La confiabilidad de su red afectara las herramientas distribuidas de una forma mucho menor que a las herramientascentralizadas. Usted no puede siquiera usar una herramienta centralizada sin conexion de red, excepto por algunasordenes muy limitadas. Con herramientas distribuidas, si sus conexion cae mientras usted esta trabajando, podrıanisiquiera darse cuenta. Lo unico que que no podra hacer es comunicarse con repositorios en otros computadores, algoque es relativamente raro comparado con las operaciones locales. Si tiene colaboradores remotos en su equipo, puedeser importante.

1.4.1. Ventajas para proyectos de codigo abiertoSi descubre un proyecto de codigo abierto y decide que desea comenzar a trabajar en el, y ese proyecto usa una

herramienta de control distribuido de revisiones, usted es de inmediato un par con la gente que se considera el “alma”del proyecto. Si ellos publican sus repositorios, usted puede copiar inmediatamente el historial del proyecto, hacercambios y guardar su trabajo, usando las mismas herramientas de la misma forma que ellos. En contraste, con unaherramienta centralizada, usted debe usar el programa en un modo “solo lectura” a menos que alguien le otorguepermisos para consignar cambios en el repositorio central. Hasta entonces, no podra almacenar sus cambios y susmodificaciones locales correran el riesgo de danarse cuando trate de actualizar su vista del repositorio.

Las bifurcaciones (forks) no son un problema

Se ha mencionado que las herramientas de control distribuido de versiones albergan un riesgo a los proyectosde codigo abierto, puesto que se vuelve muy sencillo hacer una “bifurcacion”1 del desarrollo del proyecto. Una bi-furcacion sucede cuando hay diferencias de opinion o actitud entre grupos de desarrolladores que desemboca en ladecision de la imposibilidad de continuar trabajando juntos. Cada parte toma una copia mas o menos completa delcodigo fuente del proyecto y toma su propio rumbo.

En algunas ocasiones los lıderes de las bifurcaciones reconcilian sus diferencias. Con un sistema centralizado decontrol de revisiones, el proceso tecnico de reconciliarse es doloroso, y se hace de forma muy manual. Usted tiene quedecidir que historial de revisiones va a “ganar”, e injertar los cambios del otro equipo en el arbol de alguna manera.Con esto usualmente se pierde algo o todo del historial de la revision de alguna de las partes.

Lo que las herramientas distribuidas hacen con respecto a las bifurcaciones, es que las bifurcaciones son la unicaforma de desarrollar un proyecto. Cada cambio que usted hace es potencialmente un punto de bifurcacion. La granfortaleza de esta aproximacion es que las herramientas distribuidas de control de revisiones tiene que ser bueno alfusionar las bifurcaciones, porque las bifurcaciones son absolutamente fundamentales: pasan todo el tiempo.

Si todas las porciones de trabajo que todos hacen, todo el tiempo, se enmarcan en terminos de bifurcacionesy fusiones, entonces a aquello a lo que se refiere en el mundo del codigo abierto a una “bifurcacion” se conviertepuramente en una cuestion social. Lo que hacen las herramientas distribuidas es disminuir la posibilidad de unabifurcacion porque:

Eliminan la distincion social que imponen las herramientas centralizadas: aquella entre miembros (personas conpermiso de consignar) y forasteros (los que no tienen el permiso).

Facilitan la reconciliacion despues de una bifurcacion social, porque todo lo que concierne al programa decontrol de revisiones es una fusion.

1N. del T. fork.

6

Page 17: Mercurial

Algunas personas se resisten a las herramientas distribuidas porque desean mantener control completo sobre susproyectos, y creen que las herramientas centralizadas les dan tal control. En todo caso, si este es su parecer, y ustedpublica sus repositorios de CVS o Subversion, hay muchas herramientas disponibles que pueden obtener el historialcompleto (aunque sea lentamente) y recrearlo en otro sitio que usted no controla. Siendo ası un control ilusorio, puestoque esta impidiendo la fluidez de colaboracion en lugar de prevenir que alguien se sienta impulsado a obtener unacopia y hacer una bifurcacion con su historial.

1.4.2. Ventajas para proyectos comercialesMuchos proyectos comerciales tienen grupos de trabajo distribuidos alrededor del globo. Quienes contribuyen y

estan lejos de un repositorio central veran una ejecucion mas lenta de los comandos y tal vez menos confiabilidad.Los sistemas de control de revision comerciales intentan amortiguar estos problemas con adiciones de replicacionremota que usualmente son muy costosos y complicados de administrar. Un sistema distribuido no padece estos prob-lemas. Mejor aun, puede colocar varios servidores autorizados, por ejemplo, uno por sitio, de tal forma que no hayacomunicacion redundante entre repositorios sobre enlaces de conexion costosos.

Los sistemas de control de revisiones distribuidos tienden a ser poco escalables. No es inusual que costosos sis-temas centralizados caigan ante la carga combinada de unas cuantas docenas de usuarios concurrentes. De nuevo, lasrespuestas tıpicas de replicacion tienden a ser costosas y complejas de instalar y administrar. Dado que la carga enun servidor central—si es que tiene uno—es muchas veces menor con una herramienta distribuida (debido a que losdatos estan replicados en todas partes), un solo servidor economico puede tratar las necesidades de equipos muchomas grandes, y la replicacion para balancear la carga se vuelve cosa de guiones.

Si tiene un empleado en el campo, se beneficiara grandemente de un sistema distribuido de control de versionesal resolver problemas en el sitio del cliente. La herramienta le permitira generar construcciones a la medida, probardiferentes arreglos de forma independiente y buscar de forma eficiente las fuentes de fallos en el historial y regresionesen los ambientes de los clientes, todo sin necesidad de conectarse al servidor de su companıa.

1.5. ¿Por que elegir Mercurial?Mercurial cuenta con un conjunto unico de propiedades que lo hacen una eleccion particularmente buena como

sistema de control de revisiones, puesto que:

Es facil de aprender y usar.

Es liviano.

Escala de forma excelente.

Es facil de acondicionar.

Si los sistemas de control de revisiones le son familiares, deberıa estar listo para usar Mercurial en menos de cincominutos. Si no, solo va a tomar unos pocos minutos mas. Las ordenes de Mercurial y su conjunto de caracterısticasson uniformes y consistentes generalmente, y basta con que siga unas pocas reglas generales en lugar de un montonde excepciones.

En un proyecto pequeno, usted puede comenzar a trabajar con Mercurial en pocos momentos. Crear nuevos cam-bios y ramas, transferir cambios (localmente o por la red); y las operaciones relacionadas con el estado y el historialson rapidas. Mercurial buscar ser ligero y no incomodar en su camino combinando poca sobrecarga cognitiva conoperaciones asombrosamente rapidas.

La utilidad de Mercurial no se limita a proyectos pequenos: esta siendo usado por proyectos con centenas de milesde contribuyentes, cada uno conteniendo decenas de miles de ficheros y centenas de megabytes de codigo fuente

Si la funcionalidad basica de Mercurial no es suficiente para usted, es muy facil extenderlo. Mercurial se comportamuy bien con tareas de scripting y su limpieza interna junto con su implementacion en Python permiten anadir carac-terısticas facilmente en forma de extensiones. Hay un buen numero de extensiones utiles y populares en este momento,desde ayudar a identificar fallos hasta mejorar su desempeno.

7

Page 18: Mercurial

1.6. Comparacion de Mercurial con otras herramientasAntes de leer, por favor tenga en cuenta que esta seccion necesariamente refleja mis propias experiencias, intereses

y (tengo que decirlo) mis preferencias. He usado cada una de las herramientas de control de versiones listadas acontinuacion, y en muchos casos por varios anos.

1.6.1. SubversionSubversion es una herramienta de control de revisiones muy popular, desarrollada para reemplazar a CVS. Tiene

una arquitectura centralizada tipo cliente/servidor.Subversion y Mercurial tienen comandos con nombres similares para hacer las mismas operaciones, por lo que si

le son familiares en una, sera sencillo aprender a usar la otra. Ambas herramientas son portables en todos los sistemasoperativos populares.

Antes de la version 1.5, Subversion no tenıa soporte para fusiones. En el momento de la escritura, sus capcidadespara llevar cuenta de las funciones son nuevas, complicadas y poco estables2.

Mercurial tiene una ventaja considerable en desempeno sobre Subversion en cualquier operacion de control derevisiones que yo haya medido. He medido sus ventajas con factores desde dos hasta seis veces comparando con al-macenamiento de ficheros ra local Subversion 1.4.3, el cual es el metodo de acceso mas rapido. En los escenariosmas realistas incluyendo almacenamiento con la red de por medio, Subversion se encuentra en desventaja aun mayor.Dado que casi todas las ordenes de Subversion deben tratar con el servidor y Subversion no tiene utilidades de repli-cacion adecuadas, la capacidad del servidor y el ancho de banda se convierten en cuellos de botella para proyectosmodestamente grandes.

Adicionalmente, Subversion tiene un sobrecosto considerable en almacenamiento para evitar transacciones porred en algunas operaciones, tales como encontrar ficheros modificados (status) y desplegar informacion frente a larevision actual (diff). Como resultado, la copia de trabajo de Subversion termina siendo del mismo tamano o masgrande que un repositorio de Mercurial y el directorio de trabajo, a pesar de que el repositorio de Mercurial contieneel historial completo del proyecto.

Subversion tiene soporte amplio de otras herramientas. Mercurial por ahora esta bastante atras en este aspecto.Esta diferencia esta disminuyendo, y algunas de las herramientas GUI3, eclipsan sus equivalentes de Subversion. Aligual que Mercurial, Subversion tiene un excelente manual de usuario.

Dado que Subversion no almacena el historial de revisiones en el cliente, es muy bueno para administrar proyectosque tienen muchos ficheros binarios grandes y opacos. Si consigna cincuenta revisiones de un fichero de 10MB queno es comprimible, el esapacio en el cliente de Subversion se mantendra constante mientras que el espacio usadopor cualquier Sistema Distribuido de Control de Revisiones crecera rapidamente en proporcion con el numero derevisiones, debido a que las diferencias entre cada revision es grande.

Adicionalmente, generalmente es difıcil o mas bien, imposible mezclar diferentes versiones de un fichero binario.La habilidad de Subversion para permitirle al usuario poner una cerradura a un fichero, de modo que tenga un permisoexclusivo para consignar cambios, puede ser una ventaja significativa en un proyecto donde los ficheros binarios seanusados ampliamente.

Mercurial puede importar el historial de revisiones de un repositorio de Subversion. Tambien puede exportar elhistorial de revisiones a un repositorio de Subversion. De esta forma es sencillo “dar un vistazo” y usar Mercurialy Subversion en paralelo antes de decidirse a dar el paso. La conversion del historial es incremental, de modo quepuede aplicar una conversion inicial, y despues conversiones pequenas y adicionales posteriormente para traer nuevoscambios.

1.6.2. GitGit es una herramienta distribuida de control de revisiones desarrollada para administrar el arbol del kernel de

Linux. Al igual que Mercurial los principios de su diseno fueron influenciados por Monotone.

2N. del T. buggy3N. del T. Interfaz de Usuario Grafica

8

Page 19: Mercurial

Git tiene un conjunto de ordenes muy grande; en la version 1.5.0 ofrece 139 ordenes individuales. Tiene ciertareputacion de ser difıcil de aprender. Comparado con Git, Mercurial tiene un fuerte enfoque hacia la facilidad.

En terminos de rendimiento, Git es extremadamente rapido. En muchos casos, es mas rapido que Mercurial, por lomenos en Linux, mientras que Mercurial se comporta mejor en otras operaciones. De todas maneras en Windows, eldesempeno y el nivel general de soporte que ofrece Git, al momento de la escritura, esta bastante atras de Mercurial.

Mientras que el repositorio de Mercurial no requiere mantenimiento, el repositorio de Git requiere frecuentes“reempaquetados” de sus metadatos. Sin estos, el desempeno se degrada y el uso de espacio crece rapidamente. Unservidor que contenga repositorios de Git que no sean reempacados rigurosa y frecuentemente requerira trabajo in-tenso de disco durante las copias de seguridad, y ha habido situaciones en copias de seguridad diaria que toman masde 24 horas como resultado. Un repositorio recien reempacado de Git es un poco mas pequeno que un repositorio deMercurial, pero un repositorio sin reempacar es varios ordenes de magnitud mas grande.

El corazon de Git esta escrito en C. Muchas ordenes de Git estan implementadas como guiones de lınea de co-mandos o de Perl y la calidad de esos guiones varıa ampliamente. He encontrado muchas situaciones en las cuales losguiones no tuvieron en cuenta la presencia de errores que podrıan haber sido fatales.

Mercurial puede importar el historial de revisiones de un repositorio de Git.

1.6.3. CVSCVS es probablemente la herramienta de control de revisiones mas ampliamente usada en el planeta. Debido a su

edad y su poca pulcritud interna, ha sido ligeramente mantenida en muchos anos.Tiene una arquitectura centralizada cliente/servidor. No agrupa cambios relacionados en consignaciones atomicas,

pemitiendo que con facilidad la gente “rompa la construccion”: una persona puede consignar exitosamente parte delcambio y estar bloqueada por la necesidad de una mezcla, forzando a otras personas a ver solamente una porcion deltrabajo que estaban buscando hacer. Esto afecta tambien la forma como usted trabaja con el historial del proyecto. Siquiere ver todas las modificaciones que alguien hizo como parte de una tarea, necesitara inspeccionar manualmentelas descripciones y las marcas de tiempo de cambio de cada fichero involucrado (esto, si usted saber cuales eran talesficheros).

CVS tiene una nocion confusa de etiquetas y ramas que yo no tratarıa incluso de describir. No soporta renom-bramiento de ficheros o directorios adecuadamente, facilitando el corromper un repositorio. Casi no tiene chequeode consistencia interna, por lo tanto es casi imposible identificar por que o como se corrompio un repositorio. Yo norecomendarıa un repositorio de CVS para proyecto alguno, ni existente ni nuevo.

Mercurial puede importar el historial de revisiones de CVS. De todas maneras hay ciertas precauciones que apli-can; las cuales tambien son necesarias para cualquier herramienta importadora de historial de CVS. Debido a la faltade atomicidad de cambios y el no versionamiento de la jerarquıa del sistema de ficheros, es imposible reconstruircompletamente el historial de CVS con precision; hay cierto trabajo de conjetura involucrado y los renombramientostampoco se mostraran. Debido a que gran parte de la administracion avanzada de CVS tiene que hacerse manualmentey por lo tanto es proclive al error, es comun que los importadores de CVS encuentren muchos problemas con reposi-torios corruptos (marcas de tiempo totalmente desubicadas y ficheros que han permanecido con candados por mas deuna decada son dos de los problemas menos interesantes de los que puedo retomar de mi experiencia personal).

Mercurial puede importar el historial de revisiones de un repositorio CVS.

1.6.4. Herramientas comercialesPerforce tiene una arquitectura centralizada cliente/servidor sin almacenamiento de dato alguno de cache en el

lado del cliente. A diferencia de las herramientas modernas de control de revisiones, Perforce requiere que un usuarioejecute un comando para informar al servidor acerca de todo fichero que se vaya a editar.

El rendimiento de Perforce es muy bueno para equipos pequenos, pero se degrada rapidamente cuando el numerode usuarios va mas alla de pocas docenas. Instalaciones modestamente grandes de Perforce requiere la organizacionde proxies para soportar la carga que sus usuarios generan.

9

Page 20: Mercurial

1.6.5. Elegir una herramienta de control de revisionesCon la excepcion de CVS, toda las herramientas que se han listado anteriormente tienen fortalezas unicas que las

hacen valiosas de acuerdo al tipo de trabajo. No hay una unica herramienta de control de revisiones que sea la mejoren todas las situaciones.

Por ejemplo, Subversion es una buena eleccion para trabajar con edicion frecuente de ficheros binarios, debido asu naturaleza centralizada y soporte para poner candados a ficheros.

Personalmente encuentro las propiedades de simplicidad, desempeno, y buen soporte de fusiones de Mercurial unacombinacion llamativa que ha dado buenos frutos por varios anos.

1.7. Migrar de otra herramienta hacia MercurialMercurial viene con una extension llamada convert, que puede importar historiales de revisiones de forma incre-

mental desde varias herramientas de control de revisiones. Por “incremental”, quiero decir que puede migrar toda elhistorial de un proyecto en una primera instancia y despues volver a ejecutar la migracion posteriormente para obtenerlos nuevos cambios que han sucedido despues de la migracion inicial.

A continuacion presentamos las herramientas de revisiones que soporta el comando convert:

Subversion

CVS

Git

Darcs

Adicionalmente, convert puede exportar cambios de Mercurial hacia Subversion. Lo que hace posible probarSubversion y Mercurial en paralelo antes de lanzarse a un migracion total, sin arriesgarse a perder trabajo alguno.

El comando “hg convert” es sencillo de usar. Basta con apuntarlo hacia la ruta o el URL del repositorio fuente,opcionalmente darle el nombre del nombre del repositorio destino y comenzara a hacer su trabajo. Despues de laconversion inicial, basta con invocar de nuevo el comando para importar cambios nuevos.

10

Page 21: Mercurial

Capıtulo 2

Una gira de Mercurial: lo basico

2.1. Instalar Mercurial en su sistemaHay paquetes binarios precompilados de Mercurial disponibles para cada sistema operativo popular. Esto hace

facil empezar a usar Mercurial en su computador inmediatamente.

2.1.1. LinuxDado que cada distribucion de Linux tiene sus propias herramientas de manejo de paquetes, polıticas, y ritmos

de desarrollo, es difıcil dar un conjunto exhaustivo de instrucciones sobre como instalar el paquete de Mercurial. Laversion de Mercurial que usted tenga a disposicion puede variar dependiendo de que tan activa sea la persona quemantiene el paquete para su distribucion.

Para mantener las cosas simples, me enfocare en instalar Mercurial desde la lınea de comandos en las distribucionesde Linux mas populares. La mayorıa de estas distribuciones proveen administradores de paquetes graficos que lepermitiran instalar Mercurial con un solo clic; el nombre de paquete a buscar es mercurial.

Debian1 apt-get install mercurial

Fedora Core1 yum install mercurial

Gentoo1 emerge mercurial

OpenSUSE1 yum install mercurial

Ubuntu El paquete de Mercurial de Ubuntu esta basado en el de Debian. Para instalarlo, ejecute el siguiente comando.

1 apt-get install mercurial

El paquete de Mercurial para Ubuntu tiende a atrasarse con respecto a la version de Debian por un margen detiempo considerable (al momento de escribir esto, 7 meses), lo que en algunos casos significara que usted puedeencontrarse con problemas que ya habran sido resueltos en el paquete de Debian.

2.1.2. SolarisSunFreeWare, en http://www.sunfreeware.com, es una buena fuente para un gran numero de paquetes compi-

lados para Solaris para las arquitecturas Intel y Sparc de 32 y 64 bits, incluyendo versiones actuales de Mercurial.

11

Page 22: Mercurial

2.1.3. Mac OS XLee Cantey publica un instalador de Mercurial para Mac OS X en http://mercurial.berkwood.com. Este

paquete funciona en tanto en Macs basados en Intel como basados en PowerPC. Antes de que pueda usarlo, usteddebe instalar una version compatible de Universal MacPython [BI]. Esto es facil de hacer; simplemente siga lasinstrucciones del sitio de Lee.

Tambien es posible instalar Mercurial usando Fink o MacPorts, dos administradores de paquetes gratuitos y pop-ulares para Mac OS X. Si usted tiene Fink, use sudo apt-get install mercurial-py25. Si usa MacPorts, sudoport install mercurial.

2.1.4. WindowsLee Cantey publica un instalador de Mercurial para Windows en http://mercurial.berkwood.com. Este pa-

quete no tiene dependencias externas; “simplemente funciona”.Nota: La version de Windows de Mercurial no convierte automaticamente los finesde lınea entre estilos Windows y Unix. Si usted desea compartir trabajo con usuar-ios de Unix, debera hacer un trabajo adicional de configuracion. XXX Terminaresto.

2.2. ArrancandoPara empezar, usaremos el comando “hg version” para revisar si Mercurial esta instalado adecuadamente. La

informacion de la version que es impresa no es tan importante; lo que nos importa es si imprime algo en absoluto.

1 $ hg version2 Mercurial Distributed SCM (version 1.0.1)3

4 Copyright (C) 2005-2008 Matt Mackall <[email protected]> and others5 This is free software; see the source for copying conditions. There is NO6 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2.2.1. Ayuda integradaMercurial provee un sistema de ayuda integrada. Esto es invaluable para esas ocasiones en la que usted esta atorado

tratando de recordar como ejecutar un comando. Si esta completamente atorado, simplemente ejecute “hg help”; estoimprimira una breve lista de comandos, junto con una descripcion de que hace cada uno. Si usted solicita ayuda sobreun comando especıfico (como abajo), se imprime informacion mas detallada.

1 $ hg help init2 hg init [-e CMD] [--remotecmd CMD] [DEST]3

4 create a new repository in the given directory5

6 Initialize a new repository in the given directory. If the given7 directory does not exist, it is created.8

9 If no directory is given, the current directory is used.10

11 It is possible to specify an ssh:// URL as the destination.12 Look at the help text for the pull command for important details13 about ssh:// URLs.14

12

Page 23: Mercurial

15 options:16

17 -e --ssh specify ssh command to use18 --remotecmd specify hg command to run on the remote side19

20 use "hg -v help init" to show global options

Para un nivel mas impresionante de detalle (que usted no va a necesitar usualmente) ejecute “hg help -v”. La opcion-v es la abreviacion para --verbose, y le indica a Mercurial que imprima mas informacion de lo que harıa usualmente.

2.3. Trabajar con un repositorioEn Mercurial, todo sucede dentro de un repositorio. El repositorio para un proyecto contiene todos los ficheros que

“pertenecen a” ese proyecto, junto con un registro historico de los ficheros de ese proyecto.No hay nada particularmente magico acerca de un repositorio; es simplemente un arbol de directorios en su sistema

de ficheros que Mercurial trata como especial. Usted puede renombrar o borrar un repositorio en el momento que lodesee, usando bien sea la lınea de comandos o su explorador de ficheros.

2.3.1. Hacer una copia local de un repositorioCopiar un repositorio es solo ligeramente especial. Aunque usted podrıa usar un programa normal de copia de

ficheros para hacer una copia del repositorio, es mejor usar el comando integrado que Mercurial ofrece. Este comandose llama “hg clone”1, porque crea una copia identica de un repositorio existente.

1 $ hg clone http://hg.serpentine.com/tutorial/hello2 destination directory: hello3 requesting all changes4 adding changesets5 adding manifests6 adding file changes7 added 5 changesets with 5 changes to 2 files8 updating working directory9 2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Si nuestro clonado tiene exito, deberıamos tener un directorio local llamado hello. Este directorio contendra algunosficheros.

1 $ ls -l2 total 03 drwxr-xr-x 3 jerojasro jerojasro 120 Feb 10 18:23 hello4 $ ls hello5 Makefile hello.c

Estos ficheros tienen el mismo contenido e historial en nuestro repositorio y en el repositorio que clonamos.Cada repositorio Mercurial esta completo, es autocontenido e independiente. Contiene su propia copia de los

ficheros y el historial de un proyecto. Un repositorio clonado recuerda la ubicacion de la que fue clonado, pero no secomunica con ese repositorio, ni con ningun otro, a menos que usted le indique que lo haga.

Lo que esto significa por ahora es que somos libres de experimentar con nuestro repositorio, con la tranquilidad desaber que es una “caja de arena” privada que no afectara a nadie mas.

1N. del T. Del termino “clonar” en ingles.

13

Page 24: Mercurial

2.3.2. Que hay en un repositorio?Cuando miramos en detalle dentro de un repositorio, podemos ver que contiene un directorio llamado .hg. Aquı es

donde Mercurial mantiene todos los metadatos del repositorio.

1 $ cd hello2 $ ls -a3 . .. .hg Makefile hello.c

Los contenidos del directorio .hg y sus subdirectorios son exclusivos de Mercurial. Usted es libre de hacer lo quedesee con cualquier otro fichero o directorio en el repositorio.

Para introducir algo de terminologıa, el directorio .hg es el repositorio “real”, y todos los ficheros y directorios quecoexisten con el estan en el directorio de trabajo. Una forma sencilla de recordar esta distincion es que el repositoriocontiene el historial de su proyecto, mientras que el directorio de trabajo contiene una instantanea de su proyecto enun punto particular del historial.

2.4. Vistazo rapido al historialUna de las primeras cosas que se desea hacer con un repositorio nuevo, poco conocido, es conocer su historial. El

comando “hg log” nos permite ver el mismo.

1 $ hg log2 changeset: 4:2278160e78d43 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Sat Aug 16 22:16:53 2008 +02006 summary: Trim comments.7

8 changeset: 3:0272e0d5a5179 user: Bryan O’Sullivan <[email protected]>

10 date: Sat Aug 16 22:08:02 2008 +020011 summary: Get make to generate the final binary from a .o file.12

13 changeset: 2:fef857204a0c14 user: Bryan O’Sullivan <[email protected]>15 date: Sat Aug 16 22:05:04 2008 +020016 summary: Introduce a typo into hello.c.17

18 changeset: 1:82e55d328c8c19 user: [email protected] date: Fri Aug 26 01:21:28 2005 -070021 summary: Create a makefile22

23 changeset: 0:0a04b987be5a24 user: [email protected] date: Fri Aug 26 01:20:50 2005 -070026 summary: Create a standard "hello, world" program27

Por defecto este programa imprime un parrafo breve por cada cambio al proyecto que haya sido grabado. Dentro dela terminologıa de Mercurial, cada uno de estos eventos es llamado conjunto de cambios, porque pueden contener unregistro de cambios a varios ficheros.

14

Page 25: Mercurial

Los campos de la salida de “hg log” son los siguientes.

changeset2 Este campo tiene un numero, seguido por un :, seguido por una cadena hexadecimal. Ambos son identificadorespara el conjunto de cambios. Hay dos identificadores porque el numero es mas corto y mas facil de recordar quela cadena hexadecimal.

user3 La identidad de la persona que creo el conjunto de cambios. Este es un campo en el que se puede almacenarcualquier valor, pero en la mayorıa de los casos contiene el nombre de una persona y su direccion de correoelectronico.

date4 La fecha y hora en la que el conjunto de cambios fue creado, y la zona horaria en la que fue creado. (La fechay hora son locales a dicha zona horaria; ambos muestran la fecha y hora para la persona que creo el conjunto decambios).

summary5 La primera lınea del texto que uso la persona que creo el conjunto de cambios para describir el mismo.

El texto impreso por “hg log” es solo un sumario; omite una gran cantidad de detalles.La figura 2.1 es una representacion grafica del historial del repositorio hello, para hacer mas facil ver en que di-

reccion esta “fluyendo” el historial. Volveremos a esto varias veces en este capıtulo y en los siguientes.

0: 0a04

1: 82e5

2: fef8

3: 0272

4: 2278 (la más nueva)

(la más antigua)

4: 2278

número derevisión

identificador delconjunto de cambios

Figura 2.1: Historial grafico del repositorio hello

2.4.1. Conjuntos de cambios, revisiones, y comunicandose con otras personasYa que el ingles es un lenguaje notablemente desordenado, y el area de ciencias de la computacion tiene una

notable historia de confusion de terminos (porque usar solo un termino cuando cuatro pueden servir?), el control derevisiones tiene una variedad de frases y palabras que tienen el mismo significado. Si usted habla acerca del historialde Mercurial con alguien, encontrara que la expresion “conjunto de cambios” es abreviada a menudo como “cambio”o (por escrito) “cset”6, y algunas veces un se hace referencia a un conjunto de cambios como una “revision” o “rev”7.

Si bien no es relevante que palabra use usted para referirse al concepto “conjunto de cambios”, el identificador queusted use para referise a “un conjunto de cambios particular” es muy importante. Recuerde que el campo changeseten la salida de “hg log” identifica un conjunto de cambios usando tanto un numero como una cadena hexadecimal.

2N. del T. Conjunto de cambios.3N. del T. Usuario.4N. del T. Fecha.5N. del T. Sumario.6N. del T. Abreviatura para la expresion “changeset” en ingles.7N. del T. De nuevo, como abreviacion para el termino en ingles para “revision” (“revision”).

15

Page 26: Mercurial

El numero de revision solo es valido dentro del repositorio.

Por otro lado, la cadena hexadecimal es el identificador permanente e inmutable que siempre identificara eseconjunto de cambios en todas las copias del repositorio.

La diferencia es importante. Si usted le envıa a alguien un correo electronico hablando acerca de la “revision 33”, hayuna probabilidad alta de que la revision 33 de esa persona no sea la misma suya. Esto sucede porque el numero derevision depende del orden en que llegan los cambios al repositorio, y no hay ninguna garantıa de que los mismoscambios llegaran en el mismo orden en diferentes repositorios. Tres cambios dados a,b,c pueden aparecer en unrepositorio como 0,1,2, mientras que en otro aparecen como 1,0,2.

Mercurial usa los numeros de revision simplemente como una abreviacion conveniente. Si usted necesita hablarcon alguien acerca de un conjunto de cambios, o llevar el registro de un conjunto de cambios por alguna otra razon(por ejemplo, en un reporte de fallo), use el identificador hexadecimal.

2.4.2. Ver revisiones especıficasPara reducir la salida de “hg log” a una sola revision, use la opcion -r (o --rev). Puede usar un numero de

revision o un identificador hexadecimal de conjunto de cambios, y puede pasar tantas revisiones como desee.

1 $ hg log -r 32 changeset: 3:0272e0d5a5173 user: Bryan O’Sullivan <[email protected]>4 date: Sat Aug 16 22:08:02 2008 +02005 summary: Get make to generate the final binary from a .o file.6

7 $ hg log -r 0272e0d5a5178 changeset: 3:0272e0d5a5179 user: Bryan O’Sullivan <[email protected]>

10 date: Sat Aug 16 22:08:02 2008 +020011 summary: Get make to generate the final binary from a .o file.12

13 $ hg log -r 1 -r 414 changeset: 1:82e55d328c8c15 user: [email protected] date: Fri Aug 26 01:21:28 2005 -070017 summary: Create a makefile18

19 changeset: 4:2278160e78d420 tag: tip21 user: Bryan O’Sullivan <[email protected]>22 date: Sat Aug 16 22:16:53 2008 +020023 summary: Trim comments.24

Si desea ver el historial de varias revisiones sin tener que mencionar cada una de ellas, puede usar la notacion derango; esto le permite expresar el concepto “quiero ver todas las revisiones entre a y b, inclusive”.

1 $ hg log -r 2:42 changeset: 2:fef857204a0c3 user: Bryan O’Sullivan <[email protected]>4 date: Sat Aug 16 22:05:04 2008 +02005 summary: Introduce a typo into hello.c.6

16

Page 27: Mercurial

7 changeset: 3:0272e0d5a5178 user: Bryan O’Sullivan <[email protected]>9 date: Sat Aug 16 22:08:02 2008 +0200

10 summary: Get make to generate the final binary from a .o file.11

12 changeset: 4:2278160e78d413 tag: tip14 user: Bryan O’Sullivan <[email protected]>15 date: Sat Aug 16 22:16:53 2008 +020016 summary: Trim comments.17

Mercurial tambien respeta el orden en que usted especifica las revisiones, ası que “hg log -r 2:4” muestra 2,3,4mientras que “hg log -r 4:2” muestra 4,3,2.

2.4.3. Informacion mas detalladaAunque la informacion presentada por “hg log” es util si usted sabe de antemano que esta buscando, puede

que necesite ver una descripcion completa del cambio, o una lista de los ficheros que cambiaron, si esta tratando deaveriguar si un conjunto de cambios dado es el que usted esta buscando. La opcion -v (or --verbose) del comando“hg log” le da este nivel extra de detalle.

1 $ hg log -v -r 32 changeset: 3:0272e0d5a5173 user: Bryan O’Sullivan <[email protected]>4 date: Sat Aug 16 22:08:02 2008 +02005 files: Makefile6 description:7 Get make to generate the final binary from a .o file.8

9

Si desea ver tanto la descripcion como el contenido de un cambio, anada la opcion -p (o --patch). Esto muestrael contenido de un cambio como un diff unificado (si usted nunca ha visto un diff unificado antes, vea la seccion 12.4para un vistazo global).

1 $ hg log -v -p -r 22 changeset: 2:fef857204a0c3 user: Bryan O’Sullivan <[email protected]>4 date: Sat Aug 16 22:05:04 2008 +02005 files: hello.c6 description:7 Introduce a typo into hello.c.8

9

10 diff -r 82e55d328c8c -r fef857204a0c hello.c11 --- a/hello.c Fri Aug 26 01:21:28 2005 -070012 +++ b/hello.c Sat Aug 16 22:05:04 2008 +020013 @@ -11,6 +11,6 @@14

15 int main(int argc, char **argv)16 {

17

Page 28: Mercurial

17 - printf("hello, world!\n");18 + printf("hello, world!\");19 return 0;20 }21

2.5. Todo acerca de las opciones para comandosTomemos un breve descanso de la tarea de explorar los comandos de Mercurial para hablar de un patron en la

manera en que trabajan; sera util tener esto en mente a medida que avanza nuestra gira.Mercurial tiene un enfoque directo y consistente en el manejo de las opciones que usted le puede pasar a los

comandos. Se siguen las convenciones para opciones que son comunes en sistemas Linux y Unix modernos.

Cada opcion tiene un nombre largo. Por ejemplo, el comando “hg log” acepta la opcion --rev, como ya hemosvisto.

Muchas opciones tienen tambien un nombre corto. En vez de --rev, podemos usar -r. (El motivo para quealgunas opciones no tengan nombres cortos es que dichas opciones se usan rara vez.)

Las opciones largas empiezan con dos guiones (p.ej. --rev), mientras que las opciones cortas empiezan conuno (e.g. -r).

El nombre y uso de las opciones es consistente en todos los comandos. Por ejemplo, cada comando que lepermite pasar un ID de conjunto de cambios o un numero de revision acepta tanto la opcion -r como la --rev.

En los ejemplos en este libro, uso las opciones cortas en vez de las largas. Esto solo muestra mis preferencias, ası queno le de significado especial a eso.

Muchos de los comandos que generan salida de algun tipo mostraran mas salida cuando se les pase la opcion -v(o --verbose8), y menos cuando se les pase la opcion -q (o --quiet9).

2.6. Hacer y repasar cambiosAhora que tenemos una comprension adecuada sobre como revisar el historial en Mercurial, hagamos algunos

cambios y veamos como examinarlos.Lo primero que haremos sera aislar nuestro experimento en un repositorio propio. Usaremos el comando “hg

clone”, pero no hace falta clonar una copia del repositorio remoto. Como ya contamos con una copia local del mismo,podemos clonar esa. Esto es mucho mas rapido que clonar a traves de la red, y en la mayorıa de los casos clonar unrepositorio local usa menos espacio en disco tambien.

1 $ cd ..2 $ hg clone hello my-hello3 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd my-hello

A manera de recomendacion, es considerado buena practica mantener una copia “prıstina” de un repositorio remoto amano, del cual usted puede hacer clones temporales para crear cajas de arena para cada tarea en la que desee trabajar.Esto le permite trabajar en multiples tareas en paralelo, teniendo cada una de ellas aislada de las otras hasta que estencompletas y usted este listo para integrar los cambios de vuelta. Como los clones locales son tan baratos, clonar ydestruir repositorios no consume demasiados recursos, lo que facilita hacerlo en cualquier momento.

8N. del T. Prolijo.9N. del T. Silencioso.

18

Page 29: Mercurial

En nuestro repositorio my-hello, hay un fichero hello.c que contiene el clasico programa “hello, world”10.Usaremos el clasico y venerado comando sed para editar este fichero y hacer que imprima una segunda lınea desalida. (Estoy usando el comando sed para hacer esto solo porque es facil escribir un ejemplo automatizado con el.Dado que usted no tiene esta restriccion, probablemente no querra usar sed; use su editor de texto preferido para hacerlo mismo).

1 $ sed -i ’/printf/a\\tprintf("hello again!\\n");’ hello.c

El comando “hg status” de Mercurial nos dice lo que Mercurial sabe acerca de los ficheros en el repositorio.

1 $ ls2 Makefile hello.c3 $ hg status4 M hello.c

El comando “hg status” no imprime nada para algunos ficheros, solo una lınea empezando con “M” para el ficherohello.c. A menos que usted lo indique explıcitamente, “hg status” no imprimira nada respecto a los ficheros queno han sido modificados.

La “M” indica que Mercurial se dio cuenta de que nosotros modificamos hello.c. No tuvimos que decirle aMercurial que ıbamos a modificar ese fichero antes de hacerlo, o que lo modificamos una vez terminamos de hacerlo;el fue capaz de darse cuenta de esto por sı mismo.

Es algo util saber que hemos modificado el fichero hello.c, pero preferirıamos saber exactamente que cambioshicimos. Para averiguar esto, usamos el comando “hg diff”.

1 $ hg diff2 diff -r 2278160e78d4 hello.c3 --- a/hello.c Sat Aug 16 22:16:53 2008 +02004 +++ b/hello.c Tue Feb 10 18:23:34 2009 +00005 @@ -8,5 +8,6 @@6 int main(int argc, char **argv)7 {8 printf("hello, world!\");9 + printf("hello again!\n");

10 return 0;11 }

2.7. Grabar cambios en un nuevo conjunto de cambiosPodemos modificar, compilar y probar nuestros cambios, y usar “hg status” y “hg diff” para revisar los mis-

mos, hasta que estemos satisfechos con los resultados y lleguemos a un momento en el que sea natural que querramosguardar nuestro trabajo en un nuevo conjunto de cambios.

El comando “hg commit” nos permite crear un nuevo conjunto de cambios. Nos referiremos usualmente a estocomo “hacer una consigna” o consignar.

2.7.1. Definir un nombre de usuarioCuando usted trata de ejecutar “hg commit”11 por primera vez, no esta garantizado que lo logre. Mercurial registra

su nombre y direccion en cada cambio que usted consigna, para que mas adelante otros puedan saber quien es elresponsable de cada cambio. Mercurial trata de encontrar un nombre de usuario adecuado con el cual registrar laconsignacion. Se intenta con cada uno de los siguientes metodos, en el orden presentado.

10N. del T. Hola, mundo.11N. del T. Hacer una consignacion

19

Page 30: Mercurial

1. Si usted pasa la opcion -u al comando “hg commit” en la lınea de comandos, seguido de un nombre de usuario,se le da a esto la maxima precedencia.

2. A continuacion se revisa si usted ha definido la variable de entorno HGUSER.

3. Si usted crea un fichero en su directorio personal llamado .hgrc, con una entrada username, se usa luego. Pararevisar como debe verse este fichero, refierase a la seccion 2.7.1 mas abajo.

4. Si usted ha definido la variable de entorno EMAIL, sera usada a continuacion.

5. Mercurial le pedira a su sistema buscar su nombre de usuario local, y el nombre de maquina, y construira unnombre de usuario a partir de estos componentes. Ya que esto generalmente termina generando un nombre deusuario no muy util, se imprimira una advertencia si es necesario hacerlo.

Si todos estos procedimientos fallan, Mercurial fallara, e imprimira un mensaje de error. En este caso, no le permi-tira hacer la consignacion hasta que usted defina un nombre de usuario.

Trate de ver la variable de entorno HGUSER y la opcion -u del comando “hg commit” como formas de hacer casoomiso de la seleccion de nombre de usuario que Mercurial hace normalmente. Para uso normal, la manera mas simple ysencilla de definir un nombre de usuario para usted es crear un fichero .hgrc; los detalles se encuentran mas adelante.

Crear el fichero de configuracion de Mercurial

Para definir un nombre de usuario, use su editor de texto favorito para crear un fichero llamado .hgrc en sudirectorio personal. Mercurial usara este fichero para obtener las configuraciones personalizadas que usted haya hecho.El contenido inicial de su fichero .hgrc deberıa verse ası.

1 # Este es un fichero de configuracion de Mercurial.2 [ui]3 username = Primernombre Apellido <[email protected]>

La lınea “[ui]” define una section del fichero de configuracion, ası que usted puede leer la lınea “username = ...”como “defina el valor del elemento username en la seccion ui”. Una seccion continua hasta que empieza otra nueva,o se llega al final del fichero. Mercurial ignora las lıneas vacıas y considera cualquier texto desde el caracter “#” hastael final de la lınea como un comentario.

Escoger un nombre de usuario

Usted puede usar el texto que desee como el valor del campo de configuracion username, ya que esta informacionsera leıda por otras personas, e interpretada por Mercurial. La convencion que sigue la mayorıa de la gente es usar sunombre y direccion de correo, como en el ejemplo anterior.

Nota: El servidor web integrado de Mercurial ofusca las direcciones de correo,para dificultar la tarea de las herramientas de recoleccion de direcciones de correoque usan los spammersa. Esto reduce la probabilidad de que usted empiece a recibirmas correo basura si publica un repositorio en la red.

aN. del T. Personas que envıan correo no solicitado, tambien conocido como correo basura

2.7.2. Escribir un mensaje de consignacionCuando consignamos un cambio, Mercurial nos ubica dentro de un editor de texto, para ingresar un mensaje que

describa las modificaciones que hemos introducido en este conjunto de cambios. Esto es conocido como un mensaje deconsignacion. Sera un registro de lo que hicimos y porque lo hicimos, y sera impreso por “hg log” una vez hayamoshecho la consignacion.

1 $ hg commit

20

Page 31: Mercurial

El editor en que “hg commit” nos ubica contendra una lınea vacıa, seguida de varias lıneas que empiezan con lacadena “HG:”.

1 lınea vacıa2 HG: changed hello.c

Mercurial ignora las lıneas que empiezan con “HG:”; solo las usa para indicarnos para cuales ficheros esta registrandolos cambios. Modificar o borrar estas lıneas no tiene ningun efecto.

2.7.3. Escribir un buen mensaje de consignacionYa que por defecto “hg log” solo muestra la primera lınea de un mensaje de consignacion, lo mejor es escribir un

mensaje cuya primera lınea tenga significado por sı misma. A continuacion se encuentra un ejemplo de un mensaje deconsignacion que no sigue esta pauta, y debido a ello tiene un sumario que no es legible.

1 changeset: 73:584af0e231be2 user: Persona Censurada <[email protected]>3 date: Tue Sep 26 21:37:07 2006 -07004 summary: se incluye buildmeister/commondefs. Anade un modulo

Con respecto al resto del contenido del mensaje de consignacion, no hay reglas estrictas-y-rapidas. Mercurial nointerpreta ni le da importancia a los contenidos del mensaje de consignacion, aunque es posible que su proyecto tengapolıticas que definan una manera particular de escribirlo.

Mi preferencia personal es usar mensajes de consignacion cortos pero informativos, que me digan algo que nopuedo inferir con una mirada rapida a la salida de “hg log --patch”.

2.7.4. Cancelar una consignacionSi usted decide que no desea hacer la consignacion mientras esta editando el mensaje de la misma, simplemente

cierre su editor sin guardar los cambios al fichero que esta editando. Esto hara que no pase nada ni en el repositorio nien el directorio de trabajo.

Si ejecutamos el comando “hg commit” sin ningun argumento, se registran todos los cambios que hemos hecho,como lo indican “hg status” y “hg diff”.

2.7.5. Admirar nuestro trabajoUna vez hemos terminado la consignacion, podemos usar el comando “hg tip”12 para mostrar el conjunto de

cambios que acabamos de crear. La salida de este comando es identica a la de “hg log”, pero solo muestra la revisionmas reciente en el repositorio.

1 $ hg tip -vp2 changeset: 5:fccff93807a33 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:34 2009 +00006 files: hello.c7 description:8 Added an extra line of output9

10

11 diff -r 2278160e78d4 -r fccff93807a3 hello.c

12N. del T. Punta.

21

Page 32: Mercurial

12 --- a/hello.c Sat Aug 16 22:16:53 2008 +020013 +++ b/hello.c Tue Feb 10 18:23:34 2009 +000014 @@ -8,5 +8,6 @@15 int main(int argc, char **argv)16 {17 printf("hello, world!\");18 + printf("hello again!\n");19 return 0;20 }21

Nos referimos a la revision mas reciente en el repositorio como la revision de punta, o simplemente la punta.

2.8. Compartir cambiosAnteriormente mencionamos que los repositorios en Mercurial estan auto contenidos. Esto quiere decir que el

conjunto de cambios que acabamos de crear solo existe en nuestro repositorio my-hello. Veamos unas cuantas formasde propagar este cambio a otros repositorios.

2.8.1. Jalar cambios desde otro repositorioPara empezar, clonemos nuestro repositorio hello original, el cual no contiene el cambio que acabamos de

consignar. Llamaremos a este repositorio temporal hello-pull.

1 $ cd ..2 $ hg clone hello hello-pull3 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Usaremos el comando “hg pull” para traer los cambios de my-hello y ponerlos en hello-pull. Sin embargo,traer cambios desconocidos y aplicarlos en un repositorio es una perspectiva que asusta al menos un poco. Mercurialcuenta con el comando “hg incoming”13 para decirnos que cambios jalarıa el comando “hg pull” al repositorio,sin jalarlos.

1 $ cd hello-pull2 $ hg incoming ../my-hello3 comparing with ../my-hello4 searching for changes5 changeset: 5:fccff93807a36 tag: tip7 user: Bryan O’Sullivan <[email protected]>8 date: Tue Feb 10 18:23:34 2009 +00009 summary: Added an extra line of output

10

(Por supuesto, alguien podrıa enviar mas conjuntos de cambios al repositorio en el tiempo que pasa entre la ejecucionde “hg incoming” y la ejecucion de “hg pull” para jalar los cambios, ası que es posible que terminemos jalandocambios que no esperabamos.)

Traer cambios al repositorio simplemente es cuestion de ejecutar el comando “hg pull”, indicandole de que repos-itorio debe jalarlos.

13N. del T. Entrante, o cambios entrantes.

22

Page 33: Mercurial

1 $ hg tip2 changeset: 4:2278160e78d43 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Sat Aug 16 22:16:53 2008 +02006 summary: Trim comments.7

8 $ hg pull ../my-hello9 pulling from ../my-hello

10 searching for changes11 adding changesets12 adding manifests13 adding file changes14 added 1 changesets with 1 changes to 1 files15 (run ’hg update’ to get a working copy)16 $ hg tip17 changeset: 5:fccff93807a318 tag: tip19 user: Bryan O’Sullivan <[email protected]>20 date: Tue Feb 10 18:23:34 2009 +000021 summary: Added an extra line of output22

Como puede verse por las salidas antes-y-despues de “hg tip”, hemos jalado exitosamente los cambios en nuestrorepositorio. Aun falta un paso para que podamos ver estos cambios en nuestro directorio de trabajo.

2.8.2. Actualizar el directorio de trabajoHasta ahora hemos pasado por alto la relacion entre un repositorio y su directorio de trabajo. El comando “hg

pull” que ejecutamos en la seccion 2.8.1 trajo los cambios al repositorio, pero si revisamos, no hay rastro de esoscambios en el directorio de trabajo. Esto pasa porque “hg pull” (por defecto) no modifica el directorio de trabajo. Envez de eso, usamos el comando “hg update”14 para hacerlo.

1 $ grep printf hello.c2 printf("hello, world!\");3 $ hg update tip4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ grep printf hello.c6 printf("hello, world!\");7 printf("hello again!\n");

Puede parecer algo raro que “hg pull” no actualice el directorio de trabajo automaticamente. De hecho, hayuna buena razon para esto: usted puede usar “hg update” para actualizar el directorio de trabajo al estado en que seencontraba en cualquier revision del historial del repositorio. Si usted hubiera actualizado el directorio de trabajo a unarevision anterior—digamos, para buscar el origen de un fallo—y hubiera corrido un “hg pull” que hubiera actualizadoel directorio de trabajo automaticamente a la nueva revision, puede que no estuviera particularmente contento.

Sin embargo, como jalar-y-actualizar es una secuencia de operaciones muy comun, Mercurial le permite combina-rlas al pasar la opcion -u a “hg pull”.

1 hg pull -u

14N. del T. Actualizar.

23

Page 34: Mercurial

Si mira de vuelta la salida de “hg pull” en la seccion 2.8.1 cuando lo ejecutamos sin la opcion -u, vera que elcomando imprimio un amable recordatorio de que tenemos que encargarnos explıcitamente de actualizar el directoriode trabajo:

1 (run ’hg update’ to get a working copy)

Para averiguar en que revision se encuentra el directorio de trabajo, use el comando “hg parents”.

1 $ hg parents2 changeset: 5:fccff93807a33 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:34 2009 +00006 summary: Added an extra line of output7

Si mira de nuevo la figura 2.1, vera flechas conectando cada conjunto de cambios. En cada caso, el nodo del que laflecha sale es un padre, y el nodo al que la flecha llega es su hijo. El directorio de trabajo tiene un padre exactamentede la misma manera; ese es el conjunto de cambios que contiene actualmente el directorio de trabajo.

Para actualizar el conjunto de trabajo a una revision particular, pase un numero de revision o un ID de conjunto decambios al comando “hg update”.

1 $ hg update 22 2 files updated, 0 files merged, 0 files removed, 0 files unresolved3 $ hg parents4 changeset: 2:fef857204a0c5 user: Bryan O’Sullivan <[email protected]>6 date: Sat Aug 16 22:05:04 2008 +02007 summary: Introduce a typo into hello.c.8

9 $ hg update10 2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Si no indica explıcitamente una revision, “hg update” actualizara hasta la revision de punta, como se vio en la segundallamada a “hg update” en el ejemplo anterior.

2.8.3. Empujar cambios a otro repositorioMercurial nos permite empujar cambios a otro repositorio, desde el repositorio que estemos usando actualmente.

De la misma forma que en el ejemplo de “hg pull” arriba, crearemos un repositorio temporal para empujar allı nue-stros cambios.

1 $ cd ..2 $ hg clone hello hello-push3 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved

El comando “hg outgoing”15 nos dice que cambios serıan empujados en el otro repositorio.

1 $ cd my-hello2 $ hg outgoing ../hello-push

15N. del T. Saliente. Cambios salientes.

24

Page 35: Mercurial

3 comparing with ../hello-push4 searching for changes5 changeset: 5:fccff93807a36 tag: tip7 user: Bryan O’Sullivan <[email protected]>8 date: Tue Feb 10 18:23:34 2009 +00009 summary: Added an extra line of output

10

Y el comando “hg push” se encarga de empujar dichos cambios.

1 $ hg push ../hello-push2 pushing to ../hello-push3 searching for changes4 adding changesets5 adding manifests6 adding file changes7 added 1 changesets with 1 changes to 1 files

Al igual que “hg pull”, el comando “hg push” no actualiza el directorio de trabajo del repositorio en el que estamosempujando los cambios. (A diferencia de “hg pull”, “hg push” no ofrece la opcion -u para actualizar el directoriode trabajo del otro repositorio.)

Que pasa si tratamos de jalar o empujar cambios y el repositorio receptor ya tiene esos cambios? Nada emocio-nante.

1 $ hg push ../hello-push2 pushing to ../hello-push3 searching for changes4 no changes found

2.8.4. Compartir cambios a traves de una redLos comandos que hemos presentando en las pocas secciones anteriores no estan limitados a trabajar con reposito-

rios locales. Cada uno de ellos funciona exactamente de la misma manera a traves de una conexion de red. Simplementepase una URL en vez de una ruta local.

1 $ hg outgoing http://hg.serpentine.com/tutorial/hello2 comparing with http://hg.serpentine.com/tutorial/hello3 searching for changes4 changeset: 5:fccff93807a35 tag: tip6 user: Bryan O’Sullivan <[email protected]>7 date: Tue Feb 10 18:23:34 2009 +00008 summary: Added an extra line of output9

En este ejemplo, podemos ver que cambios empujarıamos al repositorio remoto, aunque, de manera entendible, elrepositorio remoto esta configurado para no permitir a usuarios anonimos empujar cambios a el.

1 $ hg push http://hg.serpentine.com/tutorial/hello2 pushing to http://hg.serpentine.com/tutorial/hello3 searching for changes4 ssl required

25

Page 36: Mercurial

Capıtulo 3

Una gira de Mercurial: fusionar trabajo

Hasta ahora hemos cubierto como clonar un repositorio, hacer cambios, y jalar o empujar dichos cambios de unrepositorio a otro. Nuestro siguiente paso es fusionar cambios de repositorios separados.

3.1. Fusionar lıneas de trabajoFusionar es una parte fundamental de trabajar con una herramienta de control distribuido de versiones.

Alicia y Roberto tienen cada uno una copia personal del repositorio de un proyecto en el que estan trabajando.Alicia arregla un fallo en su repositorio; Roberto anade una nueva caracterıstica en el suyo. Ambos desean queel repositorio compartido contenga el arreglo del fallo y la nueva caracterıstica.

Frecuentemente trabajo en varias tareas diferentes en un mismo proyecto al mismo tiempo, cada una aisladaconvenientemente de las otras en su propio repositorio. Trabajar de esta manera significa que a menudo debofusionar una parte de mi propio trabajo con otra.

Como fusionar es una operacion tan necesaria y comun, Mercurial la facilita. Revisemos el proceso. Empezaremosclonando (otro) repositorio (ve lo seguido que aparecen?) y haciendo un cambio en el.

1 $ cd ..2 $ hg clone hello my-new-hello3 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd my-new-hello6 $ sed -i ’/printf/i\\tprintf("once more, hello.\\n");’ hello.c7 $ hg commit -m ’A new hello for a new day.’

Ahora deberıamos tener dos copias de hello.c con contenidos diferentes. El historial de los dos repositorios divergeahora, como se ilustra en la figura 3.1.

1 $ cat hello.c2 /*3 * Placed in the public domain by Bryan O’Sullivan. This program is4 * not covered by patents in the United States or other countries.5 */6

7 #include <stdio.h>8

9 int main(int argc, char **argv)

26

Page 37: Mercurial

10 {11 printf("once more, hello.\n");12 printf("hello, world!\");13 return 0;14 }15 $ cat ../my-hello/hello.c16 /*17 * Placed in the public domain by Bryan O’Sullivan. This program is18 * not covered by patents in the United States or other countries.19 */20

21 #include <stdio.h>22

23 int main(int argc, char **argv)24 {25 printf("hello, world!\");26 printf("hello again!\n");27 return 0;28 }

0: 0a04

1: 82e5

2: fef8

3: 0272

4: 2278

5: fccf

my−hello

0: 0a04

1: 82e5

2: fef8

3: 0272

4: 2278

5: 05b9

my−new−hello

Los cambios

más recientes

difieren

historia común

revisión principal

(sin hijos)

Figura 3.1: Historial reciente divergente de los repositorios my-hello y my-new-hello

Ya sabemos que jalar los cambios desde nuestro repositorio my-hello no tendra efecto en el directorio de trabajo.

1 $ hg pull ../my-hello2 pulling from ../my-hello3 searching for changes4 adding changesets5 adding manifests6 adding file changes7 added 1 changesets with 1 changes to 1 files (+1 heads)

27

Page 38: Mercurial

8 (run ’hg heads’ to see heads, ’hg merge’ to merge)

Sin embargo, el comando “hg pull” dice algo acerca de “frentes”1.

3.1.1. Conjuntos de cambios de frentesUn frente es un cambio que no tiene descendientes, o hijos, como tambien se les conoce. La revision de punta es,

por tanto, un frente, porque la revision mas reciente en un repositorio no tiene ningun hijo. Sin embargo, un repositoriopuede contener mas de un frente.

0: 0a04

1: 82e5

2: fef8

3: 0272

4: 2278

5: 05b9

6: fccftip (y principal)

principal

Figura 3.2: Contenidos del repositorio despues de jalar my-hello a my-new-hello

En la figura 3.2 usted puede ver el efecto que tiene jalar los cambios de my-hello a my-new-hello. El historial queya existıa en my-new-hello se mantiene intacto, pero fue anadida una nueva revision. Refiriendonos a la figura 3.1,podemos ver que el ID del conjunto de cambios se mantiene igual en el nuevo repositorio, pero el numero de revisionha cambiado. (Incidentalmente, este es un buen ejemplo de porque no es seguro usar numeros de revision cuando sehabla de conjuntos de cambios). Podemos ver los frentes en un repositorio usando el comando “hg heads”2.

1 $ hg heads2 changeset: 6:fccff93807a33 tag: tip4 parent: 4:2278160e78d45 user: Bryan O’Sullivan <[email protected]>6 date: Tue Feb 10 18:23:34 2009 +00007 summary: Added an extra line of output8

9 changeset: 5:05b9c1e50b3c10 user: Bryan O’Sullivan <[email protected]>

1N. del T. El autor se refiere a heads aquı.2N. del T. Frentes.

28

Page 39: Mercurial

11 date: Tue Feb 10 18:23:36 2009 +000012 summary: A new hello for a new day.13

3.1.2. Hacer la fusionQue pasa si tratamos de usar el comando usual, “hg update”, para actualizar el nuevo frente?

1 $ hg update2 abort: crosses branches (use ’hg merge’ or ’hg update -C’)

Mercurial nos indica que el comando “hg update” no hara la fusion; no actualizara el directorio de trabajo cuandoconsidera que lo que deseamos hacer es una fusion, a menos que lo obliguemos a hacerlo. En vez de “hg update”,usamos el comando “hg merge” para hacer la fusion entre los dos frentes.

1 $ hg merge2 merging hello.c3 0 files updated, 1 files merged, 0 files removed, 0 files unresolved4 (branch merge, don’t forget to commit)

4: 2278

5: 05b9

6: fccf punta (y frente)

frente

fusión directorio de trabajo

durante la fusión

4: 2278

5: 05b9

6: fccf

punta 7: 22a5

Directorio de trabajo durante la fusión Repositorio después de consignar la fusión

Figura 3.3: Directorio de trabajo y repositorio durante la fusion, y consignacion consecuente

Esto actualiza el directorio de trabajo, de tal forma que contenga los cambios de ambos frentes, lo que se vereflejado tanto en la salida de “hg parents” como en los contenidos de hello.c.

1 $ hg parents2 changeset: 5:05b9c1e50b3c3 user: Bryan O’Sullivan <[email protected]>4 date: Tue Feb 10 18:23:36 2009 +00005 summary: A new hello for a new day.6

7 changeset: 6:fccff93807a38 tag: tip9 parent: 4:2278160e78d4

29

Page 40: Mercurial

10 user: Bryan O’Sullivan <[email protected]>11 date: Tue Feb 10 18:23:34 2009 +000012 summary: Added an extra line of output13

14 $ cat hello.c15 /*16 * Placed in the public domain by Bryan O’Sullivan. This program is17 * not covered by patents in the United States or other countries.18 */19

20 #include <stdio.h>21

22 int main(int argc, char **argv)23 {24 printf("once more, hello.\n");25 printf("hello, world!\");26 printf("hello again!\n");27 return 0;28 }

3.1.3. Consignar los resultados de la fusionSiempre que hacemos una fusion, “hg parents” mostrara dos padres hasta que consignemos (“hg commit”) los

resultados de la fusion.

1 $ hg commit -m ’Merged changes’

Ahora tenemos una nueva revision de punta; note que tiene los dos frentes anteriores como sus padres. Estos son lasmismas revisiones que mostro previamente el comando “hg parents”.

1 $ hg tip2 changeset: 7:22a572779faf3 tag: tip4 parent: 5:05b9c1e50b3c5 parent: 6:fccff93807a36 user: Bryan O’Sullivan <[email protected]>7 date: Tue Feb 10 18:23:36 2009 +00008 summary: Merged changes9

En la figura 3.3 usted puede apreciar una representacion de lo que pasa en el directorio de trabajo durante la fusioncuando se hace la consignacion. Durante la fusion, el directorio de trabajo tiene dos conjuntos de cambios como suspadres, y estos se vuelven los padres del nuevo conjunto de cambios.

3.2. Fusionar cambios con conflictosLa mayorıa de las fusiones son algo simple, pero a veces usted se encontrara fusionando cambios donde mas de

uno de ellos afecta las mismas secciones de los mismos ficheros. A menos que ambas modificaciones sean identicas,el resultado es un conflicto, en donde usted debe decidir como reconciliar ambos cambios y producir un resultadocoherente.

30

Page 41: Mercurial

Saludos!

Soy Mariam Abacha, la

esposa del anterior

dictador de Nigeria

Sani Abacha. Le

contacto en secreto,

buscando los medios

para desarrollar

Saludos!

Soy Shehu Musa

Abacha, sobrina del

anterior dictador de

Nigeria Sani Abacha.

Le contacto en secreto,

buscando los medios

para desarrollar

Saludos!

Soy Alhaji Abba

Abacha, hijo del

anterior dictador de

Nigeria Sani Abacha.

Le contacto en secreto,

buscando los medios

para desarrollar

Versión inicial

Nuestros cambios Sus cambios

Figura 3.4: Cambios con conflictos a un documento

La figura 3.4 ilustra un ejemplo con dos cambios generando conflictos en un documento. Empezamos con una solaversion del fichero; luego hicimos algunos cambios; mientras tanto, alguien mas hizo cambios diferentes en el mismotexto. Lo que debemos hacer para resolver el conflicto causado por ambos cambios es decidir como debe quedarfinalmente el fichero.

Mercurial no tiene ninguna utilidad integrada para manejar conflictos. En vez de eso, ejecuta un programa externollamado hgmerge. Es un guion de lınea de comandos que es instalado junto con Mercurial; usted puede modificarlopara que se comporte como usted lo desee. Por defecto, lo que hace es tratar de encontrar una de varias herramientaspara fusionar que es probable que esten instaladas en su sistema. Primero se intenta con unas herramientas para fusionarcambios automaticamente; si esto no tiene exito (porque la fusion demanda una guıa humana) o dichas herramientasno estan presentes, el guion intenta con herramientas graficas para fusionar.

Tambien es posible hacer que Mercurial ejecute otro programa o guion en vez de hgmerge, definiendo la variablede entorno HGMERGE con el nombre del programa de su preferencia.

3.2.1. Usar una herramienta grafica para fusionMi herramienta favorita para hacer fusiones es kdiff3, y la usare para describir las caracterısticas comunes de las

herramientas graficas para hacer fusiones. Puede ver una captura de pantalla de kdiff3 ejecutandose, en la figura 3.5.El tipo de fusion que la herramienta hace se conoce como fusion de tres vıas, porque hay tres versiones diferentes delfichero en que estamos interesados. Debido a esto la herramienta divide la parte superior de la ventana en tres paneles.

A la izquierda esta la revision base del fichero, p.ej. la version mas reciente de la que descienden las dosversiones que estamos tratando de fusionar.

En la mitad esta “nuestra” version del fichero, con las modificaciones que hemos hecho.

A la derecha esta la version del fichero de “ellos”, la que forma parte del conjunto de cambios que estamostratando de fusionar.

En el panel inferior se encuentra el resultado actual de la fusion. Nuestra tarea es reemplazar todo el texto rojo, quemuestra los conflictos sin resolver, con una fusion adecuada de “nuestra” version del fichero y la de “ellos”.

Los cuatro paneles estan enlazados; si avanzamos vertical o horizontalmente en cualquiera de ellos, los otros sonactualizados para mostrar las secciones correspondientes del fichero que tengan asociado.

31

Page 42: Mercurial

Figura 3.5: Usando kdiff3 para fusionar versiones de un fichero

En cada conflicto del fichero podemos escoger resolverlo usando cualquier combinacion del texto de la revisionbase, la nuestra, o la de ellos. Tambien podemos editar manualmente el fichero en que queda la fusion, si es necesariohacer cambios adicionales.

Hay muchas herramientas para fusionar ficheros disponibles. Se diferencian en las plataformas para las que estandisponibles, y en sus fortalezas y debilidades particulares. La mayorıa estan afinadas para fusionar texto plano, mien-tras que otras estan pensadas para formatos de ficheros especializados (generalmente XML).

3.2.2. Un ejemplo realEn este ejemplo, reproduciremos el historial de modificaciones al fichero de la figura 3.4 mostrada anteriormente.

Empecemos creando un repositorio con la version base de nuestro documento.

1 $ cat > letter.txt <<EOF2 > Greetings!3 > I am Mariam Abacha, the wife of former4 > Nigerian dictator Sani Abacha.5 > EOF6 $ hg add letter.txt7 $ hg commit -m ’419 scam, first draft’

Clonaremos el repositorio y haremos un cambio al fichero.

1 $ cd ..2 $ hg clone scam scam-cousin3 updating working directory4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

32

Page 43: Mercurial

5 $ cd scam-cousin6 $ cat > letter.txt <<EOF7 > Greetings!8 > I am Shehu Musa Abacha, cousin to the former9 > Nigerian dictator Sani Abacha.

10 > EOF11 $ hg commit -m ’419 scam, with cousin’

Y haremos otro clon, para simular a alguien mas haciendo un cambio al mismo fichero. (Esto introduce la idea deque no es tan inusual hacer fusiones consigo mismo, cuando usted aısla tareas en repositorios separados, y de hechoencuentra conflictos al hacerlo.)

1 $ cd ..2 $ hg clone scam scam-son3 updating working directory4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd scam-son6 $ cat > letter.txt <<EOF7 > Greetings!8 > I am Alhaji Abba Abacha, son of the former9 > Nigerian dictator Sani Abacha.

10 > EOF11 $ hg commit -m ’419 scam, with son’

Ahora que tenemos dos versiones diferentes de nuestro fichero, crearemos un entorno adecuado para hacer la fusion.

1 $ cd ..2 $ hg clone scam-cousin scam-merge3 updating working directory4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd scam-merge6 $ hg pull -u ../scam-son7 pulling from ../scam-son8 searching for changes9 adding changesets

10 adding manifests11 adding file changes12 added 1 changesets with 1 changes to 1 files (+1 heads)13 not updating, since new heads added14 (run ’hg heads’ to see heads, ’hg merge’ to merge)

En este ejemplo, no usare el comando normal de Mercurial para hacer la fusion (hgmerge), porque lanzarıa mi lindaherramienta automatizada para correr ejemplos dentro de una interfaz grafica de usuario. En vez de eso, definire lavariable de entorno HGMERGE para indicarle a Mercurial que use el comando merge. Este comando forma parte de lainstalacion base de muchos sistemas Unix y similares. Si usted esta ejecutando este ejemplo en su computador, no semoleste en definir HGMERGE.

1 $ export HGMERGE=merge2 $ hg merge3 merging letter.txt4 merge: warning: conflicts during merge5 merging letter.txt failed!

33

Page 44: Mercurial

6 0 files updated, 0 files merged, 0 files removed, 1 files unresolved7 There are unresolved merges, you can redo the full merge using:8 hg update -C 19 hg merge 2

10 $ cat letter.txt11 Greetings!12 <<<<<<< /tmp/tour-merge-conflictsNZwSt/scam-merge/letter.txt13 I am Shehu Musa Abacha, cousin to the former14 =======15 I am Alhaji Abba Abacha, son of the former16 >>>>>>> /tmp/letter.txt˜other.749P-k17 Nigerian dictator Sani Abacha.

Debido a que merge no puede resolver los conflictos que aparecen, el deja marcadores de fusion en el fichero conconflictos, indicando si provienen de nuestra version o de la de ellos.

Mercurial puede saber —por el codigo de salida del comando merge— que no fue posible hacer la fusion exitosa-mente, ası que nos indica que comandos debemos ejecutar si queremos rehacer la fusion. Esto puede ser util si, porejemplo, estamos ejecutando una herramienta grafica de fusion y salimos de ella porque nos confundimos o cometimosun error.

Si la fusion —automatica o manual— falla, no hay nada que nos impida “arreglar” los ficheros afectados pornosotros mismos, y consignar los resultados de nuestra fusion:

1 $ cat > letter.txt <<EOF2 > Greetings!3 > I am Bryan O’Sullivan, no relation of the former4 > Nigerian dictator Sani Abacha.5 > EOF6 $ hg resolve -m letter.txt7 hg: unknown command ’resolve’8 Mercurial Distributed SCM9

10 basic commands:11

12 add add the specified files on the next commit13 annotate show changeset information per file line14 clone make a copy of an existing repository15 commit commit the specified files or all outstanding changes16 diff diff repository (or selected files)17 export dump the header and diffs for one or more changesets18 init create a new repository in the given directory19 log show revision history of entire repository or files20 merge merge working directory with another revision21 parents show the parents of the working dir or revision22 pull pull changes from the specified source23 push push changes to the specified destination24 remove remove the specified files on the next commit25 serve export the repository via HTTP26 status show changed files in the working directory27 update update working directory28

29 use "hg help" for the full list of commands or "hg -v" for details30 $ hg commit -m ’Send me your money’

34

Page 45: Mercurial

31 $ hg tip32 changeset: 3:e29827f5cf5133 tag: tip34 parent: 1:eac603d7320835 parent: 2:78b0a9e9cf1b36 user: Bryan O’Sullivan <[email protected]>37 date: Tue Feb 10 18:23:37 2009 +000038 summary: Send me your money39

3.3. Simplificar el ciclo jalar-fusionar-consignarEl proceso de fusionar cambios delineado anteriomente es directo, pero requiere la ejecucion de tres comandos en

sucesion.

1 hg pull2 hg merge3 hg commit -m ’Fusionados cambios remotos’

En la consignacion final usted debe proveer un mensaje adecuado, que casi siempre es un fragmento de texto “derelleno” carente de valor particular.

Serıa agradable reducir la cantidad de pasos necesarios, si fuera posible. De hecho, Mercurial es distribuido juntocon una extension llamada fetch3 que hace precisamente esto.

Mercurial cuenta con un mecanismo de extension flexible que le permite a sus usuarios extender su funcionalidad,manteniendo el nucleo de Mercurial pequeno y facil de manejar. Algunas extensiones anaden nuevos comandos queusted puede usar desde la lınea de comandos, mientras que otros funcionan “tras bambalinas”, por ejemplo, anadiendofuncionalidad al servidor.

La extension fetch anade un comando llamado, no sorpresivamente, “hg fetch”. Esta extension actua como unacombinacion de “hg pull”, “hg update” y “hg merge”. Empieza jalando cambios de otro repositorio al repositorioactual. Si encuentra que los cambios anaden un nuevo frente en el repositorio actual, inicia una fusion, y luego consignael resultado de la misma con un mensaje generado automaticamente. Si no se anadieron nuevos frentes, actualiza eldirectorio de trabajo con el nuevo conjunto de cambios de punta.

Activar la extension fetch es facil. Edite su .hgrc, y vaya a (o cree) la seccion [extensions]. Luego anada unalınea que diga simplemente “fetch ”.

1 [extensions]2 fetch =

(Normalmente, a la derecha del “=” deberıa aparecer la ubicacion de la extension, pero como el comando fetch esparte de la distribucion estandar, Mercurial sabe donde buscarla.)

3N. del T. Descargar, traer.

35

Page 46: Mercurial

Capıtulo 4

Tras bambalinas

A diferencia de varios sistemas de control de revisiones, los conceptos en los que se fundamenta Mercurial son losuficientemente simples como para entender facilmente como funciona el software. Saber esto no es necesario, peroconsidero util tener un “modelo mental” de que es lo que sucede.

Comprender esto me da la confianza de que Mercurial ha sido cuidadosamente disenado para ser tanto seguro comoeficiente. Y tal vez con la misma importancia, si es facil para mı hacerme a una idea adecuada de que esta haciendo elsoftware cuando llevo a cabo una tarea relacionada con control de revisiones, es menos probable que me sosprenda sucomportamiento.

En este capıtulo, cubriremos inicialmente los conceptos centrales del diseno de Mercurial, y luego discutiremosalgunos detalles interesantes de su implementacion.

4.1. Registro del historial de Mercurial

4.1.1. Seguir el historial de un unico ficheroCuando Mercurial sigue las modificaciones a un fichero, guarda el historial de dicho fichero en un objeto de

metadatos llamado filelog1. Cada entrada en el fichero de registro contiene suficiente informacion para reconstruir unarevision del fichero que se esta siguiendo. Los ficheros de registro son almacenados como ficheros el el directorio.hg/store/data. Un fichero de registro contiene dos tipos de informacion: datos de revisiones, y un ındice paraayudar a Mercurial a buscar revisiones eficientemente.

El fichero de registro de un fichero grande, o con un historial muy largo, es guardado como ficheros separadospara datos (sufijo “.d”) y para el ındice (sufijo “.i”). Para ficheros pequenos con un historial pequeno, los datos derevisiones y el ındice son combinados en un unico fichero “.i”. La correspondencia entre un fichero en el directoriode trabajo y el fichero de registro que hace seguimiento a su historial en el repositorio se ilustra en la figura 4.1.

4.1.2. Administracion de ficheros monitoreadosMercurial usa una estructura llamada manifiesto para centralizar la informacion que maneja acerca de los ficheros

que monitorea. Cada entrada en el manifiesto contiene informacion acerca de los ficheros involucrados en un unicoconjunto de cambios. Una entrada registra que ficheros estan presentes en el conjunto de cambios, la revision de cadafichero, y otros cuantos metadatos del mismo.

4.1.3. Registro de informacion del conjunto de cambiosLa bitacora de cambios contiene informacion acerca de cada conjunto de cambios. Cada revision indica quien

consigno un cambio, el comentario para el conjunto de cambios, otros datos relacionados con el conjunto de cambios,y la revision del manifiesto a usar.

1N. del T. Fichero de registro

36

Page 47: Mercurial

.hg/store/data/README.iREADME

.hg/store/data/src/hello.c.d

.hg/store/data/src/hello.c.isrc/hello.c

Directorio de trabajo Repositorio

Figura 4.1: Relacion entre ficheros en el directorio de trabajo y ficheros de registro en el repositorio

4.1.4. Relaciones entre revisionesDentro de una bitacora de cambios, un manifiesto, o un fichero de registro, cada revision conserva un apuntador

a su padre inmediato (o sus dos padres, si es la revision de una fusion). Como mencione anteriormente, tambien hayrelaciones entre revisiones a traves de estas estructuras, y tienen naturaleza jerarquica.

Por cada conjunto de cambios en un repositorio, hay exactamente una revision almacenada en la bitacora decambios. Cada revision de la bitacora de cambios contiene un apuntador a una unica revision del manifiesto. Unarevision del manifiesto almacena un apuntador a una unica revision de cada fichero de registro al que se le hacıaseguimiento cuando fue creado el conjunto de cambios. Estas relaciones se ilustran en la figura 4.2.

Bitácora de cambios

Manifiesto

Bitácora de archivos

Figura 4.2: Relaciones entre metadatos

Como lo muestra la figura, no hay una relacion “uno a uno” entre las revisiones en el conjunto de cambios, elmanifiesto, o el fichero de registro. Si el manifiesto no ha sido modificado de un conjunto de cambios a otro, lasentradas en la bitacora de cambios para esos conjuntos de cambios apuntaran a la misma revision del manifiesto. Siun fichero monitoreado por Mercurial no sufre ningun cambio de un conjunto de cambios a otro, la entrada para dichofichero en las dos revisiones del manifiesto apuntara a la misma revision de su fichero de registro.

37

Page 48: Mercurial

4.2. Almacenamiento seguro y eficienteLa base comun de las bitacoras de cambios, los manifiestos, y los ficheros de registros es provista por una unica

estructura llamada el revlog2.

4.2.1. Almacenamiento eficienteEl revlog provee almacenamiento eficiente de revisiones por medio del mecanismo de deltas3. En vez de almacenar

una copia completa del fichero por cada revision, almacena los cambios necesarios para transformar una revisionanterior en la nueva revision. Para muchos tipos de fichero, estos deltas son tıpicamente de una fraccion porcentual deltamano de una copia completa del fichero.

Algunos sistemas de control de revisiones obsoletos solo pueden manipular deltas de ficheros de texto plano. Elloso bien almacenan los ficheros binarios como instantaneas completas, o codificados en alguna representacion de textoplano adecuada, y ambas alternativas son enfoques que desperdician bastantes recursos. Mercurial puede manejardeltas de ficheros con contenido binario arbitrario; no necesita tratar el texto plano como un caso especial.

4.2.2. Operacion seguraMercurial solo anade datos al final de los ficheros de revlog. Nunca modifica ninguna seccion de un fichero una

vez ha sido escrita. Esto es mas robusto y eficiente que otros esquemas que requieren modificar o reescribir datos.Adicionalmente, Mercurial trata cada escritura como parte de una transaccion, que puede cubrir varios ficheros.

Una transaccion es atomica: o bien la transaccion tiene exito y entonces todos sus efectos son visibles para todoslos lectores, o la operacion completa es cancelada. Esta garantıa de atomicidad implica que, si usted esta ejecutandodos copias de Mercurial, donde una de ellas esta leyendo datos y la otra los esta escribiendo, el lector nunca vera unresultado escrito parcialmente que podrıa confundirlo.

El hecho de que Mercurial solo hace adiciones a los ficheros hace mas facil proveer esta garantıa transaccional. Amedida que sea mas facil hacer operaciones como esta, mas confianza tendra usted en que sean hechas correctamente.

4.2.3. Recuperacion rapida de datosMercurial evita ingeniosamente un problema comun a todos los sistemas de control de revisiones anteriores¿

el problema de la recuperacion4 ineficiente de datos. Muchos sistemas de control de revisiones almacenan los con-tenidos de una revision como una serie incremental de modificaciones a una “instantanea”. Para reconstruir una versioncualquiera, primero usted debe leer la instantanea, y luego cada una de las revisiones entre la instantanea y su versionobjetivo. Entre mas largo sea el historial de un fichero, mas revisiones deben ser leıdas, y por tanto toma mas tiemporeconstruir una version particular.

La innovacion que aplica Mercurial a este problema es simple pero efectiva. Una vez la cantidad de informacion dedeltas acumulada desde la ultima instantanea excede un umbral fijado de antemano, se almacena una nueva instantanea(comprimida, por supuesto), en lugar de otro delta. Esto hace posible reconstruir cualquier version de un ficherorapidamente. Este enfoque funciona tan bien que desde entonces ha sido copiado por otros sistemas de control derevisiones.

La figura 4.3 ilustra la idea. En una entrada en el fichero ındice de un revlog, Mercurial almacena el rango deentradas (deltas) del fichero de datos que se deben leer para reconstruir una revision en particular.

Nota al margen: la influencia de la compresion de vıdeo

Si le es familiar la compresion de vıdeo, o ha mirado alguna vez una emision de TV a traves de cable digitalo un servicio de satelite, puede que sepa que la mayor parte de los esquemas de compresion de vıdeo almacenan

2N. del T. Contraccion de revision log, registro de revision.3N. del T. Diferencias.4N. del T. Retrieval. Recuperacion en el sentido de traer los datos, o reconstruirlos a partir de otros datos, pero no debido a una falla o calamidad,

sino a la operacion normal del sistema.

38

Page 49: Mercurial

Índice, rev 7

Índice de bitácora de revisiones (archivo .i) Datos de Bitacora de revisiones (archivo .d)

Snapshot, rev 4

Delta, rev 4 a 5

Delta, rev 5 a 6

Delta, rev 6 a 7

Delta, rev 2 a 3

Figura 4.3: Instantanea de un revlog, con deltas incrementales

cada cuadro del mismo como un delta contra el cuadro predecesor. Adicionalmente, estos esquemas usan tecnicas decompresion “con perdida” para aumentar la tasa de compresion, por lo que los errores visuales se acumulan a lo largode una cantidad de deltas inter-cuadros.

Ya que existe la posibilidad de que un flujo de vıdeo se “pierda” ocasionalmente debido a fallas en la senal, ypara limitar la acumulacion de errores introducida por la compresion con perdidas, los codificadores de vıdeo insertanperiodicamente un cuadro completo (tambien llamado “cuadro clave”) en el flujo de vıdeo; el siguiente delta es gen-erado con respecto a dicho cuadro. Esto quiere decir que si la senal de vıdeo se interrumpe, se reanudara una vez sereciba el siguiente cuadro clave. Ademas, la acumulacion de errores de codificacion se reinicia con cada cuadro clave.

4.2.4. Identificacion e integridad fuerteAdemas de la informacion de deltas e instantaneas, una entrada en un revlog contiene un hash criptografico de

los datos que representa. Esto hace difıcil falsificar el contenido de una revision, y hace facil detectar una corrupcionaccidental.

Los hashes proveen mas que una simple revision de corrupcion: son usados como los identificadores para lasrevisiones. Los hashes de identificacion de conjuntos de cambios que usted ve como usuario final son de las revisionesde la bitacora de cambios. Aunque los ficheros de registro y el manifiesto tambien usan hashes, Mercurial solo los usatras bambalinas.

Mercurial verifica que los hashes sean correctos cuando recupera revisiones de ficheros y cuando jala cambiosdesde otro repositorio. Si se encuentra un problema de integridad, Mercurial se quejara y detendra cualquier operacionque este haciendo.

Ademas del efecto que tiene en la eficiencia en la recuperacion, el uso periodico de instantaneas de Mercurial lohace mas robusto frente a la corrupcion parcial de datos. Si un fichero de registro se corrompe parcialmente debido aun error de hardware o del sistema, a menudo es posible reconstruir algunas o la mayorıa de las revisiones a partir delas secciones no corrompidas del fichero de registro, tanto antes como despues de la seccion corrompida. Esto no serıaposible con un sistema de almacenamiento basado unicamente en deltas.

39

Page 50: Mercurial

4.3. Historial de revisiones, ramas y fusionesCada entrada en el revlog de Mercurial conoce la identidad de la revision de su ancestro inmediato, al que se conoce

usualmente como su padre. De hecho, una revision contiene sitio no solo para un padre, sino para dos. Mercurial usaun hash especial, llamado el “ID nulo”, para representar la idea de “no hay padre aquı”. Este hash es simplemente unacadena de ceros.

En la figura 4.4 usted puede ver un ejemplo de la estructura conceptual de un revlog. Los ficheros de registro,manifiestos, y bitacoras de cambios comparten la misma estructura; solo difieren en el tipo de datos almacenados encada delta o instantanea.

La primera revision en un revlog (al final de la imagen) tiene como padre al ID nulo, en las dos ranuras disponiblespara padres. En una revision normal, la primera ranura para padres contiene el ID de la revision padre, y la segundacontiene el ID nulo, senalando ası que la revision solo tiene un padre real. Un par de revisiones que tenga el mismo IDpadre son ramas. Una revision que representa una fusion entre ramas tiene dos IDs de revision normales en sus ranuraspara padres.

4.4. El directorio de trabajoEn el directorio de trabajo, Mercurial almacena una instantanea de los ficheros del repositorio como si fueran los

de un conjunto de cambios particular.El directorio de trabajo “sabe” que conjunto de cambios contiene. Cuando usted actualiza el directorio de traba-

jo para que contenga un conjunto de cambios particular, Mercurial busca la revision adecuada del manifiesto paraaveriguar que ficheros estaba monitoreando cuando se hizo la consignacion del conjunto de cambios, y que revisionde cada fichero era la actual en ese momento. Luego de eso, recrea una copia de cada uno de esos ficheros, con losmismos contenidos que tenıan cuando fue consignado el conjunto de cambios.

El estado de directorio5 contiene el conocimiento de Mercurial acerca del directorio de trabajo. Allı se detalla aque conjunto de cambios es actualizado el directorio de trabajo, y todos los ficheros que Mercurial esta monitoreandoen este directorio.

Tal como la revision de un revlog tiene espacio para dos padres, para que pueda representar tanto una revisionnormal (con un solo padre) o una fusion de dos revisiones anteriores, el estado de directorio tiene espacio para dospadres. Cuando usted usa el comando “hg update”, el conjunto de cambios al que usted se actualiza es almacenadoen la casilla destinada al “primer padre”, y un ID nulo es almacenado en la segunda. Cuando usted hace una fusion(“hg merge”) con otro conjunto de cambios, la casilla para el primer padre permanece sin cambios, y la casilla parael segundo es actualizada con el conjunto de cambios con el que usted acaba de hacer la fusion. El comando “hgparents” le indica cuales son los padres del estado de directorio.

4.4.1. Que pasa en una consignacionEl estado de directorio almacena informacion sobre los padres para algo mas que mero registro. Mercurial usa los

padres del estado de directorio como los padres de un nuevo conjunto de cambios cuando usted hace una consignacion.La figura 4.5 muestra el estado normal del directorio de trabajo, que tiene un unico conjunto de cambios como

padre. Dicho conjunto de cambios es la punta, el conjunto de cambios mas reciente en el repositorio que no tienehijos.

Es util pensar en el directorio de trabajo como en “el conjunto de cambios que estoy a punto de enviar”. Cualquierfichero que usted le diga a Mercurial que fue anadido, borrado, renombrado o copiado, se vera reflejado en ese conjuntode cambios, como tambien se veran las modificaciones a cualquiera de los ficheros que Mercurial ya este monitorean-do; el nuevo conjunto de cambios dentra los padres del directorio de trabajo como propios.

Luego de una consignacion, Mercurial actualizara los padres del directorio de trabajo, de tal manera que el primerpadre sea el ID del nuevo conjunto de cambios, y el segundo sea el ID nulo. Esto puede verse en la figura 4.6. Mercurialno toca ninguno de los ficheros del directorio de trabajo cuando usted hace la consignacion; solo modifica el estado dedirectorio para anotar sus nuevos padres.

5N. del T. dirstate, en ingles en el original.

40

Page 51: Mercurial

4.4.2. Creacion de un nuevo frenteEs perfectamente normal actualizar el directorio de trabajo a un conjunto de cambios diferente a la punta actual. Por

ejemplo, usted podrıa desear saber en que estado se encontraba su proyecto el martes pasado, o podrıa estar buscandoen todos los conjuntos de cambios para saber cuando se introdujo un fallo. En casos como estos, la accion naturales actualizar el directorio de trabajo al conjunto de cambios de su interes, y examinar directamente los ficheros en eldirectorio de trabajo para ver sus contenidos tal como estaban en el momento de hacer la consignacion. El efecto quetiene esto se muestra en la figura 4.7.

Una vez se ha actualizado el directorio de trabajo a un conjunto de cambios anterior, que pasa si se hacen cambios,y luego se hace una consignacion? Mercurial se comporta en la misma forma que describı anteriormente. Los padresdel directorio de trabajo se convierten en los padres del nuevo conjunto de cambios. Este nuevo conjunto de cambiosno tiene hijos, ası que se convierte en la nueva punta. Y el repositorio tiene ahora dos conjuntos de cambios que notienen hijos; a estos los llamamos frentes. Usted puede apreciar la estructura que esto crea en la figura 4.8.

Nota: Si usted es nuevo en Mercurial, deberıa tener en mente un “error” comun,que es usar el comando “hg pull” sin ninguna opcion. Por defecto, el comando“hg pull” no actualiza el directorio de trabajo, ası que usted termina trayendonuevos conjuntos de cambios a su repositorio, pero el directorio de trabajo sigueusando el mismo conjunto de cambios que tenıa antes de jalar. Si usted hace al-gunos cambios, y luego hace una consignacion, estara creando un nuevo frente,porque su directorio de trabajo no es sincronizado a cualquiera que sea la nuevapunta.Pongo la palabra “error” en comillas porque todo lo que usted debe hacer pararectificar la situacion es hacer una fusion (“hg merge”), y luego una consignacion(“hg commit”). En otras palabras, esto casi nunca tiene consecuencias negativas;solo sorprende a la gente. Discutire otras formas de evitar este comportamiento,y porque Mercurial se comporta de esta forma, inicialmente sorprendente, masadelante.

4.4.3. Fusion de frentesCuando usted ejecuta el comando “hg merge”, Mercurial deja el primer padre del directorio de trabajo intacto, y

escribe como segundo padre el conjunto de cambios contra el cual usted esta haciendo la fusion, como se muestra enla figura 4.9.

Mercurial tambien debe modificar el directorio de trabajo, para fusionar los ficheros que el monitorea en los dosconjuntos de cambios. Con algunas simplificaciones, el proceso es el siguiente, por cada fichero en los manifiestos deambos conjuntos de cambios.

Si ningun conjunto de cambios ha modificado un fichero, no se hace nada con el mismo.

Si un conjunto de cambios ha modificado un fichero, y el otro no lo ha hecho, se crea una copia del fichero conlas modificaciones pertinentes en el directorio de trabajo.

Si un conjunto de cambios borra un fichero, y el otro no lo ha hecho (o tambien lo borro), se borra dicho ficherodel directorio de trabajo.

Si un conjunto de cambios ha borrado un fichero, pero el otro lo ha modificado, se le pregunta al usuarioque hacer: conservar el fichero modificado, o borrarlo?

Si ambos conjuntos de cambios han modificado un fichero, se invoca el programa externo de fusion para definirel nuevo contenido del fichero fusionado. Esto puede requerir interaccion directa de parte del usuario.

Si un conjunto de cambios ha modificado un fichero, y el otro ha renombrado o copiado el mismo, asegurarsede que los cambios sigan al nuevo nombre de fichero.

41

Page 52: Mercurial

Hay mas detalles—hacer una fusion tiene una gran cantidad de casos especiales—pero estas son las elecciones mascomunes que se ven involucradas en una fusion. Como usted puede ver, muchos de los casos son completamenteautomaticos, y de hecho la mayorıa de las fusiones terminan automaticamente, sin requerir la interaccion del usuariopara resolver ningun conflicto.

Cuando considere que pasa cuando usted hace una consignacion despues de una fusion, de nuevo el directoriode trabajo es “el conjunto de cambios que estoy a punto de consignar”. Una vez termina su trabajo el comando “hgmerge”, el directorio de trabajo tiene dos padre; estos se convertiran en los padres del nuevo conjunto de cambios.

Mercurial le permite hacer multiples fusiones, pero usted debe consignar los resultados de cada fusion sucesi-vamente. Esto es necesario porque Mercurial solo monitorea dos padres, tanto para las revisiones como para losdirectorios de trabajo. Aunque tecnicamente es posible fusionar varios conjuntos de trabajo en una sola operacion, laposibilidad de confundir al usuario y crear un desorden terrible en la fusion se hace incontenible de inmediato.

4.5. Otras caracterısticas de diseno interesantesEn las secciones anteriores, he tratado de resaltar algunos de los aspectos mas importantes del diseno de Mercurial,

para mostrar que se presta gran cuidado y atencion a la confiabilidad y el desempeno. Sin embargo, la atencion alos detalles no para ahı. Hay una cantidad de aspectos de la construccion de Mercurial que encuentro interesantespersonalmente. Detallare unos cuantos de ellos aquı, aparte de los elementos “importantes” de arriba, para que, siusted esta interesado, pueda obetener una idea mejor de la cantidad de esfuerzo mental invertido en el diseno de unsistema bien disenado.

4.5.1. Compresion ingeniosaCuando es adecuado, Mercurial almacenara tanto las instantaneas como los deltas en formato comprimido. Lo hace

tratando siempre de comprimir una instantanea o delta, y conservando la version comprimida solo si es mas pequenaque la version sin compresion.

Esto implica que Mercurial hace “lo correcto” cuando almacena un fichero cuyo formato original esta comprimido,como un fichero zip o una imagen JPEG. Cuando estos tipos de ficheros son comprimidos por segunda vez, el ficheroresultante usualmente es mas grande que la version comprimida una sola vez, por lo que Mercurial almacenara elfichero zip o JPEG original.

Los deltas entre revisiones de un fichero comprimido usualmente son mas grandes que las instantaneas del mismofichero, y Mercurial de nuevo hace “lo correcto” en estos casos. El encuentra que dicho delta excede el umbral respectoal cual se deberıa almacenar una instantanea completa del fichero, ası que almacena la instantanea, ahorrando espaciode nuevo respecto al enfoque simplista de usar unicamente deltas.

Recompresion de red

Cuando almacena las revisiones en disco, Mercurial usa el algoritmo de compresion “deflacion” (el mismo usadoen el popular formato de fichero zip), que provee una buena velocidad con una tasa de compresion respetable. Sinembargo, cuando se transmiten datos de revisiones a traves de una conexion de red, Mercurial descomprime los datoscomprimidos de las revisiones.

Si la conexion es hecha a traves de HTTP, Mercurial recomprime el flujo completo de datos usando un algoritmo decompresion que brinda una mejor tasa de compresion (el algoritmo Burrows-Wheeler del ampliamente usado paquetede compresion bzip2). Esta combinacion de algoritmo y compresion del flujo completo de datos (en vez de unarevision a la vez) reduce sustancialmente la cantidad de bytes a transferir, brindando ası un mejor desempeno de redsobre casi todo tipo de redes.

(Si la conexion se hace sobre ssh, Mercurial no recomprmime el flujo, porque ssh puede hacer esto por sı mismo.)

4.5.2. Reordenado de lectura/escritura y atomicidadAnadir datos al final de un fichero no es todo lo que hace falta para garantizar que un lector no vera una escritura

parcial. Si recuerda la figura 4.2, las revisiones en la bitacora de cambios apuntan a revisiones en el manifiesto, y las

42

Page 53: Mercurial

revisiones en el manifiesto apuntan a revisiones en ficheros de registro. Esta jerarquıa es deliberada.Un escritor inicia una transaccion al escribir los datos del ficheros del fichero de registro y el manifiesto, y no

escribe nada en la bitacora de cambios hasta que dichas escrituras hayan terminado. Un lector empieza leyendo datosde la bitacora de cambios, luego del manifiesto, y finalmente del fichero de registro.

Como el escritor siempre termina de escribir los datos en el fichero de registro y en el manifiesto antes de escribira la bitacora de cambios, un lector nunca vera un apuntador a una version parcialmente escrita de revisiones delmanifiesto desde la bitacora de cambios, y nunca leera un apuntador a una revision parcialmente escrita del fichero deregistro desde el manifiesto.

4.5.3. Acceso concurrenteEl reordenado de lectura/escritura y la atomicidad garantizan que Mercurial nunca necesita bloquear un repositorio

cuando esta leyendo datos, aun si se esta escribiendo al repositorio mientras se hace la lectura. Esto tiene un granefecto en la escalabilidad; usted puede tener cualquier cantidad de procesos Mercurial leyendo datos de un repositoriode manera segura al mismo tiempo, sin importar si se esta escribiendo al mismo o no.

La naturaleza carente de bloqueos de la lectura significa que si usted esta compartiendo un repositorio en unsistema multiusuario, no necesita dar a los usuarios locales permisos de escritura a su repositorio para que ellospuedan clonarlo o jalar cambios; solo necesitan permisos de lectura. (Esta no es una caracterıstica comun entre lossistemas de control de revisiones, ası que no la de por hecha! Muchos de ellos requieren que los lectores sean capacesde bloquear el repositorio antes de poder leerlo, y esto requiere acceso de escritura en al menos un directorio, lo quepor supuesto se convierte en una fuente de todo tipo de problemas administrativos y de seguridad bastante molestos.)

Mercurial usar bloqueos para asegurarse de que solo un proceso pueda escribir a un repositorio al mismo tiempo(el mecanismo de bloqueo es seguro incluso sobre sistemas de ficheros notoriamente hostiles al bloqueo, como NFS).Si un repositorio esta bloqueado, los escritores esperaran un buen rato para revisar si el repositorio ya ha sido desblo-queado, pero si el repositorio sique bloqueado por mucho tiempo, el proceso que intenta escribir fallara por tiempo deespera maximo. Esto significa que sus guiones automaticos diarios no se quedaran esperando para siempre, apilandosesi el sistema se cayo sin que nadie se diera cuenta, por ejemplo. (Sı, el tiempo de espera maximo es configurable, decero a infinito).

Acceso seguro al estado de directorio

Al igual que con los datos de revision, Mercurial no requiere un bloqueo para leer el fichero de estado de directorio;sı se usa un bloqueo para escribir a el. Para evitar la posibilidad de leer una copia parcialmente escrita del fichero deestado de directorio, Mercurial escribe a un fichero con un nombre unico en el mismo directorio del fichero de estadode directorio, y luego renombra atomicamente este fichero temporal a dirstate6. Ası se garantiza que el ficherollamado dirstate este completo, y no parcialmente escrito.

4.5.4. Evitar movimientos de brazoUn aspecto crıtico para el desempeno de Mercurial es evitar los movimientos del brazo de lectura del disco duro, ya

que cualquier movimiento de brazo es mucho mas costoso que incluso una operacion de lectura relativamente grande.Es por esto que, por ejemplo, el estado de directorio es almacenado como un solo fichero. Si hubiera un estado de

directorio por cada directorio que Mercurial monitorea, el disco harıa un movimiento de brazo por cada directorio. Encambio, Mercurial lee el estado de directorio completo en un solo paso.

Mercurial tambien usa un esquema de “copiar al escribir” cuando clona un repositorio en un mismo medio dealmacenamiento local. En vez de copiar cada fichero de revlog del repositorio viejo al nuevo, se crea un “enlace duro”,que es una manera sucinta de decir “estos dos nombres apuntan al mismo fichero”. Cuando Mercurial esta a punto deescribir a uno de los ficheros de revlog, revisa si la cantidad de nombres apuntando al fichero es de mas de uno. Si loes, mas de un repositorio esta usando el fichero, ası que Mercurial hace una nueva copia del fichero, privada para esterepositorio.

6N. del T. Estado de directorio.

43

Page 54: Mercurial

Algunos desarrolladores de control de revisiones han indicado que la idea de hacer una copia privada completade un fichero no es eficiente desde el punto de vista de almacenamiento. Aunque esto es cierto, el almacenamientoes barato, y este metodo brinda el maximo rendimiento al tiempo que delega la mayor parte del trabajo de manejo deficheros al sistema operativo. Un esquema alternativo seguramente reducirıa el desempeno y aumentarıa la complejidaddel software, cada uno de los cuales es mucho mas importante para la “sensacion” que se tiene del software en el trabajodıa a dıa.

4.5.5. Otros contenidos del estado de directorioDebido a que Mercurial no lo fuerza a indicar si usted esta modificando un fichero, se usa el estado de directorio

para almacenar informacion extra para poder determinar efecientemente si usted ha modificado un fichero. Por cadafichero en el directorio de trabajo, se almacena el momento en que Mercurial modifico por ultima vez el fichero, y eltamano del fichero en ese momento.

Cuando usted anade (“hg add”), remueve (“hg remove”), renombra (“hg rename”) o copia (“hg copy”) ficheros,Mercurial actualiza el estado de directorio para saber que hacer con dichos ficheros cuando usted haga la consignacion.

Cuando Mercurial esta revisando el estado de los ficheros en el directorio de trabajo, revisa primero la fecha demodificacion del fichero. Si no ha cambiado, el fichero no ha sido modificado. Si el tamano del fichero ha cambiado,el fichero ha sido modificado. Solo en el caso en que el tiempo de modificacion ha cambiado, pero el tamano no, esnecesario leer el contenido del fichero para revisar si ha cambiado. Almacenar estos pocos datos reduce dramatica-mente la cantidad de datos que Mercurial debe leer, lo que brinda una mejora en el rendimiento grande, comparadocon otros sistemas de control de revisiones.

44

Page 55: Mercurial

Segundo padre

32bf9a5f22c0

Hash de revisión

34b8b7a15ea1

...

Datos de Revisión (delta o snapshot)

Primer padre

000000000000

Segundo padre

000000000000

Hash de revisión

ff9dc8bc2a8b

...

Datos de revisión (delta o snapshot)

Primer padre

34b8b7a15ea1

Segundo padre

000000000000

Hash de revisión

1b67dc96f27a

...

Datos de revisión (delta o snapshot)

Primer padre

ff9dc8bc2a8b

Segundo padre

000000000000

Hash de revisión

5b80c922ebdd

...

Datos de revisión (delta o snapshot)

Primer padre

ecacb6b4c9fd

Segundo padre

000000000000

Hash de revisión

32bf9a5f22c0

...

Datos de revisión (delta o snapshot)

Primer padre

ff9dc8bc2a8b

Segundo padre

000000000000

Hash de revisión

ecacb6b4c9fd

...

Datos de revisión (delta o snapshot)

Revisión principal

(sin hijos)

Revisión de fusión

(dos padres)

Ramas

(dos revisiones,

mismo padre)

Primera revisión

(ambos padres nulos)

Primer padre

5b80c922ebdd

Figura 4.4:

45

Page 56: Mercurial

e7639888bb2f

7b064d8bac5e

000000000000

Historia en el repositorio

e7639888bb2f

000000000000

Primer padre

Segundo padre

Padres del directorio de trabajo

Figura 4.5: El directorio de trabajo puede tener dos padres

dfbbb33f3fa3

e7639888bb2f

7b064d8bac5e

000000000000

Historia en el repositorio

dfbbb33f3fa3

000000000000

Primer padre

Segundo padre

Padres del directorio de trabajo

Nuevo

conjunto

de

cambios

Figura 4.6: El directorio de trabajo obtiene nuevos padres luego de una consignacion

46

Page 57: Mercurial

e7639888bb2f

7b064d8bac5e

000000000000

Historia en el repositorio

7b064d8bac5e

000000000000

Primer padre

Segundo padre

Padres del directorio de trabajo

Figura 4.7: El directorio de trabajo, actualizado a un conjunto de cambios anterior

e7639888bb2f

7b064d8bac5e

000000000000

ffb20e1701ea

000000000000

Primer padre

Segundo padre

Padres del directorio de trabajo

ffb20e1701ea

Cabeza Pre−existente Cabeza recién creada (y tip)

Figura 4.8: Despues de una consignacion hecha mientras se usaba un conjunto de cambios anterior

47

Page 58: Mercurial

7b064d8bac5e

000000000000

ffb20e1701ea

e7639888bb2f

Primer padre (sin cambio)

Segundo padre

Padres del directorio de trabajo

ffb20e1701ea

Cabeza pre−existente Cabeza recién creada(y tip)

e7639888bb2f

Figura 4.9: Fusion de dos frentes

48

Page 59: Mercurial

Capıtulo 5

Mercurial dıa a dıa

5.1. Como indicarle a Mercurial que ficheros seguirMercurial no trabaja con ficheros en su repositorio a menos que usted se lo indique explıcitamente. La orden “hg

status” le mostrara cuales ficheros son desconocidos para Mercurial; se emplea un “?” para mostrar tales ficheros.Para indicarle a Mercurial que tenga en cuenta un fichero, emplee la orden “hg add”. Una vez que haya adicionado

el fichero, la lınea referente al fichero al aplicar la orden “hg status” para tal fichero cambia de “?” a “A”.

1 $ hg init add-example2 $ cd add-example3 $ echo a > a4 $ hg status5 ? a6 $ hg add a7 $ hg status8 A a9 $ hg commit -m ’Added one file’

10 $ hg status

Despues de invocar “hg commit”, los ficheros que haya adicionado antes de consignar no se listaran en la salidade “hg status”. La razon para esto es que “hg status” solamente le muestra aquellos ficheros “interesantes” —los que usted haya modificado o a aquellos sobre los que usted haya indicado a Mercurial hacer algo— de formapredeterminada. Si tiene un repositorio que contiene miles de ficheros, rara vez deseara saber cuales de ellos estansiendo seguidos por Mercurial, pero que no han cambiado. (De todas maneras, puede obtener tal informacion; masadelante hablaremos de ello.)

Cuando usted anade un fichero, Mercurial no hace nada con el inmediatamente. En cambio, tomara una instantaneadel estado del fichero la proxima vez que usted consigne. Continuara haciendo seguimiento a los cambios que hagasobre el fichero cada vez que consigne, hasta que usted lo elimine.

5.1.1. Nombramiento explıcito e implıcito de ficherosMercurial tiene un comportamiento util en el cual si a una orden, le pasa el nombre de un directorio, todas las

ordenes lo interpretaran como “Deseo operar en cada fichero de este directorio y sus subdirectorios”.

1 $ mkdir b2 $ echo b > b/b3 $ echo c > b/c4 $ mkdir b/d

49

Page 60: Mercurial

5 $ echo d > b/d/d6 $ hg add b7 adding b/b8 adding b/c9 adding b/d/d

10 $ hg commit -m ’Added all files in subdirectory’

Tenga en cuenta que en este ejemplo Mercurial imprimio los nombres de los ficheros que se adicionaron, mientras queno lo hizo en el ejemplo anterior cuando adicionamos el fichero con nombre a.

En el ultimo caso hicimos explıcito el nombre del fichero que deseabamos adicionar en la lınea de ordenes, yMercurial asume en tales casos que usted sabe lo que esta haciendo y no imprime informacion alguna.

Cuando hacemos implıcitos los nombres de los ficheros dando el nombre de un directorio, Mercurial efectua elpaso extra de imprimir el nombre de cada fichero con el que va a hacer algo. Esto para aclarar lo que esta sucediendo,y reducir en lo posible una sorpresa silenciosa pero fatal. Este comportamiento es comun a la mayorıa de ordenes enMercurial.

5.1.2. Nota al margen: Mercurial trata ficheros, no directoriosMercurial no da seguimiento a la informacion de los directorios. En lugar de eso tiene en cuenta las rutas de

los ficheros. Antes de crear un fichero, primero crea todos los directorios que hagan falta para completar la ruta delmismo. Despues de borrar un fichero, borra todos los directorios vacıos que estuvieran en la ruta del fichero borrado.Suena como una diferencia trivial, pero tiene una consecuencia practica menor: no es posible representar un directoriocompletamente vacıo en Mercurial.

Los directorios vacıos rara vez son utiles, y hay soluciones alternativas no intrusivas que usted puede emplear paraobtener el efecto apropiado. Los desarrolladores de Mercurial pensaron que la complejidad necesaria para administrardirectorios vacıos no valıa la pena frente al beneficio limitado que esta caracterıstica podrıa traer.

Si necesita un directorio vacıo en su repositorio, hay algunas formas de lograrlo. Una es crear un directorio,despues hacer “hg add” a un fichero “oculto” dentro de ese directorio. En sistemas tipo Unix, cualquier fichero cuyonombre comience con un punto (“.”) es tratado como oculto por la mayorıa de comandos y herramientas GUI. Estaaproximacion se ilustra en la figura 5.1.

1 $ hg init hidden-example2 $ cd hidden-example3 $ mkdir empty4 $ touch empty/.hidden5 $ hg add empty/.hidden6 $ hg commit -m ’Manage an empty-looking directory’7 $ ls empty8 $ cd ..9 $ hg clone hidden-example tmp

10 updating working directory11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved12 $ ls tmp13 empty14 $ ls tmp/empty

Figura 5.1: Simular un directorio vacıo con un fichero oculto

Otra forma de abordar la necesidad de un directorio vacıo es simplemente crear uno en sus guiones de construccionantes de que lo necesiten.

50

Page 61: Mercurial

5.2. Como dejar de hacer seguimiento a un ficheroSi decide que un fichero no pertenece a su repositorio, use la orden “hg remove”; se borrara el fichero y le

indicara a Mercurial que deje de hacerle seguimiento. Los ficheros eliminados se representan con “R” al usar “hgstatus”.

1 $ hg init remove-example2 $ cd remove-example3 $ echo a > a4 $ mkdir b5 $ echo b > b/b6 $ hg add a b7 adding b/b8 $ hg commit -m ’Small example for file removal’9 $ hg remove a

10 $ hg status11 R a12 $ hg remove b13 removing b/b

Despues de hacer “hg remove” a un fichero, Mercurial dejara de hacer seguimiento al mismo, incluso si recrea elfichero con el mismo nombre en su directorio de trabajo. Si decide recrear un fichero con el mismo nombre y desea queMercurial le haga seguimiento, basta con hacerle “hg add”. Mercurial sabra que el fichero recientemente adicionadono esta relacionado con el fichero anterior que tenıa el mismo nombre.

5.2.1. Al eliminar un fichero no se afecta su historialEs preciso tener en cuenta que eliminar un fichero tiene solo dos efectos.

Se elimina la version actual del fichero del directorio de trabajo.

Mercurial deja de hacer seguimiento a los cambios del fichero desde la proxima consignacion.

Al eliminar un fichero no se altera de ninguna manera el historial del mismo.Si actualiza su directorio de trabajo a un conjunto de cambios en el cual el fichero que elimino aun era tenido en

cuenta, este reaparecera en el directorio de trabajo, con los contenidos que este tenıa cuando se consigno tal conjuntode cambios. Si usted actualiza el directorio de trabajo a un conjunto de cambios posterior en el cual el fichero habıasido eliminado, Mercurial lo eliminara de nuevo del directorio de trabajo.

5.2.2. Ficheros perdidosMercurial considera como perdido un fichero que usted borro, pero para el que no se uso “hg remove”. Los

ficheros perdidos se representan con “!” al visualizar “hg status”. Las ordenes de Mercurial generalmente no harannada con los ficheros perdidos.

1 $ hg init missing-example2 $ cd missing-example3 $ echo a > a4 $ hg add a5 $ hg commit -m ’File about to be missing’6 $ rm a7 $ hg status8 ! a

51

Page 62: Mercurial

Si su repositorio contiene un fichero que “hg status” reporta como perdido, y desea que el mismo se vaya, sepuede usar “hg remove --after” posteriormente para indicarle a Mercurial que usted deseaba borrar tal fichero.

1 $ hg remove --after a2 $ hg status3 R a

Por otro lado, si borro un fichero perdido por accidente, puede usar “hg revert nombre de fichero” pararecuperar el fichero. Reaparecera, sin modificaciones.

1 $ hg revert a2 $ cat a3 a4 $ hg status

5.2.3. Nota al margen: ¿Por que decirle explıcitamente a Mercurial que elimine un fichero?Es posible que se haya preguntado por que Mercurial exige que usted le indique explıcitamente que esta borrando

un fichero. Al principio del desarrollo de Mercurial, este permitıa que usted borrara el fichero sin mas; Mercurial sedarıa cuenta de la ausencia del fichero automaticamente despues de la ejecucion de “hg commit”, y dejarıa de hacerseguimiento al fichero. En la practica, resultaba muy sencillo borrar un fichero accidentalmente sin darse cuenta.

5.2.4. Atajo util—agregar y eliminar ficheros en un solo pasoMercurial ofrece una orden combinada, “hg addremove”, que agrega los ficheros que no tienen seguimiento y

marca los ficheros faltantes como eliminados.

1 $ hg init addremove-example2 $ cd addremove-example3 $ echo a > a4 $ echo b > b5 $ hg addremove6 adding a7 adding b

La orden “hg commit” se puede usar con la opcion -A que aplica el mismo agregar-eliminar, seguido inmediatamentede una consignacion.

1 $ echo c > c2 $ hg commit -A -m ’Commit with addremove’3 adding c

5.3. Copiar ficherosMercurial ofrece la orden “hg copy” para hacer una copia nueva de un fichero. Cuando se copia un fichero con

esta orden, Mercurial lleva un registro indicando que el nuevo fichero es una copia del fichero original. Los ficheroscopiados se tratan de forma especial cuando usted hace una fusion con el trabajo de alguien mas.

52

Page 63: Mercurial

5.3.1. Resultados de copiar un fichero durante una fusionDurante una fusion los cambios “siguen” una copia. Para ilustrar lo que esto significa, haremos un ejemplo. Comen-

zaremos con el mini repositorio usual que contiene un solo fichero

1 $ hg init my-copy2 $ cd my-copy3 $ echo line > file4 $ hg add file5 $ hg commit -m ’Added a file’

Debemos hacer algo de trabajo en paralelo, de forma que tengamos algo para fusionar. Aquı clonamos el repositorio.

1 $ cd ..2 $ hg clone my-copy your-copy3 updating working directory4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

De vuelta en el repositorio inicial, usemos la orden “hg copy” para hacer una copia del primer fichero que creamos.

1 $ cd my-copy2 $ hg copy file new-file

Si vemos la salida de la orden “hg status”, el fichero copiado luce tal como un fichero que se ha anadidonormalmente.

1 $ hg status2 A new-file

Pero si usamos la opcion -C de la orden “hg status”, se imprimira otra lınea: el fichero desde el cual fue copiadonuestro fichero recien anadido.

1 $ hg status -C2 A new-file3 file4 $ hg commit -m ’Copied file’

Ahora, en el repositorio que clonamos, hagamos un cambio en paralelo. Adicionaremos una lınea de contenido alfichero original que creamos.

1 $ cd ../your-copy2 $ echo ’new contents’ >> file3 $ hg commit -m ’Changed file’

Hemos modificado el fichero file en este repositorio. Cuando jalemos los cambios del primer repositorio y fu-sionemos las dos cabezas, Mercurial propagara los cambios que hemos hecho localmente en file a su copia, new-file.

1 $ hg pull ../my-copy2 pulling from ../my-copy3 searching for changes4 adding changesets5 adding manifests6 adding file changes

53

Page 64: Mercurial

7 added 1 changesets with 1 changes to 1 files (+1 heads)8 (run ’hg heads’ to see heads, ’hg merge’ to merge)9 $ hg merge

10 merging file and new-file11 0 files updated, 1 files merged, 0 files removed, 0 files unresolved12 (branch merge, don’t forget to commit)13 $ cat new-file14 line15 new contents

5.3.2. ¿Por que los cambios se reflejan en las copias?Este comportamiento de cambios en ficheros que se propagan a las copias de los ficheros parecerıa esoterico, pero

en la mayorıa de casos es absolutamente deseable. Es indispensable recordar que esta propagacion solamente sucedecuando fusionamos. Por lo tanto si sobre un fichero se usa “hg copy”, y se modifica el fichero original durante elcurso normal de su trabajo, nada pasara.

Lo segundo a tener en cuenta es que las modificaciones solamente se propagaran en las copias unicamente si losrepositorios de los cuales esta jalando los cambios no saben de la copia.

Explicaremos a continuacion la razon de este comportamiento de Mercurial. Digamos que yo he aplicado unarreglo de un fallo importante a un fichero fuente y consigne los cambios. Por otro lado, usted decidio hacer “hgcopy” sobre el fichero en su repositorio, sin saber acerca del fallo o sin ver el arreglo, y ha comenzado a trabajar sobresu copia del fichero.

Si jala y fusiona mis cambios y Mercurial no hubiera propagado los cambios en las copias, su fichero fuente tendrıael fallo, a menos que usted haya recordado propagar el arreglo del fallo a mano, el mismo permanecerıa en su copiadel fichero.

Mercurial previene esta clase de problemas, gracias a la propagacion automatica del cambio que arreglo el fallodel fichero original. Hasta donde se, Mercurial es el unico sistema de control de revisiones que propaga los cambiosen las copias de esta forma.

Cuando su historial de cambios tiene un registro de la copia y la subsecuente fusion, usualmente no es necesariopropagar los cambios el fichero original a las copias del mismo, y por esta razon Mercurial propaga unicamente loscambios en las copias hasta este punto y no mas alla.

5.3.3. Como hacer que los cambios no sigan a la copia?Si por algun motivo usted decide que esta caracterıstica de propagacion automatica de cambios en las copias

no es para usted, simplemente use la orden usual de su sistema para copiar ficheros (en sistemas tipo Unix, es cp),y posteriormente use “hg add” sobre la nueva copia hecha a mano. Antes de hacerlo, de todas maneras, relea laseccion 5.3.2, y tome una decision asegurandose que este comportamiento no es el apropiado para su caso especıfico.

5.3.4. Comportamiento de la orden “hg copy”Cuando usa la orden “hg copy”, Mercurial hace una copia de cada fichero fuente tal como se encuentra en el direc-

torio actual. Esto significa que si usted hace modificaciones a un fichero, y le aplica “hg copy” sin haber consignadoprimero los cambios, la nueva copia contendra tambien las modificaciones que haya hecho hasta ese punto. (Estecomportamiento me parece poco intuitivo, y por tal motivo lo menciono.)

La orden “hg copy” actua de forma parecida a la orden cp de Unix (puede usar el alias “hg cp” si le es mascomodo). El ultimo argumento es el destino, y todos los argumentos previos son las fuentes. Si solamente indica unfichero como la fuente, y el destino no existe, se crea un fichero nuevo con ese nombre.

1 $ mkdir k2 $ hg copy a k

54

Page 65: Mercurial

3 $ ls k4 a

Si el destino es un directorio, Mercurial copia las fuentes en este.

1 $ mkdir d2 $ hg copy a b d3 $ ls d4 a b

La copia de un directorio es recursiva, y preserva la estructura del directorio fuente.

1 $ hg copy c e2 copying c/a/c to e/a/c

Si tanto la fuente como el destino son directorios, la estructura de la fuente se recrea en el directorio destino.

1 $ hg copy c d2 copying c/a/c to d/c/a/c

De la misma forma que la orden “hg rename”, si copia un fichero manualmente y desea que Mercurial sepa queha copiado un fichero, basta con aplicar la opcion --after a la orden “hg copy”.

1 $ cp a z2 $ hg copy --after a z

5.4. Renombrar ficherosLa necesidad de renombrar un fichero es mas comun que hacer una copia del mismo. La razon por la cual discutı la

orden “hg copy” antes de hablar acerca de cambiar el nombre de los ficheros, es que Mercurial trata el renombrar unfichero de la misma forma que una copia. Por lo tanto, saber lo que hace Mercurial cuando usted copia un fichero leindica que esperar cuando renombra un fichero.

Cuando usa la orden “hg rename”, Mercurial hace una copia de cada fichero fuente, lo borra y lo marca comofichero eliminado.

1 $ hg rename a b

La orden “hg status” muestra la nueva copia del fichero como anadida y el fichero inicial de la copia, como elimi-nado.

1 $ hg status2 A b3 R a

De la misma forma en que se usa la orden “hg copy”, debemos usar la opcion -C de la orden “hg status” paraverificar que el fichero anadido realmente comienza a ser seguido por Mercurial como una copia del fichero original,ahora eliminado.

1 $ hg status -C2 A b3 a4 R a

55

Page 66: Mercurial

Igual que con “hg remove” y “hg copy”, puede indicarsele a Mercurial acerca de un renombramiento inmediatocon la opcion --after. El comportamiento de la orden “hg rename” y las opciones que acepta, son similares a laorden “hg copy” en casi todo.

5.4.1. Renombrar ficheros y fusionar cambiosDado que el renombrado de Mercurial se implementa como un copiar-y-eliminar, la misma propagacion de cambios

ocurre cuando usted fusiona despues de renombrar como despues de hacer una copia.Si yo modifico un fichero y usted lo renombra a un nuevo fichero, y posteriormente fusionamos nuestros respectivos

cambios, mi modificacion al fichero bajo su nombre original se propagara en el fichero con el nuevo nombre. (Es loque se esperarıa que “simplemente funcione,” pero, no todos los sistemas de control de revisiones hacen esto.)

Aunque el hecho de que los cambios sigan la copia es una caracterıstica respecto a la cual usted puede estarde acuerdo y decir “si, puede ser util,” deberıa ser claro que el seguimiento de cambios de un renombramiento esdefinitivamente importante. Sin esto, serıa muy sencillo que los cambios se quedaran atras cuando los ficheros serenombran.

5.4.2. Cambios de nombre divergentes y fusionEl caso de renombramiento con nombres divergentes ocurre cuando dos desarrolladores comienzan con un fichero—

llamemoslo foo—en sus repositorios respectivos.

1 $ hg clone orig anne2 updating working directory3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved4 $ hg clone orig bob5 updating working directory6 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Anne renombra el fichero a bar.

1 $ cd anne2 $ hg mv foo bar3 $ hg ci -m ’Rename foo to bar’

Mientras que Bob lo renombra como quux.

1 $ cd ../bob2 $ hg mv foo quux3 $ hg ci -m ’Rename foo to quux’

Veo esto como un conflicto porque cada desarrollador ha expresado intenciones diferentes acerca de como consid-era deberıa haberse nombrado el fichero.

¿Que cree que deberıa pasar cuando fusionen su trabajo? El comportamiento de Mercurial es que siempre preservaambos nombres cuando fusiona los conjuntos de cambios que contienen nombres divergentes.

1 # See http://www.selenic.com/mercurial/bts/issue4552 $ cd ../orig3 $ hg pull -u ../anne4 pulling from ../anne5 searching for changes6 adding changesets7 adding manifests

56

Page 67: Mercurial

8 adding file changes9 added 1 changesets with 1 changes to 1 files

10 1 files updated, 0 files merged, 1 files removed, 0 files unresolved11 $ hg pull ../bob12 pulling from ../bob13 searching for changes14 adding changesets15 adding manifests16 adding file changes17 added 1 changesets with 1 changes to 1 files (+1 heads)18 (run ’hg heads’ to see heads, ’hg merge’ to merge)19 $ hg merge20 warning: detected divergent renames of foo to:21 bar22 quux23 1 files updated, 0 files merged, 0 files removed, 0 files unresolved24 (branch merge, don’t forget to commit)25 $ ls26 bar quux

Tenga en cuenta que Mercurial le advierte acerca de nombres divergentes, pero deja que usted decida que hacercon la divergencia despues de la fusion.

5.4.3. Cambios de nombre convergentes y fusionOtra clase de conflicto al cambiar el nombre de ficheros ocurre cuando dos personas eligen renombrar diferentes

ficheros fuente al mismo destino. En este caso Mercurial aplica su maquinaria de fusion usual, y le permite a ustedguiar la situacion a una resolucion adecuada.

5.4.4. Otros casos lımite relacionados con renombramientosMercurial tiene un fallo de mucho tiempo en el cual no es capaz de fusionar cuando por un lado hay un fichero con

un nombre dado, mientras que en otro hay un directorio con el mismo nombre. Esto esta documentado como Fallo deMercurial No. 29.

1 $ hg init issue292 $ cd issue293 $ echo a > a4 $ hg ci -Ama5 adding a6 $ echo b > b7 $ hg ci -Amb8 adding b9 $ hg up 0

10 0 files updated, 0 files merged, 1 files removed, 0 files unresolved11 $ mkdir b12 $ echo b > b/b13 $ hg ci -Amc14 adding b/b15 created new head16 $ hg merge17 abort: Is a directory: /tmp/issue29HYiaCx/issue29/b

57

Page 68: Mercurial

5.5. Recuperarse de equivocacionesMercurial tiene unas ordenes poderosas que le ayudaran a recuperarse de equivocaciones comunes.La orden “hg revert” le permite deshacer cambios que haya hecho a su directorio de trabajo. Por ejemplo, si

aplico “hg add” a un fichero por accidente, ejecute “hg revert” con el nombre del fichero que anadio, y en tantoque el fichero no haya sido tocado de forma alguna, no sera adicionado, ni seguido por Mercurial. Tambien puede usar“hg revert” para deshacerse de cambios erroneos a un fichero.

Tenga en cuenta que la orden “hg revert” se usa para cambios que no han sido consignados. Cuando hayaconsignado un cambio, si decide que era un error, puede hacer algo todavıa, pero sus opciones pueden estar maslimitadas.

Para obtener informacion acerca de la orden “hg revert” y detalles de como tratar con cambios consignados, veael capıtulo 9.

58

Page 69: Mercurial

Capıtulo 6

Colaborar con otros

Debido a su naturaleza descentralizada, Mercurial no impone polıtica alguna de como deben trabajar los gruposde personas. Sin embargo, si el control distribuido de versiones es nuevo para usted, es bueno tener herramientas yejemplos a la mano al pensar en posibles modelos de flujo de trabajo.

6.1. La interfaz web de MercurialMercurial tiene una poderosa interfaz web que provee bastantes capacidades utiles.Para uso interactivo, la interfaz le permite visualizar uno o varios repositorios. Puede ver el historial de un reposi-

torio, examinar cada cambio (comentarios y diferencias), y ver los contenidos de cada directorio y fichero.Adicionalmente la interfaz provee notificaciones RSS de los cambios del repositorio. Esto le permite “subscribirse”a

un repositorio usando su herramienta de lectura de notificaciones favorita, y ser notificado automaticamente de la ac-tividad en el repositorio tan pronto como sucede. Me gusta mucho mas este modelo que el estar suscrito a una lista decorreo a la cual se envıan las notificaciones, dado que no requiere configuracion adicional de parte de quien sea queesta administrando el repositorio.

La interfaz web tambien permite a los usuarios remotos clonar repositorios, jalar cambios, y (cuando el servidoresta configurado para permitirlo) empujar cambios al mismo. El protocolo de entunelamiento HTTP de Mercurialcomprime datos agresivamente, de forma que trabaja eficientemente incluso en conexiones de red con poco ancho debanda.

La forma mas sencilla de empezar a trabajar con la interfaz web es usar su navegador para visitar un repositorioexistente, como por ejemplo el repositorio principal de Mercurial en http://www.selenic.com/repo/hg?style=gitweb.

Si esta interesado en proveer una interfaz web a sus propios repositorios, Mercurial ofrece dos formas de hacerlo.La primera es usando la orden “hg serve”, que esta enfocada a servir “de forma liviana” y por intervalos cortos. Paramas detalles de como usar esta orden vea la seccion 6.4 mas adelante. Si tiene un repositorio que desea hacer disponiblede forma permanente, Mercurial tiene soporte embebido para el estandar CGI (Common Gategay Interface), que esmanejado por todos los servidores web comunes. Vea la seccion 6.6 para los detalles de la configuracion a traves deCGI.

6.2. Modelos de colaboracionCon una herramienta adecuadamente flexible, tomar decisiones acerca del flujo de trabajo es mucho mas un reto

de ingenierıa social que un problema tecnico. Mercurial impone pocas limitaciones sobre como puede estructurar elflujo de trabajo en un proyecto, ası que depende de usted y de su grupo definir y trabajar con un modelo que se ajustea sus necesidades particulares.

59

Page 70: Mercurial

6.2.1. Factores a tener en cuentaEl aspecto mas importante que usted debe considerar de cualquier modelo es que tan bien se ajusta a las necesidades

y capacidades de la gente que lo usara. Esto puede parecer auto-evidente; aun ası, usted no puede permitirse olvidarloni por un momento.

Una vez definı un modelo de flujo de trabajo que parecıa ser perfectamente adecuado para mı, pero que causo unacantidad considerable de consternacion y friccion dentro de mi equipo de desarrollo. A pesar de mis intentos de ex-plicar porque necesitabamos un conjunto complejo de ramas, y de como debıan fluir los cambios entre ellas, algunosmiembros del equipo se molestaron. Aunque ellos eran personas inteligentes, no querıan prestar atencion a las lim-itaciones bajo las cuales estabamos operando, o comparar las consecuencias de esas limitaciones con los detalles delmodelo que yo estaba proponiendo.

No esconda bajo la alfombra los problemas sociales o tecnicos que usted pueda preveer. Sin importar el esquemaque usted use, debe hacer planes para enfrentar posibles errores o problemas. Considere anadir procedimientos autom-atizados para prevenir, o recuperarse rapidamente de, los problemas que usted pueda anticipar. A manera de ejemplo,si usted planea tener una rama en la vayan los cambios que no esten listos para produccion, harıa bien en pensar en laposibilidad de que alguien fusione accidentalmente dichos cambios en una rama para publicacion. Usted podrıa evitareste problema en particular escribiendo un gancho que evite que se fusionen cambios desde ramas inapropiadas.

6.2.2. Anarquıa informalNo sugerirıa un enfoque de “todo vale” como algo sostenible, pero es un modelo que es facil de entender, y que

funciona perfectamente bien en unas cuantas situaciones inusuales.Como un ejemplo, muchos proyectos tienen un grupo informal de colaboradores que rara vez se reunen fısica-

mente. A algunos grupos les gusta evitar el aislamiento de trabajar a distancia organizando “sprints” ocasionales. Enun sprint, una cantidad de gente se reune en un mismo sitio (el cuarto de conferencias de una empresa, el cuarto dereuniones de un hotel, ese tipo de lugares) y pasar varios dıas mas o menos encerrados allı, trabajando intensamenteen un punado de proyectos.

Un sprint es el lugar perfecto para usar el comando “hg serve”, ya que “hg serve” no requiere una infraestruc-tura especial de servidores. Usted puede empezar a trabajar con “hg serve” en momentos, leyendo la seccion 6.4 masabajo. Luego simplemente dıgale a las personas cerca suyo que usted esta ejecutando un servidor, envıeles la URL atraves de un mensaje instantaneo, y tendra de inmediato una forma rapida de trabajar juntos. Ellos pueden escribir suURL en un navegador web y revisar sus cambios rapidamente; o ellos pueden jalar un arreglo de fallo que usted hayahecho y verificarlo; o pueden clonar una rama que contiene una nueva caracterıstica y probarla.

Lo bueno, y lo malo, de hacer cosas de manera ad hoc como aquı, es que solo las personas que saben de suscambios, y donde estan, pueden verlos. Un enfoque tan informal sencillamente no puede escalarse mas alla de unpunado de personas, porque cada individuo tiene que saber de n repositorios diferentes de los cuales jalar.

6.2.3. Un repositorio central unicoPara proyectos pequenos migrando desde una herramienta centralizada de control de revisiones, tal vez la forma

mas facil de empezar es hacer que los cambios vayan a traves de un repositorio central compartido. Este es tambien el“bloque base” para esquemas mas ambiciosos de flujo de trabajo.

Los colaboradores empiezan clonando una copia de este repositorio. Ellos pueden jalar cambios de el siempre quelo necesiten, y algunos (o tal vez todos los) desarrolladores tienen permiso para empujar cambios de vuelta cuandoesten listos para que los demas los vean.

Bajo este modelo, para los usuarios tiene sentido jalar cambios directamente entre ellos, sin ir a traves del reposi-torio central. Considere un caso en el que yo tengo un arreglo tentativo de fallo, pero me preocupa que al publicarlo enel repositorio central rompa los arboles de todos los demas cuando lo jalen. Para reducir el potencial de dano, puedopedirle a usted que clone mi repositorio en un repositorio personal suyo y lo pruebe. Esto nos permite aplazar estecambio potencialmente inseguro hasta que haya tenido algo de pruebas.

En este tipo de escenario, la gente usualmente utiliza el protocolo ssh para empujar cambios de manera seguraal repositorio central, como se documenta en la seccion 6.5. Tambien es usual publicar una copia de solo lectura del

60

Page 71: Mercurial

repositorio sobre HTTP usando CGI, como en la seccion 6.6. Publicar a traves de HTTP satisface las necesidades deaquellos que no tienen permiso para empujar, y de aquellos que desean usar navegadores web para explorar el historialdel repositorio.

6.2.4. Trabajo con muchas ramasLos proyectos de cierta talla tienden de manera natural a progresar de forma simultanea en varios frentes. En el

caso del software, es comun que un proyecto tenga versiones periodicas oficiales. Una version puede entrar a “modomantenimiento” por un tiempo despues de su primera publicacion; las versiones de mantenimiento tienden a contenersolamente arreglos de fallos, no nuevas caracterısticas. En paralelo con las versiones de mantenimiento, puede haberuna o varias versiones futuras en desarrollo. La gente usa normalmente la palabra “rama” para referirse a una de lasdirecciones ligeramente distintas en las cuales avanza el desarrollo.

Mercurial esta especialmente preparado para administrar un buen numero de ramas simultaneas pero no identicas.Cada “direccion de desarrollo” puede vivir en su propio repositorio central, y usted puede mezclar los cambios de unaa otra cuando sea necesario. Dado que los repositorios son independientes entre sı, los cambios inestables de una ramade desarrollo nunca afectaran una rama estable a menos que alguien mezcle explıcitamente los cambios.

A continuacion hay un ejemplo de como podrıa hacerse esto en la practica. Digamos que tiene una “rama principal”en un servidor central.

1 $ hg init main2 $ cd main3 $ echo ’This is a boring feature.’ > myfile4 $ hg commit -A -m ’We have reached an important milestone!’5 adding myfile

Alguien lo clona, hace cambios locales, los prueba, y los empuja de vuelta.Una vez que la rama principal alcanza un hito de proyecto se puede usar la orden “hg tag” para dar un nombre

permanente a la revision del hito.

1 $ hg tag v1.02 $ hg tip3 changeset: 1:471e846844054 tag: tip5 user: Bryan O’Sullivan <[email protected]>6 date: Tue Feb 10 18:23:16 2009 +00007 summary: Added tag v1.0 for changeset a1e37e7a184f8

9 $ hg tags10 tip 1:471e8468440511 v1.0 0:a1e37e7a184f

Digamos que en la rama principal ocurre mas desarrollo.

1 $ cd ../main2 $ echo ’This is exciting and new!’ >> myfile3 $ hg commit -m ’Add a new feature’4 $ cat myfile5 This is a boring feature.6 This is exciting and new!

Cuando se usa la etiqueta con que se identifico la version, la gente que clone el repositorio en el futuro puede usar“hg update” para obtener una copia del directorio de trabajo igual a cuando se creo la etiqueta de la revision que seconsigno.

61

Page 72: Mercurial

1 $ cd ..2 $ hg clone -U main main-old3 $ cd main-old4 $ hg update v1.05 1 files updated, 0 files merged, 0 files removed, 0 files unresolved6 $ cat myfile7 This is a boring feature.

Adicionalmente, justo despues de que la rama principal se etiquete, alguien puede clonarla en el servidor a unanueva rama “estable”, tambien en el servidor.

1 $ cd ..2 $ hg clone -rv1.0 main stable3 requesting all changes4 adding changesets5 adding manifests6 adding file changes7 added 1 changesets with 1 changes to 1 files8 updating working directory9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Alguien que requiera hacer un cambio en la rama estable puede clonar ese repositorio, hacer sus cambios, consignar-los, y empujarlos de vuelta.

1 $ hg clone stable stable-fix2 updating working directory3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved4 $ cd stable-fix5 $ echo ’This is a fix to a boring feature.’ > myfile6 $ hg commit -m ’Fix a bug’7 $ hg push8 pushing to /tmp/branchingOpMbpu/stable9 searching for changes

10 adding changesets11 adding manifests12 adding file changes13 added 1 changesets with 1 changes to 1 files

Puesto que los repositorios de Mercurial son independientes, y que Mercurial no mueve los cambios de un lado aotro automaticamente, las ramas estable y principal estan aisladas la una de la otra. Los cambios que haga en la ramaprincipal no se “filtran” a la rama estable y viceversa.

Es usual que los arreglos de fallos de la rama estable deban hacerse en la rama principal tambien. En lugar dereescribir el arreglo del fallo en la rama principal, usted puede jalar y mezclar los cambios de la rama estable a laprincipal, y Mercurial traera tales arreglos por usted.

1 $ cd ../main2 $ hg pull ../stable3 pulling from ../stable4 searching for changes5 adding changesets6 adding manifests

62

Page 73: Mercurial

7 adding file changes8 added 1 changesets with 1 changes to 1 files (+1 heads)9 (run ’hg heads’ to see heads, ’hg merge’ to merge)

10 $ hg merge11 merging myfile12 0 files updated, 1 files merged, 0 files removed, 0 files unresolved13 (branch merge, don’t forget to commit)14 $ hg commit -m ’Bring in bugfix from stable branch’15 $ cat myfile16 This is a fix to a boring feature.17 This is exciting and new!

La rama principal aun contendra los cambios que no estan en la estable y contendra ademas todos los arreglos de fallosde la rama estable. La rama estable permanece incolume a tales cambios.

6.2.5. Ramas de caracterısticasEn proyectos grandes, una forma efectiva de administrar los cambios es dividir el equipo en grupos mas pequenos.

Cada grupo tiene una rama compartida, clonada de una rama “principal” que conforma el proyecto completo. Aquellosque trabajan en ramas individuales usualmente estan aislados de los desarrollos de otras ramas.

maestro

cripto sistemadearchivos ipc memoria red seguridad

Figura 6.1: Ramas de Caracterısticas

Cuando se considera que una caracterıstica particular esta en buena forma, alguien de ese equipo de caracterısticasjala y fusiona de la rama principal a la rama de caracterısticas y empuja de vuelta a la rama principal.

6.2.6. El tren de publicacionAlgunos proyectos se organizan al estilo“tren”: una version se planifica para ser liberada cada cierto tiempo, y las

caracterısticas que esten listas cuando ha llegado el “tren”, se incorporan.Este modelo tiene cierta similitud a las ramas de caracterısticas. La diferencia es que cuando una caracterıstica

pierde el tren, alguien en el equipo de caracterısticas jala y fusiona los cambios que se fueron en la version liberadahacia la rama de caracterıstica, y el trabajo continua sobre lo fusionado para que la caracterıstica logre estar en laproxima version.

6.2.7. El modelo del kernel LinuxEl desarrollo del kernel Linux tiene una estructura jerarquica bastante horizontal, rodeada de una nube de caos

aparente. Dado que la mayorıa de los desarrolladores de Linux usan git, una herramienta de control de versionesdistribuida con capacidades similares a Mercurial, resulta de utilidad describir la forma en que el trabajo fluye en talambiente; si le gustan estas ideas, la aproximacion se traduce bien de Git a Mercurial.

En el centro de la comunidad esta Linus Torvalds, el creador de Linux. El publica un unico repositorio que esconsiderado el arbol “oficial” actual por la comunidad completa de desarrolladores. Cualquiera puede clonar el arbolde Linus, pero el es muy selectivo respecto a los arboles de los cuales jala.

63

Page 74: Mercurial

Linus tiene varios “lugartenientes de confianza”. Como regla, el jala todos los cambios que ellos publican, en lamayorıa de los casos sin siquiera revisarlos. Algunos de sus lugartenientes generalmente aceptan ser los “mantene-dores”, responsables de subsistemas especıficos dentro del kernel. Si un hacker cualquiera desea hacer un cambio a unsubsistema y busca que termine en el arbol de Linus, debe encontrar quien es el mantenedor del subsistema y solici-tarle que tome su cambio. Si el mantenedor revisa los cambios y esta de acuerdo en tomarlos, estos pasaran al arbol deLinus a su debido tiempo.

Cada lugarteniente tiene su forma particular de revisar, aceptar y publicar los cambios; y para decidir cuandopresentarlos a Linus. Adicionalmente existen varias ramas conocidas que mucha gente usa para propositos distintos.Por ejemplo, algunas personas mantienen repositorios “estables” de versiones anteriores del kernel, a los cuales aplicanarreglos de fallos crıticos cuando es necesario. Algunos mantenedores publican varios arboles: uno para cambiosexperimentales; uno para cambios que van a ofrecer al mantenedor principal; y ası sucesivamente. Otros publican soloun arbol.

Este modelo tiene dos caracterısticas notables. La primera es que es de “jalar exclusivamente”. Usted debe solicitar,convencer o incluso rogar a otro desarrollador para que tome sus cambios, porque casi no hay arboles en los cualesmas de una persona pueda publicar, y no hay forma de publicar cambios en un arbol que alguien mas controla.

La segunda es que esta basado en reputacion y meritocracia. Si usted es un desconocido, Linus probablementeignorara sus cambios, sin siquiera responderle. Pero el mantenedor de un subsistema probablemente los revisara, ylos acogera en caso de que aprueben su criterio de aplicabilidad. A medida que usted ofrezca “mejores” cambios aun mantenedor, habra mas posibilidad de que se confıe en su juicio y se acepten los cambios. Si usted es reconocidoy mantiene una rama durante bastante tiempo para algo que Linus no ha aceptado, personas con intereses similarespueden jalar sus cambios regularmente para estar al dıa con su trabajo.

La reputacion y meritocracia no necesariamente son transversales entre “personas” de diferentes subsistemas. Siusted es un hacker respetado pero especializado en almacenamiento, y trata de arreglar un fallo de redes, tal cambiopuede recibir un nivel de escrutinio de un mantenedor de redes comparable con el que se le harıa a un completoextrano.

Personas que vienen de proyectos con un ordenamiento distinto, sienten que el proceso comparativamente caoticodel Kernel Linux es completamente lunatico. Es objeto de los caprichos individuales; la gente desecha cambios cuandolo desea; y el ritmo de desarrollo es alucinante. A pesar de eso Linux es una pieza de software exitosa y bien reconocida.

6.2.8. Solamente jalar frente a colaboracion publicaUna fuente perpetua de discusiones en la comunidad de codigo abierto yace en la afirmacion de que el modelo de

desarrollo en el cual la gente solamente jala cambios de otros “es mejor que” uno en el cual muchas personas puedenpublicar cambios a un repositorio compartido.

Tıpicamente los partidarios del modelo de publicar usan las herramientas que se apegan a este modelo. Si ustedusa una herramienta centralizada de control de versiones como Subversion, no hay forma de elegir que modelo va ausar: la herramienta le ofrece publicacion compartida, y si desea hacer cualquier otra cosa, va a tener que tomar unaaproximacion artificial (tal como aplicar parches a mano).

Una buena herramienta de control de versiones distribuida, tal como Mercurial, soportara los dos modelos. Ustedy sus colaboradores pueden estructurar como trabajaran juntos basados en sus propias necesidades y preferencias, sintener que llevar a cabo las peripecias que la herramienta les obligue a hacer.

6.2.9. Cuando la colaboracion encuentra la administracion ramificadaUna vez que usted y su equipo configuren algunos repositorios compartidos y comiencen a propagar cambios

entre sus repositorios locales y compartidos, usted comenzara a encarar un reto relacionado, pero un poco distinto:administrar las direcciones en las cuales su equipo puede moverse. A pesar de que esta ıntimamente ligado acercade como interactua su equipo, este es un tema lo suficientemente denso para ameritar un tratamiento aparte, en elcapıtulo 8.

64

Page 75: Mercurial

6.3. Aspectos tecnicos de la colaboracionLo que resta del capıtulo lo dedicamos a las cuestiones de servir datos a sus colaboradores.

6.4. Compartir informalmente con “hg serve”La orden “hg serve” de Mercurial satisface de forma espectacular las necesidades de un grupo pequeno, acoplado

y agil. Tambien sirve como una demostracion de como se siente usar los comandos usando la red.Ejecute “hg serve” dentro de un repositorio, y en pocos segundos se iniciara un servidor HTTP especializado;

aceptara conexiones desde cualquier cliente y servira datos de este repositorio mientras lo mantenga funcionando.Todo el que conozca la URL del servidor que usted ha iniciado, y que pueda comunicarse con su computador a travesde la red, puede usar un navegador web o Mercurial para leer datos del repositorio. Un URL para una instancia de “hgserve” ejecutandose en un portatil deberıa lucir similar a http://my-laptop.local:8000/.

La orden “hg serve” no es un servidor web de proposito general. Solamente puede hacer dos cosas:

Permitir la visualizacion del historial del repositorio que esta sirviendo, desde un navegador web.

Hablar el protocolo de conexion de Mercurial, para que la gente pueda hacer “hg clone” o “hg pull” (jalar)cambios desde tal repositorio.

En particular, “hg serve” no permitira que los usuarios remotos modifiquen su repositorio. Es de tipo solo lectura.Si esta familiarizandose con Mercurial, no hay nada que le impida usar “hg serve” para servir un repositorio en

su propio computador, y posteriormente usar ordenes como “hg clone”, “hg incoming”, para comunicarse con elservidor como si el repositorio estuviera alojado remotamente. Esto puede ayudarle a adecuarse rapidamente a usarcomandos en repositorios alojados en la red.

6.4.1. Cuestiones adicionales para tener en cuentaDado que permite lectura sin autenticacion a todos sus clientes, deberıa usar “hg serve” exclusivamente en am-

bientes en los cuales esto no sea importante, o en los cuales tenga control completo acerca de quien puede acceder asu red y jalar cambios de su repositorio.

La orden “hg serve” no tiene conocimiento acerca de programas cortafuegos que puedan estar instalados en susistema o en su red. No puede detectar o controlar sus cortafuegos. Si otras personas no pueden acceder a su instanciade “hg serve”, lo siguiente que deberıa hacer (despues de asegurarse que tienen el URL correcto) es verificar suconfiguracion de cortafuegos.

De forma predeterminada, “hg serve” escucha conexiones entrantes en el puerto 8000. Si otro proceso esta es-cuchando en tal puerto, usted puede especificar un puerto distinto para escuchar con la opcion -p.

Normalmente, cuando se inicia “hg serve”, no muestra ningun mensaje, lo cual puede ser algo desconcertante.Si desea confirmar que en efecto esta ejecutandose correctamente, y averiguar que URL deberıa enviar a sus colabo-radores, inıcielo con la opcion -v.

6.5. Uso del protocolo Secure Shell (ssh)Usted puede publicar y jalar cambios en la red de forma segura usando el protocolo Secure Shell (ssh). Para usarlo

exitosamente, tendra que hacer algo de configuracion a nivel de cliente o de servidor.Si no esta familiarizado con ssh, es un protocolo de red que le permite comunicarse de manera segura con otro

computador. Para usarlo con Mercurial, debera crear una o mas cuentas de usuario en un servidor de forma tal que losusuarios remotos puedan entrar y ejecutar ordenes.

(Si ssh le es familiar, probablemente encontrara elemental una porcion del material a continuacion.)

65

Page 76: Mercurial

6.5.1. Como leer y escribir URLs de sshLos URLs de ssh tienden a lucir de la siguiente forma:

1 ssh://[email protected]:22/hg/hgbook

1. La parte “ssh://” le indica a Mercurial que use el protocolo ssh.

2. El componente “bos@” indica el nombre del usuario que esta entrando al servidor. Puede omitirlo si el usuarioremoto coincide con el usuario local.

3. “hg.serpentine.com” es el nombre del servidor al cual se desea entrar.

4. El “:22” identifica el numero del puerto en el servidor al cual se conectara. El predeterminado es el 22, ası quesolamente necesitara especificar esa porcion si no esta usando el puerto 22.

5. La ultima porcion del URL es la ruta local del repositorio en el servidor.

El componente de la ruta del URL para ssh es una fuente de confusion, puesto que no hay una forma estandarpara que las herramientas puedan interpretarlo. Algunos programas se comportan de manera distinta a otros cuandomanipulan estas rutas. No es la situacion ideal, pero es muy poco probable que vaya a cambiar. Por favor lea losparrafos siguientes cuidadosamente.

Mercurial trata la ruta al repositorio en el servidor como relativa al directorio personal del usuario remoto. Porejemplo, si el usuario foo en el servidor tiene el directorio personal /home/foo, entonces un URL ssh que tenga comoruta a bar realmente se refiere al directorio /home/foo/bar.

Si desea especificar una ruta relativa a otro directorio de usuario, puede usar una ruta que comience con unavirgulilla, seguida del nombre del usuario (llamemosle otrousuario), ası

1 ssh://server/˜otheruser/hg/repo

Y si realmente desea especifica una ruta absoluta en el servidor, comience con el componente de la ruta con dosbarras, como en el siguiente ejemplo:

1 ssh://server//absolute/path

6.5.2. Encontrar un cliente ssh para su sistemaCasi todos los sistemas tipo Unix vienen con OpenSSH preinstalado. Si usted esta usando un sistema de estos, eje-

cute which ssh para identificar donde esta instalada la orden ssh (usualmente estara en /usr/bin). Si por casualidadno esta presente, vea la documentacion de sus sistema para averiguar como instalarlo.

En Windows, primero tendra que descargar un cliente adecuado. Hay dos alternativas:

El excelente paquete PuTTY [Tat] de Simon Tatham, que ofrece un conjunto completo de ordenes de clientessh.

Si tiene alta tolerancia al dolor, puede usar el porte de Cygwin para OpenSSH.

En cualquier caso, tendra que editar su fichero Mercurial.ini para indicarle a Mercurial donde encontrar la ordenreal del cliente. Por ejemplo, si esta usando PuTTY, tendra que usar la orden plink como un cliente de lınea decomandos para ssh.

1 [ui]2 ssh = C:/ruta/a/plink.exe -ssh -i "C:/ruta/a/mi/llave/privada"

Nota: La ruta a plink no deberıa contener espacios o caracteres en blanco, oMercurial no podra encontrarlo correctamente (por lo tanto, probablemente no serıabuena idea colocarlo en C:\Program Filesa

aN. del T. Ubicarlo en C:\Archivos de Programa tampoco lo serıa.

66

Page 77: Mercurial

6.5.3. Generar un par de llavesPara evitar la necesidad de teclear una clave de forma repetitiva cada vez que necesita usar el cliente, recomiendo

generar un par de llaves. En un sistema tipo Unix, la orden ssh-keygen se encargara de la tarea. En Windows, siesta usando PuTTY, necesitara la orden puttygen.

Cuando genera un par de llaves, se aconseja comedidamente protegerlas con una frase clave. (La unica oportunidaden la cual usted no querrıa hacerlo, es cuando esta usando el protocolo ssh para tareas automatizadas en una red segura.)

No basta con generar un par de llaves. Se requiere adicionar la llave publica al conjunto de llaves autorizadasdel usuario que usted usa para acceder a la maquina remota. Para aquellos servidores que usen OpenSSH (la granmayorıa), significara anadir la llave publica a la lista en el fichero llamado authorized keys en su directorio .ssh.

En sistemas tipo Unix, su llave publica tendra la extension .pub. Si usa puttygen en Windows, puede guardar lallave publica en un fichero de su eleccion, o pegarla desde la ventana en la cual se despliega directamente en el ficheroauthorized keys.

6.5.4. Uso de un agente de autenticacionUn agente de autenticacion es un demonio que almacena frases clave en memoria (olvidara las frases clave si sale

de su sesion y vuelve a entrar). Un cliente ssh notara si el agente esta corriendo, y le solicitara una frase clave. Sino hay un agente de autenticacion corriendo, o el agente no almacena la frase clave necesaria, tendra que teclear sufrase clave cada vez que Mercurial intente comunicarse con un servidor para usted (p.e. cada vez que jale o publiquecambios).

El problema de almacenar frases claves en un agente es que es posible para un atacante bien preparado recuperarel texto plano de su frase clave, en algunos casos incluso si su sistema ya ha sido reiniciado. Es su decision si es unriesgo aceptable. Lo que sı es seguro es que evita reteclear.

En sistemas tipo Unix, el agente se llama ssh-agent, y usualmente se ejecuta automaticamente cuando usted iniciasesion. Tendra que usar la orden ssh-add para anadir frases claves al agente. En Windows, si esta usando PuTTY, laorden pageant actua como el agente. El anade un icono a su barra del sistema que le permitira administrar las frasesclave almacenadas.

6.5.5. Configurar el lado del servidor apropiadamenteDado que puede ser dispendioso configurar ssh si es algo nuevo para usted, hay una variedad de cosas que podrıan ir

mal. Anada a eso Mercurial y hay mucho mas en que pensar. La mayor parte de estos problemas potenciales ocurren enel lado del servidor, no en el cliente. Las buenas noticias es que una vez tiene una configuracion funcional, usualmenteesta continuara trabajando indefinidamente.

Antes de intentar que Mercurial hable con un servidor ssh, es mejor asegurarse de que puede usar la orden normalssh o putty para comunicarse primero con el servidor. Si tiene problemas usando estas ordenes directamente, deseguro Mercurial no funcionara. Pero aun, esto escondera el problema subyacente. Cuando desee revisar un problemarelacionado con ssh y Mercurial, deberıa asegurarse primero que las ordenes de ssh en el lado del cliente funcionanprimero, antes de preocuparse por si existe un problema con Mercurial.

Lo primero para asegurar en el lado del servidor es que usted pueda iniciar sesion desde otra maquina. Si no puedeentrar usando ssh o putty, el mensaje de error que obtenga le puede dar pistas de que ha ido mal. Los problemas mascomunes son los siguientes:

Si obtiene un error de “conexion rehusada”, es posible que no haya un demonio SSH corriendo en el servidor oque no pueda accederse a el debido a la configuracion de cortafuegos.

Si obtiene un error de “no hay ruta hasta el servidor”, puede tener una direccion incorrecta para el servidor o uncortafuegos con bloqueo agresivo que negara la existencia del servidor.

Si obtiene un mensaje de “permiso denegado”, puede que haya tecleado mal el usuario en el servidor, o quehaya tecleado incorrectamente la frase clave de su llave o la clave del usuario remoto.

67

Page 78: Mercurial

En resumen, si tiene problemas al comunicarse con el demonio ssh del servidor, primero asegurese de que se este eje-cutando. En muchos sistemas estara instalado, pero deshabilitado de forma predeterminada. Una vez que haya hechoesto tendra que revisar si el cortafuegos del servidor esta configurado para recibir conexiones entrantes en el puertoen el cual esta escuchando el demonio de ssh (usualmente el 22). No trate de buscar otras posibilidades exoticas oconfiguraciones erradas hasta que haya revisado primero estas dos.

Si esta usando un agente de autenticacion en el lado del cliente para almacenar las frase claves de sus contrasenas,deberıa poder entrar al servidor sin necesidad de que se le soliciten frases claves o contrasenas. Si se le preguntaalguna, a continuacion algunas posibilidades:

Puede haber olvidado usar ssh-add o pageant para guardar la frase clave.

Puede haber almacenado una frase clave para una llave distinta.

Si se le solicita la clave del usuario remoto, hay otras posibilidades que deben revisarse:

O bien el directorio del usuario o su directorio .ssh tiene permisos excesivamente abiertos. Como resultado eldemonio ssh no confiara ni leera su fichero authorized keys. Por ejemplo, un directorio personal o .ssh conpermisos de escritura para grupo mostrara a veces este sıntoma.

El fichero authorized keys del usuario puede tener un problema. Si alguien distinto al usuario es dueno delmismo o puede escribir en el fichero, el demonio ssh no confiara en el y no lo leera.

En un mundo ideal, deberıa poder ejecutar la siguiente orden exitosamente, y deberıa imprimir exactamente unalınea de salida, la fecha y hora actual.

1 ssh miservidor date

Si en su servidor tiene guion que se ejecuta a la entrada e imprime letreros o cualquier otra cosa, incluso cuando seejecutan ordenes no interactivas como esta, deberıa arreglarlo antes de continuar, de forma que solamente imprima algosi se ejecuta interactivamente. De otra forma estos letreros al menos llenaran la salida de Mercurial. Incluso podrıancausar problemas potenciales cuando se ejecuten ordenes de forma remota. Mercurial intenta detectar e ignorar losletreros en sesiones no interactivas de ssh, pero no es a prueba de tontos. (Si edita sus guiones de entrada en elservidor, la forma usual de ver si un guion de lınea de comandos se ejecuta en un interprete interactivo es verificar elcodigo de retorno de la orden tty -s.)

Cuando verifique que el venerado ssh funciona en su servidor, el siguiente paso es asegurarse de que Mercurialcorre en el servidor. La siguiente orden deberıa ejecutarse satisfactoriamente:

1 ssh miservidor hg version

Si ve un mensaje de error en lugar de la salida usual de “hg version”, sera porque Mercurial no fue instalado en/usr/bin. Si este es el caso, no se preocupe; no necesita hacerlo. Pero deberıa revisar los posibles problemas:

¿Esta instalado Mercurial en el servidor? ¡Se que suena trivial pero es mejor revisar!

Tal vez la ruta de busqueda de la interfaz de ordenes (normalmente vıa la variable de entorno PATH) simplementeesta mal configurada.

Puede ser que su variable de ambiente PATH solamente apunte al lugar en el cual esta el ejecutable hg si la sesionde entrada es interactiva. Esto puede suceder si establece la ruta en el guion de lınea de comandos de entradaincorrecto. Consulte la documentacion de su interprete de ordenes.

La variable de ambiente PYTHONPATH puede requerir contener la ruta a los modulos de Mercurial en Python.Puede que ni siquiera esta establecida; podrıa estar incorrecta; o puede ser que se establezca unicamente cuandohay entradas interactivas.

Si puede ejecutar “hg version” sobre una conexion ssh, ¡felicitaciones! Ha logrado la interaccion entre el clientey el servidor. Ahora deberıa poder acceder a los repositorios de Mercurial que tiene el usuario en el servidor. Si tieneproblemas con Mercurial y ssh en este punto, intente usar la opcion --debug para tener informacion mas clara de loque esta sucediendo.

68

Page 79: Mercurial

6.5.6. Compresion con sshMercurial no comprime datos cuando usa el protocolo ssh, dado que el protocolo puede comprimir datos transpar-

entemente. Pero el comportamiento predeterminado del cliente ssh es no utilizar compresion.Sobre cualquier red distinta a una LAN rapida (incluso con una red inalambrica), hacer uso de compresion puede

mejorar el rendimiento de las operaciones de Mercurial que involucren la red. Por ejemplo, sobre WAN, alguien hamedido que la compresion reduce la cantidad de tiempo requerido para clonar un repositorio particularmente grandede 51 minutos a 17 minutos.

Tanto ssh como plink aceptan la opcion -C para activar la compresion. Puede editar facilmente su hgrc parahabilitar la compresion para todos los usos de Mercurial con el protocolo ssh.

1 [ui]2 ssh = ssh -C

Si usa ssh, puede reconfigurarlo para que siempre use compresion cuando se comunique con su servidor. Parahacerlo, edite su fichero .ssh/config (que puede no existir aun), de la siguiente forma:

1 Host hg2 Compression yes3 HostName hg.ejemplo.com

Que define un alias, hg. Cuando lo usa con la orden ssh o con una URL de Mercurial con protocolo ssh, hara quessh se conecte a hg.ejemplo.com usando compresion. Esto le brindara un nombre mas corto para teclear, junto concompresion, los cuales por derecho propio son buenos.

6.6. Servir sobre HTTP usando CGIDependiendo de que tan ambicioso sea, configurar la interfaz CGI de Mercurial puede tomar desde unos minutos

hasta varias horas.Comenzaremos con el ejemplo mas sencillo, y nos dirigiremos hacia configuraciones mas complejas. Incluso para

el caso mas basico necesitara leer y modificar la configuracion de su servidor web.Nota: Configurar un servidor web es una actividad compleja, engorrosa y alta-mente dependiente del sistema. De ninguna manera podremos cubrir todos los ca-sos posibles con los cuales pueda encontrarse. Use su discrecion y juicio respectoa las siguientes secciones. Preparese para cometer muchas equivocaciones, y em-plear bastante tiempo leyendo las bitacoras de error de su servidor.

6.6.1. Lista de chequeo de la configuracion del servidor webAntes de continuar, tomese un tiempo para revisar ciertos aspectos de la configuracion de su sistema:

1. ¿Tiene un servidor web? Mac OS X viene con Apache, pero otros sistemas pueden no tener un servidor webinstalado.

2. Si tiene un servidor web instalado, ¿Esta ejecutandose? En la mayorıa de sistemas, aunque este presente, puedeno estar habilitado de forma predeterminada.

3. ¿Esta configurado su servidor para permitir ejecutar programas CGI en el directorio donde planea hacerlo? Casitodos los servidores de forma predeterminada deshabilitan explıcitamente la habilidad de ejecutar programasCGI.

69

Page 80: Mercurial

Si no tiene un servidor web instalado, y no tiene experiencia configurando Apache, deberıa considerar usar elservidor web lighttpd en lugar de Apache. Apache tiene una reputacion bien ganada por su configuracion barrocay confusa. A pesar de que lighttpd tiene menos caracterısticas que Apache en ciertas areas, muchas de ellas no sonrelevantes para servir repositorios de Mercurial. Y definitivamente es mucho mas sencillo comenzar con lighttpdque con Apache.

6.6.2. Configuracion basica de CGIEn sistemas tipo Unix es comun que los usuarios tengan un subdirectorio con un nombre como public html en

su directorio personal, desde el cual pueden servir paginas web. Un fichero llamado foo en este directorio sera visibleen una URL de la forma http://www.example.com/˜username/foo.

Para comenzar, encuentre el guion hgweb.cgi que deberıa estar presente en su instalacion de Mercurial. Si nopuede encontrar rapidamente una copia local en su sistema, puede descargarlo del repositorio principal de Mercurialen http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi.

Tendra que copiar este guion en su directorio public html, y asegurarse que sea ejecutable.

1 cp .../hgweb.cgi ˜/public_html2 chmod 755 ˜/public_html/hgweb.cgi

El argumento 755 de la orden chmod es un poco mas general que hacerlo ejecutable: asegura que el guion sea ejecutablepor cualquiera, y que el “grupo” y los “otros” no tengan permiso de escritura. Si dejara los permisos de escrituraabiertos, el subsistema suexec de Apache probablemente se negarıa a ejecutar el guion. De hecho, suexec tambieninsiste en que el directorio en el cual reside el guion no tenga permiso de escritura para otros.

1 chmod 755 ˜/public_html

¿Que podrıa resultar mal?

Cuando haya ubicado el CGI en el sitio correspondiente, abra un navegador e intente visitar el URL http://myhostname/˜myuser/hgweb.cgi, sin dejarse abatir por un error. Hay una alta probabilidad de que esta primeravisita al URL sea fallida, y hay muchas razones posibles para este comportamiento. De hecho, podrıa toparse con cadauno de los errores que describimos a continuacion, ası que no deje de leerlos cuidadosamente. A continuacion presentolos problemas que yo tuve en un sistema con Fedora 7, con una instalacion nueva de Apache, y una cuenta de usuarioque cree especıficamente para desarrollar este ejercicio.

Su servidor web puede tener directorios por usuario deshabilitados. Si usa Apache, busque el fichero de con-figuracion que contenga la directiva UserDir. Si no esta presente en sitio alguno, los directorios por usuario estandeshabilitados. Si la hay, pero su valor es disabled, los directorios por usuario estaran deshabilitados. En caso con-trario, la directiva UserDir tendra el nombre del subdirectorio bajo el cual Apache mirara en el directorio de cadausuario, por ejemplo public html.

Los permisos de sus ficheros pueden ser demasiado restrictivos. El servidor web debe poder recorrer su directoriopersonal y los directorios que esten bajo public html, ademas de tener permiso para leer aquellos que esten adentro.A continuacion una receta rapida para hacer que sus permisos esten acordes con las necesidades basicas.

1 chmod 755 ˜2 find ˜/public_html -type d -print0 | xargs -0r chmod 7553 find ˜/public_html -type f -print0 | xargs -0r chmod 644

Otra posibilidad con los permisos es que obtenga una ventana completamente en blanco cuando trata de cargarel guion. En este caso, es posible que los permisos que tiene son demasiado permisivos. El subsistema suexec deApache no ejecutara un guion que tenga permisos de escritura para el grupo o el planeta, por ejemplo.

Su servidor web puede estar configurado para evitar la ejecucion de programas CGI en los directorios de usuario.A continuacion presento una configuracion predeterminada por usuario en mi sistema Fedora.

70

Page 81: Mercurial

1 <Directory /home/*/public_html>2 AllowOverride FileInfo AuthConfig Limit3 Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec4 <Limit GET POST OPTIONS>5 Order allow,deny6 Allow from all7 </Limit>8 <LimitExcept GET POST OPTIONS>9 Order deny,allow

10 Deny from all11 </LimitExcept>12 </Directory>

Si encuentra un grupo de instrucciones de Directory similares en su configuracion de Apache, la directiva a revisares Options. Adicione ExecCGI al final de esta lista en caso de que haga falta y reinicie su servidor web.

Si resulta que Apache le muestra el texto del guion CGI en lugar de ejecutarlo, necesitara o bien descomentar (sise encuentra presente) o adicionar una directiva como la siguiente:

1 AddHandler cgi-script .cgi

Otra posibilidad es que observe una traza de Python en colores informando que no puede importar un modulorelacionado con mercurial. ¡Esto es un gran progreso! El servidor es capaz de ejecutar su guion CGI. Este errorsolamente ocurrira si esta ejecutando una instalacion privada de Mercurial en lugar de una instalacion para todo elsistema. Recuerde que el servidor que ejecuta el programa CGI no cuenta con variables de entorno de las cuales ustedsı dispone en una sesion interactiva. Si este error le ocurre, edite su copia de hgweb.cgi y siga las indicaciones dentrodel mismo para establecer de forma adecuada su variable de entorno PYTHONPATH.

Finalmente, puede que se encuentre con otra traza a todo color de Python al visitar el URL: esta seguramentese referira a que no puede encontrar /path/to/repository1. Edite su guion hgweb.cgi y reemplace la cadena/path/to/repository con la ruta completa al repositorio que desea servir.

En este punto, cuando trate de recargar la pagina, debera tener una linda vista HTML del historial de su repositorio.¡Uff!

Configuracion de lighttpd

En mi intencion de ser exhaustivo, intente configurar lighttpd, un servidor web con creciente aceptacion, paraservir los repositorios de la misma forma en que lo describı anteriormente con Apache. Ya supere los problemas quemostre con Apache, muchos de los cuales no son especıficos del servidor. Por lo tanto estaba seguro de que mispermisos para directorios y ficheros eran correctos y que mi guion hgweb.cgi tambien lo era.

Dado que ya Apache estaba en ejecucion correctamente, lograr que lighttpd sirviera mi repositorio fue rapido (enotras palabras, si esta tratando de usar lighttpd, debera leer la seccion de Apache). Primero tuve que editar la seccionmod access para habilitar mod cgi y mod userdir, los cuales estaban inhabilitados en mi instalacion predeterminada.Anadı posteriormente unas lıneas al final del fichero de configuracion, para hacer lo propio con los modulos.

1 userdir.path = "public_html"2 cgi.assign = ( ".cgi" => "" )

Hecho esto, lighttpd funciono inmediatamente para mı. Si hubiera configurado lighttpd antes que Apache, habrıatenido casi los mismos problemas a nivel de configuracion del sistema que con Apache. De todas maneras, consideroque lighttpd es bastante mas sencillo de configurar que Apache, a pesar de haber usado Apache por lo menos poruna decada, y de que esta fue mi primera experiencia con lighttpd.

1N. del T. Ruta al repositorio.

71

Page 82: Mercurial

6.6.3. Compartir varios repositorios con un guion CGIEl guion hgweb.cgi permite publicar unicamente un repositorio, una restriccion frustrante. Si desea publicar mas

de uno sin complicarse con varias copias del mismo guion, cada una con un nombre distinto, resulta mucho mejor usarel guion hgwebdir.cgi.

El procedimiento para configurar hgwebdir.cgi tiene una porcion adicional respecto al trabajo requerido conhgweb.cgi. Primero se debe obtener una copia del guion. Si no tiene una a mano, puede descargarla del ftp principaldel repositorio de Mercurial en http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi.

Necesitara una copia del guion en su directorio public html, y debe asegurarse de que tenga permisos de ejecu-cion.

1 cp .../hgwebdir.cgi ˜/public_html2 chmod 755 ˜/public_html ˜/public_html/hgwebdir.cgi

Con la configuracion basica, intente visitar en su navegador http://myhostname/˜myuser/hgwebdir.cgi. Deberıamostrar una lista vacıa de repositorios. Si obtiene una ventana en blanco o un mensaje de error, verifique la lista deproblemas potenciales en la seccion 6.6.2.

El guion hgwebdir.cgi se apoya en un fichero externo de configuracion. De forma predeterminada, busca unfichero llamado hgweb.config en el mismo directorio. Tendra que crear el fichero, y permitir lectura de todo elmundo. El formato del fichero es similar a un fichero “ini” de Windows, que puede ser interpretado por el moduloConfigParser [Pyt] de Python.

La forma mas sencilla de configurar hgwebdir.cgi es mediante una seccion llamada collections. Esta pub-licara automaticamente todos los repositorios en los directorios que usted especifique. La seccion deberıa lucir ası:

1 [collections]2 /mi/ruta = /mi/ruta

Mercurial lo interpreta buscando el nombre del directorio que este a la derecha del sımbolo “=”; ubicando repositoriosen la jerarquıa de directorios; y usando el texto a la izquierda para eliminar el texto de los nombres que mostrara en lainterfaz web. El componente restante de la ruta despues de esta eliminacion usualmente se llama “ruta virtual”.

Dado el ejemplo de arriba, si tenemos un repositorio cuya ruta local es /mi/ruta/este/repo, el guion CGIeliminara la porcion inicial /mi/ruta del nombre y publicara el repositorio con una ruta virtual este/repo. Si elURL base de nuestro guion CGI es http://myhostname/˜myuser/hgwebdir.cgi, el URL completo al repositoriosera http://myhostname/˜myuser/hgwebdir.cgi/this/repo.

Si reemplazamos /mi/ruta en el lado izquierdo de este ejemplo con /mi, hgwebdir.cgi eliminara solamente/mi del nombre del repositorio, y nos ofrecera la ruta virtual ruta/este/repo en lugar de este/repo.

El guion hgwebdir.cgi buscara recursivamente en cada directorio listado en la seccion collections de su ficherode configuracion, pero no hara el recorrido recursivo dentro de los repositorios que encuentre.

El mecanismo de collections permite publicar facilmente repositorios de una forma “hacer y olvidar”. Sola-mente requiere configurar el guion CGI y el fichero de configuracion una vez. Despues de eso puede publicar y sacarde publicacion un repositorio en cualquier momento incluyendolo o excluyendolo de la jerarquıa de directorios en lacual le haya indicado a hgwebdir.cgi que mirase.

Especificacion explıcita de los repositorios a publicar

Ademas del mecanismo collections, el guion hgwebdir.cgi le permite publicar una lista especıfica de reposi-torios. Para hacerlo, cree una seccion paths, con los contenidos de la siguiente forma:

1 [paths]2 repo1 = /mi/ruta/a/un/repo3 repo2 = /ruta/a/otro/repo

72

Page 83: Mercurial

En este caso, la ruta virtual (el componente que aparecera en el URL) esta en el lado derecho de cada definicion,mientras que la ruta al repositorio esta a la derecha. Note que no tiene que haber relacion alguna entre la ruta virtualque elija y el lugar del repositorio en su sistema de ficheros.

Si lo desea, puede usar tanto collections como paths simultaneamente en un solo fichero de configuracion.Nota: Si varios repositorios tienen la misma ruta virtual, hgwebdir.cgi no repor-tara ningun error. Pero se comportara de manera impredecible.

6.6.4. Descarga de ficheros fuenteLa interfaz web de Mercurial permite a los usuarios descargar un conjunto de cualquier revision. Este conjunto

contendra una replica del directorio de trabajo en la revision en cuestion, pero no contendra una copia de los datos delrepositorio.

Esta caracterıstica no esta habilitada de forma predeterminada. Para habilitarla adicione un allow archive a laseccion [web] de su fichero hgrc.

6.6.5. Opciones de configuracion en WebLas interfaces web de Mercurial (la orden “hg serve”, y los guiones hgweb.cgi y hgwebdir.cgi) tienen varias

opciones de configuracion disponibles. Todas ellas van en la seccion [web].

allow archive Determina que mecanismos de descarga soportara Mercurial. Si habilita esta caracterıstica, los usuarios dela interfaz web podran descargar una copia de la revision del repositorio que esten viendo. Para activar lacaracterıstica de descarga de conjunto, el valor tendra una secuencia de palabras extraıdas de la lista de abajo.

bz2 Un fichero tar con el metodo de compresion bzip2. Tiene la mejor tasa de compresion, pero usa mastiempo de procesamiento en el servidor.

gz Un fichero tar, comprimido con gzip.

zip Un fichero zip, comprimido con LZW. Este formato posee la peor tasa de compresion, pero es muy usadoen el mundo Windows.

Si da una lista vacıa o no tiene la entrada allow archive, esta caracterıstica se deshabilitara. A continuacion sepresenta un ejemplo de como habilitar los tres formatos soportados.

1 [web]2 allow_archive = bz2 gz zip

allowpull Booleano. Determina si la interfaz web permite a los usuarios remotos emplear “hg pull” y “hg clone” sobreel repositorio HTTP. Si se coloca no o false, solamente se habilita la porcion de los procesos “orientados-a-humanos” de la interfaz web.

contact Cadena. Una cadena en forma libre (pero preferiblemente corta) que identifica a la persona o grupo a cargodel repositorio. Usualmente contiene el nombre y la direccion de correo electronico de una persona o de unalista de correo. A veces tiene sentido colocar esta opcion en el fichero .hg/hgrc del repositorio, pero en otrasoportunidades es mejor hacerlo en el hgrc global si todos los repositorios tienen un unico mantenedor.

maxchanges Entero. La cantidad maxima de conjuntos de cambios a mostrar de forma predeterminada en cada pagina.

maxfiles Entero. La cantidad maxima predeterminada de ficheros modificados a desplegar en una pagina.

stripes Entero. Si la interfaz web despliega “franjas” para facilitar la visualizacion alineada de filas cuando se ve unatabla, este valor controla la cantidad de filas en cada franja.

style Controla la plantilla que Mercurial usa para desplegar la interfaz web. Mercurial viene con dos plantillas web,llamadas default y gitweb (La primera es mas atractiva visualmente). Puede especificar una plantilla propia;consulte el capıtulo 11. A continuacion mostramos como habilitar el estilo gitweb.

73

Page 84: Mercurial

1 [web]2 style = gitweb

templates Ruta. Directorio en el que se buscaran los ficheros plantilla. De forma predeterminada, el guion busca en eldirectorio en el cual fue instalado.

Si usa hgwebdir.cgi, puede anadir otras opciones de configuracion en la seccion [web] del fichero hgweb.configen lugar del fichero hgrc si lo considera mas conveniente. Estas opciones son motd y style.

Opciones especıficas para repositorios individuales

Ciertas opciones de configuracion de [web] deben estar ubicadas en el .hg/hgrc de un repositorio en lugar delfichero del usuario o el hgrc global.

description Cadena. Una cadena de forma libre (preferiblemente corta) que describa los contenidos o el proposito del repos-itorio.

name Cadena. El nombre para visualizar en la interfaz web del repositorio. Sustituye el nombre predeterminado, elcual es el ultimo componente de la ruta del repositorio.

Opciones especıficas a la orden “hg serve”

Algunas opciones en la seccion [web] de un fichero hgrc son de uso exclusivo de la orden “hg serve”.

accesslog Ruta. El nombre del fichero en el cual se escribe la bitacora de acceso. En principio, la orden “hg serve”escribe esta informacion a la salida estandar, no a un fichero. Las lıneas de la bitacora se escriben en un formatode fichero “combinado” estandar, usado por casi todos los servidores web.

address Cadena. La direccion local en la cual el servidor debe escuchar peticiones entrantes. De forma predeterminada,el servidor escucha en todas las direcciones.

errorlog Ruta. El nombre de un fichero en el cual escribir la bitacora de error. En principio, la orden “hg serve” escribeesta informacion en la salida de error estandar, no a un fichero.

ipv6 Booleano. Si se usa o no el protocolo IPv6. IPv6 no se usa de manera predeterminada.

port Entero. El numero del puerto TCP en el cual el servidor esperara conexiones. El puerto predeterminado esel 8000.

Elegir el fichero hgrc correcto para las configuraciones de [web]

Es importante recordar que un servidor web como Apache o lighttpd se ejecutara bajo un ID de usuario quegeneralmente no es el suyo Los guiones CGI ejecutados por su servidor, tales como hgweb.cgi, se ejecutaran tambiencon ese ID de usuario.

Si anade opciones [web] a su fichero personal hgrc los guiones CGI no leeran tal fichero hgrc. Tales configu-raciones solamente afectaran el comportamiento de la orden “hg serve” cuando usted la ejecuta. Para logar que losguiones CGI vean sus configuraciones, o bien cree un fichero hgrc en el directorio personal del usuario bajo cuyo IDse ejecuta su servidor web, o anada tales opciones al fichero global hgrc.

74

Page 85: Mercurial

Capıtulo 7

Nombres de ficheros y asociacion depatrones

Mercurial provee mecanismos que le permiten trabajar con nombres de ficheros en una manera consistente yexpresiva.

7.1. Nombrado de ficheros simpleMercurial usa un mecanismo unificado “bajo el capo” para manejar nombres de ficheros. Cada comando se com-

porta de manera uniforme con respecto a los nombres de fichero. La manera en que los comandos operan con nombresde fichero es la siguiente.

Si usted especifica explıcitamente nombres reales de ficheros en la lınea de comandos, Mercurial opera unicamentesobre dichos ficheros, como usted esperarıa.

1 $ hg add COPYING README examples/simple.py

Cuando usted provee el nombre de un directorio, Mercurial interpreta eso como “opere en cada fichero en este di-rectorio y sus subdirectorios”. Mercurial va por todos los ficheros y subdirectorios de un directorio en orden alfabetico.Cuando encuentra un subdirectorio, lo recorrera antes de continuar con el directorio actual.

1 $ hg status src2 ? src/main.py3 ? src/watcher/_watcher.c4 ? src/watcher/watcher.py5 ? src/xyzzy.txt

7.2. Ejecucion de comandos sin ningun nombre de ficheroLos comandos de Mercurial que trabajan con nombres de fichero tienen comportamientos por defecto adecuados

cuando son utilizados sin pasar ningun patron o nombre de fichero. El tipo de comportamiento depende de lo quehaga el comando. Aquı presento unas cuantas reglas generales que usted puede usar para que es lo que probablementehara un comando si usted no le pasa ningun nombre de fichero con el cual trabajar.

Muchos comandos operaran sobre el directorio de trabajo completo. Por ejemplo, esto es lo que hace el comando“hg add”,

75

Page 86: Mercurial

Si el comando tiene efectos difıciles o incluso imposibles de revertir, se le obligara a usted a proveer explıcita-mente al menos un nombre o patron (ver mas abajo). Esto lo proteje a usted de, por ejemplo, borrar ficherosaccidentalmente al ejecutar “hg remove” sin ningun argumento.

Es facil evitar este comportamiento por defecto, si no es el adecuado para usted. Si un comando opera normalmenteen todo el directorio de trabajo, usted puede llamarlo para que trabaje solo en el directorio actual y sus subdirectoriopasandole el nombre “.”.

1 $ cd src2 $ hg add -n3 adding ../MANIFEST.in4 adding ../examples/performant.py5 adding ../setup.py6 adding main.py7 adding watcher/_watcher.c8 adding watcher/watcher.py9 adding xyzzy.txt

10 $ hg add -n .11 adding main.py12 adding watcher/_watcher.c13 adding watcher/watcher.py14 adding xyzzy.txt

Siguiendo la misma lınea, algunos comandos normalmente imprimen las rutas de ficheros con respecto a la raız delrepositorio, aun si usted los llama dentro de un subdirectorio. Dichos comandos imprimiran las rutas de los ficherosrespecto al directorio en que usted se encuentra si se les pasan nombres explıcitos. Vamos a ejecutar el comando “hgstatus” desde un subdirectorio, y a hacer que opere en el directorio de trabajo completo, a la vez que todas las rutasde ficheros se imprimen respecto a nuestro subdirectorio, pasandole la salida del comando “hg root”.

1 $ hg status2 A COPYING3 A README4 A examples/simple.py5 ? MANIFEST.in6 ? examples/performant.py7 ? setup.py8 ? src/main.py9 ? src/watcher/_watcher.c

10 ? src/watcher/watcher.py11 ? src/xyzzy.txt12 $ hg status ‘hg root‘13 A ../COPYING14 A ../README15 A ../examples/simple.py16 ? ../MANIFEST.in17 ? ../examples/performant.py18 ? ../setup.py19 ? main.py20 ? watcher/_watcher.c21 ? watcher/watcher.py22 ? xyzzy.txt

76

Page 87: Mercurial

7.3. Reportar que esta pasandoEl ejemplo con el comando “hg add” en la seccion anterior ilustra algo mas que es util acerca de los comandos de

Mercurial. Si un comando opera en un fichero que usted no paso explıcitamente en la lınea de comandos, usualmentese imprimira el nombre del fichero, para que usted no sea sorprendido por lo que sucede.

Esto es el principio de mınima sorpresa. Si usted se ha referido explıcitamente a un fichero en la lınea de comandos,no tiene mucho sentido repetir esto de vuelta a usted. Si Mercurial esta actuando en un fichero implıcitamente, porqueusted no paso nombres, ni directorios, ni patrones (ver mas abajo), lo mas seguro es decirle a usted que se esta haciendo.

Usted puede silenciar a los comandos que se comportan de esta manera usando la opcion -q. Tambien puede hacerque impriman el nombre de cada fichero, aun aquellos que usted indico explıcitamente, usando la opcion -v.

7.4. Uso de patrones para identificar ficherosAdemas de trabajar con nombres de ficheros y directorios, Mercurial le permite usar patrones para identificar

ficheros. El manejo de patrones de Mercurial es expresivo.En sistemas tipo Unix (Linux, MacOS, etc.), el trabajo de asociar patrones con nombres de ficheros recae sobre el

interprete de comandos. En estos sistemas, usted debe indicarle explıcitamente a Mercurial que el nombre que se lepasa es un patron. En Windows, el interprete no expande los patrones, ası que Mercurial identificara automaticamentelos nombres que son patrones, y hara la expansion necesaria.

Para pasar un patron en vez de un nombre normal en la lınea de comandos, el mecanismo es simple:

1 syntax:patternbody

Un patron es identificado por una cadena de texto corta que indica que tipo de patron es, seguido por un dos puntos,seguido por el patron en sı.

Mercurial soporta dos tipos de sintaxis para patrones. La que se usa con mas frecuencia se denomina glob1; es elmismo tipo de asociacion de patrones usado por el interprete de Unix, y tambien deberıa ser familiar para los usuariosde la lınea de comandos de Windows.

Cuando Mercurial hace asociacion automatica de patrones en Windows, usa la sintaxis glob. Por esto, usted puedeomitir el prefijo “glob:” en Windows, pero tambien es seguro usarlo.

La sintaxis re2 es mas poderosa; le permite especificar patrones usando expresiones regulares, tambien conocidascomo regexps.

A proposito, en los ejemplos siguientes, por favor note que yo tengo el cuidado de rodear todos mis patrones concomillas sencillas, para que no sean expandidos por el interprete antes de que Mercurial pueda verlos.

7.4.1. Patrones glob estilo interpreteEste es un vistazo general de los tipos de patrones que usted puede usar cuando esta usando asociacion con patrone

glob.La secuencia “*” se asocia con cualquier cadena, dentro de un unico directorio.

1 $ hg add ’glob:*.py’2 adding main.py

La secuencia “**” se asocia con cualquier cadena, y cruza los lımites de los directorios. No es una elementoestandar de los tokens de glob de Unix, pero es aceptado por varios interpretes Unix populares, y es muy util.

1 $ cd ..2 $ hg status ’glob:**.py’

1N. del T. Grupo, coleccion, aglomeracion.2N. del T. Expresiones regulares.

77

Page 88: Mercurial

3 A examples/simple.py4 A src/main.py5 ? examples/performant.py6 ? setup.py7 ? src/watcher/watcher.py

La secuencia “?” se asocia con cualquier caracter sencillo.

1 $ hg status ’glob:**.?’2 ? src/watcher/_watcher.c

El caracter “[” marca el inicio de una clase de caracteres. Ella se asocia con cualquier caracter sencillo dentro dela clase. La clase se finaliza con un caracter “]”. Una clase puede contener multiples rangos de la forma “a-f”, queen este caso es una abreviacion para “abcdef”.

1 $ hg status ’glob:**[nr-t]’2 ? MANIFEST.in3 ? src/xyzzy.txt

Si el primer caracter en aparecer despues de “[” en la clase de caracteres es un “!”, se niega la clase, haciendo que seasocie con cualquier caracter sencillo que no se encuentre en la clase.

Un “{” marca el inicio de un grupo de subpatrones, en donde todo el grupo es asociado si cualquier subpatron enel grupo puede ser asociado. El caracter “,” separa los subpatrones, y el “}” finaliza el grupo.

1 $ hg status ’glob:*.{in,py}’2 ? MANIFEST.in3 ? setup.py

Cuidado!

No olvide que si usted desea asocia un patron con cualquier directorio, no deberıa usar el elemento para asociarcon cualquier cadena “*”, ya que este solo generara asociaciones dentro de un solo directorio. En vez de eso, use elcaracter para asociar con cualquier cadena “**”. Este pequeno ejemplo ilustra la diferencia entre los dos.

1 $ hg status ’glob:*.py’2 ? setup.py3 $ hg status ’glob:**.py’4 A examples/simple.py5 A src/main.py6 ? examples/performant.py7 ? setup.py8 ? src/watcher/watcher.py

7.4.2. Asociacion con patrones de expresiones regulares reMercurial acepta la misma sintaxis para expresiones regulares del lenguaje de programacion Python (internamente

se usa el motor de expresiones regulares de Python). Esta sintaxis esta basada en la misma del lenguaje Perl, que es eldialecto mas popular en uso (por ejemplo, tambien se usa en Java).

No discutire el dialecto de expresiones regulares de Mercurial en detalle aquı, ya que las mismas no son usadasfrecuentemente. Las expresiones regulares al estilo Perl se encuentran documentadas exhaustivamente en una multitudde sitios web, y en muchos libros. En vez de eso, me enfocare en unas cuantas cosas que usted deberıa conocer si tienela necesidad de usar expresiones regulares en Mercurial.

78

Page 89: Mercurial

Una expresion regular es comparada contra un nombre de fichero completo, relativo a la raız del repositorio. Enotras palabras, aun si usted se encuentra en un subdirectorio foo, si desea asociar ficheros en este directorio, su patrondebe empezar con “foo/”.

Un detalle a tener en cuenta es que, si le son familiares las expresiones regulares al estilo Perl, las de Mercurialestan enraızadas. Esto es, que la asociacion de una expresion se hace desde el inicio de la cadena; no se buscancoincidencias dentro de la cadena. Para buscar coincidencias en cualquier sitio dentro de una cadena, empiece supatron con un “.*”.

7.5. Filtrado de ficherosMercurial no solo le provee una variedad de formas para especificar ficheros; le permite limitar aun mas dichos

ficheros mediante el uso de filtros. Los comandos que operan con nombres de fichero aceptan dos opciones de filtrado.

-I, o --include, le permite especificar un patron con el que deben coincidir los ficheros para ser procesados.

-X, o --exclude, le brinda una manera de evitar procesar ficheros, si coinciden con este patron.

Usted puede pasar multiples veces las opciones -I y -X en la lınea de comandos, e intercalarlos como desee. Pordefecto, Mercurial interpreta los patrones que usted pase usando la sintaxis glob (pero usted puede usar expresionesregulares si lo necesita).

El filtro -I puede verse como un “procese todos los ficheros que coincidan con este filtro”.

1 $ hg status -I ’*.in’2 ? MANIFEST.in

El filtro -X puede verse como “procese unicamente los ficheros que no coincidan con este patron”.

1 $ hg status -X ’**.py’ src2 ? src/watcher/_watcher.c3 ? src/xyzzy.txt

7.6. Ignorar ficheros y directorios no deseadosXXX.

7.7. Sensibilidad a mayusculasSi usted esta trabajando en un ambiente de desarrollo mixto que contiene tanto sistemas Linux (u otro Unix) y

sistemas Mac o Windows, deberıa tener en mente el hecho de que ellos tratan case (“N” versus “n”) of file names inincompatible ways. This is not very likely to affect you, and it’s easy to deal with if it does, but it could surprise youif you don’t know about it.

Operating systems and filesystems differ in the way they handle the case of characters in file and directory names.There are three common ways to handle case in names.

Completely case insensitive. Uppercase and lowercase versions of a letter are treated as identical, both whencreating a file and during subsequent accesses. This is common on older DOS-based systems.

Case preserving, but insensitive. When a file or directory is created, the case of its name is stored, and can beretrieved and displayed by the operating system. When an existing file is being looked up, its case is ignored.This is the standard arrangement on Windows and MacOS. The names foo and FoO identify the same file. Thistreatment of uppercase and lowercase letters as interchangeable is also referred to as case folding.

79

Page 90: Mercurial

Case sensitive. The case of a name is significant at all times. The names foo and FoO identify different files.This is the way Linux and Unix systems normally work.

On Unix-like systems, it is possible to have any or all of the above ways of handling case in action at once. Forexample, if you use a USB thumb drive formatted with a FAT32 filesystem on a Linux system, Linux will handlenames on that filesystem in a case preserving, but insensitive, way.

7.7.1. Almacenamiento portable y seguro de repositoriosEl mecanismo de almacenamiento de los repositorios en Mercurial es robusto frente a sensibilidad/insensibilidad

a mayusculas. Los nombres de fichero son traducidos para que puedan ser almacenados de manera segura tanto ensistemas sensibles como insensibles a mayusculas. Esto significa que usted puede usar herramientas normales decopia de ficheros para transferir un repositorio Mercurial a, por ejemplo, una memoria USB, y trasladar de manerasegura la memoria y el repositorio de ida y vuelta entre un Mac, un PC ejecutando Windows, y un sistema Linux

7.7.2. Deteccion de conflictos de mayusculas/minusculasAl operar en el directorio de trabajo, Mercurial respeta la polıtica de nombrado del sistema de ficheros en que

se encuentre el directorio de trabajo. Si el sistema de ficheros conserva las diferencias entre mayusculas, pero no essensible a ellas, Mercurial tratara los nombres que solo difieren en mayusculas como uno solo y el mismo.

Un aspecto importante de este enfoque es que es posible consignar un conjunto de cambios en un sistema deficheros sensible a mayusculas (tıpicamente Linux o Unix) que terminara causando problemas para usuarios en sis-temas insensibles a mayusculas (usualmente en Windows o MacOS). Si un usuario de Linux consigna cambios a dosficheros, uno de ellos llamado myfile.c y el otro llamado MyFile.C, ambos seran almacenados correctamente enel repositorio. Y seran representados correctamente como ficheros separados, en los directorios de trabajo de otrosusuarios de Linux.

Si un usuario de Windows o Mac jalan este cambio, no tendran problemas inicialmente, porque el mecanismo dealmacenamiento de Mercurial es seguro frente a sensibilidad/insensibilidad a mayusculas. Sin embargo, una vez queellos traten de actualizar (“hg update”) el directorio de trabajo con ese conjunto de cambios, o hagan fusion (“hgmerge”) con ese conjunto de cambios, Mercurial vera el conflicto entre los dos nombres de fichero que el sistema deficheros tratarıa como el mismo, e impedira que ocurra la actualizacion o fusion.

7.7.3. Arreglar un conflicto de mayusculas/minusculasSi usted esta usando Windows o Mac en un entorno mixto donde algunos de sus colaboradores estan usando Linux

o Unix, y Mercurial reporta un conflicto de mayusculas/minusculas cuando usted trata de actualizar (“hg update”) ofusionar (“hg merge”), el procedimiento para arreglar el problema es simple.

Solo busque un sistema Linux o Unix cercano, clone el repositorio problema allı, y use el comando “hg rename”de Mercurial para cambiar los nombres de cualquiera de los ficheros o directorios problematicos para que no causenmas conflictos. Consigne este cambio, y jalelo (“hg pull”) o empujelo (“hg push”) a su sistema Windows o MacOS,y actualıcelo (“hg update”) a la revision con los nombres que ya no generan conflictos.

El conjunto de cambios con los nombres con conflictos de mayusculas/minusculas permanecera en el historial desu proyecto, y usted no podra actualizar (“hg update”) su directorio de trabajo a dicho conjunto de cambios en unsistema Windows o MacOS, pero puede continuar el desarrollo sin impedimentos.

Nota: Antes de la version 0.9.3, Mercurial no usaba un mecanismos seguro frentea sensibilidad/insensibilidad a mayusculas o minusculas, y no detectaba los con-flictos con nombres de ficheros. Si usted esta usando una version mas antigua deMercurial en Windows o MacOS, le recomiendo energicamente que se actualice.

80

Page 91: Mercurial

Capıtulo 8

Administracion de versiones y desarrolloramificado

Mercurial ofrece varios mecanismos que le permiten administrar un proyecto que avanza en multiples frentessimultaneamente. Para entender estos mecanismos, demos un vistazo a la estructura usual de un proyecto de software.

Muchos proyectos de software liberan una version “mayor” que contiene nuevas caracterısticas substanciales. Enparalelo, pueden liberar versiones “menores”. Usualmente estas son identicas a las versiones mayores en las cualesestan basadas, pero con arreglos para algunos fallos.

En este capıtulo, comenzaremos hablando de como mantener registro de etapas del proyecto como las liberacionesde una version. Continuaremos hablando del flujo de trabajo entre las diferentes fases de un proyecto, y como puedeayudar Mercurial a aislar y administrar tal trabajo.

8.1. Dar un nombre persistente a una revisionCuando usted decide otorgar a una revision el nombre particular de una “version”, es buena idea grabar la identidad

de tal revision. Esto le permitira reproducir dicha version en una fecha posterior, para cualquiera que sea el propositoque se tenga en ese momento (reproducir un fallo, portar a una nueva plataforma, etc).

1 $ hg init mytag2 $ cd mytag3 $ echo hello > myfile4 $ hg commit -A -m ’Initial commit’5 adding myfile

Mercurial le permite dar un nombre permanente a cualquier revision usando la orden “hg tag”. Sin causa desorpresa, esos nombres se llaman “tags” (etiquetas).

1 $ hg tag v1.0

Una etiqueta no es mas que un “nombre simbolico” para una revision. Las etiquetas existen unicamente para suconveniencia, brindandole una forma permanente y sencilla de referirse a una revision; Mercurial no interpreta deninguna manera los nombres de las etiquetas que usted use. Mercurial tampoco impone restriccion alguna al nombrede una etiqueta, mas alla de lo necesario para asegurar que una etiqueta pueda procesarse sin ambiguedades. El nombrede una etiqueta no puede tener ninguno de los siguientes caracteres:

Dos puntos (ASCII 58, “:”)

Retorno de carro (return) (ASCII 13, “\r”)

81

Page 92: Mercurial

Nueva lınea (ASCII 10, “\n”)

Puede usar la orden “hg tags” para ver las etiquetas presentes en su repositorio. Al desplegarse, cada revisionmarcada se identifica primero con su nombre, despues con el numero de revision y finalmente con un hash unico de larevision.

1 $ hg tags2 tip 1:515e5c86f4963 v1.0 0:1d3fd3640aaa

Note que tip aparece en en listado generado por “hg tags”. La etiqueta tip es una etiqueta “flotante” especial, queidentifica siempre la revision mas reciente en el repositorio.

Al desplegar la orden “hg tags”, las etiquetas se listan en orden inverso, por numero de revision. Lo que significausualmente que las etiquetas mas recientes se listan antes que las mas antiguas. Tambien significa que la etiqueta tipsiempre aparecera como primera etiqueta listada al desplegar la orden “hg tags”.

Cuando usted ejecuta “hg log”, si se muestra una revision que tenga etiquetas asociadas a ella, se imprimirantales etiquetas.

1 $ hg log2 changeset: 1:515e5c86f4963 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:29 2009 +00006 summary: Added tag v1.0 for changeset 1d3fd3640aaa7

8 changeset: 0:1d3fd3640aaa9 tag: v1.0

10 user: Bryan O’Sullivan <[email protected]>11 date: Tue Feb 10 18:23:29 2009 +000012 summary: Initial commit13

Siempre que requiera indicar un ID de revision a una orden de Mercurial, aceptara un nombre de etiqueta en sulugar. Internamente, Mercurial traducira su nombre de etiqueta en el ID de revision correspondiente, y lo usara.

1 $ echo goodbye > myfile22 $ hg commit -A -m ’Second commit’3 adding myfile24 $ hg log -r v1.05 changeset: 0:1d3fd3640aaa6 tag: v1.07 user: Bryan O’Sullivan <[email protected]>8 date: Tue Feb 10 18:23:29 2009 +00009 summary: Initial commit

10

No hay lımites en la cantidad de etiquetas por repositorio, o la cantidad de etiquetas que una misma revision puedatener. Siendo practicos, no es muy buena idea tener “demasiadas” (la cantidad variara de un proyecto a otro), debido aque la intencion es ayudarle a encontrar revisiones. Si tiene demasiadas etiquetas, la facilidad de usarlas para identificarrevisiones disminuira rapidamente.

Por ejemplo, si su proyecto tiene etapas (milestones) frecuentes, de pocos dıas, es perfectamente razonable asig-narle una etiqueta a cada una de ellas. Pero si tiene un sistema de construccion automatica de binarios que asegura

82

Page 93: Mercurial

que cada revision puede generarse limpiamente, estarıa introduciendo mucho ruido si se usara una etiqueta para ca-da generacion exitosa. Mas bien, podrıa usar tags para generaciones fallidas (¡en caso de que estas sean raras!), osimplemente evitar las etiquetas para llevar cuenta de la posibilidad de generacion de binarios.

Si quiere eliminar una etiqueta que no desea, use “hg tag --remove”.

1 $ hg tag --remove v1.02 $ hg tags3 tip 3:31eda97c4db9

Tambien puede modificar una etiqueta en cualquier momento, para que identifique una revision distinta, simplementeusando una nueva orden “hg tag”. Debera usar la opcion -f para indicarle a Mercurial que realmente desea actualizarla etiqueta.

1 $ hg tag -r 1 v1.12 $ hg tags3 tip 4:eb33d1c642f44 v1.1 1:515e5c86f4965 $ hg tag -r 2 v1.16 abort: tag ’v1.1’ already exists (use -f to force)7 $ hg tag -f -r 2 v1.18 $ hg tags9 tip 5:b26c4daf4a9d

10 v1.1 2:55d99d62d333

De todas maneras habra un registro permanente de la antigua identidad de la etiqueta, pero Mercurial no la usara.Por lo tanto no hay problema al marcar con una etiqueta una revision incorrecta; lo unico que debe hacer es mover laetiqueta hacia la revision correcta tan pronto como localice el error.

Mercurial almacena las etiquetas en un fichero controlado por revisiones en su repositorio. Si ha creado etiquetas,las encontrara en un fichero llamado .hgtags. Cuando invoca la orden “hg tag”, Mercurial modifica este fichero,y hace la consignacion del cambio al mismo automaticamente. Esto significa que cada vez que ejecuta “hg tag”,vera un conjunto de cambios correspondiente en la salida de “hg log”.

1 $ hg tip2 changeset: 5:b26c4daf4a9d3 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:30 2009 +00006 summary: Added tag v1.1 for changeset 55d99d62d3337

8.1.1. Manejo de conflictos entre etiquetas durante una fusionUsualmente no tendra que preocuparse por el fichero .hgtags, pero a veces hace su aparicion durante una fusion.

El formato del fichero es sencillo: Consiste de una serie de lıneas. Cada lınea comienza con un hash de conjunto decambios, seguido por un espacio, seguido por el nombre de una etiqueta.

Si esta resolviendo un conflicto en el fichero .hgtags durante una fusion, hay un detalle para tener en cuenta almodificar el fichero .hgtags: cuando Mercurial procesa las etiquetas en el repositorio, nunca lee la copia de trabajodel fichero .hgtags. En cambio, lee la version consignada mas reciente del fichero.

Una consecuencia desafortunada de este diseno es que usted no puede verificar que su fichero .hgtags fusion-ado sea correcto hasta despues de haber consignado un cambio. Ası que si se encuentra resolviendo un conflicto en.hgtags durante una fusion, asegurese de ejecutar la orden “hg tags” despues de consignar. Si encuentra un erroren el fichero .hgtags, la orden reportara el lugar del error, que podra arreglar y despues consignar. Posteriormenteejecute de nuevo la orden “hg tags” para asegurarse de que su arreglo fue aplicado correctamente .

83

Page 94: Mercurial

8.1.2. Etiquetas y clonadoPuede haber notado que la orden “hg clone” tiene la opcion -r que le permite clonar una copia exacta del

repositorio hasta un conjunto de cambios especıfico. El nuevo clon no tendra historial posterior a la revision que ustedhaya especificado. Esto tiene una interaccion con etiquetas que puede sorprender a los desprevenidos.

Recuerde que una etiqueta se almacena como una revision al fichero .hgtags, ası que cuando usted crea una eti-queta, el conjunto de cambios en el cual esta se almacena necesariamente se refiere a un conjunto de cambios anterior.Cuando ejecuta “hg clone -r foo” para clonar un repositorio hasta la etiqueta foo, el nuevo clon no contendra elhistorial que creo la etiqueta que uso para clonar el repositorio. El resultado es que tendra exactamente el subconjuntocorrecto del historial del proyecto en el nuevo repositorio, pero, no la etiqueta que podrıa haber esperado.

8.1.3. Cuando las etiquetas permanentes son demasiadoDado que las etiquetas de Mercurial estan controladas por revisiones y se llevan en el historial del proyecto, todas

las personas involucradas veran las etiquetas que usted haya creado. El hecho de dar nombres a las revisiones tieneusos mas alla que simplemente hacer notar que la revision 4237e45506ee es realmente v2.0.2. Si esta tratando deencontrar un fallo sutil, posiblemente desearıa colocar una etiqueta recordandole algo como “Ana vio los sıntomas enesta revision”.

Para estos casos, lo que usted posiblemente desearıa serıan etiquetas locales. Puede crear una etiqueta local conla opcion -l de la orden “hg tag”. Esto guardara la etiqueta en un fichero llamado .hg/localtags. A diferencia de.hgtags, .hg/localtags no esta controlado por revisiones. Cualquier etiqueta que usted cree usando -l se manten-dra local al repositorio en el que este trabajando en ese momento.

8.2. El flujo de cambios—El gran cuadro vs. el pequenoRetomando lo mencionado en el comienzo de un capıtulo, pensemos en el hecho de que un proyecto tiene muchas

piezas concurrentes de trabajo en desarrollo al mismo tiempo.Puede haber prisa por una nueva version “principal”; una nueva version con un arreglo de fallo a la ultima version;

y una version de “mantenimiento correctivo” a una version antigua que ha entrado en modo de mantenimiento.Usualmente la gente se refiere a esas direcciones concurrentes de desarrollo como “ramas”. Sin embargo, ya hemos

visto que en varias ocasiones Mercurial trata a todo el historial como una serie de ramas y fusiones. Realmente lo quetenemos aquı es dos ideas que se relacionan perifericamente, pero que en esencia comparten un nombre.

“El gran cuadro” Las ramas representan un barrido de la evolucion del proyecto; la gente les da nombres yhablan acerca de ellas en sus conversaciones.

“El cuadro pequeno” Las ramas son artefactos de las actividades diarias de desarrollar y fusionar cambios.Exponen la narrativa de como se desarrollo el codigo.

8.3. Administrar ramas en repositorios estilo gran cuadroEn Mercurial la forma mas sencilla de aislar una rama del “gran cuadro” es a traves de un repositorio dedica-

do. Si cuenta con un repositorio compartido existente —llamemoslo myproject—que alcanzo la etapa “1.0”, puedecomenzar a prepararse para versiones de mantenimiento futuras a partir de la version 1.0 marcando con una etiquetala revision con la cual preparo la version 1.0.

1 $ cd myproject2 $ hg tag v1.0

Ahora puede clonar un repositorio compartido nuevo myproject-1.0.1 con tal etiqueta.

84

Page 95: Mercurial

1 $ cd ..2 $ hg clone myproject myproject-1.0.13 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Posteriormente, si alguien necesita trabajar en la reparacion de un fallo deberıa dirigirse a la liberacion de ver-sion 1.0.1 que viene en camino, ellos clonarıan el repositorio myproject-1.0.1, harıan sus cambios y los empujarıande vuelta.

1 $ hg clone myproject-1.0.1 my-1.0.1-bugfix2 updating working directory3 2 files updated, 0 files merged, 0 files removed, 0 files unresolved4 $ cd my-1.0.1-bugfix5 $ echo ’I fixed a bug using only echo!’ >> myfile6 $ hg commit -m ’Important fix for 1.0.1’7 $ hg push8 pushing to /tmp/branch-repo07QkL5/myproject-1.0.19 searching for changes

10 adding changesets11 adding manifests12 adding file changes13 added 1 changesets with 1 changes to 1 files

Mientras tanto, el desarrollo para la siguiente version mayor puede continuar aislado e incolume, en el repositoriomyproject.

1 $ cd ..2 $ hg clone myproject my-feature3 updating working directory4 2 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd my-feature6 $ echo ’This sure is an exciting new feature!’ > mynewfile7 $ hg commit -A -m ’New feature’8 adding mynewfile9 $ hg push

10 pushing to /tmp/branch-repo07QkL5/myproject11 searching for changes12 adding changesets13 adding manifests14 adding file changes15 added 1 changesets with 1 changes to 1 files

8.4. No repita trabajo: fusion entre ramasEn muchos casos, cuando tiene un fallo para arreglar en una rama de mantenimiento, es muy probable que el

fallo tambien este en la rama principal (y posiblemente en otras ramas de mantenimiento tambien). Solamente undesarrollador extrano desearıa corregir el mismo fallo muchas veces, por tanto, veremos varias alternativas con las queMercurial puede ayudarle a administrar tales arreglos de fallo sin duplicar su trabajo.

En el caso mas sencillo, basta con jalar los cambios de la rama de mantenimiento a la rama objetivo en su clonlocal.

85

Page 96: Mercurial

1 $ cd ..2 $ hg clone myproject myproject-merge3 updating working directory4 3 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ cd myproject-merge6 $ hg pull ../myproject-1.0.17 pulling from ../myproject-1.0.18 searching for changes9 adding changesets

10 adding manifests11 adding file changes12 added 1 changesets with 1 changes to 1 files (+1 heads)13 (run ’hg heads’ to see heads, ’hg merge’ to merge)

A continuacion debera mezclar las cabezas de las dos ramas, y empujar de nuevo a la rama principal.

1 $ hg merge2 1 files updated, 0 files merged, 0 files removed, 0 files unresolved3 (branch merge, don’t forget to commit)4 $ hg commit -m ’Merge bugfix from 1.0.1 branch’5 $ hg push6 pushing to /tmp/branch-repo07QkL5/myproject7 searching for changes8 adding changesets9 adding manifests

10 adding file changes11 added 2 changesets with 1 changes to 1 files

8.5. Nombrar ramas dentro de un repositorioLa aproximacion correcta en casi todas las oportunidades es aislar las ramas en los repositorios. Es facil de entender

gracias a su simplicidad; y es difıcil cometer errores. Hay una relacion uno a uno entre las ramas y los directorios conlos que esta trabajando en su sistema. Esto le permite usar emplear herramientas usuales (que no son conscientes deMercurial) para trabajar con los ficheros dentro de una rama/repositorio.

Si se encuentra mas en la categorıa “usuario diestro” (y sus colaboradores tambien), puede considerar otra alter-nativa para administrar las ramas. He mencionado con anterioridad la distincion a nivel humano entre las ramas estilo“cuadro pequeno” y “gran cuadro”. Mientras que Mercurial trabaja con muchas ramas del estilo “cuadro pequeno”en el repositorio todo el tiempo (por ejemplo cuando usted jala cambios, pero antes de fusionarlos), tambien puedetrabajar con varias ramas del “cuadro grande”.

El truco para trabajar de esta forma en Mercurial se logra gracias a que puede asignar un nombre persistente a unarama. Siempre existe una rama llamada default. Incluso antes de que empiece a nombrar ramas por su cuenta, puedeencontrar indicios de la rama default si los busca.

Por ejemplo, cuando invoca la orden “hg commit”, y se lanza su editor para introducir el mensaje de la consignacion,busque la lınea que contiene el texto “HG: branch default” al final. Le esta indicando que su consignacion ocur-rira en la rama llamada default.

Use la orden “hg branches” para empezar a trabajar con ramas nombradas. Esta orden mostrara las ramas pre-sentes en su repositorio, indicandole que conjunto de cambios es la punta de cada una.

1 $ hg tip2 changeset: 0:c54ce9a68981

86

Page 97: Mercurial

3 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:16 2009 +00006 summary: Initial commit7

8 $ hg branches9 default 0:c54ce9a68981

Dado que todavıa no ha creado ramas nombradas, la unica que vera sera default.Para hallar cual es la rama “actual”, invoque la orden “hg branch”, sin argumento alguno. Le informara en que ra-

ma se encuentra el padre del conjunto de cambios actual.

1 $ hg branch2 default

Para crear una nueva rama, invoque la orden “hg branch” de nuevo. En esta oportunidad, ofrezca un argumento:el nombre de la rama que desea crear.

1 $ hg branch foo2 marked working directory as branch foo3 $ hg branch4 foo

Despues de crear la rama, usted podrıa desear ver el efecto que tuvo la orden “hg branch”. ¿Que reportan lasordenes “hg status” y “hg tip”?

1 $ hg status2 $ hg tip3 changeset: 0:c54ce9a689814 tag: tip5 user: Bryan O’Sullivan <[email protected]>6 date: Tue Feb 10 18:23:16 2009 +00007 summary: Initial commit8

Nada cambia en el directorio actual, y no se ha anadido nada al historial. Esto sugiere que al ejecutar la orden “hgbranch” no hay un efecto permanente; solamente le indica a que nombre de rama usara la proxima vez que consigneun conjunto de cambios.

Cuando consigna un cambio, Mercurial almacena el nombre de la rama en la cual consigno. Una vez que hayacambiado de la rama default y haya consignado, vera que el nombre de la nueva rama se mostrara cuando use laorden “hg log”, “hg tip”, y otras ordenes que desplieguen la misma clase de informacion.

1 $ echo ’hello again’ >> myfile2 $ hg commit -m ’Second commit’3 $ hg tip4 changeset: 1:b108bc883cbc5 branch: foo6 tag: tip7 user: Bryan O’Sullivan <[email protected]>8 date: Tue Feb 10 18:23:17 2009 +00009 summary: Second commit

10

87

Page 98: Mercurial

Las ordenes del tipo “hg log” imprimiran el nombre de la rama de cualquier conjunto de cambios que no este en larama default. Como resultado, si nunca usa ramas nombradas, nunca vera esta informacion.

Una vez que haya nombrado una rama y consignado un cambio con ese nombre, todas las consignaciones subse-cuentes que desciendan de ese cambio heredaran el mismo nombre de rama. Puede cambiar el nombre de una rama encualquier momento con la orden “hg branch”.

1 $ hg branch2 foo3 $ hg branch bar4 marked working directory as branch bar5 $ echo new file > newfile6 $ hg commit -A -m ’Third commit’7 adding newfile8 $ hg tip9 changeset: 2:7f37f4b1f9a3

10 branch: bar11 tag: tip12 user: Bryan O’Sullivan <[email protected]>13 date: Tue Feb 10 18:23:17 2009 +000014 summary: Third commit15

Esto es algo que no hara muy seguido en la practica, debido que los nombres de las ramas tienden a tener vidas largas.(Esto no es una regla, solamente una observacion.)

8.6. Tratamiento de varias ramas nombradas en un repositorioSi tiene mas de una rama nombrada en un repositorio, Mercurial recordara la rama en la cual esta su directorio de

trabajo cuando invoque una orden como “hg update” o “hg pull -u”. Se actualizara su directorio de trabajo actuala la punta de esta rama, sin importar cual sea la punta “a lo largo del repositorio”. Para actualizar a una revision queesta en una rama con distinto nombre, puede necesitar la opcion -C de “hg update”.

Este comportamiento puede ser sutil, ası que veamoslo en accion. Primero, recordemos en que rama estamostrabajando, y que ramas estan en nuestro repositorio.

1 $ hg parents2 changeset: 2:7f37f4b1f9a33 branch: bar4 tag: tip5 user: Bryan O’Sullivan <[email protected]>6 date: Tue Feb 10 18:23:17 2009 +00007 summary: Third commit8

9 $ hg branches10 bar 2:7f37f4b1f9a311 foo 1:b108bc883cbc (inactive)12 default 0:c54ce9a68981 (inactive)

Estamos en la rama bar, pero existe otra rama mas antigua llamada “hg foo”.Podemos hacer “hg update” entre los tipos de las ramas foo y bar sin necesidad de usar la opcion -C, puesto que

esto solamente implica ir linealmente hacia adelante y atras en nuestro historial de cambios.

88

Page 99: Mercurial

1 $ hg update foo2 0 files updated, 0 files merged, 1 files removed, 0 files unresolved3 $ hg parents4 changeset: 1:b108bc883cbc5 branch: foo6 user: Bryan O’Sullivan <[email protected]>7 date: Tue Feb 10 18:23:17 2009 +00008 summary: Second commit9

10 $ hg update bar11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved12 $ hg parents13 changeset: 2:7f37f4b1f9a314 branch: bar15 tag: tip16 user: Bryan O’Sullivan <[email protected]>17 date: Tue Feb 10 18:23:17 2009 +000018 summary: Third commit19

Si volvemos a la rama foo e invocamos la orden “hg update”, nos mantendra en foo, sin movernos a la punta debar.

1 $ hg update foo2 0 files updated, 0 files merged, 1 files removed, 0 files unresolved3 $ hg update4 0 files updated, 0 files merged, 0 files removed, 0 files unresolved

Al consignar un cambio a la rama foo se introducira una nueva cabeza.

1 $ echo something > somefile2 $ hg commit -A -m ’New file’3 adding somefile4 created new head5 $ hg heads6 changeset: 3:1ca81328634a7 branch: foo8 tag: tip9 parent: 1:b108bc883cbc

10 user: Bryan O’Sullivan <[email protected]>11 date: Tue Feb 10 18:23:17 2009 +000012 summary: New file13

14 changeset: 2:7f37f4b1f9a315 branch: bar16 user: Bryan O’Sullivan <[email protected]>17 date: Tue Feb 10 18:23:17 2009 +000018 summary: Third commit19

89

Page 100: Mercurial

8.7. Nombres de ramas y fusionesPosiblemente ha notado que las fusiones en Mercurial no son simetricas. Supongamos que su repositorio tiene dos

cabezas, 17 y 23. Si yo invoco “hg update” a 17 y aplico “hg merge” a 23, Mercurial almacena 17 como el primerpadre de la fusion, y 23 como el segundo. Mientras que si hago “hg update” a 23 y despues aplico “hg merge” con17, grabara a 23 como el primer padre, y 17 como el segundo.

Esto afecta el como elige Mercurial el nombre de la rama cuando usted hace la fusion. Despues de una fusion,Mercurial mantendra el nombre de la rama del primer padre cuando consigne el resultado de la fusion. Si el primernombre de su padre es foo, y fusiona con bar, el nombre de la rama continuara siendo foo despues de fusionar.

No es inusual que un repositorio contenga varias cabezas, cada una con el mismo nombre de rama. Digamos queestoy trabajando en la rama foo, y usted tambien. Consignamos cambios distintos; yo jalo sus cambios; Ahora tengodos cabezas, cada una afirmando estar en la rama foo. El resultado de una fusion sera una unica cabeza en la rama foocomo usted esperarıa.

Pero si estoy trabajando en la rama bar, y fusiono el trabajo de la rama foo, el resultado permanecera en la ramabar.

1 $ hg branch2 bar3 $ hg merge foo4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved5 (branch merge, don’t forget to commit)6 $ hg commit -m ’Merge’7 $ hg tip8 changeset: 4:a6a8b6aebf339 branch: bar

10 tag: tip11 parent: 2:7f37f4b1f9a312 parent: 3:1ca81328634a13 user: Bryan O’Sullivan <[email protected]>14 date: Tue Feb 10 18:23:17 2009 +000015 summary: Merge16

En un ejemplo mas concreto, si yo estoy trabajando en la rama bleeding-edge, y deseo traer los arreglos masrecientes de la rama estable, Mercurial elegira el nombre de rama “correcto” (bleeding-edge) cuando yo jale unafusion desde estable.

8.8. Normalmente es util nombrar ramasNo deberıa considerar que las ramas nombradas son aplicables unicamente en situaciones con muchas ramas de

larga vida cohabitando en un mismo repositorio. Son muy utiles incluso en los casos de una rama por repositorio.En el caso mas sencillo, dar un nombre a cada rama ofrece un registro permanente acerca de en que conjunto de

cambios se genero la rama. Esto le ofrece mas contexto cuando este tratando de seguir el historial de un proyectoramificado de larga vida.

Si esta trabajando con repositorios compartidos, puede configurar el gancho pretxnchangegroup para que cadauno bloquee los cambios con nombres de rama “incorrectos” que estan por adicionarse. Este provee una defensasencilla, pero efectiva, para evitar que la gente publique accidentalmente cambios de una rama “super nueva” a larama “estable”. Tal gancho podrıa verse de la siguiente forma dentro de un repositorio compartido de hgrc.

1 [hooks]2 pretxnchangegroup.branch = hg heads --template ’branches ’ | grep mybranch

90

Page 101: Mercurial

Capıtulo 9

Encontrar y arreglar sus equivocaciones

Errar es humano, pero tratar adecuadamente las consecuencias requiere un sistema de control de revisiones deprimera categorıa. En este capıtulo, discutiremos algunas tecnicas que puede usar cuando encuentra que hay un prob-lema enraizado en su proyecto. Mercurial tiene unas caracterısticas poderosas que le ayudaran a isolar las fuentes delos problemas, y a dar cuenta de ellas apropiadamente.

9.1. Borrar el historial local

9.1.1. La consignacion accidentalTengo el problema ocasional, pero persistente de teclear mas rapido de lo que pienso, que aveces resulta en

consignar un conjunto de cambios incompleto o simplemente malo. En mi caso, el conjunto de cambios incompletoconsiste en que cree un nuevo fichero fuente, pero olvide hacerle “hg add”. Un conjunto de cambios“simplementemalo” no es tan comun, pero sı resulta muy molesto.

9.1.2. Hacer rollback una transaccionEn la seccion 4.2.2, mencione que Mercurial trata modificacion a un repositorio como una transaccion. Cada vez

que consigna un conjunto de cambios o lo jala de otro repositorio, Mercurial recuerda lo que hizo. Puede deshacer,o hacer roll back1, exactamente una de tales acciones usando la orden “hg rollback”. (Ver en la seccion 9.1.4 unaanotacion importante acerca del uso de esta orden.)

A continuacion una equivocacion que me sucede frecuentemente: consignar un cambio en el cual he creado unnuevo fichero, pero he olvidado hacerle “hg add”.

1 $ hg status2 M a3 $ echo b > b4 $ hg commit -m ’Add file b’

La salida de “hg status” despues de la consignacion confirma inmediatamente este error.

1 $ hg status2 ? b3 $ hg tip4 changeset: 1:be1cdd4c4fea5 tag: tip

1N. del T. El significado igual que en los ambientes de sistemas manejadores de bases de datos se refiere a la atomicidad e integridad al devolverun conjunto de acciones que permitan dejar el repositorio en un estado consistente previo

91

Page 102: Mercurial

6 user: Bryan O’Sullivan <[email protected]>7 date: Tue Feb 10 18:23:29 2009 +00008 summary: Add file b9

La consignacion capturo los cambios en el fichero a, pero no el nuevo fichero b. Si yo publicara este conjunto decambios a un repositorio compartido con un colega, es bastante probable que algo en a se refiriera a b, el cual podrıano estar presente cuando jalen mis cambios del repositorio. Me convertirıa el sujeto de cierta indignacion.

Como sea, la suerte me acompana—Encontre mi error antes de publicar el conjunto de cambios. Uso la orden “hgrollback”, y Mercurial hace desaparecer el ultimo conjunto de cambios.

1 $ hg rollback2 rolling back last transaction3 $ hg tip4 changeset: 0:c40e0a2ea41e5 tag: tip6 user: Bryan O’Sullivan <[email protected]>7 date: Tue Feb 10 18:23:29 2009 +00008 summary: First commit9

10 $ hg status11 M a12 ? b

El conjunto de cambios ya no esta en el historial del repositorio, y el directorio de trabajo cree que el fichero a hasido modificado. La consignacion y el roll back dejaron el directorio de trabajo exactamente como estaba antes de laconsignacion; el conjunto de cambios ha sido eliminado totlamente. Ahora puedo hacer “hg add” al fichero b, y hacerde nuevo la consignacion.

1 $ hg add b2 $ hg commit -m ’Add file b, this time for real’

9.1.3. Erroneamente jaladoMantener ramas de desarrollo separadas de un proyecto en distintos repositorios es una practica comun con Mercu-

rial. Su equipo de desarrollo puede tener un repositorio compartido para la version “0.9” y otra con cambios distintospara la version “1.0”.

Con este escenario, puede imaginar las consecuencias si tuviera un repositorio local “0.9”, y jalara accidentalmentelos cambios del repositorio compartido de la version “1.0” en este. En el peor de los casos, por falta de atencion, esposible que publique tales cambios en el arbol compartido “0.9”, confundiendo a todo su equipo de trabajo (pero nose preocupe, volveremos a este terrorıfico escenario posteriormente). En todo caso, es muy probable que usted se decuenta inmediatamente, dado que Mercurial mostrara el URL de donde esta jalando, o que vea jalando una sospechosagran cantidad de cambios en el repositorio.

La orden “hg rollback” excluira eficientemente los conjuntos de cambios que haya acabado de jalar. Mercurialagrupa todos los cambios de un “hg pull” a una unica transaccion y bastara con un “hg rollback” para deshaceresta equivocacion.

9.1.4. Despues de publicar, un roll back es futilEl valor de “hg rollback” se anula cuando ha publicado sus cambios a otro repositorio. Un cambio desaparece

totalmente al hacer roll back, pero solamente en el repositorio en el cual aplica “hg rollback”. Debido a que un rollback elimina el historial, no hay forma de que la desaparicion de un cambio se propague entre repositorios.

92

Page 103: Mercurial

Si ha publicado un cambio en otro repositorio—particularmente si es un repositorio publico—esencialmenteesta “en terreno agreste,” y tendra que reparar la equivocacion de un modo distinto. Lo que pasara si publica unconjunto de cambios en algun sitio, hacer rollback y despues volver a jalar del repositorio del cual habıa publicado, esque el conjunto de cambios reaparecera en su repositorio.

(Si esta absolutamente segruro de que el conjunto de cambios al que desea hacer rollback es el cambio mas recientedel repositorio en el cual publico, y sabe que nadie mas pudo haber jalado de tal repositorio, puede hacer rollback delconjunto de cambios allı, pero es mejor no confiar en una solucion de este estilo. Si lo hace, tarde o temprano unconjunto de cambios lograra colarse en un repositorio que usted no controle directamente (o del cual se ha olvidado),y volvera a hostigarle.)

9.1.5. Solamente hay un roll backMercurial almacena exactamente una transaccion en su bitacora de transacciones; tal transaccion es la mas reciente

de las que haya ocurrido en el repositorio. Esto significa que solamente puede hacer roll back a una transaccion. Siespera poder hacer roll back a una transaccion despues al antecesor, observara que no es el comportamiento queobtendra.

1 $ hg rollback2 rolling back last transaction3 $ hg rollback4 no rollback information available

Una vez que haya aplicado un rollback en una transaccion a un repositorio, no podra volver a hacer rollback hasta quehaga una consignacion o haya jalado.

9.2. Revertir un cambio equivocadoSi modifica un fichero y se da cuenta que no querıa realmente cambiar tal fichero, y todavıa no ha consignado

los cambios, la orden necesaria es “hg revert”. Observa el conjunto de cambios padre del directorio y restaura loscontenidos del fichero al estado de tal conjunto de cambios. (Es una forma larga de decirlo, usualmente deshace susmodificaciones.)

Ilustremos como actua la orden “hg revert” con un ejemplo pequeno. Comenzaremos modificando un fichero alcual Mercurial ya esta siguiendo.

1 $ cat file2 original content3 $ echo unwanted change >> file4 $ hg diff file5 diff -r f8694d2c79ed file6 --- a/file Tue Feb 10 18:23:21 2009 +00007 +++ b/file Tue Feb 10 18:23:21 2009 +00008 @@ -1,1 +1,2 @@9 original content

10 +unwanted change

Si no queremos ese cambio, podemos aplicar “hg revert” al fichero.

1 $ hg status2 M file3 $ hg revert file4 $ cat file5 original content

93

Page 104: Mercurial

La orden “hg revert” nos brinda un grado adicional de seguridad guardando nuestro fichero modificado con laextension .orig.

1 $ hg status2 ? file.orig3 $ cat file.orig4 original content5 unwanted change

Este es un resumen de casos en los cuales la orden “hg revert” es de utilidad. Describiremos cada uno de elloscon mas detalle en la seccion siguiente.

Si usted modifica un fichero, lo restaurara a su estado sin modificacion previo.

Si usted hace “hg add” a un fichero, revertira el estado de “adicionado” del fichero, pero no lo tocara

Si borra un fichero sin decirle a Mercurial, restaurara el fichero con sus contenidos sin modificacion.

Si usa la orden “hg remove” para eliminar un fichero, deshara el estado “removido” del fichero, y lo restau-rara con sus contenidos sin modificacion.

9.2.1. Errores al administrar ficherosLa orden “hg revert” es util para mas que ficheros modificados. Le permite reversar los resultados de todas las

ordenes de administracion de ficheros que provee Mercurial—“hg add”, “hg remove”, y las demas.Si usted hace “hg add” a un fichero, y no deseaba que Mercurial le diera seguimiento, use “hg revert” para

deshacer la adicion. No se preocupe; Mercurial no modificara de forma alguna el fichero. Solamente lo “desmarcara”.

1 $ echo oops > oops2 $ hg add oops3 $ hg status oops4 A oops5 $ hg revert oops6 $ hg status7 ? oops

De forma similar, Si le solicita a Mercurial hacer “hg remove” a un fichero, puede usar “hg revert” para restaru-rarlo a los contenidos que tenıa la revision padre del directorio de trabajo.

1 $ hg remove file2 $ hg status3 R file4 $ hg revert file5 $ hg status6 $ ls file7 file

Funciona de la misma manera para un fichero que usted haya eliminado manualmente, sin decirle a Mercurial (recuerdeque en la terminologıa de Mercurial esta clase de fichero se llama “faltante”).

1 $ rm file2 $ hg status3 ! file4 $ hg revert file

94

Page 105: Mercurial

5 $ ls file6 file

Si usted revierte un “hg copy”, el fichero a donde se copio permanece en su directorio de trabajo, pero sinseguimiento. Dado que una copia no afecta el fichero fuente de copiado de ninguna maner, Mercurial no hace na-da con este.

1 $ hg copy file new-file2 $ hg revert new-file3 $ hg status4 ? new-file

Un caso ligeramente especial:revertir un renombramiento

Si hace “hg rename” a un fichero, hay un detalle que debe tener en cuenta. Cuando aplica “hg revert” a uncambio de nombre, no es suficiente proveer el nombre del fichero destino, como puede verlo en el siguiente ejemplo.

1 $ hg rename file new-file2 $ hg revert new-file3 $ hg status4 ? new-file

Como puede ver en la salida de “hg status”, el fichero con el nuevo nombre no se identifica mas como agregado,pero el fichero con el nombre-inicial se elimna! Esto es contra-intuitivo (por lo menos para mı), pero por lo menos esfacil arreglarlo.

1 $ hg revert file2 no changes needed to file3 $ hg status4 ? new-file

Por lo tanto, recuerde, para revertir un “hg rename”, debe proveer ambos nombres, la fuente y el destino.(A proposito, si elimina un fichero, y modifica el fichero con el nuevo nombre, al revertir ambos componentes

del renombramiento, cuando Mercurial restaure el fichero que fue eliminado como parte del renombramiento, nosera modificado. Si necesita que las modificaciones en el fichero destino del renombramiento se muestren, no olvidecopiarlas encima.)

Estos aspectos engorrosos al revertir un renombramiento se constituyen discutiblemente en un fallo de Mercurial.

9.3. Tratar cambios consignadosConsidere un caso en el que ha consignado el cambio a, y otro cambio b sobre este; se ha dado cuenta que el

cambio a era incorrecto. Mercurial le permite “retroceder” un conjunto de cambios completo automaticamente, yconstruir bloques que le permitan revertir parte de un conjunto de cambios a mano.

Antes de leer esta seccion, hay algo para tener en cuenta: la orden “hg backout” deshace cambios adicionandoal historial, sin modificar o borrar. Es la herramienta correcta si esta arreglando fallos, pero no si esta tratando dedeshacer algun cambio que tiene consecuencias catastroficas. Para tratar con esos, vea la seccion 9.4.

9.3.1. Retroceder un conjunto de cambiosLa orden “hg backout” le permite “deshacer” los efectos de todo un conjunto de cambios de forma automatizada.

Dado que el historial de Mercurial es inmutable, esta orden no se deshace del conjunto de cambios que usted deseadeshacer. En cambio, crea un nuevo conjunto de cambios que reversa el conjunto de cambios que usted indique.

95

Page 106: Mercurial

La operacion de la orden “hg backout” es un poco intrincada, y lo ilustraremos con algunos ejemplos. Primerocrearemos un repositorio con algunos cambios sencillos.

1 $ hg init myrepo2 $ cd myrepo3 $ echo first change >> myfile4 $ hg add myfile5 $ hg commit -m ’first change’6 $ echo second change >> myfile7 $ hg commit -m ’second change’

La orden “hg backout” toma un ID de conjunto de cambios como su argumento; el conjunto de cambios aretroceder. Normalmente “hg backout” le ofrecera un editor de texto para escribir el mensaje de la consignacion,para dejar un registro de por que esta retrocediendo. En este ejemplo, colocamos un mensaje en la consignacionusando la opcion -m.

9.3.2. Retroceder el conjunto de cambios puntaComenzamos retrocediendo el ultimo conjunto de cambios que consignamos.

1 $ hg backout -m ’back out second change’ tip2 reverting myfile3 changeset 2:1d9ee76a7513 backs out changeset 1:cab6a78bf14b4 $ cat myfile5 first change

Puede ver que la segunda lınea de myfile ya no esta presente. La salida de “hg log” nos da una idea de lo que laorden “hg backout” ha hecho.

1 $ hg log --style compact2 2[tip] 1d9ee76a7513 2009-02-10 18:23 +0000 bos3 back out second change4

5 1 cab6a78bf14b 2009-02-10 18:23 +0000 bos6 second change7

8 0 60b8d10ede6c 2009-02-10 18:23 +0000 bos9 first change

10

Vea que el nuevo conjunto de cambios que “hg backout” ha creado es un hijo del conjunto de cambios que retroced-imos. Es mas sencillo de ver en la figura 9.1, que presenta una vista grafica del historial de cambios. Como puede ver,el historial es bonito y lineal.

9.3.3. Retroceso de un cambio que no es la puntaSi desea retrocede un cambio distinto al ultimo que ha consignado, use la opcion --merge a la orden “hg backout”.

1 $ cd ..2 $ hg clone -r1 myrepo non-tip-repo3 requesting all changes4 adding changesets

96

Page 107: Mercurial

primer cambio

segundo cambio

reversarsegundo cambio

Figura 9.1: Retroceso de un cambio con la orden “hg backout”

5 adding manifests6 adding file changes7 added 2 changesets with 2 changes to 1 files8 updating working directory9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

10 $ cd non-tip-repo

Que resulta en un retroceso de un conjunto de cambios “en un solo tiro”, una operacion que resulta normalmenterapida y sencilla.

1 $ echo third change >> myfile2 $ hg commit -m ’third change’3 $ hg backout --merge -m ’back out second change’ 14 reverting myfile5 created new head6 changeset 3:688f1a6067e5 backs out changeset 1:cab6a78bf14b7 merging with changeset 3:688f1a6067e58 merging myfile9 0 files updated, 1 files merged, 0 files removed, 0 files unresolved

10 (branch merge, don’t forget to commit)

Si ve los contenidos del fichero myfile despues de finalizar el retroceso, vera que el primer y el tercer cambioestan presentes, pero no el segundo.

1 $ cat myfile2 first change3 third change

Como lo muestra el historial grafico en la figura 9.2, Mercurial realmente consigna dos cambios en estas situaciones(los nodos encerrados en una caja son aquellos que Mercurial consigna automaticamente). Antes de que Mercurialcomience el proceso de retroceso, primero recuerda cual es el padre del directorio de trabajo. Posteriormente hace un

97

Page 108: Mercurial

retroceso al conjunto de cambios objetivo y lo consigna como un conjunto de cambios. Finalmente, fusiona con elpadre anterior del directorio de trabajo, y consigna el resultado de la fusion.

primer cambio

segundo cambio

tercer cambioreversar

segundo cambio

fusión

automatizarfusión

Figura 9.2: Retroceso automatizado de un cambio a algo que no es la punta con la orden “hg backout”

El resultado es que usted termina “donde estaba”, solamente con un poco de historial adicional que deshace elefecto de un conjunto de cambios que usted querıa evitar.

Use siempre la opcion --merge

De hecho, dado que la opcion --merge siempre hara lo “correcto” este o no retrocediendo el conjunto de cambiospunta (p.e. no tratara de fusionar si esta retrocediendo la punta, dado que no es necesario), usted deberıa usar siempreesta opcion cuando ejecuta la orden “hg backout”.

9.3.4. Mas control sobre el proceso de retrocesoA pesar de que recomiendo usar siempre la opcion --merge cuando esta retrocediendo un cambio, la orden “hg

backout” le permite decidir como mezclar un retroceso de un conjunto de cambios. Es muy extrano que usted necestitetomar control del proceso de retroceso de forma manual, pero puede ser util entender lo que la orden “hg backout”esta haciendo automaticamente para usted. Para ilustrarlo, clonemos nuestro primer repositorio, pero omitamos elretroceso que contiene.

1 $ cd ..2 $ hg clone -r1 myrepo newrepo3 requesting all changes4 adding changesets5 adding manifests6 adding file changes7 added 2 changesets with 2 changes to 1 files

98

Page 109: Mercurial

8 updating working directory9 1 files updated, 0 files merged, 0 files removed, 0 files unresolved

10 $ cd newrepo

Como en el ejemplo anterior, consignaremos un tercer cambio, despues haremos retroceso de su padre, y veremosque pasa.

1 $ echo third change >> myfile2 $ hg commit -m ’third change’3 $ hg backout -m ’back out second change’ 14 reverting myfile5 created new head6 changeset 3:688f1a6067e5 backs out changeset 1:cab6a78bf14b7 the backout changeset is a new head - do not forget to merge8 (use "backout --merge" if you want to auto-merge)

Nuestro nuevo conjunto de cambios es de nuevo un descendiente del conjunto de cambio que retrocedimos; es por lotanto una nueva cabeza, no un descendiente del conjunto de cambios que era la punta. La orden “hg backout” fuemuy explıcita diciendolo.

1 $ hg log --style compact2 3[tip]:1 688f1a6067e5 2009-02-10 18:23 +0000 bos3 back out second change4

5 2 72a18afb4ae5 2009-02-10 18:23 +0000 bos6 third change7

8 1 cab6a78bf14b 2009-02-10 18:23 +0000 bos9 second change

10

11 0 60b8d10ede6c 2009-02-10 18:23 +0000 bos12 first change13

De nuevo, es mas sencillo lo que paso viendo una grafica del historial de revisiones, en la figura 9.3. Esto nosaclara que cuando usamos “hg backout” para retroceder un cambio a algo que no sea la punta, Mercurial anade unanueva cabeza al repositorio (el cambio que consigno esta encerrado en una caja).

Despues de que la orden “hg backout” ha terminado, deja un nuevo conjunto de cambios de “retroceso” como elpadre del directorio de trabajo.

1 $ hg parents2 changeset: 2:72a18afb4ae53 user: Bryan O’Sullivan <[email protected]>4 date: Tue Feb 10 18:23:13 2009 +00005 summary: third change6

Ahora tenemos dos conjuntos de cambios aislados.

1 $ hg heads2 changeset: 3:688f1a6067e53 tag: tip

99

Page 110: Mercurial

primer cambio

segundo cambio

tercer cambioreversar

segundo cambio

Figura 9.3: Retroceso usando la orden “hg backout”

4 parent: 1:cab6a78bf14b5 user: Bryan O’Sullivan <[email protected]>6 date: Tue Feb 10 18:23:13 2009 +00007 summary: back out second change8

9 changeset: 2:72a18afb4ae510 user: Bryan O’Sullivan <[email protected]>11 date: Tue Feb 10 18:23:13 2009 +000012 summary: third change13

Reflexionemos acerca de lo que esperamos ver como contenidos de myfile. El primer cambio deberıa estar pre-sente, porque nunca le hicimos retroceso. El segundo cambio debio desaparecer, puesto que es el que retrocedimos.Dado que la grafica del historial muestra que el tercer camlio es una cabeza separada, no esperamos ver el tercercambio presente en myfile.

1 $ cat myfile2 first change3 second change4 third change

Para que el tercer cambio este en el fichero, hacemos una fusion usual de las dos cabezas.

1 $ hg merge2 merging myfile3 0 files updated, 1 files merged, 0 files removed, 0 files unresolved4 (branch merge, don’t forget to commit)5 $ hg commit -m ’merged backout with previous tip’6 $ cat myfile7 first change8 third change

Despues de eso, el historial grafica de nuestro repositorio luce como la figura 9.4.

100

Page 111: Mercurial

primer cambio

segundo cambio

tercer cambioreversar

segundo cambio

fusiónmanual

Figura 9.4: Fusion manual de un retroceso

9.3.5. Por que “hg backout” hace lo que haceEsta es una descripcion corta de como trabaja la orden “hg backout”.

1. Se asegura de que el directorio de trabajo es “limpio”, esto es, que la salida de “hg status” deberıa ser vacıa.

2. Recuerda el padre actual del directorio de trabajo. A este conjunto de cambio lo llamaremos orig

3. Hace el equivalente de un “hg update” para sincronizar el directorio de trabajo con el conjunto de cambios queusted quiere retroceder. Lo llamaremos backout

4. Encuentra el padre del conjunto de cambios. Lo llamaremos parent.

5. Para cada fichero del conjunto de cambios que el retroceso afecte, hara el equivalente a “hg revert -rparent” sobre ese fichero, para restaurarlo a los contenidos que tenıa antes de que el conjunto de cambios fueraconsignado.

6. Se consigna el resultado como un nuevo conjunto de cambios y tiene a backout como su padre.

7. Si especifica --merge en la lınea de comandos, se fusiona con orig, y se consigna el resultado de la fusion.

Una vıa alternativa de implementar la orden “hg backout” serıa usar “hg export” sobre el conjunto de cambiosa retroceder como un diff y despues usar laa opcion --reverse de la orden patch para reversar el efecto del cambiosin molestar el directorio de trabajo. Suena mucho mas simple, pero no funcionarıa bien ni de cerca.

La razon por la cual “hg backout” hace una actualizacion, una consignacion, una fusion y otra consignacion espara dar a la maquinaria de fusion la mayor oportunidad de hacer un buen trabajo cuando se trata con todos los cambiosentre el cambio que esta retrocediendo y la punta actual.

101

Page 112: Mercurial

Si esta retrocediendo un conjunto de cambios que esta a unas 100 atras en su historial del proyecto, las posibili-dades de que una orden patch sea capaz de ser aplicada a un diff reverso, claramente no son altas, porque los cambiosque intervienen podrıan “no coincidir con el contexto” que patch usa para determinar si puede aplicar un parche (siesto suena como chachara, vea una discusion de la orden patch en 12.4). Adicionalmente, la maquinaria de fusionde Mercurial manejara ficheros y directorios renombrados, cambios de permisos, y modificaciones a ficheros binarios,nada de lo cual la orden patch puede manejar.

9.4. Cambios que nunca debieron ocurrirEn la mayorıa de los casos, la orden “hg backout” es exactamente lo que necesita para deshacer los efectos de

un cambio. Deja un registro permanente y exacto de lo que usted hizo, cuando se consigno el conjunto de cambiosoriginal y cuando se hizo la limpieza.

En ocasiones particulares, puede haber consignado un cambio que no deberıa estar de ninguna forma en el repos-itorio. Por ejemplo, serıa muy inusual, y considerado como una equivocacion, consignar los ficheros objeto junto conel codigo fuente. Los ficheros objeto no tienen valor intrınseco y son grandes, por lo tanto aumentan el tamano delrepositorio y la cantidad de tiempo que se emplea al clonar o jalar cambios.

Antes de discutir las opciones que tiene si consigno cambio del tipo “bolsa de papel deschable” (el tipo que estan malo que le gustarıa colocarse una bolsa de papel desechable en su cabeza), permıtame discutir primero unasaproximaciones que probablemente no funcionen.

Dado que Mercurial trata de forma acumulativa al historial—cada cambio se coloca encima de todos los cambiosque le preceden—usualmente usted no puede hacer que unos cambios desastrosos desaparezcan. La unica excepciones cuando usted ha acabado de consignar un cambio y este no ha sido publicado o jalado en otro repositorio. Ahı escuando puede usar la orden “hg rollback” con seguridad, como detalle en la seccion 9.1.2.

Despues de que usted haya publicado un cambio en otro repositorio, usted podrıa usar la orden “hg rollback”para hacer que en su copia local desaparezca el cambio, pero no tendra las consecuencias que desea. El cambioestara presente en un repositorio remoto, y reaparecera en su repositorio local la proxima vez que jale

Si una situacion como esta se presenta, y usted sabe en que repositorios su mal cambio se ha propagado, puedeintentar deshacerse del conjunto de cambios de todos los repositorios en los que se pueda encontrar. Esta por supuesto,no es una solucion satisfactoria: si usted deja de hacerlo en un solo repositorio, mientras este eliminandolo, el cambiotodavıa estara “allı afuera”, y podrıa propagarse mas tarde.

Si ha consignado uno o mas cambios despues del cambio que desea desaparecer, sus opciones son aun mas reduci-das. Mercurial no provee una forma de “cabar un hueco” en el historial, dejando los conjuntos de cambios intactos.

XXX This needs filling out. The hg-replay script in the examples directory works, but doesn’t handle mergechangesets. Kind of an important omission.

9.4.1. Como protegerse de cambios que han “escapado”Si ha consignado cambios a su repositorio local y estos han sido publicados o jalados en cualquier otro sitio, no es

necesariamente un desastre. Puede protegerse de antemano de ciertas clases de conjuntos de cambios malos. Esto esparticularmente sencillo si su equipo de trabajo jala cambios de un repositorio central.

Al configurar algunos ganchos en el repositorio central para validar conjuntos de cambios (ver capıtulo 10), puedeprevenir la publicacion automaticamente de cierta clase de cambios malos. Con tal configuracion, cierta clase deconjuntos de cambios malos tenderan naturalmente a“morir” debido a que no pueden propagarse al repositorio central.Esto sucedera sin necesidad de intervencion explıcita.

Por ejemplo, un gancho de cambios de entrada que verifique que un conjunto de cambios compila, puede prevenirque la gente “rompa la compilacion” inadvertidamente.

9.5. Al encuentro de la fuente de un falloAunque es muy bueno poder retroceder el conjunto de cambios que origino un fallo, se requiere que usted sepa

cual conjunto de cambios retroceder. Mercurial brinda una orden invaluable, llamada “hg bisect”, que ayuda a

102

Page 113: Mercurial

automatizar este proceso y a alcanzarlo muy eficientemente.La idea tras la orden “hg bisect” es que el conjunto de cambios que ha introducido un cambio de comportamiento

pueda identificarse con una prueba binaria sencilla. No tiene que saber que pieza de codigo introdujo el cambio, pero sirequiere que sepa como probar la existencia de un fallo. La orden “hg bisect” usa su prueba para dirigir su busquedadel conjunto de cambios que introdujo el codigo causante del fallo.

A continuacion un conjunto de escenarios que puede ayudarle a entender como puede aplicar esta orden.

La version mas reciente de su programa tiene un fallo que usted recuerda no estaba hace unas semanas, pero nosabe cuando fue introducido. En este caso, su prueba binaria busca la presencia de tal fallo.

Usted arreglo un fallo en un apurto, y es hora de dar por cerrado el caso en la base de datos de fallos de suequipo de trabajo. La base de datos de fallos requiere el ID del conjunto de cambios que permita dar por cerradoel caso, pero usted no recuerda que conjunto de cambios arreglo tal fallo. De nuevo la prueba binaria revisa lapresencia del fallo.

Su programa funciona correctamente, pero core 15 % mas lento que la ultima vez que lo midio. Usted deseasaber que conjunto de cambios introdujo esta disminucion de desempeno. En este caso su prueba binaria mideel desempeno de su programa, para ver donde es “rapido” y donde es “lento”.

Los tamanos de los componentes del proyecto que usted lleva se expandieron recientemente, y sospecha quealgo cambio en la forma en que se construye su proyecto.

Para estos ejemplos deberıa ser claro que la orden “hg bisect” es util no solamente para encontrar la fuente delos fallos. Puede usarla para encontrar cualquier “propiedad emergente” de un repositorio (Cualquier cosa que ustedno pueda encontrar con una busqueda de texto sencilla sobre los ficheros en el arbol) para la cual pueda escribir unaprueba binaria.

A continuacion introduciremos algo terminologıa, para aclarar que partes del proceso de busqueda son su respon-sabilidad y cuales de Mercurial. Una prueba es algo que usted ejecuta cuando “hg bisect” elige un conjunto decambios. Un sondeo es lo que “hg bisect” ejecuta para decidir si una revision es buena. Finalmente, usaremos lapalabra “biseccionar’, en frases como “buscar con la orden “hg bisect””.

Una forma sencilla de automatizar el proceso de busqueda serıa probar cada conjunto de cambios. Lo cual escalamuy poco. Si le tomo diez minutos hacer pruebas sobre un conjunto de cambios y tiene 10.000 conjuntos de cambiosen su repositorio, esta aproximacion exhaustiva tomarıa en promedio 35 dıas para encontrar el conjunto de cambiosque introdujo el fallo. Incluso si supiera que el fallo se introdujo en un de los ultimos 500 conjuntos de cambios ylimitara la busqueda a ellos, estarıa tomabdi mas de 40 horas para encontrar al conjunto de cambios culpable.

La orden “hg bisect” usa su conocimiento de la “forma” del historial de revisiones de su proyecto para haceruna busqueda proporcional al logaritmo del numero de conjunto de cambios a revisar (el tipo de busqueda que realizase llama busqueda binaria). Con esta aproximacion, el buscar entre 10.000 conjuntos de cambios tomara menos de3 horas, incluso a diez minutos por prueba (La busqueda requerira cerca de 14 pruebas). Al limitar la busqueda a laultima centena de conjuntos de cambios, tomara a lo sumo una hora (Apenas unas 7 pruebas).

La orden “hg bisect” tiene en cuenta la naturaleza “ramificada” del historial de revisiones del proyecto conMercurial, ası que no hay problemas al tratar con ramas, fusiones o cabezas multiples en un repositorio. Puede evitarramas enteras de historial con un solo sondeo.

9.5.1. Uso de la orden “hg bisect”A continuacion un ejemplo de “hg bisect” en accion.Nota: En las versiones 0.9.5 y anteriores de Mercurial, “hg bisect” no era unaorden incluıda en la distribucion principal: se ofrecıa como una extension de Mer-curial. Esta seccion describe la orden embebida y no la extension anterior.

Creamos un repostorio para probar el comando “hg bisect” de forma aislada

1 $ hg init mybug2 $ cd mybug

103

Page 114: Mercurial

Simularemos de forma sencilla un proyecto con un fallo: haremos cambios triviales en un ciclo, e indicaremos queun cambio especıfico sea el “fallo”. Este ciclo crea 35 conjuntos de cambios, cada uno anade un unico fichero alrepositorio. Representaremos nuestro “fallo” con un fichero que contiene el texto “tengo un gub”.

1 $ buggy_change=222 $ for (( i = 0; i < 35; i++ )); do3 > if [[ $i = $buggy_change ]]; then4 > echo ’i have a gub’ > myfile$i5 > hg commit -q -A -m ’buggy changeset’6 > else7 > echo ’nothing to see here, move along’ > myfile$i8 > hg commit -q -A -m ’normal changeset’9 > fi

10 > done

A continuacion observaremos como usar la orden “hg bisect”. Podemos usar el mecanismo de ayuda embebidaque trae Mercurial.

1 $ hg help bisect2 hg bisect [-gbsr] [REV]3

4 subdivision search of changesets5

6 This command helps to find changesets which introduce problems.7 To use, mark the earliest changeset you know exhibits the problem8 as bad, then mark the latest changeset which is free from the9 problem as good. Bisect will update your working directory to a

10 revision for testing. Once you have performed tests, mark the11 working directory as bad or good and bisect will either update to12 another candidate changeset or announce that it has found the bad13 revision.14

15 options:16

17 -r --reset reset bisect state18 -g --good mark changeset good19 -b --bad mark changeset bad20 -s --skip skip testing changeset21 -U --noupdate do not update to target22

23 use "hg -v help bisect" to show global options

La orden “hg bisect” trabaja en etapas, de la siguiente forma:

1. Usted ejecuta una prueba binaria.

Si la prueba es exitosa, usted se lo indicara a “hg bisect” ejecutando la orden “hg bisect good”.

Si falla, ejecutara la orden “hg bisect --bad”.

2. La orden usa su informacion para decidir que conjuntos de cambios deben probarse a continuacion.

3. Actualiza el directorio de trabajo a tal conjunto de cambios y el proceso se lleva a cabo de nuevo.

104

Page 115: Mercurial

El proceso termina cuando “hg bisect” identifica un unico conjunto de cambios que marca el punto donde se encon-tro la transicion de “exitoso” a “fallido”.

Para comenzar la busqueda, es indispensable ejecutar la orden “hg bisect --reset”.

1 $ if hg -v | head -1 | grep -e "version 0.*"2 > then3 #On mercurial 1.0 --init disappeared4 > hg bisect --init5 > fi

En nuestro caso, la prueba binaria es sencilla: revisamos si el fichero en el repositorio contiene la cadena “tengoun gub”. Si la tiene, este conjunto de cambios contiene aquel que “causo el fallo”. Por convencion, un conjunto decambios que tiene la propiedad que estamos buscando es “malo”, mientras que el otro que no la tiene es “bueno”.

En la mayorıa de casos, la revision del directorio actual (usualmente la punta) exhibe el problema introducido porel cambio con el fallo, por lo tanto la marcaremos como “mala”.

1 $ hg bisect --bad

Nuestra proxima tarea es nominar al conjunto de cambios que sabemos no tiene el fallo; la orden “hg bisect”“acotara” su busqueda entre el primer par de conjuntos de cambios buenos y malos. En nuestro caso, sabemos que larevision 10 no tenıa el fallo. (Mas adelante dire un poco mas acerca de la eleccion del conjunto de cambios “bueno”.)

1 $ hg bisect --good 102 Testing changeset 22:ec1c6526e0eb (24 changesets remaining, ˜4 tests)3 0 files updated, 0 files merged, 12 files removed, 0 files unresolved

Note que esta orden mostro algo.

Nos dijo cuantos conjuntos de cambios debe considerar antes de que pueda identifica aquel que introdujo elfallo, y cuantas pruebas se requeriran.

Actualizo el directorio de trabajo al siguiente conjunto de cambios, y nos dijo que conjunto de cambios esta eval-uando.

Ahora ejecutamos nuestra prueba en el directorio de trabajo. Usamos la orden grep para ver si nuestro fichero“malo” esta presente en el directorio de trabajo. Si lo esta, esta revision es mala; si no esta revision es buena.

1 $ if grep -q ’i have a gub’ *2 > then3 > result=bad4 > else5 > result=good6 > fi7 $ echo this revision is $result8 this revision is bad9 $ hg bisect --$result

10 Testing changeset 16:8fb73e2ea5f9 (12 changesets remaining, ˜3 tests)11 0 files updated, 0 files merged, 6 files removed, 0 files unresolved

Esta prueba luce como candidata perfecta para automatizarse, por lo tanto la convertimos en una funcion de interfazde comandos.

105

Page 116: Mercurial

1 $ mytest() {2 > if grep -q ’i have a gub’ *3 > then4 > result=bad5 > else6 > result=good7 > fi8 > echo this revision is $result9 > hg bisect --$result

10 > }

Ahora podemos ejecutar un paso entero de pruebas con un solo comando, mytest.

1 $ mytest2 this revision is good3 Testing changeset 19:ffbbdeaade97 (6 changesets remaining, ˜2 tests)4 3 files updated, 0 files merged, 0 files removed, 0 files unresolved

Unas invocaciones mas de nuestra prueba, y hemos terminado.

1 $ mytest2 this revision is good3 Testing changeset 20:c0c13593daf1 (3 changesets remaining, ˜1 tests)4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved5 $ mytest6 this revision is good7 Testing changeset 21:d70e5938b22b (2 changesets remaining, ˜1 tests)8 1 files updated, 0 files merged, 0 files removed, 0 files unresolved9 $ mytest

10 this revision is good11 The first bad revision is:12 changeset: 22:ec1c6526e0eb13 user: Bryan O’Sullivan <[email protected]>14 date: Tue Feb 10 18:23:14 2009 +000015 summary: buggy changeset16

Aunque tenıamos unos 40 conjuntos de cambios en los cuales buscar, la orden “hg bisect” nos permitio encontrarel conjunto de cambios que introdujo el “fallo” con solo cinco pruebas. Porque el numero de pruebas que la orden “hgbisect” ejecuta crece logarıtmicamente con la cantidad de conjuntos de cambios a buscar, la ventaja que esto tienefrente a la busqueda por“fuerza bruta” crece con cada conjunto de cambios que usted adicione.

9.5.2. Limpieza despues de la busquedaCuando haya terminado de usar la orden “hg bisect” en un repositorio, puede usar la orden “hg bisect reset”

para deshacerse de la informacion que se estaba usando para lograr la busqueda. Lar orden no usa mucho espacio,ası que no hay problema si olvida ejecutar la orden. En todo caso, “hg bisect” no le permitira comenzar una nuevabusqueda sobre el repositorio hasta que no aplique “hg bisect reset”.

1 $ hg bisect --reset

106

Page 117: Mercurial

9.6. Consejos para encontrar fallos efectivamente

9.6.1. Dar una entrada consistenteLa orden “hg bisect” requiere que usted ofrezca un reporte correcto del resultado de cada prueba que aplique.

Si usted le dice que una prueba falla cuando en realidad era acertada, podrıa detectar la inconsistencia. Si puedeidentificar una inconsistencia en sus reportes, le dira que un conjunto de cambios particular es a la vez bueno y malo.Aunque puede no hacerlo; estarıa tratando de reportar un conjunto de cambios como el responsable de un fallo aunqueno lo sea.

9.6.2. Automatizar tanto como se puedaCuando comence a usar la orden “hg bisect”, intente ejecutar algunas veces las pruebas a mano desde la lınea

de comandos. Es una aproximacion a la cual no esta acostumbrado. Despues de algunos intentos, me di cuenta queestaba cometiendo tantas equivocaciones que tenıa que comenzar de nuevo con mis busquedas varias veces antes dellegar a los resultados deseados.

Mi problema inicial al dirigir a la orden “hg bisect” manualmente ocurrieron incluso con busquedas en reposito-rios pequenos; si el problema que esta buscando es mas sutil, o el numero de pruebas que “hg bisect” debe aplicar,la posibilidad de errar es mucho mas alta. Una vez que comence a automatizar mis pruebas, obtuve mejores resultados.

La clave para las pruebas automatizadas se puede resumir en:

pruebe siempre buscando el mismo sıntoma y

ofrezca siempre datos consistentes a la orden “hg bisect”.

En mi tutorial de ejemplo anterior, la orden grep busca el sıntoma, y la construccion if toma el resultado de estaprueba y verifica que siempre alimentamos con los mismos datos a la orden “hg bisect”. La funcion mytest los unede una forma reproducible, logrando que cada prueba sea uniforme y consistente.

9.6.3. Verificar los resultadosDado que la salida de la busqueda de “hg bisect” es tan buena como los datos ofrecidos por usted, no confıe en

esto como si fuera la verdad absoluta. Una forma sencilla de asegurarse es ejecutar manualmente su prueba a cada unode los siguientes conjuntos de cambios:

El conjunto de cambios que se reporto como la primera version erronea. Su prueba deberıa dar un reporte defallo.

El conjunto de cambios padre (cada padre, si es una fusion). Su prueba deberıa reportar este(os) conjunto(s) decambios como bueno(s).

Un hijo del conjunto de cambios. Su prueba deberıa reportar al conjunto de cambios hijo como malo.

9.6.4. Tener en cuenta la interferencia entre fallosEs posible que su busqueda de un fallo pueda viciarse por la presencia de otro. Por ejemplo, digamos que su

programa se revienta en la revision 100, y que funciono correctamente en la revision 50. Sin su conocimiento, alguienintrodujo un fallo con consecuencias grandes en la revision 60, y lo arreglo en la revision 80. Sus resultados estarıandistorcionados de una o muchas formas.

Es posible que este fallo “enmascare” completamente al suyo, y que podrıa haberse revelado antes de que su propiofallo haya tenido oportunidad de manifestarse. Si no puede saltar el otro fallo (por ejemplo, este evita que su proyectose arme o compile), y de esta forma no se pueda revisar si su fallo este presente en un conjunto particular de cambios,la orden “hg bisect” no podra ayudarle directamente. En cambio, puede marcar este conjunto de cambios como alejecutar “hg bisect --skip”.

107

Page 118: Mercurial

Un problema distinto podrıa surgir si su prueba de la presencia de un fallo no es suficientemente especıfica. Siusted busca “mi programa se revienta”, entonces tanto su fallo como el otro fallo sin relacion que terminan presentandosıntomas distintos, podrıa terminar confundiendo a “hg bisect”.

Otra situacion en la cual serıa de mucha utilidad emplear a “hg bisect --skip” surge cuando usted no puedeprobar una revision porque su proyecto estaba en una situacion de rompimiento y por lo tanto en un estado en elcual era imposible hacer la prueba en esa revision, tal vez porque alguien consigno un cambio que hacıa imposible laconstruccion del proyecto.

9.6.5. Acotar la busqueda perezosamenteElegir los primeros “buenos” y “malos” conjuntos de cambios que marcaran los lımites de su busqueda en general

es sencillo, pero vale la pena discutirlo. Desde la perspectiva de “hg bisect”, el conjunto de cambios “mas nuevo”por convencion es el “malo”, y el otro conjunto de cambios es el “bueno”.

Si no recuerda cual podrıa ser el cambio “bueno”, para informar a “hg bisect”, podrıa hacer pruebas aleatoriasen el peor de los casos. Pero recuerde eliminar aquellos conjuntos de cambios que podrıan no exhibir el fallo (tal vezporque la caracterıstica donde se presenta el fallo todavıa no esta presente) y aquellos en los cuales otro fallo puedeenmascararlo (como se discutio anteriormente).

Incluso si termina “muy atras” por miles de conjuntos de cambios o meses de historial, solamente estara adicio-nando unas pruebas contadas para “hg bisect”, gracias al comportamiento logarıtmico.

108

Page 119: Mercurial

Capıtulo 10

Manejo de eventos en repositorios medianteganchos

Mercurial ofrece un poderoso mecanismo para permitirle a usted automatizar la ejecucion de acciones en respuestaa eventos que ocurran en un repositorio. En algunos casos, usted puede controlar incluso la respuesta de Mercurial adichos eventos.

Mercurial usa el termino gancho para identificar estas acciones. Los ganchos son conocidos como “disparadores”en algunos sistemas de control de revisiones, pero los dos nombres se refieren al mismo concepto.

10.1. Vistazo general de ganchos en MercurialA continuacion se encuentra una breve lista de los ganchos que Mercurial soporta. Volveremos a cada uno de estos

ganchos con mas detalle despues, en la seccion 10.8.

changegroup Es ejecutado luego de que un grupo de conjuntos de cambios ha sido traıdo al repositorio desde algun otro sitio.

commit Es ejecutado despues de la creacion de un conjunto de cambios en el repositorio local.

incoming Es ejecutado una vez por cada conjunto de cambios traıdo al repositorio desde otra ubicacion. Note la diferenciarespecto al gancho changegroup, que es ejecutado una vez por cada grupo de conjuntos de cambios que setraiga.

outgoing Es ejecutado luego de que un grupo de conjuntos de cambios ha sido transmitido desde el repositorio.

prechangegroup Es ejecutado antes de iniciar la recepcion de un grupo de conjuntos de cambios en el repositorio.

precommit De control. Es ejecutado antes de iniciar una consignacion.

preoutgoing De control. Es ejecutado antes de iniciar la transmision de un grupo de conjuntos de cambios desde el repositorio.

pretag De control. Es ejecutado antes de crear una etiqueta.

pretxnchangegroup De control. Es ejecutado despues de haber recibido un grupo de conjuntos de cambios en el repositorio local,pero antes de que la transaccion se complete y los cambios sean permanentes dentro del repositorio.

pretxncommit De control. Es ejecutado luego de la creacion de un conjunto de cambios en el repositorio local, pero antes deque la transaccion que hace permanente el cambio sea completada.

preupdate De control. Es ejecutado antes de iniciar una actualizacion o fusion en el directorio de trabajo.

tag Es ejecutado despues de la creacion de una etiqueta.

109

Page 120: Mercurial

update Es ejecutado despues de que termina una actualizacion o una fusion.

Cada uno de los ganchos cuya descripcion empieza con la frase “de control” tiene la facultad de determinar si unaactividad puede continuar. Si el gancho se ejecuta con exito, la actividad puede continuar; si falla, o bien la actividadno es permitida, o se deshacen los cambios que se puedan haber llevado a cabo, dependiendo del gancho involucrado.

10.2. Ganchos y seguridad

10.2.1. Los ganchos se ejecutan con sus privilegios de usuarioCuando usted ejecuta un comando de Mercurial en un repositorio, y el comando causa la ejecucion de un gancho,

dicho gancho se ejecuta en su sistema, en su cuenta de usuario, con sus privilegios. Ya que los ganchos son elementosarbitrarios de codigo ejecutable, usted deberıa tratarlos con un nivel adecuado de desconfianza. No instale un ganchoa menos en que confıe en quien lo creo y en lo que el gancho hace.

En algunos casos, usted puede estar expuesto a ganchos que usted no instalo. Si usted usa Mercurial en un sistemaextrano, tenga en cuenta que Mercurial ejecutara los ganchos definidos en el fichero hgrc.

Si esta trabajando con un repositorio propiedad de otro usuario, Mercurial podra ejecutar los ganchos definidosen el repositorio de dicho usuario, pero los ejecutara como “usted”. Por ejemplo, si usted jala (“hg pull”) desde eserepositorio, y el .hg/hgrc define un gancho saliente (outgoing), dicho gancho se ejecuta bajo su cuenta de usuario,aun cuando usted no es el propietario del repositorio.

Nota: Esto solo aplica si usted esta jalando desde un repositorio en un sistemade ficheros local o de red. Si esta jalando a traves de http o ssh, cualquier gan-cho saliente (outgoing) se ejecutara bajo la cuenta que esta ejecutando el procesoservidor, en el servidor.

XXX Para ver que ganchos han sido definidos en un repositorio, use el comando “hg config hooks”. Si ustedesta trabajando en un repositorio, pero comunicandose con otro que no le pertenece (por ejemplo, usando “hg pull”o “hg incoming”), recuerde que los ganchos que debe considerar son los del otro repositorio, no los del suyo.

10.2.2. Los ganchos no se propaganEn Mercurial, no se hace control de revisiones de los ganchos, y no se propagan cuando usted clona, o jala de, un

repositorio. El motivo para esto es simple: un gancho es codigo ejecutable arbitrario. Se ejecuta bajo su identidad, consu nivel de privilegios, en su maquina.

Serıa extremadamente descuidado de parte de cualquier sistema distribuido de control de revisiones el implementarcontrol de revisiones para ganchos, ya que esto ofrecerıa maneras facilmente aprovechables de subvertir las cuentasde los usuarios del sistema de control de revisiones.

Ya que Mercurial no propaga los ganchos, si usted esta colaborando con otras personas en un proyecto comun,no deberıa asumir que ellos estan usando los mismos ganchos para Mercurial que usted usa, o que los de ellos estanconfigurado correctamente. Usted deberıa documentar los ganchos que usted espera que la gente use.

En una intranet corporativa, esto es algo mas facil de manejar, ya que usted puede, por ejemplo, proveer unainstalacion “estandar” de Mercurial en un sistema de ficheros NFS, y usar un fichero hgrc global para definir losganchos que veran todos los usuarios. Sin embargo, este enfoque tiene sus lımites; vea mas abajo.

10.2.3. Es posible hacer caso omiso de los ganchosMercurial le permite hacer caso omiso de la deficinion de un gancho, a traves de la redefinicion del mismo. Usted

puede deshabilitar el gancho fijando su valor como una cadena vacıa, o cambiar su comportamiento como desee.Si usted instala un fichero hgrc a nivel de sistema o sitio completo que define algunos ganchos, debe entender que

sus usuarios pueden deshabilitar o hacer caso omiso de los mismos.

110

Page 121: Mercurial

10.2.4. Asegurarse de que ganchos crıticos sean ejecutadosAlgunas veces usted puede querer hacer respetar una polıtica, y no permitir que los demas sean capaces de evitarla.

Por ejemplo, usted puede tener como requerimiento que cada conjunto de cambios debe pasar un riguroso conjunto depruebas. Definir este requerimientos a traves de un gancho en un fichero hgrc global no servira con usuarios remotosen computadoras portatiles, y por supuesto que los usuarios locales pueden evitar esto a voluntad haciendo caso omisodel gancho.

En vez de eso, usted puede definir las polıticas para usar Mercurial de tal forma que se espere que los usuar-ios propaguen los cambios a traves de un servidor “canonico” bien conocido que usted ha asegurado y configuradoapropiadamente.

Una manera de hacer esto es a traves de una combinacion de ingenierıa social y tecnologıa. Cree una cuenta deacceso restringido; los usuarios pueden empujar cambios a traves de la red a los repositorios administrados por estacuenta, pero no podran ingresar a dicha cuenta para ejecutar ordenes en el interprete de comandos. En este escenario,un usuario puede enviar un conjunto de cambios que contenga la porquerıa que el desee.

Cuando alguien empuja un conjunto de cambios al servidor del que todos jalan, el servidor probara el conjunto decambios antes de aceptarlo como permanente, y lo rechazara si no logra pasar el conjunto de pruebas. Si la gente solojala cambios desde este servidor de filtro, servira para asegurarse de que todos los cambios que la gente jala han sidoexaminados automaticamente

10.3. Precauciones con ganchos pretxn en un repositorio de acceso com-partido

Si usted desea usar ganchos para llevar a cabo automaticamente algun trabajo en un repositorio al que variaspersonas tienen acceso compartido, debe tener cuidado con la forma de hacerlo.

Mercurial solo bloquea un repositorio cuando esta escribiendo al mismo, y solo las partes de Mercurial que escribenal repositorio le prestan atencion a los bloqueos. Los bloqueos de escritura son necesarios para evitar que multiplesescritores simultaneos interfieran entre sı, corrompiendo el repositorio.

Ya que Mercurial tiene cuidado con el orden en que lee y escribe datos, no necesita adquirir un bloqueo cuandodesea leer datos del repositorio. Las partes de Mercurial que leen del repositorio nunca le prestan atencion a losbloqueos. Este esquema de lectura libre de bloqueos incremententa en gran medida el desempeno y la concurrencia.

Sin embargo, para tener un gran desempeno es necesario hacer sacrificios, uno de los cuales tiene el potencial decausarle problemas a menos de que usted este consciente de el. Describirlo requiere algo de detalle respecto a comoMercurial anade conjuntos de cambios al repositorio y como lee esos cambios de vuelta.

Cuando Mercurial escribe metadatos, los escribe directamente en el fichero de destino. Primero escribe los datosdel fichero, luego los datos del manifiesto (que contienen punteros a los nuevos datos del fichero), luego datos de labitacora de cambios (que contienen punteros a los nuevos datos del manifiesto). Antes de la primera escritura a cadafichero, se guarda un registro de donde estaba el final de fichero en su registro de transacciones. Si la transacciondebe ser deshecha, Mercurial simplemente trunca cada fichero de vuelta al tamano que tenıa antes de que empezara latransaccion.

Cuando Mercurial lee metadatos, lee la bitacora de cambios primero, y luego todo lo demas. Como un lector soloaccedera a las partes del manifiesto o de los metadatos de fichero que el puede ver en la bitacora de cambios, nuncapuede ver datos parcialmente escritos.

Algunos ganchos de control (pretxncommit y pretxnchangegroup) se ejecutan cuando una transaccion esta casicompleta. Todos los metadatos han sido escritos, pero Mercurial aun puede deshacer la transaccion y hacer que losdatos recien escritos desaparezcan.

Si alguno de estos ganchos permanece en ejecucion por mucho tiempo, abre una ventana de tiempo en la que unlector puede ver los metadatos de conjuntos de cambios que aun no son permanentes y que no deberıa considerarseque esten “realmante ahı”. Entre mas tiempo tome la ejecucion del gancho, mas tiempo estara abierta esta ventana.

111

Page 122: Mercurial

10.3.1. Ilustracion del problemaEn principio, un buen uso del gancho pretxnchangegroup serıa ensamblar y probar automaticamente todos los

cambios entrantes antes de que sean aceptados en un repositorio central. Esto le permitirıa a usted garantizar que nadiepueda empujar cambios que “rompan el ensamblaje”. Pero si un cliente puede jalar cambios mientras estan siendoprobados, la utilidad de esta prueba es nula; alguien confiado puede jalar cambios sin probar, lo que potencialmentepodrıa romper su proceso de ensamblaje.

La respuesta tecnica mas segura frente a este retos es montar dicho repositorio “guardian” como unidireccional.Permita que reciba cambios desde el exterior, pero no permita que nadie jale cambios de el (use el gancho preoutgoingpara bloquear esto). Configure un gancho changegroup para que si el ensamblaje o prueba tiene exito, el gancho em-puje los nuevos cambios a otro repositorio del que la gente pueda jalar.

En la practica, montar un cuello de botella centralizado como este a menudo no es una buena idea, y la visibilidadde las transacciones no tiene nada que ver con el problema. A medida que el tamano de un proyecto—y el tiempoque toma ensamblarlo y probarlo—crece, usted se acerca rapidamente a un lımite con este enfoque “pruebe antes decomprar”, en el que tiene mas conjuntos de cambios a probar que tiempo para ocuparse de ellos. El resultado inevitablees frustracion para todos los que esten involucrados.

Una aproximacion que permite manejar mejor el crecimiento es hacer que la gente ensamble y pruebe antes deempujar, y ejecutar el ensamble y pruebas automaticas centralmente despues de empujar, para asegurarse de que todoeste bien. La ventaja de este enfoque es que no impone un lımite a la rata en la que un repositorio puede aceptarcambios.

10.4. Tutorial corto de uso de ganchosEscribir un gancho para Mercurial es facil. Empecemos con un gancho que se ejecute cuando usted termine un

“hg commit”, y simplemente muestre el hash del conjunto de cambios que usted acaba de crear. El gancho se lla-mara commit.

1 $ hg init hook-test2 $ cd hook-test3 $ echo ’[hooks]’ >> .hg/hgrc4 $ echo ’commit = echo committed $HG_NODE’ >> .hg/hgrc5 $ cat .hg/hgrc6 [hooks]7 commit = echo committed $HG_NODE8 $ echo a > a9 $ hg add a

10 $ hg commit -m ’testing commit hook’11 committed 233120f5d09c76e782c6ea86ccee729e735bf48f

Figura 10.1: Un gancho simple que se ejecuta al hacer la consignacion de un conjunto de cambios

Todos los ganchos siguen el patron del ejemplo 10.1. Usted puede anadir una entrada a la seccion [hooks] de sufichero hgrc. A la izquierda esta el nombre del evento respecto al cual dispararse; a la derecha esta la accion a llevara cabo. Como puede ver, es posible ejecutar cualquier orden de la lınea de comandos en un gancho. Mercurial le pasainformacion extra al gancho usando variables de entorno (busque HG NODE en el ejemplo).

10.4.1. Llevar a cabo varias acciones por eventoA menudo, usted querra definir mas de un gancho para un tipo de evento particular, como se muestra en el ejem-

plo 10.2. Mercurial le permite hacer esto anadiendo una extension al final del nombre de un gancho. Usted extiende

112

Page 123: Mercurial

el nombre del gancho poniendo el nombre del gancho, seguido por una parada completa (el caracter “.”), seguidode algo mas de texto de su eleccion. Por ejemplo, Mercurial ejecutara tanto commit.foo como commit.bar cuandoocurra el evento commit.

1 $ echo ’commit.when = echo -n "date of commit: "; date’ >> .hg/hgrc2 $ echo a >> a3 $ hg commit -m ’i have two hooks’4 committed df4f3f8972104d2a43d114e51edd05719efccd425 date of commit: Tue Feb 10 18:23:23 GMT 2009

Figura 10.2: Definicion de un segundo gancho commit

Para dar un orden bien definido de ejecucion cuando hay multiples ganchos definidos para un evento, Mercurialordena los ganchos de acuerdo a su extension, y los ejecuta en dicho orden. En el ejemplo de arribam commit.bar seejecutara antes que commit.foo, y commit se ejecutara antes de ambos.

Es una buena idea usar una extension descriptiva cuando usted define un gancho. Esto le ayudara a recordar paraque se usa el gancho. Si el gancho falla, usted recibira un mensaje de error que contiene el nombre y la extension delgancho, ası que usar una extension descriptiva le dara una pista inmediata de porque el gancho fallo (vea un ejemploen la seccion 10.4.2).

10.4.2. Controlar cuando puede llevarse a cabo una actividadEn los ejemplos anteriores, usamos el gancho commit, que es ejecutado despues de que se ha completado una

consignacion. Este es uno de los varios ganchos que Mercurial ejecuta luego de que una actividad termina. Talesganchos no tienen forma de influenciar la actividad como tal.

Mercurial define un numero de eventos que ocurren antes de que una actividad empiece; o luego de que empiece,pero antes de que termine. Los ganchos que se disparan con estos eventos tienen la capacidad adicional de elegir si laactividad puede continuar, o si su ejecucion es abortada.

El gancho pretxncommit se ejecuta justo antes de que una consignacion se ejecute. En otras palabras, los metadatosque representan el conjunto de cambios han sido escritos al disco, pero no se ha terminado la transaccion. El ganchopretxncommit tiene la capacidad de decidir si una transaccion se completa, o debe deshacerse.

Si el gancho pretxncommit termina con un codigo de salida de cero, se permite que la transaccion se complete; laconsignacion termina; y el gancho commit es ejecutado. Si el gancho pretxncommit termina con un codigo de salidadiferente de cero, la transaccion es revertida; los metadatos representando el conjunto de cambios son borrados; y elgancho commit no es ejecutado.

El gancho en el ejemplo 10.3 revisa si el mensaje de consignacion contiene el ID de algun fallo. Si lo contiene, laconsignacion puede continuar. Si no, la consignacion es cancelada.

10.5. Escribir sus propios ganchosCuando usted escriba un gancho, puede encontrar util el ejecutar Mercurial o bien pasandole la opcion -v, o con el

valor de configuracion verbose fijado en “true” (verdadero). Cuando lo haga, Mercurial imprimira un mensaje antesde llamar cada gancho.

10.5.1. Escoger como debe ejecutarse su ganchoUsted puede escribir un gancho que funcione como un programa normal —tıpicamente un guion de lınea de

comandos—o como una funcion de Python que se ejecuta dentro del proceso Mercurial.Escribir un gancho como un programa externo tiene la ventaja de que no requiere ningun conocimiento del fun-

cionamiento interno de Mercurial. Usted puede ejecutar comandos Mercurial normales para obtener la informcion

113

Page 124: Mercurial

1 $ cat check_bug_id2 #!/bin/sh3 # check that a commit comment mentions a numeric bug id4 hg log -r $1 --template {desc} | grep -q "\<bug *[0-9]"5 $ echo ’pretxncommit.bug_id_required = ./check_bug_id $HG_NODE’ >> .hg/hgrc6 $ echo a >> a7 $ hg commit -m ’i am not mentioning a bug id’8 transaction abort!9 rollback completed

10 abort: pretxncommit.bug_id_required hook exited with status 111 $ hg commit -m ’i refer you to bug 666’12 committed d558e8617cbdf0b482db27a79d17f9a28af4701f13 date of commit: Tue Feb 10 18:23:23 GMT 2009

Figura 10.3: Uso del gancho pretxncommit para controlar consignaciones

extra que pueda necesitar. La contraparte de esto es que los ganchos externos son mas lentos que los ganchos internosejecutados dentro del proceso.

Un gancho Python interno tiene acceso completo a la API de Mercurial, y no se “externaliza” a otro proceso,ası que es inherentemente mas rapido que un gancho externo. Adicionalmente es mas facil obtener la mayorıa dela informacion que un gancho requiere a traves de llamadas directas a la API de Mercurial que hacerlo ejecutandocomandos Mercurial.

Si se siente a gusto con Python, o requiere un alto desempeno, escribir sus ganchos en Python puede ser una buenaeleccion. Sin embargo, cuando usted tiene un gancho bastante directo por escribir y no le importa el desempeno (elcaso de la mayorıa de los ganchos), es perfectamente admisible un guion de lınea de comandos.

10.5.2. Parametros para ganchosMercurial llama cada gancho con un conjunto de parametros bien definidos. En Python, un parametro se pasa

como argumento de palabra clave a su funcion de gancho. Para un programa externo, los parametros son pasadoscomo variables de entornos.

Sin importar si su gancho esta escrito en Python o como guion de lınea de comandos, los nombres y valores delos parametros especıficos de los ganchos seran los mismos. Un parametro booleano sera representado como un valorbooleano en Python, pero como el numero 1 (para “verdadero”) o 0 (para falso) en una variable de entorno para ungancho externo. Si un parametro se llama foo, el argumento de palabra clave para un gancho en Python tambien sellamara foo, mientras que la variable de entorno para un gancho externo se llamara HG FOO.

10.5.3. Valores de retorno de ganchos y control de actividadesUn gancho que se ejecuta exitosamente debe terminar con un codigo de salida de cero, si es externo, o retornar

el valor booleano “falso”, si es interno. Un fallo se indica con un codigo de salida diferente de cero desde un ganchoexterno, o un valor de retorno booleano “verdadero”. Si un gancho interno genera una excepcion, se considera que elgancho ha fallado.

Para los ganchos que controlan si una actividad puede continuar o no, cero/falso quiere decir “permitir”, mientrasque no-cero/verdadero/excepcion quiere decir “no permitir”.

10.5.4. Escribir un gancho externoCuando usted define un gancho externo en su fichero hgrc y el mismo es ejecutado, dicha definicion pasa a su

interprete de comandos, que hace la interpretacion correspondiente. Esto significa que usted puede usar elementos

114

Page 125: Mercurial

normales del interprete en el cuerpo del gancho.Un gancho ejecutable siempre es ejecutado con su directorio actual fijado al directorio raız del repositorio.Cada parametro para el gancho es pasado como una variable de entorno; el nombre esta en mayusculas, y tiene

como prefijo la cadena “HG ”.Con la excepcion de los parametros para los ganchos, Mercurial no define o modifica ninguna variable de entorno

al ejecutar un gancho. Es util recordar esto al escribir un gancho global que podrıa ser ejecutado por varios usuarioscon distintas variables de entorno fijadas. En situaciones con multiples usuarios, usted no deberıa asumir la existenciade ninguna variable de entorno, ni que sus valores sean los mismos que tenıan cuando usted probo el gancho en suambiente de trabajo.

10.5.5. Indicar a Mercurial que use un gancho internoLa sintaxis para definir un gancho interno en el fichero hgrc es ligeramente diferente de la usada para un gan-

cho externo. El valor del gancho debe comenzar con el texto “python:”, y continuar con el nombre completamentecualificado de un objeto invocable que se usara como el valor del gancho.

El modulo en que vive un gancho es importado automaticamente cuando se ejecuta un gancho. Siempre que ustedtenga el nombre del modulo y la variable de entorno PYTHONPATH ajustada adecuadamente, todo deberıa funcionar sinproblemas.

El siguiente fragmento de ejemplo de un fichero hgrc ilustra la sintaxis y significado de los conceptos queacabamos de describir.

1 [hooks]2 commit.example = python:mymodule.submodule.myhook

Cuando Mercurial ejecuta el gancho commit.example, importa mymodule.submodule, busca el objeto invocablellamado myhook, y lo invoca (llama).

10.5.6. Escribir un gancho internoEl gancho interno mas sencillo no hace nada, pero ilustra la estructura basica de la API1 para ganchos:

1 def myhook(ui, repo, **kwargs):2 pass

El primer argumento para un gancho Python siempre es un objeto mercurial.ui.ui. El segundo es un objeto repos-itorio; de momento, siempre es una instancia de mercurial.localrepo.localrepository. Despues de estos dosargumentos estan los argumentos de palabra clave. Los argumentos que se pasen dependeran del tipo de gancho que seeste llamando, pero un gancho siempre puede ignorar los argumentos que no le interesen, relegandolos a un diccionariode argumentos por palabras clave, como se hizo arriba con **kwargs.

10.6. Ejemplos de ganchos

10.6.1. Escribir mensajes de consignacion significativosEs difıcil de imaginar un mensaje de consignacion util y al mismo tiempo muy corto. El simple gancho pretxncommit

de la figura 10.4 evitara que usted consigne un conjunto de cambios con un mensaje de menos de 10 bytes de longitud.

1N. del T. Application Progamming Interface, Interfaz para Programacion de Aplicaciones

115

Page 126: Mercurial

1 $ cat .hg/hgrc2 [hooks]3 pretxncommit.msglen = test ‘hg tip --template {desc} | wc -c‘ -ge 104 $ echo a > a5 $ hg add a6 $ hg commit -A -m ’too short’7 transaction abort!8 rollback completed9 abort: pretxncommit.msglen hook exited with status 1

10 $ hg commit -A -m ’long enough’

Figura 10.4: Un gancho que prohıbe mensajes de consignacion demasiado cortos

10.6.2. Comprobar espacios en blanco finalesUn uso interesante para ganchos relacionados con consignaciones es ayudarle a escribir codigo mas limpio. Un

ejemplo simple de “codigo mas limpio” es la regla de que un cambio no debe anadir lıneas de texto que contengan“espacios en blanco finales”. El espacio en blanco final es una serie de caracteres de espacio y tabulacion que seencuentran al final de una lınea de texto. En la mayorıa de los casos, el espacio en blanco final es innecesario, ruidoinvisible, pero ocasionalmente es problematico, y la gente en general prefiere deshacerse de el.

Usted puede usar cualquiera de los ganchos precommit o pretxncommit para revisar si tiene el problema de losespacios en blanco finales. Si usa el gancho precommit, el gancho no sabra que ficheros se estan consignando, por loque se tendra que revisar cada fichero modificado en el repositorio para ver si tiene espacios en blanco finales. Si ustedsolo quiere consignar un cambio al fichero foo, y el fichero bar contiene espacios en blanco finales, hacer la revisionen el gancho precommit evitara que usted haga la consignacion de foo debido al problem en bar. Este no parece elenfoque adeucado.

Si usted escogiera el gancho pretxncommit, la revision no ocurrirıa sino hasta justo antes de que la transaccionpara la consignacion se complete. Esto le permitira comprobar por posibles problemas solo en los ficheros que seranconsignados. Sin embargo, si usted ingreso el mensaje de consignacion de manera interactiva y el gancho falla, latransaccion sera deshecha; usted tendra que reingresar el mensaje de consignacion luego de que corrija el problemacon los espacios en blanco finales y ejecute “hg commit” de nuevo.

1 $ cat .hg/hgrc2 [hooks]3 pretxncommit.whitespace = hg export tip | (! egrep -q ’ˆ\+.*[ \t]$’)4 $ echo ’a ’ > a5 $ hg commit -A -m ’test with trailing whitespace’6 adding a7 transaction abort!8 rollback completed9 abort: pretxncommit.whitespace hook exited with status 1

10 $ echo ’a’ > a11 $ hg commit -A -m ’drop trailing whitespace and try again’

Figura 10.5: Un gancho simple que revisa si hay espacios en blanco finales

La figura 10.5 presenta un gancho pretxncommit simple que comprueba la existencia de espacios en blancofinales. Este gancho es corto, pero no brinda mucha ayuda. Termina con un codigo de salida de error si un cambioanade una lınea con espacio en blanco final a cualquier fichero, pero no muestra ninguna informacion que pueda ser

116

Page 127: Mercurial

util para identificar el fichero o la lınea de texto origen del problema. Tambien tiene la agradable propiedad de noprestar atencion a las lıneas que no sufrieron modificaciones; solo las lıneas que introducen nuevos espacios en blancofinales causan problemas.

1 $ cat .hg/hgrc2 [hooks]3 pretxncommit.whitespace = .hg/check_whitespace.py4 $ echo ’a ’ >> a5 $ hg commit -A -m ’add new line with trailing whitespace’6 a, line 2: trailing whitespace added7 commit message saved to .hg/commit.save8 transaction abort!9 rollback completed

10 abort: pretxncommit.whitespace hook exited with status 111 $ sed -i ’s, *$,,’ a12 $ hg commit -A -m ’trimmed trailing whitespace’13 a, line 2: trailing whitespace added14 commit message saved to .hg/commit.save15 transaction abort!16 rollback completed17 abort: pretxncommit.whitespace hook exited with status 1

Figura 10.6: Un mejor gancho para espacios en blanco finales

El ejemplo de la figura 10.6 es mucho mas complejo, pero tambien mas util. El gancho procesa un diff unificadopara revisar si alguna lınea anade espacios en blanco finales, e imprime el nombre del fichero y el numero de lıneade cada ocurrencia. Aun mejor, si el cambio anade espacios en blanco finales, este gancho guarda el mensaje deconsignacion e imprime el nombre del fichero en el que el mensaje fue guardado, antes de terminar e indicarle aMercurial que deshaga la transaccion, para que uste pueda usar “hg commit -l nombre fichero” para reutilizarel mensaje de consignacion guardado anteriormente, una vez usted haya corregido el problema.

Como anotacion final, note en la figura 10.6 el uso de la caracterıstica de edicion in-situ de perl para eliminar losespacios en blanco finales en un fichero. Esto es lo suficientemente conciso y poderoso para que lo presente aquı.

1 perl -pi -e ’s,\s+$,,’ nombre fichero

10.7. Ganchos adicionalesMercurial se instala con varios ganchos adicionales. Usted puede encontrarlos en el directorio hgext del arbol de

ficheros fuente de Mercurial. Si usted esta usando un paquete binario de Mercurial, los ganchos estaran ubicados en eldirectorio hgext en donde su instalador de paquetes haya puesto a Mercurial.

10.7.1. acl—control de acceso a partes de un repositorioLa extension acl le permite controlar a que usuarios remotos les esta permitido empujar conjuntos de cambios a

un servidor en red. Usted puede proteger cualquier porcion de un repositorio (incluyendo el repositorio completo), detal manera que un usuario remoto especıfico pueda empujar cambios que no afecten la porcion protegida.

Esta extension implementa control de acceso basado en la identidad del usuario que empuja los conjuntos decambios, no en la identidad de quien hizo la consignacion de los mismos. Usar este gancho tiene sentido solo si setiene un servidor adecuadamente asegurado que autentique a los usuarios remotos, y si usted desea segurarse de quesolo se le permita a ciertos usuarios empujar cambios a dicho servidor.

117

Page 128: Mercurial

Configuracion del gancho acl

Para administrar los conjuntos de cambios entrantes, se debe usar el gancho acl como un gancho de tipo pretxnchangegroup.Esto le permite ver que ficheros son modificados por cada conjunto de cambios entrante, y deshacer el efecto de ungrupo de conjuntos de cambios si alguno de ellos modifica algun fichero “prohibido”. Ejemplo:

1 [hooks]2 pretxnchangegroup.acl = python:hgext.acl.hook

La extension acl es configurada mediante tres secciones.La seccion [acl] solo tiene una entrada, sources2, que lista las fuentes de los conjuntos de cambios entrantes a

las que el gancho debe prestar atencion. Usualmente usted no necesita configurar esta seccion.

serve Controlar conjuntos de cambios entrantes que estan llegando desde un repositorio a traves de http o ssh. Este esel valor por defecto de sources, y usualmente es el unico valor de configuracion que necesitara para este ıtem.

pull Controlar conjuntos de cambios entrantes que lleguen vıa un pull (jalado) desde un repositorio local.

push Controlar conjuntos de cambios entrantes que lleguen vıa un push (empuje) desde un repositorio local.

bundle Controlar conjuntos de cambios entrantes que lleguen desde otro repositorio a traves de un paquete.

La seccion [acl.allow] controla los usuarios a los que les esta permitido anadir conjuntos de cambios al repos-itorio. Si esta seccion no esta presente, se le permite acceso a todos los usuarios excepto a los que se les haya negadoexplıcitamente el acceso. Si esta seccion no esta presente, se niega el acceso a todos los usuarios excepto a todos a losque se les haya permitido de manera explıcita (ası que una seccion vacıa implica que se niega el acceso a todos losusuarios).

La seccion [acl.deny] determina a que usuarios no se les permite anadir conjuntos de cambios al repositorio. Siesta seccion no esta presente o esta vacıa, no se niega el acceso a ningun usuario.

La sintaxis para los ficheros [acl.allow] y [acl.deny] es identica. A la izquierda de cada entrada se encuentraun patron glob que asocia ficheros o directorios, respecto a la raız del repositorio; a la derecha, un nombre usuario.

En el siguiente ejemplo, el usuario escritordoc solo puede empujar cambios al directorio docs del repositorio,mientras que practicante puede enviar cambios a cualquier fichero o directorio excepto fuentes/sensitivo.

1 [acl.allow]2 docs/** = escritordoc3

4 [acl.deny]5 fuentes/sensitivo/** = practicante

Pruebas y resolucion de problemas

Si usted desea probar el gancho acl, ejecutelo habilitando la opcion de salida de depuracion habilitada. Ya queusted probablemente lo estara ejecutando en un servidor donde no es conveniente (o incluso posible) pasar la opcion--debug, no olvide que usted puede habilitar la salida de depuracion en su hgrc:

1 [ui]2 debug = true

Con esto habilitado, el gancho acl imprimira suficiente informacion para permitirle saber porque esta permitiendo odenegando la operacion de empujar a usuarios especıficos.

2N. del T. Fuentes.

118

Page 129: Mercurial

10.7.2. bugzilla—integracion con BugzillaLa extension bugzilla anade un comentario a un fallo Bugzilla siempre que encuentre una referencia al ID de

dicho fallo en un mensaje de consignacion. Usted puede instalar este gancho en un servidor compartido, para que cadavez que un usuario remoto empuje cambios al servidor, el gancho sea ejecutado.

Se anade un comentario al fallo que se ve ası (usted puede configurar los contenidos del comentario—vea masabajo):

1 Changeset aad8b264143a, made by Joe User <[email protected]> in2 the frobnitz repository, refers to this bug.3

4 For complete details, see5 http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a6

7 Changeset description:8 Fix bug 10483 by guarding against some NULL pointers

El valor de este gancho se encuentra en que automatiza el proceso de actualizar un fallo cuando un conjunto de cambiosse refiera a el. Si usted configura este gancho adecuadamente, hara facil para la gente navegar directamente desde unfallo Bugzilla a un conjunto de cambios que se refiere a ese fallo.

Usted puede usar el codigo de este gancho como un punto de partida para otras recetas de integracion con Bugzillaaun mas exoticas. Aca hay algunas posibilidades:

Requerir que cada conjunto de cambios tenga un ID de fallo en su mensaje de consignacion. En este caso, ustedquerra configurar el gancho como uno de tipo pretxncommit. Esto le permitira al gancho rechazar cambios queno contiene IDs de fallos.

Permitir a los conjuntos de cambios entrantes modificar automaticamente el estado de un fallo, ası como simple-mente anadir un comentario. Por ejemplo, el gancho podrıa reconocer la cadena “corregido fallo 31337” comola senal de que deberıa actualizar el estado del fallo 31337 a “requiere pruebas”.

Configuracion del gancho bugzilla

Usted deberıa configurar este gancho en el hgrc de su servidor como un gancho incoming3, por ejemplo comosigue:

1 [hooks]2 incoming.bugzilla = python:hgext.bugzilla.hook

Debido a la naturaleza especializada de este gancho, y porque Bugzilla no fue escrito con este tipo de integracionen mente, configurar este gancho es un proceso algo complejo.

Antes de empezar, usted debe instalar la interfaz de Python para MySQL en los sistemas en los que se vaya aejecutar el gancho. Si no esta disponible como paquete binario para su sistema, usted puede descargar el paquetedesde [Dus].

La informacion para configurar este gancho se ubica en la seccion [bugzilla] de su hgrc.

version La version de Bugzilla instalada en el servidor. El esquema de base de datos que Bugzilla usa cambia ocasion-almente, ası que este gancho debe saber exactamente que esquema usar. A la fecha, la unica version soportadaes la 2.16.

host El nombre de maquina (hostname) del servidor MySQL que almacena sus datos Bugzilla. La base de datos debeser configurada para permitir conexiones desde las maquinas en las que usted ejecute el gancho bugzilla.

3N. del T. Entrante.

119

Page 130: Mercurial

user El nombre de usuario que se usara para conectarse al servidor MySQL. La base de datos debe ser configuradapara permitir a dicho usuario conectarse desde cualquiera de las maquinas en las que se ejecute el ganchobugzilla. Este usuario debe tener acceso y poder modificar las tablas de Bugzilla. El valor por defecto paraeste ıtem es bugs, que es el nombre estandar del usuario para Bugzilla en una base de datos MySQL.

password La contrasena MySQL para el usuario configurado anteriormente. Esta es almacenada como texto plano, ası queusted debera asegurarse de que los usuarios no autorizados no puedan leer el fichero hgrc en donde usted guardaesta informacion.

db El nombre de la base de datos Bugzilla en el servidor MySQL. El nombre por defecto para este ıtem es bugs,que es el nombre estandar de la base de datos MySQL en donde Bugzilla almacena sus datos.

notify Si usted desea que Bugzilla envıe un correo de notificacion a los suscriptores despues de que el gancho hayaanadido un comentario a un fallo, necesitara que este gancho ejecute un comando siempre que actualice la basede datos. El comando que se ejecute depende de en donde haya sido instalado Bugzilla, pero tıpicamente severa ası, si usted ha instalado Bugzilla en /var/www/html/bugzilla:

1 cd /var/www/html/bugzilla && ./processmail %s [email protected]

El programa processmail de Bugzilla espera recibir un ID de fallo (el gancho reemplaza “ %s” por el ID delfallo) y una direccion de correo. Tambien espera poder escribir a ciertos ficheros en el directorio en que seejecuta. Si Bugzilla y este gancho no estan instalados en la misma maquina, usted debera encontrar una manerade ejecutar processmail en el servidor donde esta instalado Bugzilla.

Asociar nombres de consignadores a nombres de usuario Bugzilla

Por defecto, el gancho bugzilla trata de usar la direccion de correo electronico de la persona que hizo laconsignacion del conjunto de cambios como el nombre de usuario Bugzilla con el cual debe actualizar el fallo. Siesto no se ajusta a sus necesidades, es posible asociar direcciones de correo a nombres de usuario Bugzilla usando unaseccion [usermap].

Cada ıtem en la seccion [usermap] contiene una direccion de correo electronico a la izquierda, y un nombre deusuario Bugzilla a la derecha.

1 [usermap]2 [email protected] = jane

Usted puede mantener los datos de [usermap] en un fichero hgrc, o decirle al gancho bugzilla que lea la informa-cion desde un fichero usermap externo. En este caso, usted puede almacenar los datos de usermap en (por ejemplo) unrepositorio modificable por los usuarios. Esto hace posible para sus usuarios mantener sus propias entradas usermap.El fichero hgrc principal se verıa ası:

1 # fichero hgrc normal se refiere a un fichero usermap externo2 [bugzilla]3 usermap = /home/hg/repos/userdata/bugzilla-usermap.conf

Mientras que el fichero usermap al que se hace referencia se verıa ası:

1 # bugzilla-usermap.conf - dentro de un repositorio hg2 [usermap]3 [email protected] = steph

120

Page 131: Mercurial

Configurar el texto que se anade a un fallo

Usted puede configurar el texto que este gancho anade como comentario; usted los especifica como una plantillaMercurial. Varias entradas hgrc (aun en la seccion [bugzilla]) controlan este comportamiento.

strip La cantidad de elementos iniciales de ruta a remover de un nombre de ruta del repositorio para construir una rutaparcial para una URL. Por ejemplo, si los repositorios en su servidor se ubican en /home/hg/repos, y ustedtiene un repositorio cuya ruta es /home/hg/repos/app/tests, entonces fijar strip a 4 resultara en una rutaparcial de app/tests. El gancho hara disponible esta ruta parcial cuando expanda una plantilla, como webroot.

template El texto de la plantilla a usar. En adicion a las variables usuales relacionadas con conjuntos de cambios, estaplantilla puede usar hgweb (el valor del ıtem de configuracion hgweb de arriba) y webroot (la ruta construidausando strip arriba).

Adicionalmente, usted puede anadir un ıtem baseurl a la seccion [web] de su hgrc. El gancho bugzilla pub-licara esto cuando expanda una plantilla, como la cadena base a usar cuando se construya una URL que le permita alos usuarios navegar desde un comentario de Bugzilla a la vista de un conjunto de cambios. Ejemplo:

1 [web]2 baseurl = http://hg.domain.com/

A continuacion se presenta un ejemplo completo de configuracion para el gancho bugzilla.

1 [bugzilla]2 host = bugzilla.example.com3 password = mypassword4 version = 2.165 # server-side repos live in /home/hg/repos, so strip 4 leading6 # separators7 strip = 48 hgweb = http://hg.example.com/9 usermap = /home/hg/repos/notify/bugzilla.conf

10 template = Changeset {node|short}, made by {author} in the {webroot}11 repo, refers to this bug. nFor complete details, see12 {hgweb}{webroot}?cmd=changeset;node={node|short} nChangeset13 description: n t{desc|tabindent}

Pruebas y resolucion de problemas

Los problemas mas comunes que aparecen en la configuracion del gancho bugzilla suelen estar relacionados conla ejecucion del guion de Bugzilla processmail y la asociacion de nombres de consignadores a nombres de usuario.

Recuerde que en la seccion 10.7.2 arriba el usuario que ejecuta el proceso Mercurial en el servidor es tambienel usuario que ejecutara el guion processmail. El guion processmail algunas veces hace que Bugzilla escriba enficheros en su directorio de configuracion, y los ficheros de configuracion de Bugzilla usualmente son propiedad delusuario bajo el cual se ejecuta el servidor web.

Usted puede hacer que processmail sea ejecutado con la identidad del usuario adecuado usando el comandosudo. A continuacion se presenta una entrada de ejemplo para un fichero sudoers.

1 hg_user = (httpd_user) NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s

Esto permite que el usuario hg user ejecute el programa processmail-wrapper con la identidad del usuario httpd user.Esta indireccion a traves de un guion envoltorio es necesaria, porque processmail espera que al ser ejecutado su

directorio actual sea aquel en el cual se instalo Bugzilla; usted no puede especificar ese tipo de condicion en un ficherosudoers. Los contenidos del giuon envoltorio son simples:

121

Page 132: Mercurial

1 #!/bin/sh2 cd ‘dirname $0‘ && ./processmail "$1" [email protected]

No parece importar que direccion de correo se le pase a processmail.Si su [usermap] no es configurada correctamente, los usuarios veran un mensaje de error del gancho bugzilla

cuando empujen cambios al servidor. El mensaje de error se vera ası:

1 cannot find bugzilla user id for [email protected]

Lo que esto quiere decir es que la direccion del consignador, [email protected], no es un nombre deusuario Bugzilla valido, ni tiene una entrada en su [usermap] que lo asocie con un nombre de usuario valido Bugzilla.

10.7.3. notify—enviar notificaciones de correo electronicoAunque el servidor web embebido de Mercurial provee notificaciones de cambios en cada repositorio, muchas

personas prefieren recibir las notificaciones de cambios vıa correo electronico. El gancho notify4 le permite a ustedenviar notificaciones a un conjunto de direcciones de correo cuando lleguen conjuntos de cambios en los que lossubscriptores esten interesados.

De la misma forma que con el gancho bugzilla, el gancho notify esta orientado a plantillas, ası que usted puedepersonalizar los contenidos del mensaje de notificacion que se envıa.

Por defecto, el gancho notify incluye un diff de cada conjunto de cambios que se envıa; usted puede limitar eltamano del diff, o desactivar completamente esta caracterıstica. Es util para permitir a los subscriptores revisar loscambios inmediatamente, en vez de tener que hacer clic para visitar una URL.

Configuracion del gancho notify

Usted puede configurar el gancho notify para enviar un mensaje de correo por conjunto de cambios entrante, ouno por grupo entrante de conjuntos de cambios (todos los que llegaron en un unico empuje o jalado).

1 [hooks]2 # enviar un correo por grupo de cambios3 changegroup.notify = python:hgext.notify.hook4 # enviar un correo por cambio5 incoming.notify = python:hgext.notify.hook

La informacion para configurar este gancho se ubica en la seccion [notify] de un fichero hgrc.

test Por defecto, este gancho no envıa correos en absoluto; en vez de eso, imprime el mensaje que se enviarıa. Fijeeste ıtem en false para permitir el envıo de correos. El motivo por el que el envıo de correos esta desactivado esque hacen falta varios intentos para configurar esta extension exactamente como usted desea, y serıa maleducadoenviar a los subscriptores una cantidad de notificaciones “rotas” mientras usted depura su configuracion.

config La ruta a un fichero de configuracion que contiene informacion de subscripcion. Esto se mantiene separadodel hgrc principal para que usted pueda mantenerlo en un repositorio. La gente puede clonar ese repositorio,actualizar sus subscripciones, y empujar los cambios de vuelta a su servidor.

strip La cantidad de caracteres iniciales de separacion de ruta a remover de la ruta del repositorio, al decidir si unrepositorio tiene subscriptores. Por ejemplo, si los repositorios en su servidor estan en /home/hg/repos, ynotify esta trabajando con un repositorio llamado /home/hg/repos/shared/test, fijar strip a 4 hara quenotify elimine las partes iniciales de la ruta hasta shared/test, y asociara los subscriptores frente a dicharuta.

4N. del T. Notificacion.

122

Page 133: Mercurial

template El texto de plantilla a usar cuando se envıen mensajes. Especifica los contenidos de la cabecera del mensaje y elcuerpo del mismo.

maxdiff El numero maximo de lıneas de datos de diff a anadir al final de un mensaje. Si la longitud de un diff es mayora eso, se trunca. Por defecto, esta fijado en 300. Fije esto a 0 para omitir los diffs en los correos de notificacion.

sources Una lista de fuentes de conjuntos de cambios a considerar. Esto le permite a usted indicar a notify para quesolo envıe correos acerca de cambios que usuarios remotos hayan empujado al repositorio vıa un servidor, porejemplo. Vea la seccion 10.8.3 para las fuentes que usted puede especificar aquı.

Si usted fija el ıtem baseurl en la seccion [web], usted lo puede usar en una plantilla; estara disponible comowebroot.

A continuacion se presenta un ejemplo completo de configuracion para el gancho notify.

1 [notify]2 # enviar correo3 test = false4 # datos de subscriptores estan en el repositorio notify5 config = /home/hg/repos/notify/notify.conf6 # repos estan en /home/hg/repos on server, ası que elimine 47 # caracteres"/"8 strip = 49 template = X-Hg-Repo: {webroot}

10 Subject: {webroot}: {desc|firstline|strip}11 From: {author}12

13 changeset {node|short} in {root}14 details: {baseurl}{webroot}?cmd=changeset;node={node|short}15 description:16 {desc|tabindent|strip}17

18 [web]19 baseurl = http://hg.example.com/

Esto producira un mensaje que se vera como el siguiente:

1 X-Hg-Repo: tests/slave2 Subject: tests/slave: Handle error case when slave has no buffers3 Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT)4

5 changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave6 details: http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b57 description:8 Handle error case when slave has no buffers9 diffs (54 lines):

10

11 diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h12 --- a/include/tests.h Wed Aug 02 15:19:52 2006 -070013 +++ b/include/tests.h Wed Aug 02 15:25:26 2006 -070014 @@ -212,6 +212,15 @@ static __inline__ void test_headers(void *h)15 [...snip...]

123

Page 134: Mercurial

Pruebas y resolucion de problemas

No olvide que por defecto, la extension notify no enviara ningun correo electronico hasta que usted la configureexplıcitamente para hacerlo, fijando el valor de test a false. Hasta que usted haga eso, simplemente se imprimira elmensaje que se enviarıa.

10.8. Informacion para escritores de ganchos

10.8.1. Ejecucion de ganchos internosUn gancho interno es llamado con argumentos de la siguiente forma:

1 def myhook(ui, repo, **kwargs):2 pass

El parametro ui es un objeto mercurial.ui.ui. El parametro repo es un objeto mercurial.localrepo.localrepository.Los nombres y valores de los parametros en **kwargs dependen del gancho que se invoque, con las siguientes carac-terısticas en comun:

Si hay un parametro llamado node o parentN , contendra un ID hexadecimal de un conjunto de cambios. Lacadena vacıa es usada para representar un “ID de conjunto de cambios nulo” en vez de una cadena de ceros.

Si hay un parametro llamado url, contendra la URL de un repositorio remoto, si puede ser determinada.

Los parametros booleanos son representados como objetos bool de Python.

Un gancho interno es ejecutado sin cambiar el directorio de trabajo del proceso (a diferencia de los ganchosexternos, que son ejecutados desde la raız del repositorio). El gancho no debe cambiar el directorio de trabajo delproceso, porque esto harıa que falle cualquier llamada que se haga a la API de Mercurial.

Si un gancho retorna el valor booleano “false”5, se considera que este tuvo exito. Si retorna “true”6 o genera unaexcepcion, se considera que ha fallado. Una manera util de pensar en esta convencion de llamado es “dıgame si ustedfallo”.

Note que los IDs de conjuntos de cambios son pasados a los ganchos de Python como cadenas hexadecimales, nocomo los hashes binarios que la API de Mercurial usa normalmente. Para convertir un hash de hexadecimal a binario,use la funcion mercurial.node.bin.

10.8.2. Ejecucion de ganchos externosUn gancho externo es pasado al interprete de comandos del usuario bajo el cual se ejecuta Mercurial. Las car-

acterısticas del interprete, como sustitucion de variables y redireccion de comandos, estan disponibles. El gancho esejecutado desde el directorio raız del repositorio (a diferencia de los ganchos internos, que se ejecutan desde el mismodirectorio en que Mercurial fue ejecutado).

Los parametros para el gancho se pasan como variables de entorno. El nombre de cada variable de entorno se pasaa mayusculas y se le anade el prefijo “HG ”. Por ejemplo, si el nombre de un parametro es “node”, el nombre de lavariable de entorno que almacena el parametro se llamara “HG NODE”.

Un parametro booleano se representa con la cadena “1” para “true”, “0” para “false”. Si una variable se llamaHG NODE, HG PARENT1 o HG PARENT2, contendra un ID de conjunto de cambios representado como una cadena hex-adecimal. La cadena vacıa es usada para representar un “ID de conjunto de cambios nulo” en vez de una cadena deceros. Si una variable de entorno se llama HG URL, contendra la URL de un repositorio remoto, si puede ser determi-nada.

Si un gancho termina con un codigo de salida de cero, se considera que tuvo exito. Si termina con un codigo desalida diferente de cero, se considera que fallo.

5N. del T. Falso.6N. del T. Verdadero.

124

Page 135: Mercurial

10.8.3. Averiguar de donde vienen los conjuntos de cambiosUn gancho que involucra la transferencia de conjuntos de cambios entre un repositorio local y otro puede ser capaz

de averiguar informacion acerca de “el otro lado”. Mercurial sabe como son transferidos los conjuntos de cambios, yen muchos casos tambien desde o hacia donde estan siendo transferidos.

Fuentes de conjuntos de cambios

Mercurial le indicara a un gancho cuales son, o fueron, los medios usados para transferir los conjuntos de cambiosentre repositorios. Esta informacion es provista por Mercurial en un parametro Python llamado source7, o una variablede entorno llamada HG SOURCE.

serve Los conjuntos de cambios son transferidos desde o hacia un repositorio remoto a traves de http o ssh.

pull Los conjuntos de cambios son transferidos vıa una operacion de jalado de un repositorio a otro.

push Los conjuntos de cambios son transferidos vıa un empuje de un repositorio a otro.

bundle Los conjuntos de cambios son transferidos desde o hacia un paquete.

A donde van los cambios—URLs de repositorios remotos

Cuando es posible, Mercurial le indicara a los ganchos la ubicacion de “el otro lado” de una actividad que transfieradatos de conjuntos de cambios entre repositorios. Esto es provisto por Mercurial en un parametro Python llamado url,o en una variable de entorno llamada HG URL.

No siempre esta informacion esta disponible. Si un gancho es invocado un repositorio que es servido a traves dehttp o ssh, Mercurial no puede averiguar donde esta el repositorio remoto, pero puede saber desde donde se conecta elcliente. En esos casos, la URL tendra una de las siguientes formas:

remote:ssh:ip-address—cliente ssh remoto, en la direccion IP dada.

remote:http:ip-address—cliente remoto http, en la direccion IP dada. Si el cliente esta usando SSL,tendra la forma remote:https:ip-address.

Vacıo—no se pudo descubrir informacion acerca del cliente remoto.

10.9. Referencia de ganchos

10.9.1. changegroup—luego de anadir conjuntos de cambios remotosEste gancho es ejecutado luego de que un grupo de conjuntos de cambios preexistentes ha sido anadido al repos-

itorio, por ejemplo vıa un “hg pull” o “hg unbundle”. Este gancho es ejecutado una vez por cada operacion queanade uno o mas conjuntos de cambios. Este gancho se diferencia del gancho incoming, que es ejecutado una vez porcada conjunto de cambios, sin importar si los cambios llegan en grupo.

Algunos usos posibles para este gancho includen el probar o ensamblar los conjuntos de cambios anadidos, actu-alizar una base de datos de fallos, o notificar a subscriptores de que el repositorio contiene nuevos cambios.

Parametros para este gancho:

node Un ID de conjunto de cambios. El ID del primer conjunto de cambios que fue anadido en el grupo. Todos losconjuntos de cambios entre este y la punta (tip), inclusive, fueron anadidos por un unico jalado (“hg pull”),empuje (“hg push”) o “hg unbundle”.

source Una cadena. La fuente de estos cambios. Vea la seccion 10.8.3 para mas detalles.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Veta tambien: incoming (seccion 10.9.3), prechangegroup (seccion 10.9.5), pretxnchangegroup (seccion 10.9.9)7N. del T. Fuente.

125

Page 136: Mercurial

10.9.2. commit—luego de la creacion de un nuevo conjunto de cambiosEste gancho es ejecutado luego de la creacion de un nuevo conjunto de cambios.Parametros para este gancho:

node Un ID de conjunto de cambios. El ID de conjunto de cambios del conjunto de cambios que acabo de serconsignado.

parent1 Un ID de conjunto de cambios. El ID de conjunto de cambios del primer padre del conjunto de cambios queacaba de ser consignado.

parent2 Un ID de conjunto de cambios. El ID de conjunto de cambios del segundo padre del conjunto de cambios queacaba de ser consignado.

Vea tambien: precommit (seccion 10.9.6), pretxncommit (seccion 10.9.10)

10.9.3. incoming—luego de que un conjunto de cambios remoto es anadidoEste gancho es ejecutado luego de que un conjunto de cambios preexistente ha sido anadido al repositorio, por

ejemplo, vıa un “hg push”. Si un grupo de conjuntos de cambios fue anadido en una sola operacion, este gancho esejecutado una vez por cada conjunto de cambios anadido.

Usted puede usar este gancho para los mismos fines que el gancho changegroup (seccion 10.9.1); simplementealgunas veces es mas conveniente ejecutar un gancho una vez por cada grupo de conjuntos de cambios, mientras queotras es mas util correrlo por cada conjunto de cambios.

Parametros para este gancho:

node Un ID de conjunto de cambios. El ID del conjunto de cambios recien anadido.

source Una cadena. La fuente de estos cambios. Vea la seccion 10.8.3 para mas detalles.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Vea tambien: changegroup (seccion 10.9.1) prechangegroup (seccion 10.9.5), pretxnchangegroup (seccion 10.9.9)

10.9.4. outgoing—luego de la propagacion de los conjuntos de cambiosEste gancho es ejecutado luego de que un grupo de conjuntos de cambios ha sido propagado fuera de este reposi-

torio, por ejemplo por un comando “hg push” o “hg bundle”.Un uso posible para este gancho es notificar a los administradores que los cambios han sido jalados.Parametros para este gancho:

node Un ID de conjunto de cambios. El ID del primer conjunto de cambios del grupo que fue enviado.

source Una cadena. La fuente de la operacion (vea la seccion 10.8.3). Si un cliente remoto jalo cambios de esterepositorio, source sera serve. Si el cliente que obtuvo los cambios desde este repositorio era local, sourcesera bundle, pull, o push, dependiendo de la operacion que llevo a cabo el cliente.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Vea tambien: preoutgoing (seccion 10.9.7)

126

Page 137: Mercurial

10.9.5. prechangegroup—antes de empezar la adicion de conjuntos de cambios remotosEste gancho de control es ejecutado antes de que Mercurial empiece a anadir un grupo de conjuntos de cambios de

otro repositorio.Este gancho no tiene ninguna informacion acerca de los conjuntos de cambios que van a ser anadidos, porque es

ejecutado antes de que se permita que empiece la transmision de dichos conjuntos de cambios. Si este gancho falla,los conjuntos de cambios no seran transmitidos.

Un uso para este gancho es prevenir que se anadan cambios externos a un repositorio. Por ejemplo, usted podrıausarlo para “congelar” temporal o permanentemente una rama ubicada en un servidor para que los usuarios no puedanempujar cambios a ella, y permitiendo al mismo tiempo modificaciones al repositorio por parte de un administradorlocal.

Parametros para este gancho:

source Una cadena. La fuente de estos cambios. Vea la seccion 10.8.3 para mas detalles.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Vea tambien: changegroup (seccion 10.9.1), incoming (seccion 10.9.3), , pretxnchangegroup (seccion 10.9.9)

10.9.6. precommit—antes de iniciar la consignacion de un conjunto de cambiosEste gancho es ejecutado antes de que Mercurial inicie la consignacion de un nuevo conjunto de cambios. Es

ejecutado antes de que Mercurial tenga cualquier de los metadatos para la consignacion, como los ficheros a serconsignados, el mensaje de consignacion, o la fecha de consignacion.

Un uso para este gancho es deshabilitar la capacidad de consignar nuevos conjuntos de cambios, pero permi-tiendo conjuntos de cambios entrantes. Otro es ejecutar un proceso de ensamble/compilacion o prueba, y permitir laconsignacion solo si el ensamble/compilacion o prueba tiene exito.

Parametros para este gancho:

parent1 Un ID de conjunto de cambios. El ID de conjunto de cambios del primer padre del directorio de trabajo.

parent2 Un ID de conjunto de cambios. El ID de conjunto de cambios del segundo padre del directorio de trabajo.

Si la consignacion continua, los padres del directorio de trabajo se convertiran en los padres del nuevo conjunto decambios.

Vea tambien: commit (seccion 10.9.2), pretxncommit (seccion 10.9.10)

10.9.7. preoutgoing—antes de empezar la propagacion de conjuntos de cambiosEste gancho es ejecutado antes de que Mercurial conozca las identidades de los conjuntos de cambios que deben

ser transmitidos.Un uso para este gancho es evitar que los cambios sean transmitidos a otro repositorio.Parametros para este gancho:

source Una cadena. La fuente la operacion que esta tratando de obtener cambios de este repositorio (vea la sec-cion 10.8.3). Revise la documentacion para el parametro source del gancho outgoing, en la seccion 10.9.4,para ver los posibles valores de este parametro.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Vea tambien: outgoing (seccion 10.9.4)

127

Page 138: Mercurial

10.9.8. pretag—antes de etiquetar un conjunto de cambiosEste gancho de control es ejecutado antes de la creacion de una etiqueta. Si el gancho termina exitosamente, la

creacion de la etiqueta continua. Si el gancho falla, no se crea la etiqueta.Parametros para este gancho:

local Un booleano. Indica si la etiqueta es local a esta instancia del repositorio (p.e. almacenado en .hg/localtags)o administrado por Mercurial (almacenado en .hgtags).

node Un ID de conjunto de cambios. El ID del conjunto de cambios a etiquetar.

tag Una cadena. El nombre de la etiqueta por crear.

Si la etiqueta que se va a crear se encuentra bajo control de revisiones, los ganchos precommit y pretxncommit(secciones 10.9.2 y 10.9.10) tambien seran ejecutados.

Vea tambien: tag (seccion 10.9.12)

10.9.9. pretxnchangegroup—antes de completar la adicion de conjuntos de cambiosremotos

Este gancho de control es ejecutado antes de una transaccion—la que maneja la adicion de un grupo de conjuntosde cambios nuevos desde fuera del repositorio—se complete. Si el gancho tiene exito, la transaccion se completa, ytodos los conjuntos de cambios se vuelven permanentes dentro de este repositorio. Si el gancho falla, la transaccion esdeshecha, y los datos para los conjuntos de cambios son eliminados.

Este gancho puede acceder a los metadatos asociados con los conjuntos de cambios casi anadidos, pero no debehacer nada permanente con estos datos. Tampoco debe modificar el directorio de trabajo.

Mientras este gancho esta corriendo, si otro proceso Mercurial accesa el repositorio, podra ver los conjuntos decambios casi anadidos como si fueran permanentes. Esto puede llevar a condiciones de carrera si usted no tomaprecauciones para evitarlas.

Este gancho puede ser usado para examinar automaticamente un grupo de conjuntos de cambios. Si el gancho falla,todos los conjuntos de cambios son “rechazados” cuando la transaccion se deshace.

Parametros para este gancho:

node Un ID de conjunto de cambios. El ID del primer conjunto de cambios que fue anadido en el grupo. Todos losconjuntos de cambios entre este y el tip, inclusive, fueron anadidos por un unico “hg pull”, “hg push” o “hgunbundle”.

source Una cadena. La fuente de estos cambios. Vea la seccion 10.8.3 para mas detalles.

url Una URL. La ubicacion del repositorio remoto, si es conocida. Vea la seccion 10.8.3 para mas informacion.

Vea tambien: changegroup (seccion 10.9.1), incoming (seccion 10.9.3), prechangegroup (seccion 10.9.5)

10.9.10. pretxncommit—antes de completar la consignacion de un nuevo conjunto decambios

Este gancho de control es ejecutado antes de que una transaccion—que maneja una nueva consignacion—se com-plete. Si el gancho tiene exito, la transaccion se completa y el conjunto de cambios se hace permanente dentro de esterepositorio. Si el gancho falla, la transaccion es deshecha, y los datos de consignacion son borrados.

Este gancho tiene acceso a los metadatos asociados con el practicamente nuevo conjunto de cambios, pero nodeberıa hacer nada permanente con estos datos. Tampoco debe modificar el directorio de trabajo.

Mientras este gancho esta corriendo, si otro proceso Mercurial accesa este repositorio, podra ver el practicamentenuevo conjunto de cambios como si fuera permanente. Esto puede llevar a condiciones de carrera si usted no tomaprecauciones para evitarlas.

Parametros para este gancho:

128

Page 139: Mercurial

node Un ID de conjunto de cambios. El ID del conjunto de cambios recien consignado.

parent1 Un ID de conjunto de cambios. El ID de conjunto de cambios del primer padre del conjunto de cambios queacaba de ser consignado.

parent2 Un ID de conjunto de cambios. El ID de conjunto de cambios del segundo padre del conjunto de cambios queacaba de ser consignado.

Vea tambien: precommit (seccion 10.9.6)

10.9.11. preupdate—antes de actualizar o fusionar el directorio de trabajoEste gancho de control es ejecutado antes de actualizar o fusionar el directorio de trabajo. Es ejecutado solo si las

revisiones usuales de Mercurial antes de las actualizaciones determinan que la actualizacion o fusion pueden proceder.Si el gancho termina exitosamente, la actualizacion o fusion pueden proceder.; si falla, la actualizacion o fusion noempiezan.

Parametros para este gancho:

parent1 Un ID de conjunto de cambios. El ID del padre al que el directorio de trabajo sera actualizado. Si se esta fusio-nando el directorio de trabajo, no cambiara este padre.

parent2 Un ID de conjunto de cambios. Solo esta definido si se esta fusionando el directorio de trabajo. El ID de larevision con la cual esta siendo fusionado el directorio de trabajo.

Vea tambien: update (seccion 10.9.13)

10.9.12. tag—luego de etiquetar un conjunto de cambiosEste gancho es ejecutado luego de la creacion de una etiqueta.Parametros para este gancho:

local Un booleano. Indica si la etiqueta es local a esta instancia del repositorio (p.e. almacenado en .hg/localtags)o administrado por Mercurial (almacenado en .hgtags).

node Un ID de conjunto de cambios. El ID del conjunto de cambios que fue etiquetado.

tag Una cadena. El nombre de la etiqueta que fue creada.

Si la etiqueta creada esta bajo control de revisiones, el gancho commit (seccion 10.9.2) es ejecutado antes de estegancho.

Vea tambien: pretag (seccion 10.9.8)

10.9.13. update—luego de actualizar o fusionar el directorio de trabajoEste gancho es ejecutado despues de una actualizacion o fusion en el directorio de trabajo. Ya que una fusion

puede fallar (si el comando externo hgmerge no puede resolver los conflictos en un fichero), este gancho indica si laactualizacion o fusion fueron completados adecuadamente.

error Un booleano. Indica si la actualizacion o fusion fue completada exitosamente.

parent1 Un ID de conjunto de cambios. El ID del padre al cual fue actualizado el directorio de trabajo. Si se fusiono eldirectorio de trabajo, no se habra cambiado este padre.

parent2 Un ID de conjunto de cambios. Solo esta definido si se fusiono el directorio de trabajo. El ID de la revision conla que fue fusionado el directorio de trabajo.

Vea tambien: preupdate (seccion 10.9.11)

129

Page 140: Mercurial

Capıtulo 11

Personalizar los mensajes de Mercurial

Mercurial provee un poderoso mecanismo que permite controlar como despliega la informacion. El mecanismo sebasa en plantillas. Puede usar plantillas para generar salida especıfica para una orden particular o para especificar lavisualizacion completa de la interfaz web embebida.

11.1. Usar estilos que vienen con MercurialHay ciertos estilos listos que vienen con Mercurial. Un estilo es simplemente una plantilla predeterminada que

alguien escribio e instalo en un sitio en el cual Mercurial puede encontrarla.Antes de dar un vistazo a los estilos que trae Mercurial, revisemos su salida usual.

1 $ hg log -r12 changeset: 1:522d8d7a8fda3 tag: mytag4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:30 2009 +00006 summary: added line to end of <<hello>> file.7

Es en cierta medida informativa, pero ocupa mucho espacio—cinco lıneas de salida por cada conjunto de cambios.El estilo compact lo reduce a tres lıneas, presentadas de forma suscinta.

1 $ hg log --style compact2 3[tip] b638ce454bd0 2009-02-10 18:23 +0000 bos3 Added tag v0.1 for changeset 4b75acdd46984

5 2[v0.1] 4b75acdd4698 2009-02-10 18:23 +0000 bos6 Added tag mytag for changeset 522d8d7a8fda7

8 1[mytag] 522d8d7a8fda 2009-02-10 18:23 +0000 bos9 added line to end of <<hello>> file.

10

11 0 d0c3b909ac1b 2009-02-10 18:23 +0000 bos12 added hello13

El estilo de la bitacora de cambios vislumbra el poder expresivo del sistema de plantillas de Mercurial. Esteestilo busca seguir los estandares de bitacora de cambios del proyecto GNU[RS].

130

Page 141: Mercurial

1 $ hg log --style changelog2 2009-02-10 Bryan O’Sullivan <[email protected]>3

4 * .hgtags:5 Added tag v0.1 for changeset 4b75acdd46986 [b638ce454bd0] [tip]7

8 * .hgtags:9 Added tag mytag for changeset 522d8d7a8fda

10 [4b75acdd4698] [v0.1]11

12 * goodbye, hello:13 added line to end of <<hello>> file.14

15 in addition, added a file with the helpful name (at least i hope16 that some might consider it so) of goodbye.17 [522d8d7a8fda] [mytag]18

19 * hello:20 added hello21 [d0c3b909ac1b]22

No es una sorpresa que el estilo predeterminado de Mercurial se llame default1.

11.1.1. Especificar un estilo predeterminadoPuede modificar el estilo de presentacion que Mercurial usara para toda orden vıa el fichero hgrc indicando el

estilo que prefiere usar.

1 [ui]2 style = compact

Si escribe un estilo, puede usarlo bien sea proveyendo la ruta a su fichero de estilo o copiando su fichero deestilo a un lugar en el cual Mercurial pueda encontrarlo (tıpicamente el subdirectorio templates de su directorio deinstalacion de Mercurial).

11.2. Ordenes que soportan estilos y plantillasTodas las ordenes de Mercurial“relacionadas con log” le permiten usar estilos y plantillas: “hg incoming”, “hg

log”, “hg outgoing” y “hg tip”.Al momento de la escritura del manual estas son las unicas ordenes que soportan estilos y plantillas. Dado que son

las ordenes mas importantes que necesitan personalizacion, no ha habido muchas solicitudes desde la comunidad deusuarios de Mercurial para anadir soporte de plantillas y estilos a otras ordenes.

11.3. Cuestiones basicas de plantillasUna plantilla de Mercurial es sencillamente una pieza de texto. Cierta porcion nunca cambia, otras partes se

expanden, o reemplazan con texto nuevo cuando es necesario.1N. del T. predeterminado

131

Page 142: Mercurial

Antes de continuar, veamos de nuevo un ejemplo sencillo de la salida usual de Mercurial:

1 $ hg log -r12 changeset: 1:522d8d7a8fda3 tag: mytag4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:30 2009 +00006 summary: added line to end of <<hello>> file.7

Ahora, ejecutemos la misma orden, pero usemos una plantilla para modificar su salida:

1 $ hg log -r1 --template ’i saw a changeset\n’2 i saw a changeset

El ejemplo anterior ilustra la plantilla mas sencilla posible; es solamente una porcion estatica de codigo que seimprime una vez por cada conjunto de cambios. La opcion --template de la orden “hg log” indica a Mercurial usarel texto dado como la plantilla cuando se imprime cada conjunto de cambios.

Observe que la cadena de plantilla anterior termina con el texto “\n”. Es una secuencia de control, que le indicaa Mercurial imprimira una nueva lınea al final de cada objeto de la plantilla. Si omite esta nueva lınea, Mercurialcolocara cada pieza de salida seguida. Si desea mas detalles acerca de secuencias de control, vea la seccion 11.5.

Una plantilla que imprime una cadena fija de texto siempre no es muy util; intentemos algo un poco mas complejo.

1 $ hg log --template ’i saw a changeset: {desc}\n’2 i saw a changeset: Added tag v0.1 for changeset 4b75acdd46983 i saw a changeset: Added tag mytag for changeset 522d8d7a8fda4 i saw a changeset: added line to end of <<hello>> file.5

6 in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.7 i saw a changeset: added hello

Como puede ver, la cadena “{desc}” en la plantilla ha sido reemplazada en la salida con la descricipcion de cadaconjunto de cambios. Cada vez que Mercurial encuentra texto encerrado entre corchetes (“{” y “}”), intentara reem-plazar los corchetes y el texto con la expansion de lo que sea esta adentro. Para imprimir un corchete de forma literal,debe escaparlo, como se describe en la seccion 11.5.

11.4. Palabras claves mas comunes en las plantillasPuede empezar a escribir plantillas sencillas rapidamente con las palabras claves descritas a continuacion:

author Cadena. El autor NO modificado del conjunto de cambios.

branches Cadena. El nombre de la rama en la cual se consigno el conjunto de cambios. Sera vacıa si el nombre de la ramaes default.

date Informacion de fecha. La fecha en la cual se consigno el conjunto de cambios. No es legible por un humano, debepasarla por un firltro que la desplegara apropiadamente. En la seccion 11.6 hay mas detalles acerca de filtros. Lafecha se expresa como un par de numeros. El primer numero corresponde a una marca de tiempo UNIX UTC(segundos desde el primero de enero de 1970); la segunda es el corrimiento horario de la zona horaria del UTCen la cual se encontraba quien hizo la consignacion, en segundos.

desc Cadena. La descripcion en texto del conjunto de cambios.

132

Page 143: Mercurial

1 $ hg log -r1 --template ’author: {author}\n’2 author: Bryan O’Sullivan <[email protected]>3 $ hg log -r1 --template ’desc:\n{desc}\n’4 desc:5 added line to end of <<hello>> file.6

7 in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye.8 $ hg log -r1 --template ’files: {files}\n’9 files: goodbye hello

10 $ hg log -r1 --template ’file_adds: {file_adds}\n’11 file_adds: goodbye12 $ hg log -r1 --template ’file_dels: {file_dels}\n’13 file_dels:14 $ hg log -r1 --template ’node: {node}\n’15 node: 522d8d7a8fda9a24dcac9c5ccfcca067b933220b16 $ hg log -r1 --template ’parents: {parents}\n’17 parents:18 $ hg log -r1 --template ’rev: {rev}\n’19 rev: 120 $ hg log -r1 --template ’tags: {tags}\n’21 tags: mytag

Figura 11.1: Template keywords in use

files Lista de cadenas. Todos los ficheros modificados, adicionados o eliminados por este conjunto de cambios.

file adds Lista de cadenas. Ficheros adicionados por este conjunto de cambios.

file dels Lista de cadenas. Ficheros eliminados por este conjunto de cambios.

node Cadena. El hash de identificacion de este conjunto de cambios como una cadena hexadecimal de 40 caracteres.

parents Lista de cadenas. Los ancestros del conjunto de cambios.

rev Entero. El numero de revision del repositorio local.

tags Lista de cadenas. Todas las etiquetas asociadas al conjunto de cambios.

Unos experimentos sencillos nos mostraran que esperar cuando usamos estas palabras claves; puede ver los resul-tados en la figura 11.1.

Como mencionamos anteriormente, la palabra clave de fecha no produce salida legible por un humano, debemostratarla de forma especial. Esto involucra usar un filtro, acerca de lo cual hay mas en la seccion 11.6.

1 $ hg log -r1 --template ’date: {date}\n’2 date: 1234290210.003 $ hg log -r1 --template ’date: {date|isodate}\n’4 date: 2009-02-10 18:23 +0000

11.5. Secuencias de ControlEl motor de plantillas de Mercurial reconoce las secuencias de control mas comunmente usadas dentro de las

cadenas. Cuando ve un backslash (“\”), ve el caracter siguiente y sustituye los dos caracteres con un reemplazosencillo, como se describe a continuacion:

133

Page 144: Mercurial

\\ Backslash, “\”, ASCII 134.

\n Nueva lınea, ASCII 12.

\r Cambio de lınea, ASCII 15.

\t Tab, ASCII 11.

\v Tab Vertical, ASCII 13.

\{ Corchete abierto, “{”, ASCII 173.

\} Corchete cerrado, “}”, ASCII 175.

Como se indico arriba, si desea que la expansion en una plantilla contenga un caracter literal “\”, “{”, o “{”, debeescaparlo.

11.6. Uso de filtros con palabras clavesAlgunos de los resultados de la expansion de la plantilla no son faciles de usar de inmediato. Mercurial le permite

especificar una cadena de filtros opcionales para modificar el resultado de expandir una palabra clave. Ya ha visto elfiltro usual isodate en accion con anterioridad para hacer legible la fecha.

A continuacion hay una lista de los filtros de Mercurial mas comunmente usados. Ciertos filtros pueden aplicarsea cualquier texto, otros pueden usarse unicamente en circunstancias especıficas. El nombre de cada filtro esta seguidode la indicacion de donde puede ser usado y una descripcion de su efecto.

addbreaks Cualquier texto. Anade una etiqueta XHTML “<br/>” antes del final de cada lınea excepto en la final. Porejemplo, “foo\nbar” se convierte en “foo<br/>\nbar”.

age palabra clave date. Muestra la edad de la fecha, relativa al tiempo actual. Ofrece una cadena como “10 minutes”.

basename Cualquier texto, pero de utilidad sobre todo en palabras claves relativas a ficheros. Trata el texto como unaruta, retornando el nombre base. Por ejemplo, “foo/bar/baz”, se convierte en “baz”.

date date palabra clave. Mostrar la fecha en un formato similar a la orden date de in a similar format to the Unix,pero con la zona horaria incluıda. Una cadena como “Mon Sep 04 15:13:13 2006 -0700”.

domain Cualquier texto, pero de mayor utilidad para la palabra clave author. Encuentra la primera cadena que lucecomo una direccion de correo electronico, y extrae solamente el componente del dominio. Por ejemplo, de“Bryan O’Sullivan <[email protected]>” se extrae “serpentine.com”.

email Cualquier texto, pero de mayor utilidad para la palabra clave author. Extrae la primera cadena que luce comouna direccion de correo. Por ejemplo, de “Bryan O’Sullivan <[email protected]>” extrae “[email protected]”.

escape Cualquier texto. Reemplaza los caracteres especiales de XML/XHTML: “&”, “<” y “>” con las entidades XML.

fill68 Cualquier texto. Lograr que el texto ocupe las primeras 68 columnas. Es util emplearlo antes de pasar el textopor el filtro tabindent, y queremos que aun quepa en una ventana de fuente fija y 80 columnas.

fill76 Cualquier texto. Lograr que el texto quepa en 76 columnas.

firstline Cualquier texto. Mostrar la primera lınea de texto sin saltos de lınea.

hgdate date palabra clave. Mostrar la fecha como un par de numeros legibles. Muestra una cadena como “1157407993 25200”.

isodate date palabra clave. Mostrar la fecha como una cadena de texto en el formato. Muestra una cadena como“2006-09-04 15:13:13 -0700”.

134

Page 145: Mercurial

obfuscate Cualquier texto, pero de mayor utilidad para la palabra clave author. Muestra el campo de texto como unasecuencia de entidades XML. Esto ayuda a eliminar ciertos robots estupidos de adquisicion de correo.

person Cualquier texto, util sobre todo para la palabra clave author. Muestra el texto que hay antes de la direccion decorreo electronico. Por ejemplo, “Bryan O’Sullivan <[email protected]>” mostrarıa “Bryan O’Sullivan”.

rfc822date date palabra clave. Muestra una fecha con el mismo formato que se usa en los encabezados de correo. Mostrarıauna cadena como “Mon, 04 Sep 2006 15:13:13 -0700”.

short Hash del conjunto de cambios. Muestra la forma corta de un hash de conjunto de cambios, of a changeset hash,p.e. una cadena hexadecimal de 12 bytes.

shortdate date palabra clave. Mostrar ano, mes y dıa de una fecha. Muestrauna cadena como “2006-09-04”.

strip Cualquier texto. Elimina todos los espacios en blanco al principio y al final de la cadena.

tabindent Cualquier texto. Muestra el texto con todas las lıneas excepto la primera que comience con el caracter tab.

urlescape Cualquier texto. Escapa todos los caracteres que se consideren como “especiales” por los parsers de URL. Porejemplo, foo bar se convierte en foo%20bar.

user Cualquier texto, util sobre todo para la palabra clave author. Retorna el “usuario” de una direccion de correo.Por ejemplo, “Bryan O’Sullivan <[email protected]>” se convierte en “bos”.

Nota: Si trata de aplicar un filtro a una porcion de datos que no puede procesarse,Mercurial fallara e imprimira una excepcion de Python. Por ejemplo, el tratar deusar la salida de la palabra clave desc con el filtro isodate no resultara algo util.

11.6.1. Combinar filtrosCombinar filtros es para generar una salida en la forma como usted lo desea es muy sencillo. La cadena de fil-

tros siguientes arman una descripcion, despues aseguran que cabe limpiamente en 68 columnas, y las indenta con8 caracteres (por lo menos en sistemas tipo Unix, en los que el tab por convencion se extiende en 8 caracteres).

1 $ hg log -r1 --template ’description:\n\t{desc|strip|fill68|tabindent}\n’2 description:3 added line to end of <<hello>> file.4

5 in addition, added a file with the helpful name (at least i hope6 that some might consider it so) of goodbye.

Observe el uso de “\t” (un caracter tab) en la plantilla para forzar que la primera lınea se indente; esto es necesariopara lograr que la primera lınea luzca indentada; es necesario debido a que tabindent indenta todas las lıneas exceptola primera.

Tenga en cuenta que el orden de los filtros importa. El primer filtro se aplica primero al resultado de la palabra clave;el segundo al resultado de la aplicacion del primer filtro y ası sucesivamente. Por ejemplo, usar fill68|tabindentes muy distinto al resultado de usar tabindent|fill68.

11.7. De plantillas a estilosUna plantilla provee una forma rapida y sencilla para dar formato a una salida. Las plantillas pueden volvers ver-

bosas, y es util poder darle un nombre a una plantilla. Un fichero de estilo es una plantilla con un nombre, almacenadoen un fichero.

Mas aun, al usar un fichero de estilo se dispara el poder del motor de plantillas en un nivel imposible de alcanzarusando las opcion --template desde la lınea de ordenes.

135

Page 146: Mercurial

1 $ hg log -r1 --template ’{author}\n’2 Bryan O’Sullivan <[email protected]>3 $ hg log -r1 --template ’{author|domain}\n’4 serpentine.com5 $ hg log -r1 --template ’{author|email}\n’6 [email protected] $ hg log -r1 --template ’{author|obfuscate}\n’ | cut -c-768 &#66;&#114;&#121;&#97;&#110;&#32;&#79;&#39;&#83;&#117;&#108;&#108;&#105;&#119 $ hg log -r1 --template ’{author|person}\n’

10 Bryan O’Sullivan11 $ hg log -r1 --template ’{author|user}\n’12 bos13 $ hg log -r1 --template ’looks almost right, but actually garbage: {date}\n’14 looks almost right, but actually garbage: 1234290210.0015 $ hg log -r1 --template ’{date|age}\n’16 1 second17 $ hg log -r1 --template ’{date|date}\n’18 Tue Feb 10 18:23:30 2009 +000019 $ hg log -r1 --template ’{date|hgdate}\n’20 1234290210 021 $ hg log -r1 --template ’{date|isodate}\n’22 2009-02-10 18:23 +000023 $ hg log -r1 --template ’{date|rfc822date}\n’24 Tue, 10 Feb 2009 18:23:30 +000025 $ hg log -r1 --template ’{date|shortdate}\n’26 2009-02-1027 $ hg log -r1 --template ’{desc}\n’ | cut -c-7628 added line to end of <<hello>> file.29

30 in addition, added a file with the helpful name (at least i hope that some m31 $ hg log -r1 --template ’{desc|addbreaks}\n’ | cut -c-7632 added line to end of <<hello>> file.<br/>33 <br/>34 in addition, added a file with the helpful name (at least i hope that some m35 $ hg log -r1 --template ’{desc|escape}\n’ | cut -c-7636 added line to end of &lt;&lt;hello&gt;&gt; file.37

38 in addition, added a file with the helpful name (at least i hope that some m39 $ hg log -r1 --template ’{desc|fill68}\n’40 added line to end of <<hello>> file.41

42 in addition, added a file with the helpful name (at least i hope43 that some might consider it so) of goodbye.44 $ hg log -r1 --template ’{desc|fill76}\n’45 added line to end of <<hello>> file.46

47 in addition, added a file with the helpful name (at least i hope that some48 might consider it so) of goodbye.49 $ hg log -r1 --template ’{desc|firstline}\n’50 added line to end of <<hello>> file.51 $ hg log -r1 --template ’{desc|strip}\n’ | cut -c-7652 added line to end of <<hello>> file.53

54 in addition, added a file with the helpful name (at least i hope that some m55 $ hg log -r1 --template ’{desc|tabindent}\n’ | expand | cut -c-7656 added line to end of <<hello>> file.57

58 in addition, added a file with the helpful name (at least i hope tha59 $ hg log -r1 --template ’{node}\n’60 522d8d7a8fda9a24dcac9c5ccfcca067b933220b61 $ hg log -r1 --template ’{node|short}\n’62 522d8d7a8fda

Figura 11.2: Filtros de plantilla en accion

136

Page 147: Mercurial

11.7.1. Los ficheros de estilo mas sencillosNuestro fichero sencillo de estilo contiene una sola lınea:

1 $ echo ’changeset = "rev: {rev}\n"’ > rev2 $ hg log -l1 --style ./rev3 rev: 3

Se le indica a Mercurial, “si esta imprimiendo un conjunto de cambios, use el texto de la derecha como la plantilla”.

11.7.2. Sintaxis de ficheros de estiloLas reglas de sintaxis para un fichero de estilo son sencillas:

El fichero se procesa lınea por lınea.

Se ignoran el espacio en blanco circundante.

Se omiten las lıneas en blanco.

Si una lınea comienza con los caracteres “#” o “;”, la lınea completa se trata como un comentario, y se omitecomo si fuera vacıa.

Una lınea comienza con una palabra clave. Esta debe comenzar con una caracter alfabetico o una raya al piso,y puede contener subsecuentemente cualquier caracter alfanumerico o una raya al piso. (En notacion de expre-siones regulares debe coincidir con [A-Za-z_][A-Za-z0-9_]*.)

El proximo elemento debe ser un caracter “=”, que puede estar precedido o seguido por una cantidad arbitrariade espacio.

Si el resto de la lınea comienza y termina con caracteres encerrados entre caracteres de comillas (bien seasencillas o dobles), se trata como cuerpo de la plantilla.

Si el resto de la lınea no comienza con una comilla, se trata como el nombre de un fichero; los contenidos deeste fichero se leeran y se usaran como cuerpo de la plantilla.

11.8. Ejemplos de ficheros de estilosPara ilustrar la creacion de un fichero de estilo, construiremos algunos ejemplos. En lugar de ofrecer un fichero

completo de estilo y analizarlo, replicaremos el proceso usual de desarrollo de un fichero de estilo comenzando conalgo muy sencillo, y avanzando por una serie de ejemplos sucesivos mas completos.

11.8.1. Identificar equivocaciones en ficheros de estiloSi Mercurial encuentra un problema en un fichero de estilo en el cual usted esta trabajando, imprime un mensaje

de error suscinto, cuando usted identifique lo que significa, resulta muy util.

1 $ cat broken.style2 changeset =

Tenga en cuenta que broken.style trata de definir la palabra clave changeset, pero omite dar un contenido paraesta. Cuando se le indica a Mercurial que use este fichero de estilo, se queja inmediatamente.

137

Page 148: Mercurial

1 $ hg log -r1 --style broken.style2 abort: broken.style:1: parse error

Este mensaje de error luce intimidante, pero no es muy difıcil de seguir:

El primer componente es la forma como Mercurial dice “me rindo”.

1 abort: broken.style:1: parse error

A continuacion viene el nombre del fichero que contiene el error.

1 abort: broken.style:1: parse error

Siguendo el nombre del fichero viene el numero de lınea en la que se encontro el error.

1 abort: broken.style:1: parse error

Finalmente, la descripcion de lo que fallo.

1 abort: broken.style:1: parse error

La descripcion del problema no siempre es clara (como en este caso), pero aunque sea crıptica, casi siempre estrivial la inspeccion visual de la lınea en el fichero de estilo y encontrar lo que esta mal.

11.8.2. Identificar de forma unica un repositorioSi desea identificar un repositorio de Mercurial “de forma unica” con una cadena corta como identificador, puede

usar la primera revision en el repositorio.

1 $ hg log -r0 --template ’{node}’2 92cb4692d38c537be0935f906eeff2a47033600c

No es garantıa de unicidad, pero no es utill en ciertos casos: many cases.

No funcionara en un repositorio completamente vacıo, porque un repositorio ası no tiene una revision zero.

Tampoco funcionara en caso (muy raro) cuando el repositorio sea una fusion de dos repositorios independientesy tiene los dos directorios por ahı.

Hay ciertos casos en los cuales podrıa colocar el identificador:

Como una llave en la tabla de una base de datos que administra repositorios en un servidor.

Como una parte del par {ID repositorio, ID revision}. Almacene esta informacion de forma independiente cuan-do ejecute construcciones automatizadas u otras actividades, de forma que pueda “reconstruir” posteriormenteen caso de ser necesario.

138

Page 149: Mercurial

11.8.3. Mostrando salida parecida a SubversionIntentemos emular la salida usual que usa otro sistema de control de revisiones, Subversion.

1 $ svn log -r96532 ------------------------------------------------------------------------3 r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines4

5 On reporting a route error, also include the status for the error,6 rather than indicating a status of 0 when an error has occurred.7

8 Signed-off-by: Sean Hefty <[email protected]>9

10 ------------------------------------------------------------------------

Dado que la salida de Subversion es sencilla, es facil copiar y pegar una porcion de su salida en un fichero, yreemplazar el texto producido previamente por Subversion con valores base que quisieramos ver expandidos.

1 $ cat svn.template2 r{rev} | {author|user} | {date|isodate} ({date|rfc822date})3

4 {desc|strip|fill76}5

6 ------------------------------------------------------------------------

Esta plantilla difiere en algunos detalles de la salida producida por Subversion:

Subversion imprime una fecha “legible” (el “Wed, 27 Sep 2006” en el ejemplo de salida anterior) en parente-sis. El motor de plantillas de Mercurial no ofrece una forma sencilla de desplegar una fecha en este formato sinimprimir tambien la hora y la zona horaria.

Emulamos las lıneas de “separacion” de subversion con caracteres “-” en una lınea. Usamos la palabra claveheader del motor de plantillas para imprimir una lınea de separacion como la primera lınea de salida (ver masabajo), para lograr una salida similara a la de Subversion.

La salida de subversion incluye un conteo en el encabezado del numero de lıneas en el mensaje de consinacion.No podemos replicarlo en Mercurial; el motor de plantilla no ofrece en la actualidad un filtro que cuente lacantidad de objetos que se le pasen.

No me tomo mas de un minuto o dos de trabajo para reemplazar texto literal de un ejemplo de salida de la salidade Subversion con ciertas palabras claves y filtros para ofrecer la plantilla anterior. El fichero de estilo se refieresencillamente a la plantilla.

1 $ cat svn.style2 header = ’------------------------------------------------------------------------\n\n’3 changeset = svn.template

Podrıamos haber incluıdo el texto del fichero plantilla directamente en el fichero de estilo encerrando entre comillasy reemplazando las nuevas lıneas con secuencias “\n”, pero harıa muy difıcil de leer el fichero de estilos. La facilidadpara leer es importante cuando esta decidiendo si un texto pertenece a un fichero de estilo o a un fichero de plantillaincluıdo en el estilo. Si el fichero de estilo luce muy grande o complicado, si inserta una pieza de texto literal, mejorcoloquelo en una plantilla.

139

Page 150: Mercurial

Capıtulo 12

Administracion de cambios con Colas deMercurial

12.1. El problema de la administracion de parchesUn escenario frecuente: usted necesita instalar un paquete de software desde las fuentes, pero encuentra un fallo

que debe arreglar antes de poder comenzar a usarlo. Hace sus cambios, y se olvida del paquete por un tiempo, unosmeses despues necesita actualizar a una nueva version del paquete. Si la nueva version del paquete todavıa tiene elfallo, debe extraer su arreglo del arbol de fuentes anteriores y aplicarlo a la nueva version. Una tarea tediosa en la cuales facil equivocarse.

Este es un caso simple del problema del “manejo de parches”. Usted tiene un arbol de fuentes del “mantenedorprincipal” que no puede cambiar: necesita hacer algunos cambios locales sobre el arbol principal; y desearıa podermantener tales cambios separados, de forma tal que pueda aplicarlos a versiones mas nuevas del arbol principal.

El problema de administracion de parches surge en muchas situaciones. Probablemente la mas visible es cuando unusuario de un proyecto de software de fuentes abiertas contribuye con un arreglo de un fallo o una nueva caracterısticaa los mantenedores del proyecto en la forma de un parche.

Aquellos que distribuyen sistemas operativos que incluyen programas abiertos usualmente requieren hacer cambiosen los paquetes que distribuyen de tal forma que se armen apropiadamente en sus ambientes.

Cuando hay pocos cambios por mantener, es muy sencillo administrar un solo parche con los programas estandardiff y patch (ver la seccion 12.4 para ver como emplear tales herramientas). Cuando la cantidad de cambios comienzaa crecer, tiene sentido mantener parches como “porciones de trabajo” individual, de forma que cada cambio contienesolamente un arreglo de un fallo (el parche puede modificar varios ficheros, pero esta “haciendo una sola cosa”),y puede tener cierta cantidad de tales parches para diferentes fallos y cambios locales. En esta situacion, si envıa unparche que arregla un fallo a los mantenedores principales de un paquete y ellos incluyen su arreglo en una publicacionposterior, puede deshacerse de tal parche cuando se actualice a la nueva version.

Mantener un solo parche frente a un arbol principal es algo tedioso y es facil equivocarse, pero no es difıcil.Aunque, la complejidad del problema crece rapidamente a medida que la cantidad de parches que tiene que mantenercrece. Con mas que una pequena cantidad de cambios, entender cuales ha aplicado se convierte de algo desordenado aalgo avasallante.

Afortunadamente Mercurial provee una extension poderos: Colas de Mercurial (o simplemente “MQ”), que sim-plifica en gran medida el problema de administracion de parches.

12.2. La prehistoria de las Colas de MercurialA finales de los 90s, muchos desarrolladores del nucleo de Linux comenzaron a mantener “series de parches”

que modificaron el comportamiento del nucleo de Linux. Algunos se enfocaban en estabilidad, otros en aumentar lascaracterısticas, y otros un poco mas especulativos.

140

Page 151: Mercurial

Los tamanos de las series de parches crecieron rapidamente. En el 2002, Andrew Morton publico algunos guionesde lınea de ordenes que estuvo usando para automatizar la tarea de administrar su cola de parches. Andrew uso ex-itosamente tales guiones para administrar centenas (a veces millares) de parches en el nucleo de Linux.

12.2.1. Trabajar parches con quiltA comienzos del 2003, Andreas Gruenbacher y Martin Quinson tomaron la aproximacion de los guiones de An-

drew y publicaron una herramienta llamada “patchwork quilt” [AG], o simplemente “quilt” (ver [Gru05] el paper quelo describe). Dado que quilt automatizaba sustancialmente la administracion de parches, fue adoptado en gran medidapor desarrolladores de programas abiertos.

Quilt maneja una pila de parches sobre un arbol de directorios. Para comenzar, usted le indica a quilt que administreun arbol de directorios, le indica que ficheros manejar; Este almacena los nombres y los contenidos de estos ficheros.Para arreglar un fallo, usted crea un nuevo parche (con una sola orden), edita los ficheros que esta arreglando y“refresca” el parche.

El paso de refresco hace que quilt revise el arbol de directorios; actualiza el parche con todos los cambios que ustedhaya hecho. Puede crear otro parche sobre el primero, que hara seguimiento de los cambios requeridos para modificarel arbol desde “el arbol con un parch aplicado” a un “arbol con dos parches aplicados”.

Usted puede elegir que cambios desea aplicar al arbol. Si “pop”1 un parche, los cambios hechos por tal parchvedesapareceran del arbol de directorios. Quilt recuerda que parches ha sacado, para que pueda “introducirlos”2 posteri-ormente, ası el arbol de directorios se restaurara con las modificaciones que vienen del parche. Lo mas importante esque puede ejecutar la orden “refresh” en cualquier momento, y el ultimo parche sera actualizado. Esto significa quepuede, en cualquier momento, cambiar que parches seran aplicados y que modificaciones hacen ellos.

Quilt no tiene nada que ver con herramientas de control de versiones, y puede trabajar bien sobre un conjunto defuentes que viene de un fichero comprimido y empaquetado o una copia de trabajo de Subversion.

12.2.2. Pasar de trabajo con parches con Quilt hacia Colas de MercurialA mediados de 2005, Chris Mason tomo las caracterısticas de quilt y escribio una extension que llamo Colas de

Mercurial3, que proporciono un comportamiento a Mercurial al estilo quilt.La diferencia clave entre quilt y MQ es que quilt no sabe nada acerca del sistema de control de revisiones, mientras

que MQ esta integrado con Mercurial. Cada parche que usted introduce se representa como un conjunto de cambiosen Mercurial. Si sustrae un parche, el conjunto de cambios desaparece.4

Dado que quilt no se preocupa por las herramientas de control de revisiones, continua siendo una porcion desoftware tremendamente util para aquellas situaciones en las cuales no puede usar Mercurial y MQ.

12.3. La gran ventaja de MQNo puedo sobreestimar el valor que MQ ofrece en la unificacion de parches y el control de revisiones.La principal razon por la cual los parches han persistido en el mundo del software libre y de fuentes abiertas–a

pesar de la creciente disponibilidad de herramientas poderosas de control de revisiones– es la agilidad que ofrecen.Las herramientas tradicionales de control de revisiones llevan un registro permanente e irreversible de todo lo

que usted hace. A pesar de que esto tiene gran valor, tambien es bastante sutil. Si requiere realizar un experimento((((wild-eyed)))), debe ser cuidadoso en como lo hace, o puede dejar trazas innecesarias–o peor aun, desconcertanteso desestabilizantes— de los pasos y errores en el registro de revisiones de forma permanente.

En contraste, con la cohesion de MQ con el control de revisiones distribuidos y los parches, resulta mas sencilloaislar su trabajo. Sus parches viven encima del historial de revisiones normales, y puede hacer que ellos desaparezcan

1N. del T. saca2N. del T. push3N. del T. Mercurial Queues4N. del T. introduce originalmente es push y pop es sustraer en este contexto, usaremos el original en ingles cuando encontremos que facilita la

comprension

141

Page 152: Mercurial

o reaparezcan cuando lo desee. Si no le gusta un parche, puede desecharlo. Si un parche no satisface todo lo que usteddesea, puede arreglarlo—tantas veces como lo requiera, hasta que lo haya refinado lo suficiente hacia sus expectativas.

Por ejemplo, la integracion de parches con el control de revisiones hace que el entender los parches y revisar susefectos—y sus interacciones con el codigo en el cual estan enlazados— sea mucho mas sencillo. Dado que todo parcheque se aplique tiene un conjunto de cambios asociado, puede usar “hg log filename” para ver que conjuntos decambios y parches afectaron un fichero. Puede usar la orden bisect para hacer una busqueda binaria sobre todos losconjuntos de cambios y parches aplicados para ver donde se introdujo un fallo o donde fue arreglado. Puede usar laorden “hg annotate” para ver que conjuntos de cambios o parches modificaron una lınea particular de un ficherofuente. Y mucho mas.

12.4. Entender los parchesDado que MQ no esconde su naturaleza parche-centrica, es muy util para entender de que se tratan los parches, y

un poco acerca de las herramientas que trabajan con ellos.La orden de Unix tradicional diff compara dos ficheros, e imprime una lista de diferencias de sus lıneas. La orden

patch entiende esas diferencias como modificaciones para construir un fichero. Vea en la figura 12.1 un ejemplosencillo de tales ordenes en accion.

1 $ echo ’this is my original thought’ > oldfile2 $ echo ’i have changed my mind’ > newfile3 $ diff -u oldfile newfile > tiny.patch4 $ cat tiny.patch5 --- oldfile 2009-02-10 18:23:25.000000000 +00006 +++ newfile 2009-02-10 18:23:25.000000000 +00007 @@ -1 +1 @@8 -this is my original thought9 +i have changed my mind

10 $ patch < tiny.patch11 patching file oldfile12 $ cat oldfile13 i have changed my mind

Figura 12.1: Uso sencillo de las ordenes diff y patch

El tipo de fichero que diff genera (y que patch toma como entrada) se llama un “parche” o un “diff”; no haydiferencia entre un parche y un diff. (Usaremos el termino “parche”, dado que es el que mas comunmente se usa.)

Un parche puede comenzar con un texto arbitrario; la orden patch ignora este texto, pero MQ lo usa como elmensaje de consignacion cuando se crean conjuntos de cambios. Para encontrar el inicio del contenido de un parche,la orden patch busca la primera lınea que comience con la cadena “diff -”.

MQ trabaja con diffs unificados (patch acepta varios formatos de diff adicionales, pero MQ no). Un diff unificadocontiene dos clases de encabezados. El encabezado de fichero describe el fichero que se esta modificando; contiene elnombre del fichero a modificar. Cuando patch ve un nuevo encabezado de fichero, busca un fichero con ese nombrepara modificarlo.

Despues del encabezaado vienen varios trozos. Cada trozo comienza con un encabezado; que identifica el rango delıneas del fichero que el trozo debe modificar. Despues del encabezado, un trozo comienza y termina con unas pocaslıneas (usualmente tres) de texto del fichero que no han sido modificadas; las cuales llamamos el contexto del trozo. Sisolamente hay una pequena cantidad de contexto entre trozos sucesivos, diff no imprime un nuevo encabezado parael trozo, continua integrando los trozos, con unas lıneas de contexto entre las modificaciones.

Cada lınea de contexto comienza con un caracter de espacio. En el trozo, si una lınea comienza con “-” significa“elimine esta lınea”, si la lınea comienza con un “+” significa “inserte esta lınea”. Por ejemplo, una lınea que se

142

Page 153: Mercurial

modifica se representa con una lınea eliminada y una lınea insertada.Retomaremos aspectos mas sutiles acerca de parches posteriormente (en la seccion 12.6), pero en el momento

usted ya deberıa tener suficiente informacion para usar MQ.

12.5. Comenzar a usar Colas de MercurialDado que MQ esta implementado como una extension, debe habilitarla explıcitamente antes de comenzar a usarla.

(No necesita descargar nada; MQ viene con la distribucion estandar de Mercurial.) Para habilitar MQ, edite su fichero˜/.hgrc, y anada las lıneas de la figura 12.5.

1 [extensions]2 hgext.mq =

Figura 12.2: Lıneas a anadir en ˜/.hgrc para habilitar la extension MQ

Cuando la extension este habilitada, apareceran varios comandos. Para verificar que la extension esta trabajando,puede usar “hg help” para ver si la orden “hg qinit” esta disponible; vea un ejemplo en la figura 12.3.

1 $ hg help qinit2 hg qinit [-c]3

4 init a new queue repository5

6 The queue repository is unversioned by default. If -c is7 specified, qinit will create a separate nested repository8 for patches (qinit -c may also be run later to convert9 an unversioned patch repository into a versioned one).

10 You can use qcommit to commit changes to this queue repository.11

12 options:13

14 -c --create-repo create queue repository15

16 use "hg -v help qinit" to show global options

Figura 12.3: Como verificar que MQ esta habilitado

Puede usar MQ en cualquier repositorio de Mercurial, y sus comandos solamente operaran con tal repositorio.Para comenzar, basta con preparar el repositorio con la orden “hg qinit” (ver la figura 12.4). Esta orden crea undirectorio vacıo llamado .hg/patches, donde MQ mantendra sus metadatos. Como otras ordenes de Mercurial, laorden “hg qinit” no imprime nada cuando es exitosa.

12.5.1. Crear un nuevo parchePara comenzar a trabajar en un nuevo parche use la orden “hg qnew”. Esta orden recibe un argumento, el nombre

del parche a crear. MQ lo usara como el nombre del fichero en el directorio .hg/patches, como puede apreciarlo enla figura 12.5.

143

Page 154: Mercurial

1 $ hg init mq-sandbox2 $ cd mq-sandbox3 $ echo ’line 1’ > file14 $ echo ’another line 1’ > file25 $ hg add file1 file26 $ hg commit -m’first change’7 $ hg qinit

Figura 12.4: Preparar un repositorio para usar MQ

1 $ hg tip2 changeset: 0:90039acadb363 tag: tip4 user: Bryan O’Sullivan <[email protected]>5 date: Tue Feb 10 18:23:27 2009 +00006 summary: first change7

8 $ hg qnew first.patch9 $ hg tip

10 changeset: 1:495236f727e111 tag: qtip12 tag: first.patch13 tag: tip14 tag: qbase15 user: Bryan O’Sullivan <[email protected]>16 date: Tue Feb 10 18:23:27 2009 +000017 summary: [mq]: first.patch18

19 $ ls .hg/patches20 first.patch series status

Figura 12.5: Crear un nuevo parche

Tambien hay otros dos nuevos ficheros en el directorio .hg/patches: series y status. El fichero series listatodos los parches de los cuales MQ tiene noticia para este repositorio, con un parche por lınea. Mercurial usa el ficherostatus para mantener registros interns; da seguimiento a todos los parches que MQ ha aplicado en el repositorio.

Nota: En ciertas ocasiones usted querra editar el fichero series a mano; porejemplo, cambiar el orden en que se aplican ciertos parches. A pesar de esto, esuna mala idea editar manualmente el fichero status, dado que es facil desorientara MQ acerca de lo que esta pasando.

Una vez que haya creado un nuevo parche, puede editar los ficheros en el directorio de trabajo, como lo harıausualmente. Toda las ordenes que de a Mercurial, tales como “hg diff” y “hg annotate”, trabajaran de la mismaforma como lo han hecho antes.

12.5.2. Refrescar un parcheCuando usted llega a un punto en el cual desea guardar su trabajo, use la orden “hg qrefresh” (figura 12.5) para

actualizar el parche en el cual esta trabajando. Esta orden almacena los cambios que haya hecho al directorio actual de

144

Page 155: Mercurial

trabajo en su parche, y almacena el conjunto de cambios correspondiente que contiene los cambios.

1 $ echo ’line 2’ >> file12 $ hg diff3 diff -r 495236f727e1 file14 --- a/file1 Tue Feb 10 18:23:27 2009 +00005 +++ b/file1 Tue Feb 10 18:23:27 2009 +00006 @@ -1,1 +1,2 @@7 line 18 +line 29 $ hg qrefresh

10 $ hg diff11 $ hg tip --style=compact --patch12 1[qtip,first.patch,tip,qbase] 131b8ed49ec4 2009-02-10 18:23 +0000 bos13 [mq]: first.patch14

15 diff -r 90039acadb36 -r 131b8ed49ec4 file116 --- a/file1 Tue Feb 10 18:23:27 2009 +000017 +++ b/file1 Tue Feb 10 18:23:27 2009 +000018 @@ -1,1 +1,2 @@19 line 120 +line 221

Figura 12.6: Refrescar un parche

Puede ejecutar la orden “hg qrefresh” tan seguido como quiera, y es una buena forma de “colocar marcas” a sutrabajo. Refresque su parche en momentos oportunos; intente un experimento; si el experimento no funciona, Use “hgrevert” sobre sus modificaciones para volver al refresco anterior.

12.5.3. Aplicar un parche tras otro y dar seguimientoCuando haya terminado de trabajar en un parche, o necesite trabajar en otro, puede usar la orden “hg qnew” para

crear un nuevo parche. Mercurial aplicara este parche sobre su parche anterior. Para un ejemplo, ver la figura 12.8.Note que el parche contiene los cambios en nuestro parche anterior como parte de su contexto (lo vera mas claramenteen la salida de “hg annotate”).

Hasta ahora, con excepcion de “hg qnew” y “hg qrefresh”, hemos sido cuidadosos para aplicar unicamenteordenes usuaales de Mercurial. De todas maneras, MQ ofrece muchos comandos que son mas sencillos de usar cuandoeste pensando acerca de parches, como se puede ver en la figura 12.9:

La orden “hg qseries” lista cada parche del cual MQ tiene noticia en este repositorio, desde el mas antiguohasta el mas nuevo (El ultimo creado).

La orden “hg qapplied” lista cada parche que MQ haya aplicado en este repositorio, de nuevo, desde el masantiguo hasta el mas nuevo (El aplicado mas recientemente).

12.5.4. Manipular la pila de parchesLa discusion previa indico que debe haber una diferencia entre los parches “conocidos” y “aplicados”, y efectiva-

mente la hay. MQ puede manejar un parche sin que este haya sido aplicado al repositorio.

145

Page 156: Mercurial

1 $ echo ’line 3’ >> file12 $ hg status3 M file14 $ hg qrefresh5 $ hg tip --style=compact --patch6 1[qtip,first.patch,tip,qbase] 4fef714d728c 2009-02-10 18:23 +0000 bos7 [mq]: first.patch8

9 diff -r 90039acadb36 -r 4fef714d728c file110 --- a/file1 Tue Feb 10 18:23:27 2009 +000011 +++ b/file1 Tue Feb 10 18:23:27 2009 +000012 @@ -1,1 +1,3 @@13 line 114 +line 215 +line 316

Figura 12.7: Refrescar un parche muchas veces para acumular cambios

Un parche aplicado tiene su correspondiente conjunto de cambios en el repositorio, y los efectos del parche yel conjunto de cambios son visibles en el directorio de trabajo. Puede deshacer la aplicacion de un parche con laorden “hg qpop”. MQ sabe acerca de, o maneja un parche sustraıdo, pero el parche ya no tendra un conjunto decambios correspondientes en el repositorio, y el directorio de trabajo no contendra los cambios hechos por el parche.La figura 12.10 ilustra la diferencia entre parches aplicados y seguidos.

Puede reaplicar un parche no aplicado o sustraıdo con la orden “hg qpush”. Esto crea un nuevo conjunto decambios correspondiente al parche, y los cambios del parche estaran presentes de nuevo en el directorio de trabajo.Vea ejemplos de “hg qpop” y “hg qpush” en accion en la figura 12.11. Vea que hemos sustraıdo uno o dos parches,la salida de“hg qseries” continua igual, mientras que “hg qapplied” ha cambiado.

12.5.5. Introducir y sustraer muchos parchesMientras que “hg qpush” y “hg qpop” operan sobre un unico parche cada vez, puede introducir y sustraer varios

parches de una vez. La opcion -a de “hg qpush” introduce todos los cambios que no hayan sido aplicados, mientrasque la opcion -a de “hg qpop” sustrae todos los cambios aplicados. (Vea la seccion 12.7 mas adelante en la cual seexplican otras formas de de introducir y sustraer varios cambios.)

12.5.6. Medidas de seguridad y como saltarlasMuchas ordenes MQ revisan el directorio de trabajo antes de hacer cualquier cosa, y fallan si encuentran alguna

modificacion. Lo hacen para garantizar que usted no pierda cambio alguno de los que haya hecho, pero que no hayansido incorporados en algun parche. La figura 12.13 ilusta esto; la orden “hg qnew” no creara un nuevo parche si haycambios notorios, causados en este caso por aplicado la orden “hg add” a file3.

Las ordenes que revisan el directorio actual cuentan con una opcion “Se lo que estoy haciendo”, que siempreesta nombrada como -f. El significado exacto de -f depende de la orden. Por ejemplo, “hg qnew -f” incorporarancualquier cambio notorio en el nuevo parche que crea pero “hg qpop -f” revertira las modificaciones a cualquierfichero que haya sido afectado por el parche que esta siendo sustraıdo. ¡Asegurese de leer la documentacion de laopcion -f de cada comando antes de usarla!

146

Page 157: Mercurial

1 $ hg qnew second.patch2 $ hg log --style=compact --limit=23 2[qtip,second.patch,tip] 17da5d88f25b 2009-02-10 18:23 +0000 bos4 [mq]: second.patch5

6 1[first.patch,qbase] 4fef714d728c 2009-02-10 18:23 +0000 bos7 [mq]: first.patch8

9 $ echo ’line 4’ >> file110 $ hg qrefresh11 $ hg tip --style=compact --patch12 2[qtip,second.patch,tip] 7cf293b98474 2009-02-10 18:23 +0000 bos13 [mq]: second.patch14

15 diff -r 4fef714d728c -r 7cf293b98474 file116 --- a/file1 Tue Feb 10 18:23:27 2009 +000017 +++ b/file1 Tue Feb 10 18:23:28 2009 +000018 @@ -1,3 +1,4 @@19 line 120 line 221 line 322 +line 423

24 $ hg annotate file125 0: line 126 1: line 227 1: line 328 2: line 4

Figura 12.8: Aplicar un parche despues del primero

12.5.7. Trabajar con varios parches a la vezLa orden “hg qrefresh” siempre refresca el ultimo parche aplicado. Esto significa que usted puede suspender su

trabajo en un parche (refrescandolo), sustraerlo o introducirlo para lograr que otro parche este de ultimo y trabajar enese parche por un rato.

A continuacion un ejemplo que ilustra como puede usar esta habilidad. Digamos que esta desarrollando una nuevacaracterıstica en dos parches. El primero es un cambio en la parte fundamental de su programa, y el segundo–sobreel primero—cambia la interfaz de usuario para usar el codigo que ha anadido a la parte fundamental. Si ve que hayun fallo en la parte fundamental mientras esta trabajando en el parche de UI5, es facil arreglar la parte fundamental.Simplemente use “hg qrefresh” sobre el parche de la UI para guardar los cambios de su trabajo en progreso, y use“hg qpop” para sacar sustraer el parche de la parte fundamental. Arregla el fallo sobre la parte fundamental, aplique“hg qrefresh” sobre el parche fundamental, y aplique “hg qpush” sobre el parche de UI para continuar donde habıaquedado.

5N. del T. Interfaz de Usuario, User Interface en ingles

147

Page 158: Mercurial

1 $ hg qseries2 first.patch3 second.patch4 $ hg qapplied5 first.patch6 second.patch

Figura 12.9: Entender la pila de parches con “hg qseries” y “hg qapplied”

prevent−compiler−reorder.patch

namespace−cleanup.patch

powerpc−port−fixes.patc

report−devinfo−correctly.patch

{

{

presente en la serie,pero no aplicado

parches aplicados,Conjuntos de cambios presentes

parche aplicadomás recientemente 201ad3209902

126b84e593ae

a655daf15409

e50d59aaea3a

forbid−illegal−params.patch

fix−memory−leak.patc

Figura 12.10: Parches aplicados y no aplicados en la pila de parches de MQ

12.6. Mas acerca de parchesMQ usa la orden GNU patch para aplicar los parches, por lo tanto es util conocer ciertos detalles de como trabaja

patch, y tambien acerca de los parches.

12.6.1. La cantidad de franjasSi ve el encabezado de un parche, notara que la ruta al fichero tiene un componente adicional al principio, que no

esta presente en la ruta. Esta es una traza de como generaba anteriormente los parches la gente (algunos aun lo hacen,pero es raro con las herramientas de control de revisiones del actuales).

Alicia desempaquetarıa un comprimido, editarıa sus ficheros, y querrıa crear un parche. Por lo tanto ella renom-brarıa su directorio de trabajo, desempacarıa el comprimido de nuevo (para lo cual necesito el renombramiento), yusarıa las opciones -r y -N de diff para generar recursivamente un parche entre el directorio original y el modificado.El resultado serıa que el nombre del directorio original estarıa al principio de toda ruta en cada encabezado de fichero,y el nombre del directorio modificado estarıa al frente de la porcion derecha de la ruta del fichero.

Como alguien que reciba un parche de Alicia en la red podrıa obtener dos directorios, uno original y el otro modi-ficado con exactamente los mismos nombres, la orden patch tiene la opcion -p que indica la cantidad de componentesde la ruta a eliminar cuando se vaya a aplicar el parche. Este numero se llama la cantidad de eliminaciones.

La opcion con “-p1” significa “elimine uno”. Si patch ve un nombre de fichero foo/bar/baz en el encabezadodel fichero, eliminara foo y tratara de parchar un fichero llamado bar/baz. (Hablando estrictamente, la cantidad deeliminaciones se refiere a la cantidad de separadores de ruta (y los componentes que vayan con ellos) a eliminar. Si elcontador es uno volvera foo/bar en bar, pero /foo/bar (note la barra extra) en foo/bar.)

La cantidad a eliminar“estandar” para parches es uno; casi todos los parches contienen un componente inicial dela ruta que necesita ser eliminado. La orden “hg diff” de Mercurial genera nombres de ruta de esta forma, y la orden“hg import” y MQ esperan parches que tengan a uno como cuenta de eliminaciones.

148

Page 159: Mercurial

1 $ hg qapplied2 first.patch3 second.patch4 $ hg qpop5 Now at: first.patch6 $ hg qseries7 first.patch8 second.patch9 $ hg qapplied

10 first.patch11 $ cat file112 line 113 line 214 line 3

Figura 12.11: Modificar la pila de parches aplicados

1 $ hg qpush -a2 applying second.patch3 Now at: second.patch4 $ cat file15 line 16 line 27 line 38 line 4

Figura 12.12: Pushing all unapplied patches

Si recibe un parche de alguien de quien desea adicionar adicionar a su cola de parches, y el parche necesita unacuenta de eliminacion que no sea uno, no podra aplicar “hg qimport” en primera medida, porque “hg qimport”no tiene todavıa una opcion -p option (ver Fallo de Mercurial No. 311). Lo mejor que puede hacer es aplicar “hgqnew” por su cuenta, y despues usar “patch -pN” para aplicar tal parche, seguido de “hg addremove” para tener encuenta cualquier fichero adicionado o eliminado por el parche, seguido de “hg qrefresh”. Esta complejidad puedeser innecesaria; consulte Fallo de Mercurial No. 311 para mas informacion.

12.6.2. Estrategias para aplicar parchesCuando patch aplica un trozo, intenta varias estrategias sucesivas que decrecen en precision para intentar aplicarlo.

Esta tecnica de pruebas y error aveces permite que un parche que fue generado contra una version anterior de unfichero, sea aplicada sobre una version mas nueva del mismo.

Primero patch intenta una correspondencia perfecta donde los numeros de lınea, el contexto y el texto a modificardeben coincidir perfectamente. Si no lo logra, intenta encontrar una correspondencia exacta del contexto, sin tener encuenta el numero de lınea. Si es exitoso, imprime una lınea indicando que el trozo fue aplicado, pero a un corrimientodel numero de lınea original.

Si falla la correspondencia por contexto, patch elimina la primera y la ultima lınea del contexto, e intenta unacorrespondencia reducida del contexto. Si el trozo con contexto reducido es exitoso, imprime un mensaje indicandoque aplico el trozo con un factor difuso (el numero despues del factor difuso indica cuantas lıneas de contexto patchtuvo que eliminar antes de aplicar el parche).

149

Page 160: Mercurial

1 $ echo ’file 3, line 1’ >> file32 $ hg qnew add-file3.patch3 $ hg qnew -f add-file3.patch4 abort: patch "add-file3.patch" already exists

Figura 12.13: Crear un parche a la fuerza

Cuando ninguna de estas tecnicas funciona, patch imprime un mensaje indicando que el trozo en cuestion sedesecho. Almacena los trozos desechados (tambien llamados “descartados”) en un fichero con el mismo nombre, y laextension .rej anadida. Tambien almacena una copia igual al fichero original con la extension .orig; la copia delfichero sin extension contendra cualquier cambio hecho por los trozos que sı se aplicaron sin problema. Si usted tieneun parche que modifica foo con seis trozos, y uno de ellos falla al aplicarse, tendra : un fichero original foo.orig, unfichero foo.rej que contiene el trozo, y foo, que contiene los cambios que se aplicaron por los cinco trozos exitosos.

12.6.3. Algunos detalles de la representacion de parchesHay ciertas cosas utiles por saber acerca de como trabaja patch con los ficheros:

Deberıa ser obvio que patch no puede manipular ficheros binarios.

No se preocupa por el bit ejecutable; crea ficheros nuevos en modo lectura, pero no ejecutable.

patch intenta eliminar un fichero como una diferencia entre el fichero a eliminar y un fichero vacıo. Y por lotanto su idea de “Borre este fichero” deberıa pensarse como “toda lınea de este fichero fue eliminada” en unparche.

Trata la adicion de un fichero como un diff entre un fichero vacıo y el fichero a ser adicionado. Por lo tanto enun parche su idea de “Anadı este fichero” se verıa como “toda lınea de este fichero fue anadida”.

Trata el renombramiento de un fichero como la eliminacion del nombre anterior y la adicion del nuevo nombre.Esto significa que los ficheros renombrados dejan un rastro grande en los parches. (Tenga en cuenta que Mercu-rial no trata de inferir cuando los ficheros han sido renombrados o copiados en un parche en este momento.)

patch no puede representar ficheros vacıos, por lo tanto no puede usar un parche para representar la nocion“Anadı este fichero vacıo al arbol”.

12.6.4. Cuidado con los difusosCuando aplique un trozo con un corrimiento, o con un factor difuso, aveces sera taotalmente exitoso, tales tecnicas

inexactas dejan claramente la posibilidad de corromper el fichero parchado. Los casos mas tıpicos involucran aplicarun parche dos veces o en un sitio incorrecto del fichero. Si patch o “hg qpush” llegan a mencionar un corrimiento oun factor difuso, deberıa asegurarse que los ficheros modificados esten correctos despues del suceso.

Casi siempre es buena idea refrescar un parche que fue aplicado con un corrimiento o un factor difuso; refrescar elparche genera nueva informacion de contexto que permitira aplicarlo limpiamente. Digo “casi siempre,” no “siempre”,puesto que en ciertas ocasiones refrescar un parche lo hara fallar frente a una revision diferente del fichero. En algunoscasos, como por ejemplo, cuando usted esta manteniendo un parche que debe estar encima de multiples revisiones deun arbol de fuentes, es aceptable tener un parche aplicado algo difuso, siempre que haya verificado los resultados delproceso de parchar.

150

Page 161: Mercurial

12.6.5. Manejo de descartesSi “hg qpush” falla al aplicar un parche, mostrara un texto de error y saldra. Si ha dejado ficheros .rej, es mejor

arreglar los trozos descartados antes de introducir parches adicionales o hacer cualquier otra cosa.Si su parche solıa aplicarse limpiamente, y ya no lo hace porque ha cambiado codigo subyacente en el cual se basa

su parche, las Colas de Mercurial pueden ayudar; consulte la seccion 12.8.Desafortunadamente, no hay grandes tecnicas para tratar los trozos descartados. Casi siempre debera consultar el

fichero .rej y editar el fichero objetivo, aplicando los trozos descartados a mano.Si es aventurero, Neil Brown, un hacker del nucleo Linux, escribio una herramienta llamada wiggle [Bro], que es

mas vigorosa que patch en su intento de hacer que se aplique un parche.Otro hacker del nucleo Linux, Chris Mason (el autor de las Colas de Mercurial), escribio una herramienta similar

llamada mpatch [Mas], que sigue una aproximacion sencilla para automatizar la aplicacion de trozos descartados porpatch. La orden mpatch puede ayudar con cuatro razones comunes por las cuales un parche ha sido descartado:

El contexto en la mitad de un trozo ha cambiado.

Un trozo ha perdido cierto contexto al principio o al final.

Un trozo largo podrıa aplicarse mejor—por completo o una parte—si estaba cortado en trozos mas pequenos.

Un trozo remueve lıneas con contenido ligeramente diferente que aquellas que estan presentes en el fichero.

Si usted usa wiggle o mpatch, deberıa ser doblemente cuidadoso al revisar sus resultados cuando haya terminado.De hecho, mpatch refuerza este metodo de revisar por partida doble su salida, dejandolo a usted en un programa defusion cuando la herramienta haya terminado su trabajo, de tal forma que usted pueda verificar lo que ha hecho ypueda terminar de aplicar cualquier fusion faltante.

12.7. maximizar el rendimiento de MQMQ es muy eficiente al tratar con una gran cantidad de parches. Corrı unos experimentos de desempeno a mediados

del 2006 para una charla que dı en la conferencia EuroPython 2006 [O’S06]. Emplee la serie de parches para el nucleoLinux 2.6.17-mm1, que contaba con 1.738 parches. Los aplique sobre un repositorio del nucleo de Linux con todaslas 27.472 revisiones entre 2.6.12-rc2 y 2.6.17.

En mi portatil antiguo y lento, logre aplicar “hg qpush -a” a los 1.738 parches en 3.5 minutos, y “hg qpop -a”en 30 segundos. (En un portatil mas nuevo, el tiempo para introducir todos los parches, se logro en menos de dosminutos.) Aplique “hg qrefresh” sobre uno de los parches mas grandes (que hizo 22.779 lıneas de cambios en 287ficheros) en 6,6 segundos.

Claramente, MQ funciona adecuadamente en arboles grandes, y ademas hay unos trucos que pueden emplearsepara obtener el maximo desempeno.

En primer lugar, trate de hacer “en lote” las operaciones. Cada vez que ejecute “hg qpush” o “hg qpop”, talesordenes revisan el directorio de trabajo para asegurarse de que usted no ha hecho cambios y ha olvidado ejecutar “hgqrefresh”. En un arbol pequeno, el tiempo de esta revision puede ser mınimo, Pero en un arbol mediano (con decenasde miles de ficheros), puede tomar un segundo o mas.

Las ordenes “hg qpush” y “hg qpop” le permiten introducir o sustraer varios parches en una operacion. Puedeidentificar el “parche destino” que desee. Cuando aplique “hg qpush” con un destino, introducira tantos parchescomo sea necesario hasta que el especificado este en el tope de la pila. Cuando emplee “hg qpop” con un destino, MQsustraera parches hasta que el parche destino este en el tope.

Puede identificar un parche destino con el nombre del parche o con el numero. Si se refiere al numero, los parchesse contaran desde cero; esto significa que el primer parche es cero, el segundo es uno y ası sucesivamente.

151

Page 162: Mercurial

12.8. Actualiar los parches cuando el codigo cambiaEs comun contar con una pila de parches sobre un repositorio que usted no modifica directamente. Si esta traba-

jando en cambios de codigo de otros, o en una caracterıstica que tarda bastante en desarrollarse comparada con la tasade cambio del codigo sobre la cual se esta trabajando, necesitara sincronizarse con el codigo, y ajustar cualquier trozoen sus parches que ya no esten al dıa. A esto se le llama hacer rebase a su serie de parches.

La vıa mas sencilla de hacerlo es con “hg qpop -a” sobre sus parches, despues hacer “hg pull” de los cambiosen el repositorio, y finalmente hacer “hg qpush -a” con sus parches de nuevo. MQ dejara de de introducir parchessiempre que llegue a un parche que no se pueda aplicar debido a un conflicto, permitiendole a usted arreglarlo, aplicar“hg qrefresh” al parche afectado y continuar introduciendo hasta que haya arreglado la pila completa.

Esta aproximacion es sencilla y funciona bien si no espera cambios en el codigo original que afecte en gran medidalos parches que usted este aplicando. Si su pila de parches toca codigo que es modificado frecuentemente o de formainvasiva sobre el codigo subyacente, arreglar trozos manualmente se vuelve desgastante.

Es posible automatizar de forma parcial el proceso de rebase. Si sus parches se aplican limpiamente sobre algunasrevisiones del repositorio subyacente, MQ puede usar esta informacion para ayudarle a a resolver conflictos entre susparches y una revision distinta.

El proceso resulta un poco complejo:

1. Para comenzar, haga “hg qpush -a” sobre todos los parches que usted sepa se aplican limpiamente.

2. Guarde una copia de seguridad de su directorio de parches con “hg qsave -e -c”. Esto imprime el nom-bre del directorio en el cual se han guardado los parches. Guardara los parches en un directorio llamado.hg/patches.N , donde N es un entero pequeno. Tambien consigna un “conjunto de cambios de seguridad”sobre sus parches aplicados; esto es para mantener el historico, y guarda los estados de los ficheros series ystatus.

3. Use “hg pull” para traer los nuevos cambios en el repositorio subyacente. (No ejecute “hg pull -u”; vea masadelante por que.)

4. Actualice a la nueva revision punta con “hg update -C” para sobreescribir los parches que haya introducido.

5. Fusione todos los parches con “hg qpush -m -a”. La opcion -m de “hg qpush” le indica a MQ que haga unafusion que involucra tres fuentes si el parche falla al aplicarse.

Durante el “hg qpush -m”, cada parche en el fichero series se aplica normalmente. Si un parche se aplicadifusamente o se niea a aplicarse, MQ consulta la cola que usted guardo con “hg qsave”, y aplica una fusion de trescon el correspondiente conjunto de cambios. Esta fusion usa la maquinaria de Mercurial, por lo tanto puede mostraruna herramienta de fusion GUI para ayudarle a resolver los problemas.

Cuando termine de resolver los efectos de un parche, MQ refrescara su parche basado en el resultado de la fusion.Al final de este proceso, su repositorio tendra una cabeza extra de la antigua cola de parches, y una copia de la

cola de parches anterio estara en .hg/patches.N . Puede eliminar la cabeza extra con “hg qpop -a -n patches.N”o “hg strip”. Puede eliminar .hg/patches.N una vez que este seguro de que no lo necesita mas como copia deseguridad.

12.9. Identificar parchesLas ordenes de MQ le permiten trabajar refiriendose al nombre del parche o al numero. Es obvio hacerlo por el

nombre; por ejemplo se pasa el nombre foo.patch a “hg qpush”, que introducira los parches hasta que foo.patchse aplique.

Para hacerlo mas corto, puede referirse a un parche con un nombre y un corrimiento de numero; por ejemplo,foo.patch-2 significa “dos parches antes de foo.patch”, mientras que bar.patch+4 significa “cuatro parches de-spues de bar.patch”.

152

Page 163: Mercurial

Referirse a un parche por su ındice no es muy diferente. El primer parche que se imprime en la salida de “hgqseries” es el parche cero (si, es el primero en los sistemas que comienzan su conteo en cero); el segundo parche esuno y ası sucesivamente.

MQ facilita el trabajo cuando esta usando ordenes normales de Mercurial. Cada comando que acepte Identifi-cadores de conjuntos de cambios tambien aceptara el nombre de un parche aplicado. MQ aumenta las etiquetas nor-malmente en el repositorio con un distintivo para cada parche aplicado. Adicionalmente, las etiquetas especiales qbasey qtip identifican los parches “primero” y ultimo, respectivamente.

Junto con las capacidades de Mercurial para etiquetar, estas adiciones hacen que trabajar con parches sea muysencillo.

¿Desea enviar una bomba de parches a una lista de correo con los ultimos cambios que ha hecho?

1 hg email qbase:qtip

(¿No sabe que es una “bomba de parches”? Consulte la seccion 14.4.)

¿Desea ver todos los parches desde que se aplico foo.patch sobre los ficheros de un subdirectorio en su arbol?

1 hg log -r foo.patch:qtip subdir

Dado que MQ nombra los parches disponibles al resto de Mercurial con su maquinaria de etiquetas interna, ustedno necesita teclear el nombre completo de un parche cuando desea identificarlo por su nombre.

Otra consecuencia deseable al representar los nombres de parches como etiquetas es que cuando ejecute la orden“hg log”, desplegara el nombre del parche como una etiqueta, usualmente con la salida normal. Esto facilita distinguirvisualmente los parches aplicados de las revisiones “normales”. La figura 12.14 muestra algunos comandos usuales deMercurial al trabajar con parches.

12.10. Otra informacion utilHay una cantidad de aspectos que hacen que el uso de MQ no representen secciones en sı mismas, pero de los

cuales es bueno estar enterado. Los presentamos en aquı:

Usualmente cuando hace “hg qpop” a un parche y vuelve a hacerle “hg qpush”, el conjunto de cambios querepresenta el parche despues de introducir/sustraer tendra una identidad distinta que aquella que representaba elconjunto de cambios anteriormente. Consulte la secction B.1.13 para obtener informacion del por que de esto.

No es una buena idea aplicar “hg merge” de cambios de otra rama con un conjunto de cambios de parches, porlo menos si desea mantener la “informacion de parches” de ese conjunto de cambios y los conjuntos de cambiosque se encuentran por debajo en la pila de parches. Si intenta hacerlo, parecera que ha sido exitoso, pero MQ seconfundira.

12.11. Administrar parches en un repositorioDado que el directorio .hg/patches de MQ reside fuera del repositorio de trabajo de Mercurial, el repositorio

“subyacente” de Mercurial no sabe nada acerca de la administracion o presencia de parches.Esto presenta la interesante posibilidad de administrar los contenidos del directorio de parches como un repositorio

de Mercurial por su cuenta. Puede ser una forma util de trabajar. Por ejemplo, puede trabajar en un parche por un rato,hacerle “hg qrefresh” y despues hacer “hg commit” al estado actual del parche. Esto le permite “devolverse” a esaversion del parche posteriormente.

Puede tambien compartir diferentes versiones de la misma pila de parches entre varios repositorios subyacentes.Uso esto cuando estoy desarrollando una caracterıstica del nucleo Linux. Tengo una copia original de las fuentes del

153

Page 164: Mercurial

1 $ hg qapplied2 first.patch3 second.patch4 $ hg log -r qbase:qtip5 changeset: 1:b3508ccf62ad6 tag: first.patch7 tag: qbase8 user: Bryan O’Sullivan <[email protected]>9 date: Tue Feb 10 18:23:26 2009 +0000

10 summary: [mq]: first.patch11

12 changeset: 2:c836697fbf5513 tag: qtip14 tag: second.patch15 tag: tip16 user: Bryan O’Sullivan <[email protected]>17 date: Tue Feb 10 18:23:26 2009 +000018 summary: [mq]: second.patch19

20 $ hg export second.patch21 # HG changeset patch22 # User Bryan O’Sullivan <[email protected]>23 # Date 1234290206 024 # Node ID c836697fbf55654f972d867359527813dbbc5d4425 # Parent b3508ccf62adb8916c6a7b64105642fa516f5a9026 [mq]: second.patch27

28 diff -r b3508ccf62ad -r c836697fbf55 other.c29 --- /dev/null Thu Jan 01 00:00:00 1970 +000030 +++ b/other.c Tue Feb 10 18:23:26 2009 +000031 @@ -0,0 +1,1 @@32 +double u;

Figura 12.14: Uso de las caracterısticas de etiquetamiento al trabajar con MQ

nucleo para varias arquitecturas, y clone un rpositorio en cada una que contiene los parches en los cuales estoy traba-jando. Cuando quiero probar un cambio en una arquitectura diferente, introduzco mis parches actuales al repositoriode parches asociado con el arbol del kernel, sustraigo e introduzco todos mis parches, armo y pruebo el nucleo.

Llevar los parches en un repositorio permite que varios desarrolladores puedan trabajar en la misma serie deparches sin sobreponerse, todo sobre la fuente base subyacente que pueden o no controlar.

12.11.1. Soporte de MQ para repositorios de parchesMQ le ayuda a trabajar con el directorio .hg/patches como un repositorio; cuando usted prepara un repositorio

para trabajar con parches usando “hg qinit”, puede pasarle la opcion -c para que se cree el directorio .hg/patchescomo un repositorio de Mercurial.

154

Page 165: Mercurial

Nota: Si olvida usar la opcion -c option, puede ir al directorio .hg/patchesen cualquier momento y ejecutar “hg init”. No olvide anadir una entrada en elfichero status del fichero .hgignore, a pesar de que (“hg qinit -c” hace esto-do de forma automatica para usted); usted seguro no quiere administrar el ficherostatus.

MQ nota convenientemente que el directorio .hg/patches es un repositorio, hara “hg add” automaticamente acada parche que usted cree e importe.

MQ provee una orden corta, “hg qcommit”, que ejecuta “hg commit” en el directorio .hg/patches. Lo queahorra tecleo recurrente.

Finalmente, para administrar convenientemente el directorio de parches, puede definir el alias mq en sistemas Unix.Por ejemplo, en sistemas Linux con el interprete bash, puede incluir el siguiente recorte de codigo6 en su fichero˜/.bashrc.

1 alias mq=‘hg -R $(hg root)/.hg/patches’

Puede aplicar las ordenes de la forma “mq pull” al repositorio principal.

12.11.2. Detalles a tener en cuentaEl soporte de MQ para trabajar con un repositorio de parches es limitado en ciertos aspectos:MQ no puede detectar automaticamente los cambios que haga al directorio de parches. Si aplica “hg pull”, edita

manualmente, o hace “hg update” a los parches o el fichero series, tendra que aplicar “hg qpop -a” y despues “hgqpush -a” en el repositorio subyacente para que los cambios se reflejen allı. Si olvida hacerlo, puede confundir a MQen cuanto a que parches se han aplicado.

12.12. Otras herramientas para trabajar con parchesCuando haya trabajado por cierto tiempo con parches, deseara herramientas que le ayuden a entender y manipular

los parches con los que este tratando.La orden diffstat [Dic] genera un histograma de modificaciones hechas a cada fichero en un parche. Provee una

interesante forma de “dar un vistazo” al parche—que ficheros afecta, y cuantos cambios introduce a cada fichero yen total. (Me ha parecido interesante usar la opcion -p de diffstat, puesto que de otra forma intentara hacer cosasinteligentes con prefijos de ficheros que terminan confundiendome.)

El paquete patchutils [Wau] es invaluable. Provee un conjunto de pequenas utilidades que siguen la “filosofıaUnix”; cada una hace una cosa muy bien hecha a un parche. La orden patchutils que mas uso es filterdiff,que extrae subconjuntos de un fichero de parche. Por ejemplo, dado un parche que modifica centenas de ficheros endocenas de directorios, una unica invocacion de filterdiff puede generear un parche mas pequeno que solamentetoca aquellos ficheros con un patron. Puede ver otro ejemplo en la seccion 13.9.2.

12.13. Buenas practicas de trabajo con parchesEn caso de que este trabajando en una serie de parches para enviar a un proyecto de software libre o de fuentes

abiertas, o en una serie que desea tratar como un conjunto de cambios regular, cuando haya terminado, puede usartecnicas sencillas para mantener su trabajo bien organizado.

De nombres descriptivos a sus parches. Un buen nombre para un parche podrıa ser rework-device-alloc.patch,porque da de forma inmediata una pista del proposito del parche. Los nombres largos no deben ser un problema; nolos estara tecleando repetidamente, pero estara ejecutando regularmente ordenes como “hg qapplied” y “hg qtop”.Los nombres adecuados son especialmente importantes cuando tiene bastantes parches con los cuales trabajar, o siesta trabajando en diferentes tareas y sus parches toman solamente una porcion de su atencion.

6N. del T. snippet

155

Page 166: Mercurial

1 $ diffstat -p1 remove-redundant-null-checks.patch2 drivers/char/agp/sgi-agp.c | 5 ++---3 drivers/char/hvcs.c | 11 +++++------4 drivers/message/fusion/mptfc.c | 6 ++----5 drivers/message/fusion/mptsas.c | 3 +--6 drivers/net/fs_enet/fs_enet-mii.c | 3 +--7 drivers/net/wireless/ipw2200.c | 22 ++++++----------------8 drivers/scsi/libata-scsi.c | 4 +---9 drivers/video/au1100fb.c | 3 +--

10 8 files changed, 19 insertions(+), 38 deletions(-)11 $ filterdiff -i ’*/video/*’ remove-redundant-null-checks.patch12 --- a/drivers/video/au1100fb.c˜remove-redundant-null-checks-before-free-in-drivers13 +++ a/drivers/video/au1100fb.c14 @@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void)15 {16 driver_unregister(&au1100fb_driver);17

18 - if (drv_info.opt_mode)19 - kfree(drv_info.opt_mode);20 + kfree(drv_info.opt_mode);21 }22

23 module_init(au1100fb_init);

Figura 12.15: Las ordenes diffstat, filterdiff, y lsdiff

Tenga en cuenta en que parche esta trabajando. Use la orden “hg qtop” para dar un vistazo al texto de sus parchesfrecuentemente—por ejemplo, use “hg tip -p”)—para asegurarse en donde esta ubicado. En distintas oportunidadesme sucedio que aplique “hg qrefresh” a un parche distinto al que deseaba hacerlo, y usualmente es complejo migrarlos cambios al parche correcto despues de haberlo hecho mal.

Por este motivo, vale la pena invertir ese poco tiempo para aprender como usar otras herramientas que describı enla seccion 12.12, particularmente diffstat y filterdiff. La primera le dara una idea de que cambios esta haciendosu parche, mientras que la segunda permite seleccionar trozos de un parche para colocarlos en otro.

12.14. Recetas de MQ

12.14.1. Administrar parches “triviales”Puesto que colocar ficheros en un repositorio de Mercurial es tan sencillo, tiene bastante sentido administrar parch-

es de esta forma incluso si desea hacer algunos cambios al paquete de ficheros que descargo.Para comenzar a descargar y desempaqueter un paquete de ficheros, y volverlo en un repositorio de Mercurial:

1 $ download netplug-1.2.5.tar.bz22 $ tar jxf netplug-1.2.5.tar.bz23 $ cd netplug-1.2.54 $ hg init5 $ hg commit -q --addremove --message netplug-1.2.56 $ cd ..7 $ hg clone netplug-1.2.5 netplug

156

Page 167: Mercurial

8 updating working directory9 18 files updated, 0 files merged, 0 files removed, 0 files unresolved

Continue creando una pila de parches y haga sus cambios.

1 $ cd netplug2 $ hg qinit3 $ hg qnew -m ’fix build problem with gcc 4’ build-fix.patch4 $ perl -pi -e ’s/int addr_len/socklen_t addr_len/’ netlink.c5 $ hg qrefresh6 $ hg tip -p7 changeset: 1:4adf6cb9cc168 tag: qtip9 tag: build-fix.patch

10 tag: tip11 tag: qbase12 user: Bryan O’Sullivan <[email protected]>13 date: Tue Feb 10 18:23:26 2009 +000014 summary: fix build problem with gcc 415

16 diff -r ca53495653b5 -r 4adf6cb9cc16 netlink.c17 --- a/netlink.c Tue Feb 10 18:23:26 2009 +000018 +++ b/netlink.c Tue Feb 10 18:23:26 2009 +000019 @@ -275,7 +275,7 @@20 exit(1);21 }22

23 - int addr_len = sizeof(addr);24 + socklen_t addr_len = sizeof(addr);25

26 if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) {27 do_log(LOG_ERR, "Could not get socket details: %m");28

Digamos que pasan unas semanas o meses, y el autor del paquete libera una nueva version. Primero se traen suscambios al repositorio.

1 $ hg qpop -a2 Patch queue now empty3 $ cd ..4 $ download netplug-1.2.8.tar.bz25 $ hg clone netplug-1.2.5 netplug-1.2.86 updating working directory7 18 files updated, 0 files merged, 0 files removed, 0 files unresolved8 $ cd netplug-1.2.89 $ hg locate -0 | xargs -0 rm

10 $ cd ..11 $ tar jxf netplug-1.2.8.tar.bz212 $ cd netplug-1.2.813 $ hg commit --addremove --message netplug-1.2.8

La porcion que comienza con “hg locate” mostrada mas arriba, borra todos los ficheros en el directorio de trabajo,

157

Page 168: Mercurial

ası que la opcion --addremove de “hg commit” puede indicar que ficheros se han eliminado en la nueva version delarbol de fuentes.

Finalmente, puede aplicar sus parches encima del nuevo arbol de fuentes

1 $ cd ../netplug2 $ hg pull ../netplug-1.2.83 pulling from ../netplug-1.2.84 searching for changes5 adding changesets6 adding manifests7 adding file changes8 added 1 changesets with 12 changes to 12 files9 (run ’hg update’ to get a working copy)

10 $ hg qpush -a11 (working directory not at tip)12 applying build-fix.patch13 Now at: build-fix.patch

12.14.2. Combinar parches completosMQ provee la orden “hg qfold” que le permite combinar parches enteros. Se “integran”7 los parches que ust-

ed nombre, en el orden que especifique, en el ultimo parche aplicado, y concatena sus descripciones al final de sudescripcion. Debera sustraer los cambios para poder integrarlos.

El orden en el que integre los parches importa. Si el parche ultimamente aplicado es foo, e integra “hg qfold”bar y quux en el, terminara con un parche que tiene el mismo efecto que si hubiera aplicado primero foo, y despuesbar, seguido de quux.

12.14.3. Fusionar una porcion de un parche dentro de otroFusionar partes de un parche dentro de otro es mas complejo que combinar completamente dos parches.Si desea mover cambios de ficheros completos, puede usar las opciones filterdiff’s -i y -x para elegir las

modificaciones remover de un parche, concatenar su salida al final del parche donde desea fusionarlo. Usualmenteno necesitara modificar el parche del cual ha fusionado los cambios. En cambio, MQ reportara que hay unos trozosque se han desechado cuando usted aplique “hg qpush” (de los trozos que haya movido al otro parche), y puedesencillamente aplicar “hg qrefresh” para eliminar los trozos replicados.

Si tiene un parche que tiene varios trozos que modifican un fichero, y desea mover solamente unos de ellos, eltrabajo es un poco mas enredado, pero puede automatizarlo parcialmente. Use “lsdiff -nvv” para imprimir algunosmetadatos del parche.

1 $ lsdiff -nvv remove-redundant-null-checks.patch2 22 File #1 a/drivers/char/agp/sgi-agp.c3 24 Hunk #1 static int __devinit agp_sgi_init(void)4 37 File #2 a/drivers/char/hvcs.c5 39 Hunk #1 static struct tty_operations hvcs_ops =6 53 Hunk #2 static int hvcs_alloc_index_list(int n)7 69 File #3 a/drivers/message/fusion/mptfc.c8 71 Hunk #1 mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in9 85 File #4 a/drivers/message/fusion/mptsas.c

10 87 Hunk #1 mptsas_probe_hba_phys(MPT_ADAPTER *ioc)11 98 File #5 a/drivers/net/fs_enet/fs_enet-mii.c

7N. del T. fold

158

Page 169: Mercurial

12 100 Hunk #1 static struct fs_enet_mii_bus *create_bu13 111 File #6 a/drivers/net/wireless/ipw2200.c14 113 Hunk #1 static struct ipw_fw_error *ipw_alloc_er15 126 Hunk #2 static ssize_t clear_error(struct device16 140 Hunk #3 static void ipw_irq_tasklet(struct ipw_p17 150 Hunk #4 static void ipw_pci_remove(struct pci_de18 164 File #7 a/drivers/scsi/libata-scsi.c19 166 Hunk #1 int ata_cmd_ioctl(struct scsi_device *sc20 178 File #8 a/drivers/video/au1100fb.c21 180 Hunk #1 void __exit au1100fb_cleanup(void)

Esta orden imprime tres clases diferentes de numeros:

(en la primera columna) un numero de fichero para identificar cada fichero modificado en el parche;

(En la siguiente lınea, indentado) el numero de lınea dentro de un fichero modificado donde comienza el trozo;y

(en la misma lınea) un numero de trozo que identifica el trozo.

Tendra que hacer una inspeccion visual, y leer el parche para identificar los numeros de fichero y trozo que desea,pero puede pasar posteriormente a las opciones --files y --hunks de filterdiff, para seleccionar exactamente elfichero y el trozo que desea extraer.

Cuando tenga el trozo, puede concatenarlo al final de su parche objetivo y continuar como en la seccion 12.14.2.

12.15. Diferencias entre quilt y MQSi le es familiar quilt, MQ provee un conjunto similar de ordenes. Hay algunas diferencias en como funcionan.Debe haber notado que la mayorıa de comandos de quilt tienen su contraparte en MQ, que simplemente comienzan

con “q”. Las excepciones son las ordenes add y remove de quilt, que realmente son las ordenes “hg add” y “hgremove” de Mercurial. No hay un equivalente en MQ para la orden edit de quilt.

159

Page 170: Mercurial

Capıtulo 13

Usos avanzados de las Colas de Mercurial

Auunque es facil aprender los usos mas directos de las Colas de Mercurial, tener algo de disciplina junto conalgunas de las capacidadees menos usadas de MQ hace posible trabajar en entornos de desarrollo complejos.

En este capıtulo, usare como ejemplo una tecnica que he usado para administrar el desarrollo de un controlador dedispositivo Infiniband para el kernel de Linux. El controlador en cuestion es grande (al menos en lo que se refiere acontroladores), con 25,000 lıneas de codigo esparcidas en 35 ficheros fuente. Es mantenido por un equipo pequeno dedesarrolladores.

Aunque mucho del material en este capıtulo es especıfico de Linux, los mismos principios aplican a cualquier basede codigo de la que usted no sea el propietario principal, y sobre la que usted necesita hacer un monton de desarrollo.

13.1. El problema de multiples objetivosEl kernel de Linux cambia con rapidez, y nunca ha sido estable internamente; los desarrolladores hacen cambios

drasticos entre versiones frecuentemente. Esto significa que una version del controlador que funciona bien con unaversion particular del kernel ni siquiera compilara correctamente contra, tıpicamente, cualquier otra version.

Para mantener un controlador, debemos tener en cuenta una buena cantidad de versiones de Linux en mente.

Un objetivo es el arbol de desarrollo principal del kernel de Linux. En este caso el mantenimiento del codigoes compartido parcialmente por otros desarrolladores en la comunidad del kernel, quienes hacen modificaciones“de-afan” al controlador a medida que desarrollan y refinan subsistemas en el kernel.

Tambien mantenemos algunos “backports” para versiones antiguas del kernel de Linux, para dar soporte alas necesidades de los clientes que estan corriendo versiones antiguas de Linux que no incorporan nuestroscontroladores. (Hacer el backport de un pedazo de codigo es modificarlo para que trabaje en una version de suentorno objetivo anterior a aquella para la cual fue escrito.)

Finalmente, nosotros liberamos nuestro software de acuerdo a un cronograma que no necesariamente esta alin-eado con el que usan los distribuidores de Linux y los desarrolladores del kernel, ası que podemos entregarnuevas caracterısticas a los clientes sin forzarlos a actualizar kernels completos o distribuciones.

13.1.1. Aproximaciones tentadoras que no funcionan adecuadamenteHay dos maneras estandar de mantener una porcion de software que debe funcionar en muchos entornos diferentes.La primera es mantener varias ramas, cada una pensada para un unico entorno. El problema de esta aproximacion

es que usted debe tener una disciplina ferrea con el flujo de cambios entre repositorios. Una nueva caracterıstica o unarreglo de fallo deben empezar su vida en un repositorio “prıstino”, y luego propagarse a cada repositorio de backport.Los cambios para backports estan mas limitados respecto a las ramas a las que deberıan propagarse; un cambio parabackport que es aplicado a una rama en la que no corresponde probablemente hara que el controlador no compile.

160

Page 171: Mercurial

La segunda es mantener un unico arbol de codigo fuente lleno de declaraciones que activen o desactiven seccionesde codigo dependiendo del entorno objetivo. Ya que estos “ifdefs” no estan permitidos en el arbol del kernel de Linux,debe seguirse algun proceso manual o automatico para eliminarlos y producir un arbol limpio. Una base de codigomantenida de esta manera se convierte rapidamente en un nido de ratas de bloques condicionales que son difıciles deentender y mantener.

Ninguno de estos enfoques es adecuado para situaciones en las que usted no es “dueno” de la copia canonicade un arbol de fuentes. En el caso de un controlador de Linux que es distribuido con el kernel estandar, el arbol deLinux contiene la copia del codigo que sera considerada por el mundo como la canonica. La version oficial de “mi”controlador puede ser modificada por gente que no conozco, sin que yo siquiera me entere de ello hasta despues deque los cambios aparecen en el arbol de Linus.

Estos enfoques tienen la debilidad adicional de dificultar la generacion de parches bien formados para enviarlos ala version oficial.

En principio, las Colas de Mercurial parecen ser un buen candidato para administrar un escenario de desarrollocomo el de arriba. Aunque este es de hecho el caso, MQ tiene unas cuantas caracterısticas adicionales que hacen eltrabajo mas agradable.

13.2. Aplicar parches condicionalmente mediante guardiasTal vez la mejor manera de conservar la cordura con tantos entornos objetivo es poder escoger parches especıficos

para aplicar para cada situacion. MQ provee una caracterıstica llamada “guardias” (que se origina del comando guardsde Quilt) que hace precisamente esto. Para empezar, creemos un repositorio sencillo para experimentar.

1 $ hg qinit2 $ hg qnew hello.patch3 $ echo hello > hello4 $ hg add hello5 $ hg qrefresh6 $ hg qnew goodbye.patch7 $ echo goodbye > goodbye8 $ hg add goodbye9 $ hg qrefresh

Esto nos brinda un pequeno repositorio que contiene dos parches que no tienen ninguna dependencia respecto al otro,porque tocan ficheros diferentes.

La idea detras de la aplicacion condicional es que usted puede “etiquetar” un parche con un guardia, que sim-plemente es una cadena de texto de su eleccion, y luego decirle a MQ que seleccione guardias especıficos para usarcuando aplique parches. MQ entonces aplicara, u omitira, un parche vigilado, dependiendo de los guardias que ustedhaya seleccionado.

Un parche puede tener una cantidad arbitraria de guardias; cada uno es positivo (“aplique el parche si este guardiaes seleccionado”) o negativo (“omita este parche si este guardia es seleccionado”). Un parche sin guardias siempre esaplicado.

13.3. Controlar los guardias de un parcheEl comando “hg qguard” le permite determinar que guardias deben aplicarse a un parche, o mostrar los guardias

que estan en efecto. Sin ningun argumento, el comando muestra los guardias del parche actual de la parte mas alta dela pila.

1 $ hg qguard2 goodbye.patch: unguarded

161

Page 172: Mercurial

Para poner un guardia positivo en un parche, prefije el nombre del guardia con un “+”.

1 $ hg qguard +foo2 $ hg qguard3 goodbye.patch: +foo

Para poner un guardia negativo en un parche, prefije el nombre del guardia con un “-”.

1 $ hg qguard hello.patch -quux2 $ hg qguard hello.patch3 hello.patch: -quux

Nota: El comando “hg qguard” pone los guardias en un parche; no los modifica.Esto significa que si usted ejecuta “hg qguard +a +b” sobre un parche, y luego“hg qguard +c” en el mismo parche, el unico guardia sobre el parche despues delcomando sera +c.

Mercurial almacena los guardias en el fichero series; la forma en que son almacenados es facil tanto de entendercomo de editar a mano. (En otras palabras, usted no tiene que usar el comando “hg qguard” si no lo desea; esta biensimplemente editar el fichero series)

1 $ cat .hg/patches/series2 hello.patch #-quux3 goodbye.patch #+foo

13.4. Selecccionar los guardias a usarEl comando “hg qselect” determina que guardias estan activos en cualquier momento. El efecto de esto es

determinar que parches aplicara MQ la proxima vez que usted ejecute “hg qpush”. No tiene ningun otro efecto; enparticular, no hace nada a los parches que ya han sido aplicados.

Sin argumentos, el comando “hg qselect” lista los guardias en efecto actualmente, uno por cada lınea de salida.Cada argumento es tratado como el nombre de un guardia a aplicar.

1 $ hg qpop -a2 Patch queue now empty3 $ hg qselect4 no active guards5 $ hg qselect foo6 number of unguarded, unapplied patches has changed from 1 to 27 $ hg qselect8 foo

Si esta interesado, los guardias seleccionados actualmente estan almacenados en el fichero guards.

1 $ cat .hg/patches/guards2 foo

Podemos ver el efecto que tienen los guardias seleccionados cuando ejecutamos “hg qpush”.

1 $ hg qpush -a2 applying hello.patch3 applying goodbye.patch4 Now at: goodbye.patch

162

Page 173: Mercurial

Un guardia no puede empezar con un caracter “+” o “-”. El nombre del guardia no debe contener espacios enblanco, pero muchos otros caracteres son aceptables. Si usted trata de usar un guardia con un nombre invalido, MQ sequejara:

1 $ hg qselect +foo2 abort: guard ’+foo’ starts with invalid character: ’+’

Cambiar los guardias seleccionados cambia los parches que son aplicados.

1 $ hg qselect quux2 number of guarded, applied patches has changed from 0 to 23 $ hg qpop -a4 Patch queue now empty5 $ hg qpush -a6 patch series already fully applied

Usted puede ver en el ejemplo de abajo que los guardias negativos tienen precedencia sobre los guardias positivos.

1 $ hg qselect foo bar2 number of unguarded, unapplied patches has changed from 0 to 23 $ hg qpop -a4 no patches applied5 $ hg qpush -a6 applying hello.patch7 applying goodbye.patch8 Now at: goodbye.patch

13.5. Reglas de MQ para aplicar parchesLas reglas que MQ usa para decidir si debe aplicar un parche son las siguientes.

Un parche sin guardias es aplicado siempre.

Si el parche tiene algun guardia negativo que corresponda con cualquiera de los guardias seleccionados, se saltael parche.

Si el parche tiene algun guardia positivo que corresponda con cualquiera de los guardias seleccionados, se aplicael parche.

Si el parche tiene guardias positivos o negativos, pero ninguno corresponde con cualquiera de los guardiasseleccionados, se salta el parche.

13.6. Podar el entorno de trabajoEn el trabajo del controlador de dispositivo que mencione anteriormente, yo no aplico los parches a un arbol

normal del kernel de Linux. En cambio, uso un repositorio que solo contiene una instantanea de los ficheros fuente yde cabecera que son relevantes para el desarrollo de Infiniband. Este repositorio tiene un 1 % del tamano del repositoriodel kernel, por lo que es mas facil trabajar con el.

Luego escojo una version “base” sobre la cual son aplicados los parches. Es una instantanea del arbol del kernelde Linux en una revision de mi eleccion. Cuando tomo la instantanea, almaceno el ID de conjunto de cambios en elmensaje de consignacion. Ya que la instantanea preserva la “forma” y el contenido de las partes relevantes del arboldel kernel, puedo aplicar mis parches sobre mi pequeno repositorio o sobre un arbol normal del kernel.

163

Page 174: Mercurial

Normalmente, el arbol base sobre el que se aplican los parches deberıa ser una instantanea de un arbol de desarrollomuy reciente. Esto facilita mucho el desarrollo de parches que puedan ser enviados al arbol oficial con pocas o ningunamodificacion.

13.7. Dividir el fichero seriesYo categorizo los parches en el fichero series en una serie de grupos logicos. Cada seccion de parches similares

empieza con un bloque de comentarios que describen el proposito de los parches que le siguen.La secuencia de grupos de parches que mantengo se muestra a continuacion. El orden de los grupos es importante;

explicare porque luego de que presente los grupos.

El grupo “aceptado”. Son parches que el equipo de desarrollo ha enviado al mantenedor del subsistema Infini-band, y que el ha aceptado, pero que no estan presentes en la instantanea en la cual esta basada el repositoriopequeno. Estos son parches de “solo lectura”, presentes unicamente para transformar el arbol en un estadosimilar al del repositorio del mantenedor oficial.

El grupo “revisar”. Parches que yo he enviado, pero sobre los que que el mantenedor oficial ha solicitadomodificaciones antes de aceptarlos.

El grupo “pendiente”. Parches que no he enviado al mantenedor oficial, pero que ya estan terminados. Estosparches seran de “solo lectura” por un buen tiempo. Si el mantenedor oficial los acepta cuando los envıe, losmovere al final del grupo “aceptado”. Si el solicita que modificaciones en alguno de ellos, los movere al principiodel grupo “revisar”.

El grupo “en proceso”. Parches que estan siendo activamente desarrollados, y no deberıan ser enviados a ningunaparte aun.

El grupo “backport”. Parches que adaptan el arbol de fuentes a versiones antiguas del arbol del kernel.

El grupo “no enviar”. Parches que por alguna razon nunca deben ser enviados al mantenedor oficial del kernel.Por ejemplo, alguno de esos parches podrıa cambiar las cadenas de identificacion embebidas del controladorpara hacer mas facil la distincion, en pruebas de campo, entre una version del controlador de salida-del-arbol yuna version entregada por un vendedor de alguna distribucion.

Ahora volvemos a las razones para ordenar los grupos de parches en esta manera. Quisieramos que los parches delfondo de la pila sean tan estables como sea posible, para no tener que revisar parches mas arriba debido a cambios decontexto. Poner los parches que nunca cambiaran en el primer lugar del fichero series sirve a este proposito.

Tambien desearıamos que los parches que sabemos que debemos modificar sean aplicados sobre un arbol de fuentesque se parezca al oficial tanto como sea posible. Es por esto que mantenemos los parches aceptados disponibles poruna buena cantidad de tiempo.

Los parches “backport” y “no enviar” flotan al final del fichero series. Los parches de backport deben ser aplica-dos encima de todos los otros parches, y los parches “no enviar” pueden perfectamente quedarse fuera del camino.

13.8. Mantener la serie de parchesEn mi trabajo, uso varios guardias para controlar que parches deben ser aplicados.

Los parches “aceptados” son vigilados con accepted. Yo habilito este guardia la mayorıa de las veces. Cuandoaplico los parches sobre un arbol donde los parches ya estan presentes, puedo desactivar este parche, y losparches que lo siguen se aplicaran sin problemas.

Los parches que estan “terminados”, pero no han sido enviados, no tienen guardias. Si estoy aplicando la pilade parches a una copia del arbol oficial, no necesito habilitar ningun guardia para obtener un arbol de fuentesrazonablemente seguro.

164

Page 175: Mercurial

Los parches que necesitan revision antes de ser reenviados tienen el guardia rework.

Para aquellos parches que aun estan bajo desarrollo, uso devel.

Un parche de backport puede tener varios guardias, uno para cada version del kernel a la que aplica. Por ejemplo,un parche que hace backport de un segmento de codigo a 2.6.9 tendra un guardia 2.6.9.

La variedad de guardias me brinda una flexibilidad considerable para determinar que tipo de arbol de fuentes acabare porobtener. En la mayorıa de las situaciones, la seleccion de guardias apropiados es automatizada durante el proceso decompilacion, pero puedo ajustar manualmente los guardias a usar para circunstancias poco comunes.

13.8.1. El arte de escribir parches de backportAl usar MQ, escribir un parche de backport es un proceso simple. Todo lo que dicho parche debe hacer es modificar

una seccion de codigo que usa una caracterıstica del kernel que no esta presente en la version anterior del kernel, paraque el controlador siga funcionando correctamente en esa version anterior.

Una meta util al escribir un buen parche de backport es hacer parecer que el codigo hubiera sido escrito para laversion vieja del kernel que usted tiene como objetivo. Entre menos intrusivo el parche, mas facil sera entenderlo ymantenerlo. Si usted esta escribiendo una coleccion de parches de backport para evitar el efecto de “nido de ratas” detener muchos #ifdefs (secciones de codigo fuente que solo son usados condicionalmente) en su codigo, no introduzca#ifdefs dependientes de versiones especıficas en los parches. En vez de eso, escriba varios parches, cada uno de elloshaciendo cambios incondicionales, y controle su aplicacion usando guardias.

Hay dos razones para ubicar los parches de backport en un grupo diferente, aparte de los parches “regulares”cuyos efectos son modificados por ellos. La primera es que mezclar los dos hace mas difıcil usar herramientas como laextension patchbomb para automatizar el proceso de enviar los parches a un mantenedor oficial. La segunda es que unparche de backport puede perturbar el contexto en el que se aplica un parche regular subsecuente, haciendo imposibleaplicar el parche normal limpiamente sin que el parche de backport sea aplicado antes.

13.9. Consejos utiles para hacer desarrollo con MQ

13.9.1. Organizar parches en directoriosSi esta trabajando en un proyecto grande con MQ, no es difıcil acumular un gran numero de parches. Por ejemplo,

tengo un repositorio de parches que contiene mas de 250 parches.Si usted puede agrupar estos parches en categorıas logicas separadas, usted puede almacenarlos en diferentes

directorios si lo desea; MQ no tiene problemas manejando nombres de parches que contienen separadores de ruta.

13.9.2. Ver el historial de un parcheSi usted esta desarrollando un conjunto de parches en un perıodo de tiempo grande, es una buena idea mantenerlos

en un repositorio, como se discutio en la seccion 12.11. Si lo hace, notara rapidamente que usar el comando “hg diff”para mirar el historial del repositorio no es viable. Esto es debido en parte a que usted esta mirando la segunda derivadadel codigo real (el diff de un diff), pero tambien porque MQ anade ruido al proceso al modificar las marcas de tiempoy los nombres de directorio cuando actualiza un parche.

Sin embargo, usted puede usar la extension extdiff, que es provisto junto con Mercurial, para convertir un diffde dos versiones de un parche en algo legible. Para hacer esto, usted necesitara un paquete de un tercero llamadopatchutils [Wau]. Este paquete provee un comando llamado interdiff, que muestra las diferencias entre dos diffscomo un diff. Al usarlo en dos versiones del mismo diff, genera un diff que representa el diff de la primera a la segundaversion.

Usted puede habilitar la extension extdiff de la manera usual, anadiendo una lınea a la seccion [extensions]de su hgrc.

165

Page 176: Mercurial

1 [extensions]2 extdiff =

El comando interdiff espera recibir los nombres de dos ficheros, pero la extension extdiff le pasa un par dedirectorios al programa que ejecuta, cada uno de los cuales puede contener una cantidad arbitraria de ficheros. Poresto necesitamos un programa pequeno que ejecute interdiff en cada par de ficheros de estos dos directorios.Este programa esta disponible como hg-interdiff en el directorio examples del repositorio de codigo fuente queacompana a este libro.

1 #!/usr/bin/env python2 #3 # Adapter for using interdiff with mercurial’s extdiff extension.4 #5 # Copyright 2006 Bryan O’Sullivan <[email protected]>6 #7 # This software may be used and distributed according to the terms of8 # the GNU General Public License, incorporated herein by reference.9

10 import os, sys11

12 def walk(base):13 # yield all non-directories below the base path.14 for root, dirs, files in os.walk(base):15 for f in files:16 path = os.path.join(root, f)17 yield path[len(base)+1:], path18 else:19 if os.path.isfile(base):20 yield ’’, base21

22 # create list of unique file names under both directories.23 files = dict(walk(sys.argv[1]))24 files.update(walk(sys.argv[2]))25 files = files.keys()26 files.sort()27

28 def name(base, f):29 if f:30 path = os.path.join(base, f)31 else:32 path = base33 # interdiff requires two files; use /dev/null if one is missing.34 if os.path.exists(path):35 return path36 return ’/dev/null’37

38 ret = 039

40 for f in files:41 if os.system(’interdiff "%s" "%s"’ % (name(sys.argv[1], f),42 name(sys.argv[2], f))):

166

Page 177: Mercurial

43 ret = 144

45 sys.exit(ret)

Con el programa hg-interdiff en la ruta de busqueda de su interprete de comandos, puede ejecutarlo comosigue, desde dentro de un directorio de parches MQ:

1 hg extdiff -p hg-interdiff -r A:B my-change.patch

Ya que usted seguramente querra usar este comando tan largo a menudo, puede hacer que hgext lo haga disponiblecomo un comando normal de Mercurial, editando de nuevo su hgrc.

1 [extdiff]2 cmd.interdiff = hg-interdiff

Esto le indica a hgext que ponga a disposicion un comando interdiff, con lo que usted puede abreviar la invocacionanterior de “hg extdiff” a algo un poco mas manejable.

1 hg interdiff -r A:B my-change.patch

Nota: El comando interdiff trabaja bien solo si los ficheros contra los cualesson generadas las versiones de un parche se mantienen iguales. Si usted crea unparche, modifica los ficheros subyacentes, y luego regenera el parche, interdiffpodrıa no producir ningun resultado util.

La extension extdiff es util para mas que solamente mejorar la presentacion de los parches MQ. Para leer masacerca de esto, vaya a la seccion 14.2.

167

Page 178: Mercurial

Capıtulo 14

Anadir funcionalidad con extensiones

A pesar de que el corazon de Mercurial es muy completo desde el punto de vista de funcionalidad, carece decaracterısticas rimbombantes deliberadamente. Esta aproximacion de preservar la simplicidad mantiene el programasencillo tanto para mantenedores como para usuarios.

Si embargo Mercurial no le cierra las posibilidades a un conjunto inflexible de ordenes: usted puede anadir carac-terısticas como extensiones (aveces llamadas anadidos1). Ya hemos discutido algunas de estas extensiones en capıtulosanteriores:

La seccion 3.3 cubre la extension fetch; que combina jalar cambios y fusionarlos con los cambios locales enuna sola orden: “hg fetch”.

En el capıtulo 10, cubrimos muchas extensiones que son utiles en funcionalidades relacionadas con ganchos:Los acl anaden listas de control de acceso; bugzilla anade integracion con el sistema de seguimiento de fallosBugzilla; y notify envıa notificaciones por correo de nuevos cambios.

La extension de administracion de parches MQ es tan invaluable que amerita dos capıtulos y un apendice porsı misma. El capıtulo 12 cubre lo basico; el capıtulo 13 discute temas avanzados; y el apendice B muestra endetalle cada orden.

En este capıtulo cubriremos algunas extensiones adicionales disponibles para Mercurial, y daremos un vistazo a lamaquinaria que necesita conocer en caso de que desee escribir una extension.

En la seccion 14.1, discutiremos la posibilidad de mejorar el desempeno en gran medida con la extensioninotify.

14.1. Mejorar el desempeno con la extension inotify¿Desea lograr que las operaciones mas comunmente usadas de Mercurial se ejecuten centenas de veces mas rapido?

¡A leer!Mercurial tiene gran desempeno bajo circunstancias normales. Por ejemplo, cuando ejecuta la orden “hg status”,

Mercurial tiene que revisar casi todos los ficheros y directorios en su repositorio de forma que pueda desplegar el estadode los ficheros. Muchas otras ordenes tienen que hacer tal trabajo tras bambalinas; por ejemplo la orden “hg diff”usa la maquinaria de estado para evitar hacer operaciones de comparacion costosas en ficheros que obviamente no hancambiado.

Dado que obtener el estado de los ficheros es crucial para obtener buen desempeno, los autores de Mercurial hanoptimizado este codigo en la medida de lo posible. Sin embargo, no puede obviarse el hecho de que cuando ejecuta“hg status”, Mercurial tendra que hacer por lo menos una costosa llamada al sistema por cada fichero administrado

1N. del T. plugins

168

Page 179: Mercurial

para determinar si ha cambiado desde la ultima vez que se consigno. Para un repositorio suficientemente grande, puedetardar bastante tiempo.

Para mostrar en numeros la magnitud de este efect, cree un repositorio que contenıa 150.000 ficheros administrador.Tardo diez segundos para ejecutar “hg status”, a pesar de que ninguno de los ficheros habıa sido modificado.

Muchos sistemas operativos modernos contienen una facilidad de notificacion de ficheros. Si un programa seregistra con un servicio apropiado, el sistema operativo le notificara siempre que un fichero de interes haya sidocreado, modificado o borrado. En sistemas Linux, el componente del nucleo que lo hace se llama inotify.

La extension inotify habla con el componente inotify del nucleo para optimizar las ordenes de “hg status”.La extension tiene dos componentes. Un daemonio esta en el fondo recibiendo notificaciones del subsistema inotify.Tambien escucha conexiones de una orden regular de Mercurial. La extension modifica el comportamiento de Mercu-rial de tal forma que, en lugar de revisar el sistema de ficheros, le pregunta al daemonio. Dado que el daemonio tieneinformacion perfecta acerca del estado del repositorio, puede responder instantaneamente con el resultado, evitando lanecesidad de revisar cada directorio y fichero del repositorio.

Retomando los diez segundos que medı al ejecutar la orden “hg status” de Mercurial sobre un repositorio de150.000 ficheros. Con la extension inotify habilitada, el tiempo se disipo a 0.1 seconds, un factor cien veces masrapido.

Antes de continuar, tenga en cuenta algunos detalles:

La extension inotify es especıfica de Linux. Porque se enlaza directamente con el subsistema inotify delnucleo Linux, no funciona en otros sistemas operativos.

Deberıa funcionar en cualquier distribucion Linux a partir de comienzos del 2005. Las distribuciones mas an-tiguas deben tener un kernel sin inotify, o una version de glibc que no tiene necesariamente el soporte parala interfaz.

No todos los sistemas de ficheros pueden usarse con la extension inotify. Los sistemas de ficheros tales comoNFS no lo soportan, por ejemplo, si esta corriendo Mercurial en vaios sistemas, montados todos sobre el mismosistema de ficheros en red. El sistema inotify del kernel no tiene forma de saber acerca de los cambios hechosen otro sistema. La mayorıa de sistemas de ficheros locales (p.e. ext3, XFS, ReiserFS) deberıan funcionar bien.

Hacia mayo de 2007 la extension inotify no venıa de forma predeterminada en Mercurial2, y es un poco mascompleja de activar que otras extensiones. Pero la mejora en el desempeno bien vale la pena!

La extension venıa en dos partes: un conjunto de parches al codigo fuente de Mercurial, y una librerıa de interfaces

de Python hacia el subsistema inotify.

Nota: Hay dos librerıas de enlace de Python hacia inotify. Una de ellasse llama pyinotify, y en algunas distribuciones de Linux se encuentra comopython-inotify. Esta es la que no necesita, puesto que tiene muchos fallos, yes ineficiente para ser practica.

Para comenzar, es mejor tener una copia de Mercurial funcional instalada:Nota: Si sigue las instrucciones a continuacion, estara reemplazando y sobreescri-biendo cualquier instalacion previa de Mercurial que pudiera tener, con el codigode Mercurial “mas reciente y peligrosa”. No diga que no se le advirtio!

1. Clone el repositorio de interfaz entre Python e inotify. Armelo e instalelo:

1 hg clone http://hg.kublai.com/python/inotify2 cd inotify3 python setup.py build --force4 sudo python setup.py install --skip-build

2. Clone el repositorio crew de Mercurial. Clone el repositorio de parches de inotify de forma tal que las colasde Mercurial puedan aplicar los parches sobre el repositorio crew.

2N. del T. Desde el 2008 para kernels 2.6 viene en Mercurial, pero no esta activada de forma predeterminada

169

Page 180: Mercurial

1 hg clone http://hg.intevation.org/mercurial/crew2 hg clone crew inotify3 hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches

3. Asegurese de instalar la extension Colas de Mercurial mq y que esten habilitadas. Si nunca ha usado MQ, lea laseccion 12.5 para poder comenzar rapidamente.

4. Vaya al repositorio de inotify y aplique todos los parches de inotify con la opcion -a de la orden “hgqpush”.

1 cd inotify2 hg qpush -a

Si obtiene un mensaje de error de “hg qpush”, no deberıa continuar. Mejor pida ayuda.

5. Arme e instale la version parchada de Mercurial.

1 python setup.py build --force2 sudo python setup.py install --skip-build

Una vez que haya armado una version funcional parchada de Mercurial, todo lo que necesita es habilitar la extensioninotify colocando una entrada en su hgrc.

1 [extensions]2 inotify =

Cuando la extension inotify este habilitada, Mercurial iniciara transparente y automaticamente el daemonio de es-tado la primera vez que ejecute un comando que requiera estado del repositorio. Ejecuta un daemonio de estado porrepositorio.

El daemonio de estado se inicia silenciosamente y se ejecuta en el fondo. Si mira a la lista de procesos en ejecuciondespues de habilitar la extension inotify y ejecuta unos pocos comandos en diferentes repositorios, vera que hayalgunos procesos de hg por ahı, esperando actualizaciones del kernel y solicitudes de Mercurial.

La primera vez que ejecuta un comando de Mercurial en un repositorio cuando tiene la extension inotify ha-bilitada, correra casi con el mismo desempeno que una orden usual de Mercurial. Esto es debido a que el estado deldaemonio necesita aplicar una busqueda normal sobre el estado para poder tener una lınea de partida frente a la cualaplicar posteriormente actualizaciones del nucleo. De todas formas, todo comando posterior que haga cualquier clasede revision del estado deberıa ser notablemente mas rapido en repositorios con incluso un tamano modesto. Aun mejor,a medida que su repositorio sea mas grande, mejor desempeno vera. El daemonio inotify hace operaciones de estadode forma casi instantanea en repositorios de todos los tamanos!

Si lo desea, puede iniciar manualmente un daemonio de estado con la orden “hg inserve”. Esto le da un controlun poco mas fino acerca de como deberıa ejecutarse el daemonio. Esta orden solamente estara disponible cuando hayahabilitado la extension inotify.

Cuando este usando la extension inotify, no deberıa ver diferencia en el comportamiento de Mercurial, conla unica excepcion de que los comandos relacionados con el estado deberıan ejectuarse mucho mas rapido que co-mo solıan hacerlo. Deberıa esperar especıficamente que las ordenes no deberıan ofrecer salidas distintas; ni ofrecerresultados diferentes. Si alguna de estas situaciones ocurre, por favor reporte el fallo.

14.2. Soporte flexible de diff con la extension extdiffLa orden predeterminada “hg diff” de Mercurial despliega diffs en texto plano unificadas.

170

Page 181: Mercurial

1 $ hg diff2 diff -r d6e1fbefacc8 myfile3 --- a/myfile Tue Feb 10 18:23:22 2009 +00004 +++ b/myfile Tue Feb 10 18:23:22 2009 +00005 @@ -1,1 +1,2 @@6 The first line.7 +The second line.

Si dese emplear una herramienta externa para desplegar las modificaciones, querra usar la extension extdiff. Esta lepermitira usar por ejemplo una herramienta grafica de diff.

La extension extdiff viene con Mercurial, y es facil configurar. En la seccion [extensions] de su hgrc, bastacon anadir una entrada de una lınea para habilitar la extension.

1 [extensions]2 extdiff =

Esto introduce una orden llamada “hg extdiff”, que de forma predeterminada usa su orden del sistema diff paragenerar un diff unificado de la misma forma que lo hace el comando predeterminado “hg diff”.

1 $ hg extdiff2 --- a.d6e1fbefacc8/myfile 2009-02-10 18:23:22.000000000 +00003 +++ /tmp/extdiff83LA2p/a/myfile 2009-02-10 18:23:22.000000000 +00004 @@ -1 +1,2 @@5 The first line.6 +The second line.

El resultado no sera exactamente el mismo que con la orden interna “hg diff”, puesto que la salida de diff varıa deun sistema a otro, incluso pasando las mismas opciones.

Como lo indican las lıneas“making snapshot”, la orden “hg extdiff” funciona creando dos instantaneas desu arbol de fuentes. La primera instantanea es la revision fuente; la segunda es la revision objetivo del directoriode trabajo. La orden “hg extdiff” genera estas instantaneas en un directorio temporal, pasa el nombre de cadadirectorio a un visor de diffs temporal y borra los directorios temporales. Por cuestiones de eficiencia solamentegenera instantaneas de los directorios y ficheros que han cambiado entre dos revisiones.

Los nombres de los directorios de instantaneas tienen los mismos nombres base de su repositorio. Si su repositoriotiene por ruta /quux/bar/foo, foo sera el nombre de cada instantanea de directorio. Cada instantanea de directoriotiene sus identificadores de conjuntos de cambios al final del nombre en caso de que sea apropiado. Si una instantaneaviene de la revision a631aca1083f, el directorio se llamara foo.a631aca1083f. Una instantanea del directorio detrabajo no tendra el identificador del conjunto de cambios, y por lo tanto sera solamente foo en este ejemplo. Para vercomo luce en la practica, veamos de nuevo el ejemplo “hg extdiff” antes mencionado. Tenga en cuenta que los diffstienen los nombres de las instantaneas de directorio dentro de su encabezado.

La orden “hg extdiff” acepta dos opciones importantes. La opcion -p le permite elegir un programa para ver lasdiferencias, en lugar de diff. Con la opcion -o puede cambiar las opciones que “hg extdiff” pasa a tal programa(de forma predeterminada las opciones son“-Npru”, que tienen sentido unicamente si esta usando diff). En otrosaspectos, la orden “hg extdiff” actua de forma similar a como lo hace la orden “hg diff” de Mercurial: usted usalos mismos nombres de opciones, sintaxis y argumentos para especificar las revisiones y los ficheros que quiere, yası sucesivamente.

Por ejemplo, para ejecutar la orden usual del sistema diff, para lograr que se generen diferencias de contexto(con la opcion -c) en lugar de diferencias unificadas, y cinco lıneas de contexto en lugar de las tres predeterminadas(pasando 5 como argumento a la opcion -C).

1 $ hg extdiff -o -NprcC52 *** a.d6e1fbefacc8/myfile Tue Feb 10 18:23:22 2009

171

Page 182: Mercurial

3 --- /tmp/extdiff83LA2p/a/myfile Tue Feb 10 18:23:22 20094 ***************5 *** 1 ****6 --- 1,2 ----7 The first line.8 + The second line.

Es sencillo lanzar unas herramienta usual de diferencias. Para lanzar el visor kdiff3:

1 hg extdiff -p kdiff3 -o ’’

Si su orden para visualizar diferencias no puede tratar con directorios, puede usar un poco de scripting para lograrlo.Un ejemplo de un script con la extension mq junto con la orden interdiff esta en la seccion 13.9.2.

14.2.1. Definicion de alias de comandosAcordarse de todas las opciones de las ordenes “hg extdiff” y el visor de diferencias de su preferencia puede

ser dispendioso, y por lo tanto la extension extdiff le permite definir nuevas ordenes que invocaran su visor dediferencias con las opciones exactas.

Basta con editar su fichero hgrc, y anadir una seccion llamada [extdiff]. Dentro de esta seccion puede definirvarias ordenes. Mostraremos como anadir la orden kdiff3. Despues de definido, puede teclear “hg kdiff3” y laextension a extdiff ejecutara la orden kdiff3.

1 [extdiff]2 cmd.kdiff3 =

Si deja vacıa la porcion derecha de la definicion, como en el ejemplo, la extension extdiff usa el nombre de laorden se definira como el nombre del programa externo a ejecutar. Pero tales nombres no tienen por que ser iguales.Definimos ahora la orden llamada “hg wibble”, que ejecuta kdiff3.

1 [extdiff]2 cmd.wibble = kdiff3

Tambien puede especificar las opciones predeterminadas con las cuales desea invocar el visor de diferencias. Seusa el prefijo “opts.”, seguido por el nombre de la orden a la cual se aplican las opciones. En este ejemplos se definela orden “hg vimdiff” que ejecuta la extension DirDiff del editor vim.

1 [extdiff]2 cmd.vimdiff = vim3 opts.vimdiff = -f ’+next’ ’+execute "DirDiff" argv(0) argv(1)’

14.3. Uso de la extension transplant para seleccionarNeed to have a long chat with Brendan about this.

14.4. Enviar cambios vıa correo electronico con la extension patchbombVarios proyectos tienen la cultura de “revision de cambios”, en la cual la gente envıa sus modificaciones a una lista

de correo para que otros las lean y comenten antes de consignar la version final a un repositorio compartido. Algunosproyectos tienen personas que actuan como cancerberos; ellos aplican los cambios de otras personas a un repositoriopara aquellos que no tienen acceso.

172

Page 183: Mercurial

Mercurial facilita enviar cambios por correo para revision o aplicacion gracias a su extension patchbomb. Laextension es tan popular porque los cambios se formatean como parches y es usual que se envıa un conjunto decambios por cada correo. Enviar una gran cantidad de cambios por correos se llama por tanto “bombardear” el buzonde entrada del destinatario, de ahı su nombre “bombardeo de parches”.

Como es usual, la configuracion basica de la extension patchbomb consta de una o dos lıneas en su hgrc.

1 [extensions]2 patchbomb =

Cuando haya habilitado la extension, dispondra de una nueva orden, llamada “hg email”.La forma mejor y mas segura para invocar la orden “hg email” es ejecutarla siempre con la opcion -n; que le

mostrara lo que la orden enviarıa, sin enviar nada. Una vez que haya dado un vistazo a los cambios y verificado queesta enviando los correctos, puede volver a ejecutar la misma orden, sin la opcion -n.

La orden “hg email” acepta la misma clase de sintaxis de revisiones como cualquier otra orden de Mercurial. Porejemplo, enviara todas las revisiones entre la 7 y la punta, inclusive.

1 hg email -n 7:tip

Tambien puede especificar un repositorio para comparar. Si indica un repositoro sin revisiones, la orden “hg email”enviara todas las revisiones en el repositorio local que no estan presentes en el repositorio remoto. Si especificarevisiones adicionalmente o el nombre de una rama (la ultima con la opcion -b), respetara las revisiones enviadas.

Ejecutar la orden “hg email” sin los nombres de aquellas personas a las cuales desea enviar el correo es comple-tamente seguro: si lo hace, solicitara tales valores de forma interactiva. (Si esta usando Linux o un sistema tipo Unix,tendra capacidades estilo–readline aumentadas cuando ingrese tales encabezados, lo cual es sumamente util.)

Cuando envıe una sola revision, la orden “hg email” de forma predeterminada usara la primera lınea de descrip-cion del conjunto de cambios como el tema del unico mensaje que se enviara.

Si envıa varias revisiones, la orden “hg email” enviara normalmente un mensaje por conjunto de cambios. Colo-cara como prefacio un mensaje introductorio en el cual usted deberıa describir el proposito de la serie de cambios queesta enviando.

14.4.1. Cambiar el comportamiento de las bombas de parchesCada proyecto tiene sus propias convenciones para enviar cambios en un correo electronico; la extension patchbomb

intenta acomodarse a diferentes variaciones gracias a las opciones de la lınea de ordenes:

Puede escribir un tema para el mensaje introductorio en la lınea de ordenes con la opcin -s. Toma un argumento:el tema del mensaje a usar.

Para cambiar el correo electronico del campo del cual se origina, use la opcion -f. Toma un argumento, el correoelectronico a usar.

El comportamiento predeterminado es enviar diferencias unificadas (consulte la seccion 12.4 si desea una de-scripcion del formato), una por mensaje. Puede enviar un conjunto binario3 con la opcion -b.

Las diferencias unificadas estan precedidas por un encabezado de metadatos. Puede omitirlo, y enviar diferenciassin adornos con la opcion --plain.

Las diferencias usualmente se envıan “en lınea”, como parte del cuerpo del mensaje con la descripcion delparche. Que facilita a a la mayor cantidad de lectores citar y responder partes de un diff, dado que algunosclientes de correo solamente citaran la primera parte MIME del cuerpo de un mensaje. Si prefiere enviar ladescripcion y el diff en partes separadas del cuerpo, use la opcion -a.

3N. del T. binary bundle

173

Page 184: Mercurial

En lugar de enviar mensajes de correo puede escribirlos a un fichero con formato-mbox- con la opcion -m. Laopcion recibe un argumento, el nombre del fichero en el cual escribir.

Si desea anadir un resumen con formato-diffstat en cada parche, y uno como mensaje introductorio, use laopcion -d. La orden diffstat despliega una tabla que contiene el nombre de cada fichero parchado, el numerode lıneas afectadas, y un historgrama mostrando cuanto ha sido modificado cada fichero. Lo cual ofrece a loslectores una mirada cuantitativa de cuan complejo es el parche.

174

Page 185: Mercurial

Apendice A

Referencia de Ordenes

A.1. “hg add”—Anade ficheros en la proxima consignacion--include, tambien -I

--exclude, tambien -X

--dry-run, tambien -n

A.2. “hg diff”—imprime los cambios en el historial o el directorio actualMostrar las diferencias entre revisiones para ficheros especificados o directorios, con el formato unificado diff. Si

desea ver una descripcion del formato unificado diff, ver la seccion 12.4.De forma predeterminada, esta orden no imprime las diferencias para los ficheros binarios que Mercurial este sigu-

iendo. Para controlar este comportamiento, vea las opciones -a y --git.

A.2.1. Optionsopcion --nodates

Omite la fecha y hora cuando se muestran los encabezados de las diferencias.

--ignore-blank-lines, tambien -B

No imprime los cambios que solamente insertan o eliminan lıneas en blanco. Una lınea que contiene espacios enblanco no se considera como una lınea en blanco.

--include, tambien -I

Incluye ficheros y directorios cuyos nombres coinciden con los patrones elegidos.

--exclude, tambien -X

Excluye los ficheros y directorios cuyos nombres coinciden con los patrones elegidos.

--text, tambien -a

Si no especifica esta opcion, “hg diff” no mostrara las diferencias de los ficheros que detecte como binarios. Alespecificar -a se forza a “hg diff” a tratar los ficheros como texto, y generar diferencias para todos.

175

Page 186: Mercurial

Esta opcion es util para los ficherso que son “texto en mayor medida” pero que tienen caracteres NUL. Si lo usaen ficheros que contienen muchos datos binarios, la salida sera incomprensible.

--ignore-space-change, tambien -b

No imprime si el unico cambio que en la lınea es la cantidad de espacio en blanco.

--git, tambien -g

Mostrar diferencias compatibles con git. XXX reference a format description.

--show-function, tambien -p

Mostrar el nombre de la funcion que contiene el codigo en una porcion del encabzado usando una heurıstica simple.Esta funcionalidad se habilita de forma predeterminada, ası que la opcion -p no tiene efectos a menos que cambie elvalor de showfunc en la configuracion, como en el ejemplo siguiente.

1 $ echo ’[diff]’ >> $HGRC2 $ echo ’showfunc = False’ >> $HGRC3 $ hg diff4 diff -r a38a6c74a605 myfile.c5 --- a/myfile.c Tue Feb 10 18:23:18 2009 +00006 +++ b/myfile.c Tue Feb 10 18:23:18 2009 +00007 @@ -1,4 +1,4 @@8 int myfunc()9 {

10 - return 1;11 + return 10;12 }13 $ hg diff -p14 diff -r a38a6c74a605 myfile.c15 --- a/myfile.c Tue Feb 10 18:23:18 2009 +000016 +++ b/myfile.c Tue Feb 10 18:23:18 2009 +000017 @@ -1,4 +1,4 @@ int myfunc()18 int myfunc()19 {20 - return 1;21 + return 10;22 }

--rev, tambien -r

Especifique una o mas revisiones para comparar. La orden “hg diff” acepta hasta dos opciones -r para especificarlas revisiones a comparar.

1. Despliega las diferencias entre la revision padre y del directorio de trabajo.

2. Despliega las diferencias entre el conjunto de cambios especificados y el directorio de trabajo.

3. Despliega las diferencias entre dos conjuntos de cambios especificados.

Puede especificar dos revisiones usando o bien sea las opciones -r o la notacion de rango. Por ejemplo, las dosespecificaciones de revisiones a continuacion son equivalentes:

176

Page 187: Mercurial

1 hg diff -r 10 -r 202 hg diff -r10:20

Cuando especifica dos revisiones, esto tiene significado para Mercurial. Esto significa que “hg diff -r10:20”producira un diff que transformara los ficheros desde los contenidos en la revision 10 a los contenidos de la revision20, mientras que “hg diff -r20:10” significa lo opuesto: el diff que transformarıa los contenidos de los ficheros dela revision 20 a los contenidos de la revision 10. No puede invertir el orden de esta forma si esta haciendo un diff frenteal directorio de trabajo.

--ignore-all-space, tambien -w

A.3. “hg version”—imprime la informacion de version y derechos de re-produccion

Esta orden despliega la version de Mercurial que esta usando, y su nota de derechos de reproduccion. Hay cuatroclases de cadenas de version posibles:

La cadena “unknown”. Esta version de Mercurial no fue construida en un repositorio de Mercurial, y no puededeterminar su propia version.

Una cadena numerica corta, tal como “1.1”. Esta es una construccion de una version de Mercurial que se iden-tifica con una etiqueta especıfica en el repositorio en el cual fue armada (Esto no significa necesariamente queesta ejecutando una version oficial; alguien pudo haber anadido tal etiqueta a cualquier version del repositorioen el cual armaron Mercurial).

Una cadena hexadecimal, tal como “875489e31abe”. Esta es una construccion de una revision dada de Mercu-rial.

Una cadena hexadecimal seguida por una fecha, tal como “875489e31abe+20070205”. Esta construccion de larevision de Mercurial fue la construccion de un repositorio que tuvo cambios locales que no han sido consigna-dos.

A.3.1. Consejos y trucos¿Por que difieren los resultados de “hg diff” y “hg status”?

Cuando ejecuta la orden “hg status”, vera una lista de ficheros para los cuales Mercurial almacenara cambios laproxima vez que consigne. Si ejecuta la orden “hg diff”, vera que imprime diferencias solamente para un subconjuntode los ficheros que “hg status” liste. Hay dos posibles razones para este comportamiento:

La primera es que “hg status” imprime cierta clase de modificaciones que “hg diff” no despliega normalmente.La orden “hg diff” usualmente despliega diferencias unificadas, las cuales no tienen la habilidad de representaralgunos cambios que Mercurial puede seguir. Lo mas notable es que las diferencias tradicionales no pueden representarun cambio acerca de la ejecutabilidad de un fichero, pero Mercurial sı almacena esta informacion.

Si usa la opcion --git de “hg diff”, mostrara diferencias compatibles con git que pueden desplegar esta infor-macion adicional.

La segunda razon posible para que “hg diff” este imprimiendo diferencias para un subconjunto de ficheros delo que muestra “hg status” es que si usted le invoca sin argumento alguno, “hg diff” imprime diferencias frenteal primer padre del directorio de trabajo. Si ha ejecutado “hg merge” para fusionar dos conjuntos de cambios, perono ha consignado aun los resultados de la fusion, su directorio de trabajo tiene dos padres (use “hg parents” paraverlos). Mientras que “hg status” imprime modificaciones relativas a ambos padres despues de una fusion que nose ha consignado, “hg diff” opera aun relativo solamente al primer padre. Puede lograr que imprima las diferenciasrelativas al segundo padre especificando tal padre con la opcion -r. No hay forma de hacer que imprima las diferenciasrelativas a los dos padres.

177

Page 188: Mercurial

Generar diferencias seguras en binarios

Si usa la opcion -a para forzar que Mercurial imprima las diferencias de los ficheros que so o bien “casi comple-tamente texto” o contienen muchos datos binarios, tales diferencias no pueden aplicarse subsecuentemente a la orden“hg import” de Mercurial o a la orden patch del sistema.

Si desea generar una diferencia de un fichero binario que es seguro para usarlo como entrada a la orden “hgimport”, use la opcion “hg diff”–git cuando genere el parche. La orden patch del sistema no puede tratar conparches binarios.

178

Page 189: Mercurial

Apendice B

Referencia de las Colas de Mercurial

B.1. Referencia de ordenes MQSi desea dar un vistazo a las ordenes que ofrece MQ, use la orden “hg help mq”.

B.1.1. “hg qapplied”—imprimir los parches aplicadosLa orden “hg qapplied” imprime la pila actual de parches aplicados. Los parches se imprimen en orden de

antiguedad, primero los mas antiguos y despues los mas recientes, por lo tanto el ultimo parche de la lista es el queesta en el “tope”.

B.1.2. “hg qcommit”—consignar cambios en la cola del repositorioLa orden “hg qcommit” consigna cualquier cambio sobresaliente en el repositorio .hg/patches. Esta orden so-

lamente funciona si el directorio .hg/patches es un repositorio, p.e. usted creo el directorio con “hg qinit -c” oejecuto “hg init” en el directorio despues de correr “hg qinit”.

Esta orden es un atajo para “hg commit --cwd .hg/patches”.

B.1.3. “hg qdelete”—eliminar un parche del fichero seriesLa orden “hg qdelete” elimina la entrada del fichero series para el parche en el directorio .hg/patches. No

sca el parche si ha sido aplicado. De forma predeterminada no borra el fichero del parche; use la opcion -f parahacerlo.

Opciones:

-f Elimina el fichero del parche.

B.1.4. “hg qdiff”—imprimir la diferencia del ultimo parche aplicadoLa orden “hg qdiff” imprime un diff del parche mas recientemente aplicado. Es equivalente a “hg diff -r-2:-1”.

B.1.5. “hg qfold”—fusionar (“integrar”) varios parches en uno soloLa orden “hg qfold” fusiona muchos parches en el ultimo parche aplicado, de tal forma que el ultimo parche

aplicado es la union de todos los cambios de los parches en cuestion.Los parches a fusionar no deben haber sido aplicados; “hg qfold” saldra indicando un error si alguno ha sido

aplicado. El orden en el cual los parches se pliegan es significativo; “hg qfold a b” significa “aplique el parche masreciente, seguido de a, y seguido de b”.

179

Page 190: Mercurial

Los comentarios de los parches integrados se colocan al final de los comentarios del parche destino, con cadabloque de comentarios separado con tres asteriscos (“*”). Se usa la opcion -e para editar el mensaje de consignacionpara el conjunto de cambios/parches despues de completarse el pliegue.

Opciones:

-e Edita el mensaje de consignacion y la descripcion del parche del parche que se ha integrado.

-l Usa los contenidos del fichero dado como el nuevo mensaje de consignacion y descripcion del parche para elparche a integrar.

-m Usa el texto dado como el mensaje de consignacion y descripcion del parche para el parche integrado.

B.1.6. “hg qheader”—desplegar el encabezado/descripcion de un parcheLa orden “hg qheader” imprime el encabezado o descripcion de un parche. De forma predeterminada, imprime

el encabezado del ultimo parche aplicado. Si se da un argumento, imprime el encabezado del parche referenciado.

B.1.7. “hg qimport”—importar el parche de un tercero en la colaLa orden “hg qimport” anade una entrada de un parche externo al fichero series y copia el parche en el directorio

.hg/patches. Anade la entrada inmediatamente despues del ultimo parche aplicado, pero no introduce el parche.Si el directorio .hg/patches es un repositorio, “hg qimport” automaticamente hace un “hg add” del parche

importado.

B.1.8. “hg qinit”—preparar un repositorio para trabajar con MQLa orden “hg qinit” prepara un repositorio para trabajar con MQ. Crea un directorio llamado .hg/patches.Opciones:

-c Crea .hg/patches como un repositorio por sı mismo. Tambien crea un fichero .hgignore que ignorara elfichero status.

Cuando el directorio .hg/patches es un repositorio, las ordenes “hg qimport” y “hg qnew” hacen “hg add”automaticamente a los parches nuevos.

B.1.9. “hg qnew”—crear un parche nuevoLa orden “hg qnew” crea un parche nuevo. Exige un argumento, el nombre que se usara para tal parche. El parche

recien creado esta vacıo inicialmente. Se anade al fichero series despues del ultimo parche aplicado, y se introduceen el tope de ese parche.

Si “hg qnew” encuentra ficheros modificados en el directorio de trabajo, rehusara crear un parche nuevo a meosque se emplee -f la opcion (ver mas adelante). Este comportamiento le permite hacer “hg qrefresh” al ultimo parcheaplicado antes de aplicar un parche nuevo encima de este.

Opciones:

-f Crea un parche nuevo si los contenidos del directorio actual han sido modificados. Cualquier modificacionsignificativa se anade al parche recientemente creado, de tal forma que al finalizar la orden, el directorio detrabajo no lucira modificado.

-m Usa el texto dado como el mensaje de consignacion. Este texto se almacenara al principio del fichero del parche,antes de los datos del parche.

B.1.10. “hg qnext”—imprimir el nombre del proximo parcheLa orden “hg qnext” imprime el nombre del siguiente parche en el fichero series a continuacion del ultimo

parche aplicado. Este parche serıa el proximo parche a aplicar si se ejecutara la orden “hg qpush”.

180

Page 191: Mercurial

B.1.11. “hg qpop”—sustraer parches de la pilaLa orden “hg qpop” elimina los parches aplicados del tope de la pila de parches aplicados. De forma predetermi-

nada solamente remueve un parche.Esta orden elimina los conjuntos de cambios que representan los parches sustraıdos del repositorio, y actualiza el

directorio de trabajo para deshacer los efectos de los parches.Esta orden toma un argumento opcional, que usa como el nombre o el ındice del parche que desea sustraer. Si se da

el nombre, sustraera los parches hasta que el parche nombrado sea el ultimo parche aplicado. Si se da un numero, “hgqpop” lo trata como un ındice dentro del fichero series, contando desde cero (no cuenta las lıneas vacıas o aquellasque sean unicamente comentarios). Sustrae los parches hasta que el parche identificado por el ındice sea el ultimoparche aplicado.

La orden “hg qpop” no lee o escribe parches en el fichero series. “hg qpop” se constituye por tanto en unaforma segura de sustraer un parche del fichero series o un parche que ha eliminado o renombrado completamente.En los dos ultimos casos, use el nombre del parche tal como lo hizo cuando lo aplico.

De forma predeterminada, la orden “hg qpop” no sustraera parche alguno si el directorio de trabajo ha sido modi-ficado. Puede modificar este comportamiento con la opcion -f, que revierte todas las modificaciones del directorio detrabajo.

Opciones:

-a Sustrae todos los parches aplicados. Restaura el repositorio al estado antes de haber aplicado parche alguno.

-f Revertir forzadamente cualquier modificacion del directorio de trabajo cuando se hace sustracciones.

-n Sustraer un parche de la cola dado un nombre.

La orden “hg qpop” elimina una lınea del final del fichero status por cada parche que se sustrae.

B.1.12. “hg qprev”—imprimir el nombre del parche anteriorLa orden “hg qprev” imprime el nombre del parche en el fichero series que esta antes del ultimo parche aplicado.

Este se volvera el ultimo parche aplicado si ejecuta “hg qpop”.

B.1.13. “hg qpush”—introducir parches a la pilaLa orden “hg qpush” anade parches a la pila. De forma predeterminada anade solamente un parche.Esta orden crea un conjunto de cambios que representa cada parche aplicado y actualiza el directorio de trabajo

aplicando los efectos de los parches.Los datos predeterminados cuando se crea un conjunto de cambios corresponde a:

La fecha de consignacion y zona horaria corresponden a la hora actual de la zona. Dado que tales datos seusan para computar la identidad de un conjunto de cambios, significa que si hace “hg qpop” a un parche y“hg qpush” de nuevo, el conjunto de cambios que introduzca tendra una identidad distinta a la del conjunto decambios que sustrajo.

El autor es el mismo que el predeterminado usado por la orden “hg commit”.

El mensaje de consignacion es cualquier texto del fichero del parche que viene antes del primer encabezado deldiff. Si no hay tal texto, un mensaje predeterminado se sua para identificar el nombre del parche.

Su un parche contiene un encabezado de parche de Mercurial (XXX add link), la informacion en el encabezado delparche tiene precedencia sobre el predeterminado.

Opciones:

-a Introduce todos los parches que no han sido aplicados del fichero series hasta que no haya nada mas paraintroducir.

181

Page 192: Mercurial

-l Anade el nombre del parche al final del mensaje de consignacion

-m Si un parche no se aplica limpiamente, usa la entrada para un parche en otra cola almacenada para computar losparametros en una fusion de tres, y aplica una fusion de tres fuentes usando la maquinaria usual de Mercurial.Usa la resolucion de la fusion como el contenido del parche nuevo.

-n Usa la cola mencionada si se esta fusionando en la introduccion.

La orden “hg qpush” lee, pero no modifica el fichero series. Anade al final del fichero “hg status” una lıneapor cada parche que se introduce.

B.1.14. “hg qrefresh”—actualiza el ultimo parche aplicadoLa orden “hg qrefresh” actualiza el ultimo parche aplicado. Modifica el parche, elimina el ultimo conjunto de

cambios que represento el parche, y crea un nuevo conjunto de cambios para representar el parche modificado.La orden “hg qrefresh” busca las siguientes modificaciones:

Los cambios al mensaje de consignacion, p.e. el texto antes del primer encabezado de diff en el fichero delparche, se replejan en el nuevo conjunto de cambios que representa el parche.

Las modificaciones a los ficheros a los que se les da seguimiento en el directorio de trabajo se anade al parche.

Los cambios a los ficheros a los que se les da seguimiento con “hg add”, “hg copy”, “hg remove”, o “hgrename”. Se anaden al parche los ficheros anadidos, copiados y renombrados, mientras que los ficheros elimi-nados y las fuentes renombradas se eliminan.

Incluso si “hg qrefresh” no detecta cambios, de todas maneras recrea el conjunto de cambios que representa elcambio. Esto causa que la identidad del conjunto de cambios difiera del conjunto de cambios previo que identifico alparche.

Opciones:

-e Modificar la descripcion de la consignacion y el parche con el editor de texto preferido.

-m Modificar el mensaje de consignacion y la descripcion del parche con el texto dado.

-l Modificar el mensaje de consignacion y la descripcion del parche con el texto del fichero dado.

B.1.15. “hg qrename”—renombrar un parcheLa orden “hg qrename” renombra un parche y cambia la entrada del parche en el fichero series.Con un argumento sencillo, “hg qrename” renombra el ultimo parche aplicado. Con dos argumentos, renombra el

primer argumento con el segundo.

B.1.16. “hg qrestore”—restaurar el estado almacenado de la colaXXX No idea what this does.

B.1.17. “hg qsave”—almacena el estado actual de la colaXXX Likewise.

B.1.18. “hg qseries”—imprime la serie completa de parchesLa orden “hg qseries” imprime la serie completa de parches del fichero series. Imprime solamente los nombres

de los parches sin las lıneas en blanco o comentarios. Imprime primero el primero y de ultimo, el ultimo aplicado.

182

Page 193: Mercurial

B.1.19. “hg qtop”—imprime el nombre del parche actual“hg qtop” imprime el nombre del ultimo parche aplicado.

B.1.20. “hg qunapplied”—imprimir los parches que aun no se han aplicadoLa orden “hg qunapplied” imprime los nombres de los parches del fichero series que todavıa no han sido

aplicados. Los imprime de acuerdo al orden en el cual serıan introducidos.

B.1.21. “hg strip”—remover una revision y sus descendientesLa orden “hg strip” remueve una revision, y todos sus descendientes del repositorio. Deshace los efectos de las

revisiones removidas del repositorio, y actualiza el directorio de trabajo hasta el primer padre de la revision removida.La orden “hg strip” almacena una copia de segurida de los conjuntos de cambios en un agrupamiento, de forma

tal que puedan ser reaplicados en caso de que se hayan removido por equivocacion.Opciones:

-b Almacenar conjuntos de cambios no relacionados que se han mezclado con los conjuntos de cambios que estanen franjas con el agrupamiento de copia de seguridad.

-f Si una rama tiene varias ramas principales remueve todos los frentes. XXX This should be renamed, y usa -fpara desagrupar revisiones cuando hay cambios pendientes.

-n No almacene la copia de seguridad agrupada.

B.2. Referencia de ficheros de MQ

B.2.1. El fichero seriesEl fichero series contiene una lista de los nombres de todos los parches que MQ puede aplicar. Se representa

como una lista de nombres, uno por lınea. Se ignora el espacio en blanco al principio y al final.Las lıneas pueden contener comentario. Un comentario comienza con el caracter “#”, y va hasta el final de la lınea.

Se ignoran las lıneas vacıas y las que solamente contengan comentarios.En algun momento podrıa editar el fichero series a mano, por tal motivo se admiten comentarios y lıneas en

blanco como se menciono anteriormente. Por ejemplo, puede poner en comentario un parche temporalmente y “hgqpush” omitira tal parche cuando los aplique. Tambien puede cambiar el orden en el cual se aplican los parches,reordenando las entradas en el fichero series.

Tambien es posible colocar el fichero series bajo control de revisiones; tambien es favorable colocar todos losparches que refiera bajo control de revisiones. Si crea un directorio de parches con la opcion -c de “hg qinit”, estose hara automaticamente.

B.2.2. El fichero statusEl fichero status contiene los nombres y los hashes de los conjuntos de cambios de todos los parches que MQ ha

aplicado. A diferencia del fichero series, este NO ha sido disenado para ser editado. No deberıa colocar este ficherobajo el control de revisiones o modificarlo de forma alguna. MQ lo usa estrictamente para administracion interna.

183

Page 194: Mercurial

Apendice C

Instalar Mercurial desde las fuentes

C.1. En un sistema tipo UnixSi usa un sistema tipo Unix que tiene una version suficientemente reciente de Python (2.3 o superior) disponible,

es facil instalar Mercurial desde las fuentes.

1. Descargue un paquete fuente reciente de http://www.selenic.com/mercurial/download.

2. Descomprımalo:

1 gzip -dc mercurial-version.tar.gz | tar xf -

3. Vaya al directorio fuente y ejecute el guion de instalacion. Esto armara Mercurial y lo instalara en su directoriocasa:

1 cd mercurial-version2 python setup.py install --force --home=$HOME

Cuando termine la instalacion, Mercurial estara en el subdirectorio bin de su directorio casa. No olvide asegurarse deque este directorio este presente en el camino de busqueda de su interprete de ordenes.

Probablemente necesitara establecer la variable de ambiente PYTHONPATH de tal forma que los ejecutables de Mer-curial puedan encontrar el resto de los paquetes de Mercurial. Por ejemplo, en mi portatil, la establecıa a /home/bos/lib/python.La ruta exacta que usted use dependera de como ha sido construıdo Python en su sistema, pero deberıa ser facil de-ducirla. Si no esta seguro, mire lo que haya mostrado el script en el paso anterior, y vea donde se instalaron loscontenidos del directorio mercurial se instalaron.

C.2. En WindowsArmar e instalar Mercurial en Windows requiere una variedad de herramientas, cierta suficiencia tecnica y pacien-

cia considerable. Personalmente, no le recomiendo hacerlo si es un “usuario casual”. A menos que intente hacer hacksa Mercurial, le recomiendo que mejor use un paquete binario.

Si esta decidido a construir Mercurial desde las fuentes en Windows, siga el “camino difıcil” indicado en el wiki deMercurial en http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall, y espere que el procesosea realmente un trabajo duro.

184

Page 195: Mercurial

Apendice D

Licencia de Publicacion Abierta

Version 1.0, 8 Junio de 1999

D.1. Requerimientos en versiones modificadas y no modificadasLos trabajos bajo Publicacion Abierta pueden reproducirse y distribuirse enteros o en porciones, en cualquier

medio fısico o electronico, siempre y cuando se respeten los terminos de esta licencia, y se incorpore esta licencia osu referencia (con cualquiera de las opciones elegidas por el autor y el editor) en la reproduccion.

A continuacion mostramos la forma correcta de incorporar por referencia:

Copyright (c) ano por nombre del autor o designado. Este material puede distribuirse solamente bajo losterminos y condiciones especificados por la Licencia de Publicacion Abierta, vx.y o posterior (la ultimaversion disponible esta en http://www.opencontent.org/openpub/).

La referencia debe estar seguida inmediatamente por cualquier opcion elegida por el(os) autor(es) y/o editor(es)del documento (consulte la seccion D.6).

Se permite la redistribucion comercial de los materiales sujetos a la Publicacion Abierta.Cualquier publicacion en forma estandar de libro (papel) requerira citar al editor y autor original. Los nombres

del editor y el autor apareceran en todas las superficies externas del libro. En todas las superficies externas el nombredel editor debera aparecer en tamano de la misma medida que el tıtulo del trabajo y sera citado como poseedor conrespecto al tıtulo.

D.2. Derechos de reproduccionEl derecho de reproduccion de cada Publicacion Abierta pertenece al(os) autor(es) o designados.

D.3. Alcance de la licenciaLos terminos de licencia dsecritos aplican a todos los trabajos bajo licencia de publicacion abierta a menos que se

indique de otra forma en este documento.La simple agregacion de trabajos de Publicacion Abierta o una porcion de trabajos de Publicacion Abierta con otros

trabajos o programas en el mismo medio no causaran que esta licencia se aplique a los otros trabajos. Los agregadosdeberan contener una nota que especifique la inclusion de matrial de Publicacion Abierta y una nota de derechos dereproduccion acorde.

Separabilidad. Si cualquier porcion de esta licencia no es aplicable en alguna jurisdiccion, las porciones restantesse mantienen.

185

Page 196: Mercurial

Sin garantıa. Los trabajos de Publicacion Abierta se licencian y ofrecen “como estan” sin garantıa de ningunaclase, expresa o implıcita, incluyendo, pero no limitados a las garantıas de mercabilidad y adaptabilidad para unproposito particular o garantıa de no infraccion.

D.4. Requerimientos sobre trabajos modificadosTodas las versiones modificadas de documentos cubiertos por esta licencia, incluyendo traducciones, antologıas,

compilaciones y documentos parciales, deben seguir estos requerimientos:

1. La version modificada debe estar etiquetada como tal.

2. La persona que hace la modificacion debe estar identificada y la modificacion con fecha.

3. El dar credito al autor original y al editor si se requiere de acuerdo a las practicas academicas de citas.

4. Debe identificarse el lugar del documento original sin modificacion.

5. No puede usarse el(os) nombre(s) del autor (de los autores) para implicar relacion alguna con el documentoresultante sin el permiso explıcito del autor (o de los autores).

D.5. Recomendaciones de buenas practicasAdicional a los requerimientos de esta licencia, se solicita a los redistribuidores y se recomienda en gran medida

que:

1. Si esta distribuyendo trabajaos de Publicacion Abierta en copia dura o CD-ROM, envıe una notificacion porcorreo a los autores acerca de su intencion de redistribuir por lo menos con treinta dıas antes de que su manuscritoo el medio se congelen, para permitir a los autores tiempo para proveer documentos actualizados. Esta notifi-cacion deberıa describir las modificaciones, en caso de que haya, al documento.

2. Todas las modificaciones sustanciales (incluyendo eliminaciones) deben estar marcadas claramente en el docu-mento o si no descritas en un adjunto del documento.

3. Finalmente, aunque no es obligatorio bajo esta licencia, se considera de buenos modales enviar una copia gratisde cualquier expresion en copia dura o CD-ROM de un trabajo licenciado con Publicacion Abierta a el(os)autor(es).

D.6. Opciones de licenciaEl(os) autor(es) y/o editor de un documento licenciado con Publicacion Abierta pueden elegir ciertas opciones

anadiendo informacion a la referencia o a la copia de la licencia. Estas opciones se consideran parte de la instancia dela licencia y deben incluirse con la licencia (o su incorporacion con referencia) en trabajos derivados.

A Prohibir la distribucion de versiones substancialmente modificadas sin el permiso explıcito del(os) autor(es). Sedefinen “modificaciones substanciales” como cambios en el contenido semantico del documento, y se excluyensimples cambios en el formato o correcciones tipograficas.

Para lograr esto, anada la frase “Se prohibe la distribucion de versiones substancialmente modificadas de estedocumento sin el permiso explıcito del dueno de los derechos de reproduccion.” a la referencia de la licencia o a lacopia.

186

Page 197: Mercurial

B Esta prohibido prohibir cualquier publicacion de este trabajo o derivados como un todo o una parte en libros estandar(de papel) con propositos comerciales a menos que se obtenga un permiso previo del dueno de los derechos dereproduccion.

Para lograrlo, anada la frase “La distribucion del trabajo o derivados en cualquier libro estandar (papel) se prohibea menos que se obtenga un permiso previo del dueno de los derechos de reproduccion.” a la referencia de la licenciao la copia.

187

Page 198: Mercurial

Bibliografıa

[AG] Jean Delvare Andreas Gruenbacher, Martin Quinson. Patchwork quilt. http://savannah.nongnu.org/projects/quilt.

[BI] Ronald Oussoren Bob Ippolito. Universal macpython. http://bob.pythonmac.org/archives/2006/04/10/python-and-universal-binaries-on-mac-os-x/.

[Bro] Neil Brown. wiggle–apply conflicting patches. http://cgi.cse.unsw.edu.au/˜neilb/source/wiggle/.

[Dic] Thomas Dickey. diffstat–make a histogram of diff output. http://dickey.his.com/diffstat/diffstat.html.

[Dus] Andy Dustman. Mysql for python. http://sourceforge.net/projects/mysql-python.

[Gru05] Andreas Gruenbacher. How to survive with many patches (introduction to quilt). http://www.suse.de/˜agruen/quilt.pdf, June 2005.

[Mas] Chris Mason. mpatch–help solve patch rejects. http://oss.oracle.com/˜mason/mpatch/.

[O’S06] Bryan O’Sullivan. Achieving high performance in mercurial. In EuroPython Conference, July 2006. XXX.

[Pyt] Python.org. ConfigParser—configuration file parser. http://docs.python.org/lib/module-ConfigParser.html.

[RS] GNU Project volunteers Richard Stallman. Gnu coding standards—change logs. http://www.gnu.org/prep/standards/html_node/Change-Logs.html.

[Tat] Simon Tatham. Putty—open source ssh client for windows. http://www.chiark.greenend.org.uk/

˜sgtatham/putty/.

[Wau] Tim Waugh. patchutils–programs that operate on patch files. http://cyberelk.net/tim/patchutils/.

188

Page 199: Mercurial

Indice alfabetico

.hg/hgrc, fichero, 73, 74, 110

.hg/localtags, fichero, 84, 128, 129

.hg/patches.N , directorio, 152

.hg/patches, directorio, 143, 144, 153–155, 178, 179

.hg/store/data, directorio, 36

.hgignore, fichero, 155, 179

.hgrc, fichero, 20, 35

.hgtags, fichero, 83, 84, 128, 129

.orig, fichero, 150

.rej, fichero, 150, 151

.ssh/config, fichero, 69

.ssh, directorio, 67, 68EMAIL, variable de entorno, 20HGMERGE, variable de entorno, 31, 33HGUSER, variable de entorno, 20HG NODE, variable de entorno, 112, 124HG PARENT1, variable de entorno, 124HG PARENT2, variable de entorno, 124HG SOURCE, variable de entorno, 125HG URL, variable de entorno, 124, 125Mercurial.ini, fichero de configuracion, 66PATH, variable de entorno, 68PYTHONPATH, variable de entorno, 68, 71, 115, 183acl, extension, 117, 118, 168acl, gancho, 118addbreaks, filtro de plantilla, 134addremove, comando, 52, 149add, comando, 44, 49–51, 54, 58, 75, 77, 91, 92, 94, 146,

155, 159, 174, 179, 181opcion --dry-run, 174opcion --exclude, 174opcion --include, 174opcion -I, 174opcion -n, 174opcion -X, 174

age, filtro de plantilla, 134annotate, comando, 142, 144, 145authorized keys, fichero, 67, 68author, palabra clave de plantilla, 132, 134, 135

filtro domain, 134filtro email, 134filtro person, 135

filtro user, 135backout, comando, 95–102

opcion --merge, 96, 98, 101opcion -m, 96

basename, filtro de plantilla, 134bash, comando de sistema, 155bisect, comando, 102–108bisect, extension, 2, 142branches, comando, 86branches, palabra clave de plantilla, 132branch, comando, 87, 88bugzilla, extension, 119–122, 168bugzilla, gancho, 119, 120bundle, comando, 126changegroup, gancho, 109, 112, 125–128chmod, comando de sistema, 70clone, comando, 13, 18, 65, 73, 84

opcion -r, 84commit, comando, 19–21, 30, 41, 49, 52, 86, 112, 116,

117, 153, 155, 158, 178, 180opcion --addremove, 158opcion -A, 52opcion -l, 117opcion -u, 20

commit, gancho, 109, 112, 113, 126, 127, 129config, comando, 110convert, comando (extension conver), 10convert, extension, 10conver, extension

comando convert, 10copy, comando, 44, 52–56, 95, 181

opcion --after, 55cp, comando, 54cp, comando de sistema, 54date, filtro de plantilla, 134date, palabra clave de plantilla, 132, 134, 135

filtro age, 134filtro date, 134filtro hgdate, 134filtro isodate, 134, 135filtro rfc822date, 135filtro shortdate, 135

189

Page 200: Mercurial

desc, palabra clave de plantilla, 132, 135diffstat, comando

opcion -p, 155diffstat, comando de sistema, 155, 156, 173diff, comando, 19, 21, 144, 148, 165, 168, 170, 171,

174–178opcion --exclude, 174opcion --git, 174–176opcion --ignore-all-space, 176opcion --ignore-blank-lines, 174opcion --ignore-space-change, 175opcion --include, 174opcion --nodates, 174opcion --rev, 175opcion --show-function, 175opcion --text, 174opcion -a, 174, 177opcion -B, 174opcion -b, 175opcion -C, 171opcion -c, 171opcion -g, 175opcion -I, 174opcion -N, 148opcion -p, 175opcion -r, 148, 175, 176opcion -w, 176opcion -X, 174

diff, comando de sistema, 140, 142, 148, 171domain, filtro de plantilla, 134email, comando (extension patchbomb), 173email, comando (extension patchbomb)

opcion --plain, 173opcion -a, 173opcion -b, 173opcion -d, 173opcion -f, 173opcion -m, 173opcion -n, 173opcion -s, 173

email, filtro de plantilla, 134escape, filtro de plantilla, 134export, comando, 101extdiff, comando (extension extdiff), 167, 171, 172extdiff, comando (extension extdiff)

opcion -o, 171opcion -p, 171

extdiff, extension, 165–167, 170–172comando extdiff, 167, 171, 172

extdiff, extensioncomando extdiff

opcion-o, 171

opcion-p, 171fetch, comando, 35fetch, comando (extension fetch), 168fetch, extension, 35, 168

comando fetch, 168ficheros, palabra clave de plantilla, 134file adds, palabra clave de plantilla, 133file dels, palabra clave de plantilla, 133files, palabra clave de plantilla, 133fill68, filtro de plantilla, 134fill76, filtro de plantilla, 134filterdiff, comando

opcion --files, 159opcion --hunks, 159opcion -i, 158opcion -x, 158

filterdiff, comando de sistema, 155, 156, 158, 159firstline, filtro de plantilla, 134foo, comando, 88git, comando de sistema, 63, 175, 176grep, comando de sistema, 105, 107guards, fichero, 162header, palabra clave de plantilla, 139heads, comando, 28help, comando, 12, 13, 143, 178hg-interdiff, fichero, 166, 167hgdate, filtro de plantilla, 134hgext, extension, 167hgmerge, comando de sistema, 31, 33, 129hgrc, fichero

seccion acl.allow, 118seccion acl.deny, 118seccion acl, 118

entrada bundle, 118entrada pull, 118entrada push, 118entrada serve, 118entrada sources, 118

seccion bugzilla, 119, 121entrada db, 120entrada host, 119entrada notify, 120entrada password, 120entrada usermap, 120entrada user, 120entrada version, 119

seccion diffentrada showfunc, 175

seccion extdiff, 172seccion extensions, 35, 165, 171seccion hooks, 112seccion notify, 122

190

Page 201: Mercurial

entrada config, 122entrada maxdiff, 123entrada sources, 123entrada strip, 122entrada template, 123entrada test, 122, 124

seccion uientrada username, 20entrada verbose, 113

seccion usermap, 120, 122seccion web, 73, 74, 121, 123

entrada accesslog, 74entrada address, 74entrada allow archive, 73entrada allowpull, 73entrada baseurl, 121, 123entrada contact, 73entrada description, 74entrada errorlog, 74entrada ipv6, 74entrada maxchanges, 73entrada maxfiles, 73entrada motd, 74entrada name, 74entrada port, 74entrada stripes, 73entrada style, 73, 74entrada templates, 74

hgrc, fichero de configuracion, 69, 73, 74, 90, 110–112,114, 115, 118–122, 131, 165, 167, 170–172

hgweb.cgi, fichero, 70–74hgweb.config, fichero, 72, 74hgwebdir.cgi, fichero, 72–74hg, comando de sistema, 68import, comando, 148, 177incoming, comando, 22, 65, 110, 131incoming, gancho, 109, 119, 125–128init, comando, 155, 178inotify, extension, 168–170

comando inserve, 170inserve, comando (extension inotify), 170interdiff, comando de sistema, 165–167, 172isodate, filtro de plantilla, 134, 135kdiff3, comando de sistema, 31, 32, 172locate, comando, 157log, comando, 14–18, 20, 21, 82, 83, 87, 88, 96, 131, 132,

142, 153opcion --patch, 17opcion --rev, 16, 18opcion --template, 132, 135opcion -p, 17opcion -r, 16, 18

lsdiff comando de sistema, 158lsdiff, comando de sistema, 156mercurial.localrepo, modulo

clase localrepository, 115, 124mercurial.node, modulo

funcion bin, 124mercurial.ui, modulo

clase ui, 115, 124merge, comando, 29, 35, 40–42, 80, 90, 153, 176merge, comando de sistema, 33, 34mpatch, comando de sistema, 151mq comando de sistema, 155mq, comando de sistema, 155mq, extension, 170, 172

comando qapplied, 145, 146, 148, 155, 178comando qcommit, 155, 178comando qdelete, 178comando qdiff, 178comando qfold, 158, 178comando qguard, 161, 162comando qheader, 179comando qimport, 149, 179comando qinit, 143, 154, 178, 179, 182comando qnew, 143, 145, 146, 149, 179comando qnext, 179comando qpop, 146, 147, 151, 153, 180comando qprev, 180comando qpush, 146, 147, 150–153, 158, 162, 170,

179–182comando qrefresh, 144, 145, 147, 149, 151–153,

156, 158, 179, 181comando qrename, 181comando qrestore, 181comando qsave, 152, 181comando qselect, 162comando qseries, 145, 146, 148, 153, 181comando qtop, 155, 156, 182comando qunapplied, 182

mq, extensioncomando qdel

opcion-f, 178comando qfold

opcion-e, 179opcion-l, 179opcion-m, 179

comando qinitopcion-c, 154, 155, 178, 179, 182

comando qnewopcion-f, 146, 179opcion-m, 179

comando qpopopcion-a, 146, 151, 152, 155, 180

191

Page 202: Mercurial

opcion-f, 146, 180opcion-n, 152, 180

comando qpushopcion-a, 146, 151, 152, 155, 170, 180opcion-l, 181opcion-m, 152, 181opcion-n, 181

comando qrefreshopcion-e, 181opcion-l, 181opcion-m, 181

comando qsaveopcion-c, 152opcion-e, 152

node, palabra clave de plantilla, 133filtro short, 135

notify, extension, 122–124, 168obfuscate, filtro de plantilla, 135outgoing, comando, 24, 131outgoing, gancho, 109, 110, 126, 127pageant, comando de sistema, 67, 68parents, comando, 24, 29, 30, 40, 176parents, palabra clave de plantilla, 133patchbomb, extension, 165, 172, 173

comando email, 173patchbomb, extension

comando emailopcion--plain, 173opcion-a, 173opcion-b, 173opcion-d, 173opcion-f, 173opcion-m, 173opcion-n, 173opcion-s, 173

patchutils, paquete, 155, 165patch comando de sistema, 149patch, comando

opcion --reverse, 101opcion -p, 148

patch, comando de sistema, 101, 102, 140, 142, 148–151,177

perl, comando de sistema, 117person, filtro de plantilla, 135plink, comando de sistema, 66, 69prechangegroup, gancho, 109, 125–128precommit, gancho, 109, 116, 126–129preoutgoing, gancho, 109, 112, 126, 127pretag, gancho, 109, 128, 129pretxnchangegroup, gancho, 90, 109, 111, 112, 118, 125–

128

pretxncommit, gancho, 109, 111, 113–116, 119, 126–128

preupdate, gancho, 109, 129pull, comando, 22–25, 28, 35, 41, 65, 73, 80, 88, 92, 110,

125, 128, 152, 155opcion -u, 23, 24

push, comando, 25, 80, 125, 126, 128puttygen, comando de sistema, 67putty, comando de sistema, 67qapplied, comando (extension mq), 145, 146, 148, 155,

178qcommit, comando (extension mq), 155, 178qdelete, comando (extension mq), 178qdel, comando (extension mq)

opcion -f, 178qdiff, comando (extension mq), 178qfold, comando, 178qfold, comando (extension mq), 158, 178qfold, comando (extension mq)

opcion -e, 179opcion -l, 179opcion -m, 179

qguard, comando, 162qguard, comando (extension mq), 161, 162qheader, comando (extension mq), 179qimport, comando (extension mq), 149, 179qinit, comando, 155, 178qinit, comando (extension mq), 143, 154, 178, 179, 182qinit, comando (extension mq)

opcion -c, 154, 155, 178, 179, 182qnew, comando, 146qnew, comando (extension mq), 143, 145, 146, 149, 179qnew, comando (extension mq)

opcion -f, 146, 179opcion -m, 179

qnext, comando (extension mq), 179qpop, comando, 146, 151, 152, 155qpop, comando (extension mq), 146, 147, 151, 153, 180qpop, comando (extension mq)

opcion -a, 146, 151, 152, 155, 180opcion -f, 146, 180opcion -n, 152, 180

qprev, comando (extension mq), 180qpush, comando, 151, 152, 155qpush, comando (extension mq), 146, 147, 150–153, 158,

162, 170, 179–182qpush, comando (extension mq)

opcion -a, 146, 151, 152, 155, 170, 180opcion -l, 181opcion -m, 152, 181opcion -n, 181

192

Page 203: Mercurial

qrefresh, comando (extension mq), 144, 145, 147, 149,151–153, 156, 158, 179, 181

qrefresh, comando (extension mq)opcion -e, 181opcion -l, 181opcion -m, 181

qrename, comando (extension mq), 181qrestore, comando (extension mq), 181qsave, comando, 152qsave, comando (extension mq), 152, 181qsave, comando (extension mq)

opcion -c, 152opcion -e, 152

qselect, comando (extension mq), 162qseries, comando (extension mq), 145, 146, 148, 153,

181qtop, comando (extension mq), 155, 156, 182qunapplied, comando (extension mq), 182remove, comando, 44, 51, 52, 56, 76, 94, 159, 181

opcion --after, 52rename, comando, 44, 55, 56, 80, 95, 181

opcion --after, 56revert, comando, 52, 58, 93–95, 101, 145rev, palabra clave de plantilla, 133rfc822date, filtro de plantilla, 135rollback, comando, 91, 92, 102root, comando, 76sed, comando de sistema, 19series, fichero, 144, 152, 155, 162, 164, 178–182serve, comando, 59, 60, 65, 73, 74

opcion -p, 65shortdate, filtro de plantilla, 135short, filtro de plantilla, 135ssh-add, comando de sistema, 67, 68ssh-agent, comando de sistema, 67ssh-keygen, comando de sistema, 67ssh, comando

opcion -C, 69ssh, comando de sistema, 42, 60, 66–69status, comando, 19, 21, 49, 51–53, 55, 76, 87, 91, 95,

101, 168, 169, 176, 181opcion -C, 53, 55

status, fichero, 144, 152, 155, 179, 180, 182strip, comando, 152, 182

opcion -b, 182opcion -f, 182opcion -n, 182

strip, filtro de plantilla, 135sudo apt-get install mercurial-py25, comando de

sistema, 12sudo port install mercurial, comando de sistema,

12

sudo, comando de sistema, 121tabindent, filtro de plantilla, 134, 135tabindent, palabra clave de plantilla, 135tags, comando, 82, 83tags, palabra clave de plantilla, 133tag, comando, 61, 81, 83, 84

opcion -f, 83opcion -l, 84

tag, gancho, 109, 128, 129tar, comando de sistema, 73tip, comando, 21, 23, 87, 131, 156

opcion -p, 156transplant, extension, 172unbundle, comando, 125, 128update, comando, 23, 24, 29, 35, 40, 61, 80, 88–90, 101,

152, 155opcion -C, 88, 152

update, gancho, 110, 129urlescape, filtro de plantilla, 135user, filtro de plantilla, 135version, comando, 12, 68, 176vim, comando de sistema, 172wiggle, comando de sistema, 151zip, comando de sistema, 73

Base de datos de fallos de Mercurialfallo 29, 57fallo 311, 149

fichero de configuracionMercurial.ini (Windows), 66hgrc (Linux/Unix), 69, 73, 74, 90, 110–112, 114,

115, 118–122, 131, 165, 167, 170–172filtros de plantilla

addbreaks, 134age, 134basename, 134date, 134domain, 134email, 134escape, 134fill68, 134fill76, 134firstline, 134hgdate, 134isodate, 134, 135obfuscate, 135person, 135rfc822date, 135shortdate, 135short, 135strip, 135

193

Page 204: Mercurial

tabindent, 134, 135urlescape, 135user, 135

ganchosacl, 118bugzilla, 119, 120changegroup, 109, 112, 125–128commit, 109, 112, 113, 126, 127, 129incoming, 109, 119, 125–128outgoing, 109, 110, 126, 127prechangegroup, 109, 125–128precommit, 109, 116, 126–129preoutgoing, 109, 112, 126, 127pretag, 109, 128, 129pretxnchangegroup, 90, 109, 111, 112, 118, 125–

128pretxncommit, 109, 111, 113–116, 119, 126–128preupdate, 109, 129tag, 109, 128, 129update, 110, 129

opciones globalesopcion --debug, 68, 118opcion --exclude, 79opcion --include, 79opcion --quiet, 18opcion --verbose, 13, 17, 18opcion -I, 79opcion -q, 18, 77opcion -v, 13, 17, 18, 65, 77, 113opcion -X, 79

palabras clave de plantillaauthor, 132, 134, 135branches, 132date, 132, 134, 135desc, 132, 135ficheros, 134file adds, 133file dels, 133files, 133header, 139node, 133parents, 133rev, 133tabindent, 135tags, 133

tagstip, 125, 128special tag namesqbase, 153

qtip, 153

variables de entornoEMAIL, 20HGMERGE, 31, 33HGUSER, 20HG NODE, 112, 124HG PARENT1, 124HG PARENT2, 124HG SOURCE, 125HG URL, 124, 125PATH, 68PYTHONPATH, 68, 71, 115, 183

194