User Tools

Site Tools


projects:uocmaster

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
projects:uocmaster [2011/12/31 00:16] – [CVS] rlunaroprojects:uocmaster [2022/12/02 22:02] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +====== Master of the UOC (Universidad Oberta de Catalunya) ======
 +
 +===== Reconfigurar teclado en linux =====
 +
 +Con el teclado del sistema Toshiba 430CDT que estamos manejando 
 +se dan unas particularidades que lo hacen peculiar: 
 +
 +  - La tecla "AltGr" no está. En el lugar donde debería estar, encontramos la tecla /o/a (primero, primera)
 +  - Las teclas de mayor que y menor que, tampoco están 
 +  - La tecla de slash invertido tampoco está
 +
 +Era preciso resolver estas carencias para que el teclado funcionara
 +correctamente. A fin de lograrlo con éxito, fué preciso identificar 
 +cómo funciona el teclado en Debian. 
 +
 +==== Funcionamiento del teclado en Debian ====
 +
 +Cuando se arranca el sistema, el script ''/etc/init.d/keymap.sh'' es el 
 +encargado de cargar la configuración del teclado, que se encuentra
 +en ''/etc/console/boottime.kmap.gz''
 +
 +Si es la primera vez que se ejecuta (porque se acaba de reinstalar 
 +el sistema operativo, por ejemplo), es posible que esa lectura del 
 +mapa de teclado se efectúe de ''/var/state/misc/kernel.kmap''. En el momento
 +actual, la inspección del script indica que esto no es así ya que la 
 +ruta a ese archivo aparece comentada. 
 +
 +
 +===== Los mapas de teclado en Linux =====
 +
 +Linux posee un potente mecanismo para asignar las teclas físicas del 
 +teclado a teclas "logicas", que son las que los programas reciben. 
 +
 +Toda esta parafernalia se consigue con tres elementos: 
 +
 +  * los mapas de teclado, ficheros ascii donde se asigna una tecla física (o combinación de varias, como Alt + F1) a un caracter o varios caracteres
 +  * el fichero loadkeys, encargado de "leer" esos mapas de teclado y guardarlos en el kernel
 +  * el comando showkeys, que al pulsar una tecla devuelve el número de tecla física que tiene 
 +
 +
 +===== Reprogramando el teclado =====
 +
 +Para reprogramar el teclado, lo primero que hicimos estudiar cómo íbamos 
 +a colocar las teclas: 
 +
 +  * por su proximidad, la tecla /o/a (masculine, ordfeminine, slash) sería la tecla AltGr. 
 +  * los caracteres /o/a se colocarían como AltGr+8 y AltGr+9
 +  * el caracter backslash se colocaría en AltGr+7
 +
 +Mediante el comando showkeys identificamos la tecla /o/a como de código 41. 
 +Su programación actual es: 
 +
 +<code>
 +keycode  41 = masculine        ordfeminine      backslash        nul             
 +alt keycode  41 = Meta_grave      
 +</code>
 +
 +y será sustituida por: 
 +
 +<code>
 +keycode  41 = AltGr
 +# keycode  41 = masculine        ordfeminine      backslash        nul             
 +# alt keycode  41 = Meta_grave      
 +</code>
 +
 +El siguiente paso es identificar la tecla 7/slash. Mediante showkey
 +identificamos que se trata de la tecla de código 8, que en el mapa 
 +de teclado actual (/etc/console/boottime.kmap.gz) figura como: 
 +
 +<code>
 +keycode   8 = seven            slash            braceleft        Control_underscore
 + alt keycode   8 = Meta_seven      
 +</code>
 +
 +y al tercer valor que es el valor que tiene la tecla cuando se pulsa AltGr, lo reemplazamos por backslash: 
 +
 +<code>
 +keycode   8 = seven            slash            backslash        Control_underscore
 +</code>
 +
 +En el caso de las teclas 9 y 10, que corresponden a los caracteres 
 +8 y 9 respectivamente, se efectúa la misma operación para que 
 +retornen "masculine" y "ordfeminine" respectivamente: 
 +
 +<code>
 +keycode   9 = eight            parenleft        masculine      Delete          
 + alt keycode   9 = Meta_eight      
 +keycode  10 = nine             parenright       ordfeminine    
 + alt keycode  10 = Meta_nine       
 +</code>
 +
 +
 +Queda ahora colocar los caracteres mayor que y menor que. Por proximidad, 
 +se colocarán en las teclas AltGr+Z y AltGr+X. Donde antes estaba: 
 +
 +<code>
 +keycode  44 = z
 +keycode  45 = x         
 +</code>
 +
 +ahora quedará como: 
 +
 +<code>
 +keycode  44 = z
 + altgr keycode 44 = less               
 +keycode  45 = x         
 + altgr   keycode  45 = greater      
 +</code>
 +
 +====== Actualización del sistema ======
 +
 +Para actualizar el sistema el primer paso que hemos realizado ha sido 
 +añadir la siguiente línea en ''/etc/apt/sources.list'': 
 +
 +<code>
 +deb ftp://ftp.rediris.es/sites/ftp.es.debian.org/debian oldstable main contrib non-free 
 +</code>
 +
 +Y ejecutar el comando ''apt-get update'' para que actualice el repositorio 
 +de paquetes. Hemos de hacer notar que woody ha pasado de ser "stable"
 +"oldstable" en los repositorios de paquetes de debian. 
 +
 +A continuación hemos ejecutado "apt-get upgrade"[1] para que actualice 
 +aquellos paquetes que proceda. 
 +
 +
 +====== Otras modificaciones en el sistema operativo que no se han descrito ======
 +
 +==== Corrección de la configuración del lector de noticias ====
 +
 +El lector de noticias inn tenía un error en el fichero de configuración 
 +''/etc/news/inn.conf'' relativo al nombre del dominio y al nombre del host, 
 +que no habían sido configurados correctamente. Se modificó el fichero para 
 +que el contenido: 
 +
 +<code properties>
 +# General Settings
 +
 +domain:
 +innflags:
 +mailcmd:                /usr/lib/news/bin/innmail
 +server:   localhost
 +</code>
 +
 +Quedara así: 
 +
 +<code properties>
 +# General Settings
 +
 +domain:                 raulluna.net
 +innflags:
 +mailcmd:                /usr/lib/news/bin/innmail
 +server:                             tas
 +</code>
 +
 +Este error provocaba un mensaje de error que llegaba por correo electrónico
 +cada vez que se ejecutaba el crontab ''/etc/cron.d/inn2''
 +
 +==== Ajuste de permisos para el acceso a unidad de cdrom y a floppy disk ====
 +
 +La configuración de permisos para el acceso al cdrom y al floppy disk 
 +eran poco operativos, además de inseguros. Los permisos
 +iniciales eran rwxr-xr-x para el usuario root y el grupo root, lo que 
 +implica que en la práctica sólo el superusuario tiene acceso de lectura 
 +y escritura a estos dispositivos, y que todos los usuarios del sistema 
 +tienen acceso a los mismos una vez montados: 
 +
 +<code>
 +chupete:/# ls -lad floppy cdrom
 +drwxr-xr-x    2 root     root         4096 Nov 19  2004 cdrom
 +drwxr-xr-x    3 root     root         1024 Jul 23 18:58 floppy
 +</code>
 +
 +Resulta más adecuado hacer un uso racional de los grupos "cdrom" y "floppy"
 +de tal modo que sólo los usuarios que pertenezcan a estos grupos tengan 
 +acceso a estos dispositivos por defecto. Para ello se configuró el grupo
 +propietario de estos directorios como "cdrom" y "floppy" según el caso, y 
 +los permisos se establecieron como rwxrwx---, de tal modo que para acceder 
 +al floppy o al cdrom se tiene que pertenecer a estos grupos: 
 +
 +<code>
 +chupete:/# ls -lad floppy cdrom
 +drwxrwx---    2 root     cdrom        4096 Nov 19  2004 cdrom
 +drwxrwx---    3 root     floppy       1024 Jul 23 18:58 floppy
 +</code>
 +
 +==== Instalación de paquetes complementarios ====
 +
 +Se han instalado los siguientes paquetes complementarios: bzip2
 +
 +==== Corrección de problemas con apt ====
 +
 +Se ha resuelto un problema con la configuración de apt: la línea que había 
 +en sources.list para el acceso a security era: 
 +
 +<code>
 +deb http://security.debian.org/ stable/updates main
 +</code>
 +
 +Cuando debian pasó "sarge" a nueva versión "stable" y "woody" pasó a 
 +"oldstable" se produjo un problema con esto, ya que empezó a descargar
 +paquetes de versiones posteriores a las que funcionaban en woody, creando 
 +colisiones. Bastó corregirla línea en cuestión: 
 +
 +<code>
 +deb http://security.debian.org/ oldstable/updates main
 +</code>
 +
 +Y ejecutar apt-get update / apt-get upgrade para que todo volviera a 
 +funcionar con normalidad. 
 +
 +
 +
 +
 +====== Instalación de Programas de Control de Código Fuente ======
 +
 +==== Qué se va a evaluar ====
 +
 +
 +**Portabilidad**
 +
 +En esta sección analizaremos la disponibilidad de esta herramienta 
 +para otras plataformas distintas de Linux; tanto de las herramientas
 +cliente, para la descarga y desarrollo en Windows, pero trabajando 
 +con un servidor Linux, como en las herramientas servidor, para trabajar
 +desde cualquier plataforma a un servidor en cualquier plataforma.
 +
 +**Realizando el primer cambio**
 +
 +La primera prueba que se va a realizar es completar un cambio simple 
 +mediante la herramienta de control de código fuente; el objetivo es 
 +familiarizarse con la herramienta, comprobar su facilidad/dificultad 
 +de uso; si es fácil implantarla en un proyecto y si la curva de 
 +aprendizaje de la misma es alta.
 +
 +**Realizando un cambio concurrente**
 +
 +Un aspecto que nos ha parecido interesante evaluar es el tratamiento que 
 +hace el sistema de control de código fuente de varios cambios que se 
 +hagan en el repositorio en la misma línea de código; y en general determinar
 +que los cambios de unos usuarios no resultan destruidos por los cambios 
 +de otros usuarios. 
 +
 +El espíritu de esta prueba es también evaluar si es posible o no acceder 
 +al repositorio remotamente; en caso de que sí fuera posible, comprobar 
 +cuál es el proceso para realizarlo y ver las respuestas que se producen 
 +con este modo de acceso. 
 +
 +==== OpenCM ====
 +
 +<code>
 +chupete:~/opencm-0.1.2alpha8/base# ./configure --prefix /tmp/opencm --with-ssl-dir=/usr/lib
 +</code>
 +
 +OpenCM dice que no tiene instalada la librería openSSL a pesar de que sí 
 +está instalada: 
 +
 +<code>
 +chupete:~/opencm-0.1.2alpha8/base# apt-get install openssl
 +Reading Package Lists... Done
 +Building Dependency Tree... Done
 +Sorry, openssl is already the newest version.
 +0 packages upgraded, 0 newly installed, 0 to remove and 1  not upgraded.
 +chupete:~/opencm-0.1.2alpha8/base#
 +</code>
 +
 +
 +
 +==== GNU arch ====
 +
 +GNU arch parece que adolece del mismo problema: la comprobación de configure
 +dice que no está instalado "diff3":
 +
 +<code>
 +chupete:~/tla-1.3/=build# ../src/configure --prefix /tmp/gnu_arch/ --with-gnu-diff3 /usr/bin/diff3
 +
 +The configured versions of diff and diff3 do not handle files
 +not ending in newline correctly.
 +
 +  configured diff = diff.
 +  configured diff3 = /usr/bin/diff3.
 +
 +
 +Use config options
 +  --with-gnu-diff PROG
 +  --with-gnu-diff3 PROG
 +
 +  NOTE: especially on BSD-derived systems, you will want
 +   to grab a recent version of GNU diff and compile it for use
 +   with those config options.   You don't need to replace
 +   the native diff tools on your system, but you do need to
 +   configure tla specially.   Sorry about that -- some BSDs
 +   have made a poor decision about their native diffs.
 +
 +   (Example systems: some NetBSD, FreeBSD, and MacOS versions.)
 +</code>
 +
 +
 +Sin embargo, sí que lo está: 
 +
 +<code>
 +chupete:~/tla-1.3/=build# apt-cache search diff3
 +diff - File comparison utilities
 +chupete:~/tla-1.3/=build# apt-get install diff
 +Reading Package Lists... Done
 +Building Dependency Tree... Done
 +Sorry, diff is already the newest version.
 +0 packages upgraded, 0 newly installed, 0 to remove and 1  not upgraded.
 +chupete:~/tla-1.3/=build# diff
 +diff   diff3
 +chupete:~/tla-1.3/=build# diff3
 +diff3: missing operand
 +diff3: Try 'Diff3 --help' for more information.
 +</code>
 +
 +Con los paquetes *.deb que encontré tampoco funciona: informa que la versión
 +de libc no es la que el paquete esperaba y cancela. 
 +
 +
 +==== darcs ====
 +
 +=== Qué dice su página web ===
 +
 +
 +[1] http://abridgegame.org/darcs/
 +
 +Su página web[1] presenta a darcs como "otro sistema de control de código 
 +fuente", presentando como gran ventaja que es un sistema descentralizado. 
 +Como base teórica el autor presenta una "teoría de los parches", con raices
 +en la mecánica cuántica, que sirve de fundamento teórico del software. 
 +
 +=== Instalación ===
 +
 +
 +Según su página web, tras modificar el fichero sources.list de debian para 
 +que incluya los fuentes al proyecto: 
 +
 +<code>
 +deb http://abridgegame.org/debian woody/
 +</code>
 +
 +
 +Hemos ejecutado ''apt-get update'' para que actualice los repositorios de
 +paquetes:
 +
 +<code>
 +chupete:/etc/apt# apt-get update
 +Hit http://security.debian.org stable/updates/main Packages
 +Hit http://security.debian.org stable/updates/main Release
 +Get:1 http://abridgegame.org woody/ Packages [1100B]
 +Ign http://abridgegame.org woody/ Release
 +Fetched 1100B in 1s (763B/s)
 +Reading Package Lists... Done
 +Building Dependency Tree... Done
 +chupete:/etc/apt# 
 +</code>
 +
 +Y a continuacion apt-get install darcs para que efectúe la instalación:
 +
 +<code>
 +chupete:/etc/apt# apt-get install darcs
 +[....]
 +Official i386 Binary-1 (20031201)' in the drive '/cdrom/' and press enter
 +
 +Get:1 http://abridgegame.org woody/ darcs 1.0.3-1 [1051kB]
 +Selecting previously deselected package libgmp3.
 +(Reading database ... 34955 files and directories currently installed.)
 +Unpacking libgmp3 (from .../libgmp3_4.0.1-3_i386.deb) ...
 +Selecting previously deselected package darcs.
 +Unpacking darcs (from .../darcs_1.0.3-1_i386.deb) ...
 +
 +chupete:~# apt-get install darcs-server
 +</code>
 +
 +Es aconsejable instalar también el programa wget, que darcs necesita
 +pero no comprueba su existencia: 
 +
 +<code>
 +chupete:~# apt-get install wget
 +</code>
 +
 +
 +La instalación ha sido sencilla y sin dificultades; no en vano su autor afirma
 +que la distribución donde lo ha desarrollado y probado es Debian. 
 +
 +=== Realizando el primer cambio ===
 +
 +Igual que en el caso anterior, para las pruebas hemos utilizado el mismo 
 +proyecto "hello world" que en el caso de Aegis. El comando que crea el 
 +repositorio es "darcs initialize", que crea un directorio "_darcs" en
 +el propio subdirectorio del proyecto:
 +
 +<code>
 +pruebas2@chupete:~/hello_world> ls
 +AUTHORS        README          config.log           hello_world.kdevses
 +COPYING        TODO            config.status        hello_world.lsm
 +ChangeLog      acinclude.m4    configure            libtool
 +INSTALL        aclocal.m4      configure.files      stamp-h.in
 +Makefile       admin           configure.in         stamp-h1
 +Makefile.am    autom4te.cache  configure.in.in      subdirs
 +Makefile.dist  config.h        hello_world
 +Makefile.in    config.h.in     hello_world.kdevprj
 +pruebas2@chupete:~/hello_world> darcs initialize
 +pruebas2@chupete:~/hello_world> ls
 +AUTHORS        Makefile.in   autom4te.cache   configure.in         stamp-h.in
 +COPYING        README        config.h         configure.in.in      stamp-h1
 +ChangeLog      TODO          config.h.in      hello_world          subdirs
 +INSTALL        _darcs        config.log       hello_world.kdevprj
 +Makefile       acinclude.m4  config.status    hello_world.kdevses
 +Makefile.am    aclocal.m4    configure        hello_world.lsm
 +Makefile.dist  admin         configure.files  libtool
 +</code>
 +
 +El siguiente paso consiste en añadir todos los ficheros del proyecto 
 +al repositorio del mismo: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world> darcs add * -r
 +</code>
 +
 +Y luego se efectúa un checkout de todos los ficheros, que en el lenguaje 
 +de darcs implica grabar todos los cambios: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world> darcs record --all
 +Darcs needs to know what name (conventionally an email address) to use as the
 +patch author, e.g. 'Fred Bloggs <fred@bloggs.invalid>' If you provide one
 +now it will be stored in the file '_darcs/prefs/author' and used as a default
 +in the future.  To change your preferred author address, simply delete or edit
 +this file.
 +
 +What is your email address? Pruebas Darcs <pruebas2@tas.raulluna.net>
 +What is the patch name? Initial Release
 +Do you want to add a long comment? [yn]
 +
 +Initial Release
 +***DARCS***
 +
 +Aquí pondremos la descripción larga del parche, que en este caso 
 +es la creación inicial del repositorio.
 +
 +--Fin del Fichero--
 +</code>
 +
 +
 +Después de realizados unos cambios en el proyecto, el propio darcs nos 
 +informa de qué es lo que hemos cambiado en el mismo mediante el comando
 +''whatsnew'': 
 +
 +<code>
 +pruebas2@chupete:~/hello_world> darcs whatsnew
 +{
 +hunk ./Makefile 42
 +-build_triplet = i686-pc-linux-gnu
 +-host_triplet = i686-pc-linux-gnu
 +-target_triplet = i686-pc-linux-gnu
 +-ACLOCAL = ${SHELL} /home/rluna/hello_world/admin/missing --run aclocal-1.7
 ++build_triplet = i586-pc-linux-gnu
 ++host_triplet = i586-pc-linux-gnu
 ++target_triplet = i586-pc-linux-gnu
 ++ACLOCAL = ${SHELL} /home/pruebas2/hello_world/admin/missing --run aclocal-1.7
 +hunk ./Makefile 48
 +-AMTAR = ${SHELL} /home/rluna/hello_world/admin/missing --run tar
 +-AUTOCONF = ${SHELL} /home/rluna/hello_world/admin/missing --run autoconf
 ++AMTAR = ${SHELL} /home/pruebas2/hello_world/admin/missing --run tar
 ++AUTOCONF = ${SHELL} /home/pruebas2/hello_world/admin/missing --run autoconf
 +hunk ./Makefile 51
 +[...]
 +}
 +</code>
 +
 +Igual que en el caso anterior, simplemente ejecutando ''darcs record'' 
 +guardaremos los datos relativos al cambio en el repositorio de código 
 +fuente: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world> darcs record
 +hunk ./Makefile 42
 +-build_triplet = i686-pc-linux-gnu
 +[....]
 +What is the patch name? Tras ejecutar configure
 +Do you want to add a long comment? [yn] n
 +Finished recording patch 'Tras ejecutar configure'
 +</code>
 +
 +Una nueva ejecución del comando ''whatsnew'' indica que no hay cambios, por lo 
 +que la información en el repositorio se ha guardado con éxito. 
 +
 +
 +=== Realizando un cambio concurrente ===
 +
 +Ahora vamos a realizar un cambio al repositorio desde dos ubicaciones/
 +ususarios distintos, y vamos a observar cómo se gestionan las colisiones
 +sobre el mismo fichero de código fuente. 
 +
 +== Haciendo que el repositorio sea visible públicamente ==
 +
 +El primer paso para realizar esto es hacer que el repositorio que hemos 
 +creado, que por ahora sólo es visible para nuestro usuario, lo sea para el 
 +resto del mundo. 
 +
 +Hemos creado un directorio /var/www/repos para almacenar los repositorios 
 +que darcs crea en esta máquina, a continuación hemos hecho un enlace 
 +simbólico a nuestro proyecto desde el mismo: 
 +
 +<code>
 +chupete:/var/www/repos# ls
 +chupete:/var/www/repos# ln -s /home/pruebas2/hello_world  .
 +chupete:/var/www/repos# ls -la
 +total 8
 +drwxr-xr-x    2 root     root         4096 Jul 26 12:07 .
 +drwxr-xr-x    3 root     root         4096 Jul 26  2005 ..
 +lrwxrwxrwx    1 root     root           26 Jul 26 12:07 hello_world -> /home/pruebas2/hello_world
 +</code>
 +
 +== Descargando el repositorio para trabajar con él ==
 +
 +El segundo paso es que otro usuario -sea pruebas1- se descargue una copia
 +completa del código fuente así publicado y pueda trabajar con él. Para ello 
 +pruebas1 ha creado un directorio de trabajo con un repositorio vacío: 
 +
 +<code>
 +pruebas1@chupete:~> mkdir hello_world
 +pruebas1@chupete:~> cd hello_world
 +pruebas1@chupete:~/hello_world> darcs initialize
 +</code>
 +
 +Y mediante el comando darcs pull ha solicitado una copia completa de todos 
 +parches que tiene el usuario pruebas2 en su repositorio: 
 +
 +<code>
 +pruebas1@chupete:~/hello_world> darcs pull http://localhost/repos/hello_world
 +
 +Sat Jul 23 11:47:38 CEST 2005  Pruebas Darcs <pruebas2@tas.raulluna.net>
 +  * Initial Release
 +Shall I pull this patch? (1/2) [ynWvpxqadjk], or ? for help: y
 +
 +Sat Jul 23 18:23:02 CEST 2005  Pruebas Darcs <pruebas2@tas.raulluna.net>
 +  * Tras ejecutar configure
 +Shall I pull this patch? (2/2) [ynWvpxqadjk], or ? for help: y
 +pruebas1@chupete:~/hello_world> darcs pull http://localhost/repos/hello_world
 +
 +Sat Jul 23 11:47:38 CEST 2005  Pruebas Darcs <pruebas2@tas.raulluna.net>
 +  * Initial Release
 +Shall I pull this patch? (1/2) [ynWvpxqadjk], or ? for help:
 +pruebas1@chupete:~/hello_world> ls
 +AUTHORS        Makefile.in   autom4te.cache   configure.in         libtool
 +COPYING        README        config.h         configure.in.in      stamp-h.in
 +ChangeLog      TODO          config.h.in      darcs                stamp-h1
 +INSTALL        _darcs        config.log       hello_world          subdirs
 +Makefile       acinclude.m4  config.status    hello_world.kdevprj
 +Makefile.am    aclocal.m4    configure        hello_world.kdevses
 +Makefile.dist  admin         configure.files  hello_world.lsm
 +</code>
 +
 +== Efectuando un cambio concurrente ==
 +
 +En darcs, los dos usuarios tienen su propio repositorio de código fuente; 
 +por lo que el cambio concurrente se puede producir de dos formas: cuando 
 +el usuario pruebas1 importa un cambio a su repositorio que entra en conflicto 
 +con un cambio que haya realizado él mismo y guardado, o bien cuando el 
 +usuario pruebas2 importa un cambio realizado por el usuario pruebas1 
 +que entra en conflicto con los cambios realizados por él. 
 +
 +Entendiendo que la raiz del problema es más o menos la misma, optaremos por 
 +probar uno de los casos: cuando el usuario pruebas2 ha realizado un cambio 
 +y éste entra en conflicto con el cambio que el usuario pruebas1 ha hecho en
 +su propio repositorio. Hacerlo de este modo es más fácil ya que así el 
 +usuario pruebas1 no necesita hacer su repositorio accesible públicamente. 
 +
 +Comenzaremos por realizar los cambios con el usuario pruebas2 y guardarlos
 +en el repositorio: 
 +
 +<code>
 +pruebas2@chupete:~> cd hello_world
 +pruebas2@chupete:~/hello_world> joe hello_world/main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + change made by pruebas2" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +pruebas2@chupete:~/hello_world> make
 +pruebas2@chupete:~/hello_world> hello_world/hello_world
 +Hello, World! + change made by pruebas2
 +</code>
 +
 +Bien, el cambio ya está hecho. Ahora lo que haremos será guardar los cambios
 +en nuestro repositorio local: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world> darcs whatsnew
 +{
 +binary ./hello_world/hello_world
 +hunk ./hello_world/main.cpp 27
 +-  cout << "Hello, World!" << endl;
 ++  cout << "Hello, World! + change made by pruebas2" << endl;
 +}
 +pruebas2@chupete:~/hello_world> darcs record
 +binary ./hello_world/hello_world
 +Shall I record this patch? (1/2) [ynWsfqadjk], or ? for help: y
 +hunk ./hello_world/main.cpp 27
 +-  cout << "Hello, World!" << endl;
 ++  cout << "Hello, World! + change made by pruebas2" << endl;
 +Shall I record this patch? (2/2) [ynWsfqadjk], or ? for help: y
 +What is the patch name? Change on the line hello world
 +Do you want to add a long comment? [yn] n
 +Finished recording patch 'Change on the line hello world'
 +</code>
 +
 +Ahora el usuario pruebas1 hará un cambio en su propio repositorio: 
 +
 +<code>
 +pruebas1@chupete:~/hello_world> joe hello_world/main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS1" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Y lo guardará en darcs: 
 +
 +<code>
 +pruebas1@chupete:~/hello_world> darcs whatsnew
 +{
 +hunk ./hello_world/main.cpp 27
 +-  cout << "Hello, World!" << endl;
 ++  cout << "Hello, World! + CHANGE MADE BY PRUEBAS1" << endl;
 +}
 +pruebas1@chupete:~/hello_world> darcs record
 +Darcs needs to know what name (conventionally an email address) to use as the
 +patch author, e.g. 'Fred Bloggs <fred@bloggs.invalid>' If you provide one
 +now it will be stored in the file '_darcs/prefs/author' and used as a default
 +in the future.  To change your preferred author address, simply delete or edit
 +this file.
 +
 +What is your email address? Pruebas Darcs Uno <pruebas1@tas.raulluna.net>
 +hunk ./hello_world/main.cpp 27
 +-  cout << "Hello, World!" << endl;
 ++  cout << "Hello, World! + CHANGE MADE BY PRUEBAS1" << endl;
 +Shall I record this patch? (1/1) [ynWsfqadjk], or ? for help: y
 +What is the patch name? change on the hello world line
 +Do you want to add a long comment? [yn] n
 +
 +Finished recording patch 'change on the hello world line'
 +</code>
 +
 +
 +Y ahora integramos los cambios de pruebas2 en el repositorio de pruebas1, lo 
 +que provocará una colisión:
 +
 +<code>
 +pruebas1@chupete:~/hello_world> darcs pull http://localhost/repos/hello_world
 +Pulling from "http://localhost/repos/hello_world"...
 +
 +Fri Jul 29 12:14:21 CEST 2005  Pruebas Darcs <pruebas2@tas.raulluna.net>
 +  * Change on the line hello world
 +Shall I pull this patch? (1/1) [ynWvpxqadjk], or ? for help: y
 +We have conflicts in the following files:
 +./hello_world/main.cpp
 +Finished pulling and applying.
 +</code>
 +
 +
 +El cambio aparece en el código fuente así: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +v v v v v v v
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS1" << endl;
 +*************
 +  cout << "Hello, World! + change made by pruebas2" << endl;
 +^ ^ ^ ^ ^ ^ ^
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Que obviamente al compilar produce un error y permite la corrección. 
 +
 +
 +
 +=== Conclusiones ===
 +
 +Darcs es un sistema de control de código fuente distribuido. Cada uno de 
 +los desarrolladores tiene su propio repositorio de código fuente donde 
 +almacena sus propios cambios. Mediante su publicación en un sitio web, 
 +estos cambios se pueden hacer visibles para el resto de desarrolladores. 
 +
 +A mi modo de ver, un sistema de control de código fuente distribuido, 
 +incluso en un modelo de desarrollo como lo es el del software libre, 
 +presenta una gran desventaja cuando el número de desarrolladores es 
 +grande; ya que la sincronización de los cambios habrá de hacerse 
 +forzosamente en un repositorio común o de lo contrario existe un gran 
 +riesgo de que alguno de los miembros del equipo acabe con una versión 
 +del proyecto diferente.
 +
 +Por otra parte, he encontrado que el aprendizaje de la herramienta es 
 +muy rápido y simple: comienza por unos ejemplos básicos para ir adentrandose
 +poco a poco en cuestiones más complicadas. 
 +
 +La resolución de los conflictos se realiza directamente en el fichero 
 +implicado -no se ha hecho prueba de lo que ocurriría con los ficheros 
 +binarios-, que en el caso del código fuente puede ser una solución 
 +válida ya que al compilar se explicitaría un error. Sin embargo, los 
 +conflictos en ficheros de texto planos -un changelog, por ejemplo- 
 +podrían pasar desapercibidos al no requerir la intervención del 
 +desarrollador para su integración. 
 +
 +Aunque también puede verse el defecto como una ventaja, ya que eso 
 +permite la integración automática de parches al repositorio sin la 
 +intervención del usuario más que en la supervisión del mismo. 
 +
 +Otra cosa que sería interesante destacar, aunque no se ha podido 
 +verificar, es que existen versiones para otras plataformas: MacOS X, 
 +FreeBSD y Windows. 
 +
 +
 +
 +==== Aegis ====
 +
 +
 +=== Creación del proyecto ===
 +
 +Los pasos que se van a dar a continuación se efectuarán con el perfil 
 +de administrador de aegis, que es el usuario "pruebas1". No hemos tenido 
 +que hacer nada especial para informar a aegis que éste es el usuario 
 +administrador; simplemente hemos realizado todas las acciones
 +administrativas con este usuario.
 +
 +Para guardar todos los proyectos hemos creado un directorio /aegis, que 
 +inicialmente estará vacío. Como proyecto de pruebas hemos creado mediante
 +kdeveloper un programa "Hello World!". kdeveloper ha creado los ficheros 
 +necesarios para autoconf y automake. 
 +
 +Mediante el comando aenpr crearemos el proyecto y a continuación 
 +establecemos las características del mismo mediante aepa:
 +
 +<code shell>
 +pruebas1@chupete:~> aenpr hello_world -dir /aegis/hello_world -version -
 +aegis: project "hello_world": new project complete
 +pruebas1@chupete:~> aepa -p hello_world
 +
 +/*
 +** Project hello_world
 +*/
 +description = "The \"hello_world\" program.";
 +developer_may_review = false;
 +developer_may_integrate = false;
 +reviewer_may_integrate = false;
 +developers_may_create_changes = false;
 +umask = 022;
 +default_test_exemption = false;
 +minimum_change_number = 10;
 +reuse_change_numbers = true;
 +minimum_branch_number = 1;
 +skip_unlucky = false;
 +compress_database = false;
 +develop_end_action = goto_being_reviewed;
 +</code>
 +
 +
 +=== Asignacion de las figuras de desarrollador, revisor e integrador ===
 +
 +Áhora añadimos al proyecto las figuras de desarrollador -pruebas2-, 
 +revisor -pruebas3- e integrador -pruebas4-: 
 +
 +<code shell>
 +pruebas1@chupete:~> aend pruebas2 -p hello_world
 +aegis: project "hello_world": new developer pruebas2 complete
 +pruebas1@chupete:~> aenrv pruebas3 -p hello_world
 +aegis: project "hello_world": new reviewer pruebas3 complete
 +pruebas1@chupete:~> aeni pruebas4 -p hello_world
 +aegis: project "hello_world": new integrator pruebas4 complete
 +</code>
 +
 +=== Creamos la primera rama del proyecto ===
 +
 +Siguiendo con el usuario administrador de aegis -pruebas1-, hemos creado 
 +la rama 1.0 para comenzar los cambios al proyecto: 
 +
 +<code>
 +pruebas1@chupete:~> ael p
 +
 +List of Projects                                                         Page 1
 +                                                       Sat Jul 16 23:34:10 2005
 +
 +Project         Directory               Description
 +---------       -----------             -------------
 +hello_world     /aegis/hello_world      The "hello_world" program.
 +pruebas1@chupete:~> aenbr -p hello_world 1
 +aegis: project "hello_world": change 1: development directory
 +        "/aegis/hello_world/branch.1"
 +aegis: project "hello_world.1": new branch complete
 +pruebas1@chupete:~> aenbr -p hello_world.1 0
 +aegis: project "hello_world.1": change 0: development directory
 +        "/aegis/hello_world/branch.1/branch.0"
 +aegis: project "hello_world.1.0": new branch complete
 +</code>
 +
 +
 +=== Damos de alta el primer cambio del proyecto ===
 +
 +<code>
 +pruebas1@chupete:~> aenc -edit -p hello_world.1.0
 +aegis: project "hello_world.1.0": change 10: new change complete
 +
 +/*
 +** nc dflt hint
 +*/
 +brief_description = "Comenzamos con el proyecto";
 +description = "Primera instalacion de ficheros";
 +cause = internal_bug;
 +test_exempt = true;
 +test_baseline_exempt = true;
 +regression_test_exempt = true;
 +architecture =
 +[
 +        "unspecified",
 +];
 +</code>
 +
 +=== El desarrollador realiza el primer cambio ===
 +
 +Ahora entramos como el usuario pruebas2 al objeto de hacer la primera subida 
 +del proyecto.
 +
 +La primera subida de ficheros es la más compleja, ya que hay que crear 
 +el fichero aegis.conf con la información necesaria para que el almacenamiento
 +de versiones de los fuentes y la construcción del fichero se resuelvan 
 +satisfactoriamente. 
 +
 +Comenzaremos por visualizar el cambio que tenemos pendiente: 
 +
 +<code>
 +pruebas2@chupete:~> aedb -l -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes awaiting_development                   Sat Jul 16 23:56:38 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  10    awaiting_       Comenzamos con el proyecto
 +        development
 +</code>
 +
 +A continuación informamos a Aegis que comenzamos la fase de desarrollo. 
 +Aegis hace una copia de la rama activa tal y como está en el repositorio 
 +en un directorio creado a tal efecto para nosotros: 
 +
 +<code>
 +pruebas2@chupete:~> aedb hello_world.1.0 10
 +aegis: project "hello_world.1.0": change 10: development directory
 +        "/home/pruebas2/hello_world.1.0.C010"
 +aegis: logging to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: develop begin complete
 +</code>
 +
 +Y a continuación informamos a Aegis que pasamos a trabajar en el 
 +directorio del proyecto mediante el comando aecd:
 +
 +<code>
 +pruebas2@chupete:~> aecd -p hello_world.1.0 -c 10
 +aegis: project "hello_world.1.0": change 10: change directory
 +        /home/pruebas2/hello_world.1.0.C010 complete
 +</code>
 +
 +
 +Nuestro paso siguiente será hacer una copia completa del proyecto 
 +"hello_world" tal y como lo hemos creado con kdeveloper.
 +
 +<code>
 +pruebas2@chupete:~> ls
 +hello_world  hello_world.1.0.C010
 +pruebas2@chupete:~> cd hello_world.1.0.C010/
 +pruebas2@chupete:~/hello_world.1.0.C010> ls
 +aegis.log
 +pruebas2@chupete:~/hello_world.1.0.C010> cp ../hello_world/* . -R
 +</code>
 +
 +Añadiremos al proyecto todos los ficheros como nuevos: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> ls
 +AUTHORS        Makefile.in   aegis.log       configure.files      libtool
 +COPYING        README        autom4te.cache  configure.in         stamp-h.in
 +ChangeLog      TODO          config.h        configure.in.in      stamp-h1
 +INSTALL        acinclude.m4  config.h.in     hello_world          subdirs
 +Makefile       aclocal.m4    config.log      hello_world.kdevprj
 +Makefile.am    admin         config.status   hello_world.kdevses
 +Makefile.dist  aegis.conf    configure       hello_world.lsm
 +pruebas2@chupete:~/hello_world.1.0.C010> foreach i ( * )
 +foreach? aenf $i -p hello_world.1.0 -c 10
 +foreach? end
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file AUTHORS completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file COPYING completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file ChangeLog completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +....
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file hello_world.kdevses
 +        completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file hello_world.lsm completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file libtool completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file stamp-h.in completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file stamp-h1 completed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file subdirs completed
 +pruebas2@chupete:~/hello_world.1.0.C010>
 +</code>
 +
 +El manual de usuario dice que se tiene que aportar un fichero aegis.conf 
 +en la primera versión del proyecto, donde se registra cómo se construye 
 +el proyecto, qué herramientas de control de versiones se están utilizando, 
 +etc. 
 +
 +Sin embargo, los pasos para crearlo no están muy claros; nosotros hemos 
 +optado por copiar de /usr/doc/aegis/examples/config.example las partes 
 +que pueden sernos de utilidad: 
 +
 +<code>
 +pruebas2@chupete:/usr/doc/aegis/examples/config.example> cat make rcs diff >~/aegis.conf
 +</code>
 +
 +En principio, daremos los valores por defecto como válidos. El uso de make 
 +con aegis requiere de algunos "trucos" según su autor -que recomienda que 
 +en su lugar usemos la herramienta cook que se integra mejor con aegis-. 
 +
 +No obstante, para los proyectos autoconf y automake hace una salvedad.
 +En el manual de usuario, en la página 79 pueden leerse las instrucciones 
 +concretas para llevar a cabo esta fase para el caso de proyectos que 
 +usen Autoconf, como el nuestro. 
 +
 +El primer paso es reemplazar build_command por "exit 0" en el fichero de 
 +configuración "aegis.conf" para que añada todos los ficheros al proyecto 
 +sin construir:
 +
 +<code>
 +/*
 + * The build_command field of the project config file is used to invoke the
 + * relevant build command.  This command tells make where to find the rules.
 + * The ${s Makefile} expands to a path into the baseline during development
 + * if the file is not in the change.  Look in aesub(5) for more information
 + * about command substitutions.
 + */
 +build_command = "exit 0";
 +
 +/*  "gmake -f ${s Makefile} project=$p change=$c version=$v"; */
 +</code>
 +
 +Y el siguiente paso consistirá en informar a Aegis de este nuevo fichero. 
 +Tras varias pruebas infructuosas, nos dimos cuenta que el nombre que 
 +aegis espera para este fichero no es "aegis.conf" sino "config", así que 
 +procedemos a renombrarlo e informar a Aegis del nuevo fichero que hay en 
 +el proyecto:  
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> aenf -p hello_world.1.0 -c 10 config
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: new file aegis.conf completed
 +</code>
 +
 +El mensaje que salía puede verse aquí:
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> aeb -p hello_world.1.0 -c 10
 +aegis: project "hello_world.1.0": change 10: you must create a "config" file
 +pruebas2@chupete:~/hello_world.1.0.C010>
 +</code>
 +
 +
 +Una vez introducidos todos los cambios, procedemos a construir el proyecto. 
 +Como es la primera vez, hemos puesto "exit 0" en el comando de construcción
 +para que nos permita realizarlo sin necesidad de hacer una construcción 
 +real del mismo: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> aeb -p hello_world.1.0 -c 10
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C010/aegis.log
 +aegis: project "hello_world.1.0": change 10: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 10: development build started
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 10: development build complete
 +</code>
 +
 +El siguiente paso es indicar a Aegis que el desarrollo a finalizado. Primero 
 +generamos los ficheros de diferencias, que se usan para las revisiones.  
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> aed
 +[....]
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas2/hello_world.1.0.C010/
 +        hello_world/main.cpp > /home/pruebas2/hello_world.1.0.C010/hello_world/
 +        main.cpp,D; test $? -le 1
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas2/hello_world.1.0.C010/
 +        hello_world/main.o > /home/pruebas2/hello_world.1.0.C010/hello_world/
 +        main.o,D; test $? -le 1
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas2/hello_world.1.0.C010/
 +        hello_world/templates/cpp_template >
 +        /home/pruebas2/hello_world.1.0.C010/hello_world/templates/cpp_template,
 +        D; test $? -le 1
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas2/hello_world.1.0.C010/
 +        hello_world/templates/header_template >
 +        /home/pruebas2/hello_world.1.0.C010/hello_world/templates/header_
 +        template,D; test $? -le 1
 +[....]
 +</code>
 +
 +Tras esta operación se crearán numerosos ficheros con extensión *,D -uno 
 +por cada fichero que se ha cambiado o creado-. Estos ficheros de 
 +diferencias permiten ver a los revisores e integradores qué se ha hecho
 +en cada cambio. 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> ls
 +AUTHORS          README          config.h,        hello_world.kdevprj
 +AUTHORS,       README,       config.h.in        hello_world.kdevprj,D
 +COPYING          TODO            config.h.in,     hello_world.kdevses
 +COPYING,       TODO,         config.log         hello_world.kdevses,D
 +ChangeLog        acinclude.m4    config.log,      hello_world.lsm
 +ChangeLog,     acinclude.m4, config.status      hello_world.lsm,D
 +INSTALL          aclocal.m4      config.status,   libtool
 +INSTALL,       aclocal.m4,   configure          libtool,D
 +Makefile         admin           configure,       stamp-h.in
 +Makefile,      aegis.conf      configure.files    stamp-h.in,D
 +Makefile.am      aegis.conf,   configure.files, stamp-h1
 +Makefile.am,   aegis.log       configure.in       stamp-h1,D
 +Makefile.dist    autom4te.cache  configure.in,    subdirs
 +Makefile.dist, config          configure.in.in    subdirs,D
 +Makefile.in      config,       configure.in.in,D
 +Makefile.in,   config.h        hello_world
 +</code>
 +
 +Y finalmente damos por finalizado el cambio: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C010> aede
 +aegis: project "hello_world.1.0": change 10: development completed
 +</code>
 +
 +Con lo que queda finalizado el primer cambio. 
 +
 +
 +=== La revisión e integración del primer cambio ===
 +
 +A diferencia de otros sistemas de control de código fuente, Aegis exige 
 +que los cambios que se realizan estén revisados y e integrados. 
 +
 +== La revisión del cambio ==
 +
 +En primer lugar entraremos como el revisor y daremos por válido el 
 +cambio realizado en el proyecto. Comenzaremos por visualizar los cambios
 +que tenemos pendientes para revisar: 
 +
 +<code>
 +pruebas3@chupete:~> aerpass -l -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes being_reviewed                         Fri Jul 29 17:44:00 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  10    being_reviewed  Comenzamos con el proyecto
 +</code>
 +
 +El siguiente paso será cambiar al directorio donde se ha realizado el 
 +cambio, que está dentro del directorio "home" del usuario "pruebas2":  
 +
 +<code>
 +pruebas3@chupete:~> aecd -p hello_world.1.0 10
 +aegis: project "hello_world.1.0": change 10: change directory
 +        /home/pruebas2/hello_world.1.0.C010 complete
 +pruebas3@chupete:/home/pruebas2/hello_world.1.0.C010>
 +</code>
 +
 +
 +Para que el grupo de desarrollo pueda trabajar dentro del directorio 
 +del cambio, aegis establece los permisos adecuados del subdirectorio 
 +que contiene el cambio, hello_world.1.0.C010. 
 +
 +En concreto, establece el 'sticky bit' del grupo al que pertenece el 
 +equipo de desarrollo, que en este caso es "users".
 +
 +El comando ''aedmore'' nos permite revisar todos los cambios que se han 
 +realizado de una forma rápida: 
 +
 +<code>
 +pruebas3@chupete:/home/pruebas2/hello_world.1.0.C010> aedmore
 +::::::::::::::
 +./AUTHORS,D
 +::::::::::::::
 +--- /dev/null   Fri Nov 19 23:44:21 2004
 ++++ /home/pruebas2/hello_world.1.0.C010/AUTHORS Thu Jul 21 13:02:18 2005
 +@@ -0,0 +1 @@
 ++Raul Luna Rodriguez <rluna@tas.raulluna.net>
 +--More--(Next file: ./COPYING,D)
 +</code>
 +
 +
 +Y finalmente, si estimamos que el cambio se ha realizado correctamente, 
 +damos por válido el mismo: 
 +
 +<code>
 +pruebas3@chupete:/home/pruebas2/hello_world.1.0.C010> aerpass -p hello_world.1.0 10
 +aegis: project "hello_world.1.0": change 10: review pass complete
 +</code>
 +
 +Si consideraramos que el cambio no cumple algún requisito y quisieramos 
 +rechazarlo, utilizaríamos el comando "aerfail"
 +
 +== La integración del cambio ==
 +
 +Para ello entraremos como integrador -usuario "pruebas4"- y comenzaremos por 
 +revisar qué cambios están pendientes de integración: 
 +
 +<code>
 +pruebas4@chupete:~> aeib -l -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes awaiting_integration                   Fri Jul 29 18:26:09 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  10    awaiting_       Comenzamos con el proyecto
 +        integration
 +</code>
 +
 +Pasamos al directorio donde están los ficheros del cambio: 
 +
 +<code>
 +pruebas4@chupete:~> aecd -p hello_world.1.0 10
 +aegis: project "hello_world.1.0": change 10: change directory
 +        /home/pruebas2/hello_world.1.0.C010 complete
 +</code>
 +
 +Comenzamos por informar a aegis que la integración del cambio ha comenzado: 
 +
 +<code>
 +pruebas4@chupete:/home/pruebas2/hello_world.1.0.C010> aeib -p hello_world.1.0 10
 +aegis: project "hello_world.1.0": change 10: link baseline to integration
 +        directory
 +aegis: project "hello_world.1.0": change 10: apply change to integration
 +        directory
 +aegis: logging to /aegis/hello_world/branch.1/branch.0/delta685.001/aegis.log
 +aegis: project "hello_world.1.0": change 10: integrate begin complete
 +</code>
 +
 +El integrador debe construir y probar cada cambio: 
 +
 +<code>
 +pruebas4@chupete:/home/pruebas2/hello_world.1.0.C010> aeb
 +aegis: logging to /aegis/hello_world/branch.1/branch.0/delta685.001/aegis.log
 +aegis: project "hello_world.1.0": change 10: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 10: integration build started
 +aegis: cd /aegis/hello_world/branch.1/branch.0/delta685.001
 +aegis: user "pruebas1", group "users"
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 10: integration build complete
 +aegis: project "hello_world.1.0": change 10: removing symbolic links to
 +        baseline
 +pruebas4@chupete:/home/pruebas2/hello_world.1.0.C010> aet
 +pruebas4@chupete:/home/pruebas2/hello_world.1.0.C010>
 +</code>
 +
 +Y finalmente ya podemos dar la fase de integración como "pasada" y con ello 
 +el cambio formará parte de la nueva línea base del proyecto: 
 +
 +<code>
 +pruebas4@chupete:/home/pruebas2> aeipass -p hello_world.1.0 10
 +[un montón de información sobre la integración]
 +</code>
 +
 +Como efecto "colateral" el directorio del cambio se ha borrado, y este cambio
 +figura ya como completado: 
 +
 +<code>
 +pruebas1@chupete:~> ael c -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes                                        Fri Jul 29 18:37:51 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  10    completed       Comenzamos con el proyecto
 +</code>
 +
 +=== Efectuando un cambio concurrente ===
 +
 +Vamos a ver cómo gestiona Aegis aquellos cambios que afecten a la misma 
 +línea de código. Para ello designaremos a pruebas2 y pruebas3 como 
 +desarrolladores y les asignaremos un cambio, de tal forma que las
 +modificaciones que harán al proyecto afectarán a la misma línea de 
 +código. 
 +
 +== El administrador añade un nuevo desarrollador al proyecto ==
 +
 +<code>
 +pruebas1@chupete:~> aend pruebas3 -p hello_world.1.0
 +aegis: project "hello_world.1.0": new developer pruebas3 complete
 +</code>
 +
 +== El administrador crea dos cambios nuevos ==
 +
 +Se crearán dos cambios nuevos, uno para el desarrollador pruebas2 
 +y otro para el desarrollador pruebas3: 
 +
 +<code>
 +pruebas1@chupete:~> aenc -edit -p hello_world.1.0
 +/*
 +** Project hello_world.1.0
 +** nc dflt hint
 +*/
 +brief_description = "Change the 'hello world' line of main.cpp.";
 +description = "Change for developer pruebas2";
 +cause = internal_bug;
 +test_exempt = false;
 +test_baseline_exempt = false;
 +regression_test_exempt = true;
 +architecture =
 +[
 +        "unspecified",
 +];
 +~
 +~
 +~
 +/home/pruebas1/aegis-1766-1: 14 lines, 298 characters.
 +aegis: project "hello_world.1.0": change 11: new change complete
 +</code>
 +
 +Y el cambio para el usuario pruebas3: 
 +
 +<code>
 +pruebas1@chupete:~> aenc -edit -p hello_world.1.0
 +/*
 +** Project hello_world.1.0
 +** nc dflt hint
 +*/
 +brief_description = "Change the 'Hello World' line of the main program.";
 +description = "Change for developer pruebas3";
 +cause = internal_bug;
 +test_exempt = false;
 +test_baseline_exempt = false;
 +regression_test_exempt = true;
 +architecture =
 +[
 +        "unspecified",
 +];
 +~
 +/home/pruebas1/aegis-1777-1: 14 lines, 306 characters.
 +aegis: project "hello_world.1.0": change 12: new change complete
 +</code>
 +
 +== El primer desarrollador lleva a cabo el cambio  ==
 +
 +Igual que en otros casos, comenzamos por visualizar los cambios pendientes: 
 +
 +<code>
 +pruebas2@chupete:~> aedb -l -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes awaiting_development                   Fri Jul 29 13:43:28 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  11    awaiting_       Change the 'hello world' line of main.cpp.
 +        development
 +  12    awaiting_       Change the 'Hello World' line of the main program.
 +        development
 +</code>
 +
 +Y a continuación nos apropiamos del cambio 11: 
 +
 +<code>
 +pruebas2@chupete:~> aedb -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: development directory
 +        "/home/pruebas2/hello_world.1.0.C011"
 +aegis: logging to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: project "hello_world.1.0": change 11: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 11: develop begin complete
 +</code>
 +
 +Y comenzamos el proceso de desarrollo haciendo un "cd" al directorio con 
 +los cambios que se nos ha creado: 
 +
 +<code>
 +pruebas2@chupete:~> aecd -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: change directory
 +        /home/pruebas2/hello_world.1.0.C011 complete
 +pruebas2@chupete:~/hello_world.1.0.C011>
 +</code>
 +
 +El directorio de cambios ahora creado es muy distinto del original: todos 
 +los ficheros son enlaces simbólicos a los correspondientes ficheros que 
 +quedaron almacenados en el cambio anterior: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> ls -la | more
 +total 24
 +drwxr-sr-x    5 pruebas2 users        4096 Jul 29 18:46 .
 +drwxr-xr-x    4 pruebas2 users        4096 Jul 29 18:46 ..
 +lrwxrwxrwx    1 pruebas2 users          53 Jul 29 18:46 AUTHORS -> /aegis/hello_world/branch.1/branch.0/baseline/AUTHORS
 +lrwxrwxrwx    1 pruebas2 users          55 Jul 29 18:46 AUTHORS,D -> /aegis/hello_world/branch.1/branch.0/baseline/AUTHORS,D
 +lrwxrwxrwx    1 pruebas2 users          53 Jul 29 18:46 COPYING -> /aegis/hello_world/branch.1/branch.0/baseline/COPYING
 +lrwxrwxrwx    1 pruebas2 users          55 Jul 29 18:46 COPYING,D -> /aegis/hello_world/branch.1/branch.0/baseline/COPYING,D
 +[....]
 +</code>
 +
 +Aquellos ficheros que vayan a ser modificados han de ser "copiados" en 
 +local mediante el comando aecp -obviamente, existen los comandos aerm 
 +y aemv, además de sus correspondientes comandos "u" aecpu, aermu, aemvu
 +para deshacerlos-.
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aecp hello_world/main.cpp
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: project "hello_world.1.0": change 11: copied hello_world/main.cpp
 +aegis: project "hello_world.1.0": change 11: copy file complete
 +</code>
 +
 +Y ahora sí procedemos a realizar el cambio: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Seguimos manteniendo las fases de construcción y realización de test como 
 +triviales para mantener las pruebas dentro de unos cauces de simplicidad 
 +razonables. De este modo, indicamos a Aegis que construya el proyecto:
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aeb
 +aegis: logging to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: project "hello_world.1.0": change 11: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 11: development build started
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 11: development build complete
 +</code>
 +
 +
 +Que cree los ficheros de diferencias: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aed
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: set +e; diff -U10 -a /aegis/hello_world/branch.1/branch.0/baseline/
 +        hello_world/main.cpp /home/pruebas2/hello_world.1.0.C011/hello_world/
 +        main.cpp > /home/pruebas2/hello_world.1.0.C011/hello_world/main.cpp,D;
 +        test $? -le 1
 +aegis: project "hello_world.1.0": change 11: difference complete
 +</code>
 +
 +Creamos un test para el cambio que acabamos de hacer. Ya que no vamos 
 +a construir el programa, simplemente verificaremos que la línea en 
 +cuestión se encuentra en el fichero main.cpp: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aent
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: project "hello_world.1.0": change 11: new test test/00/t0001a.sh
 +        complete
 +pruebas2@chupete:~/hello_world.1.0.C011> joe test/00/t0001a.sh
 +#!/bin/sh
 +
 +tmp=/tmp/$$
 +here=$(pwd)
 +
 +fail()
 +{
 +  echo FAILED 1>&2
 +  cd $here
 +  rm -rf $tmp
 +  exit 1
 +}
 +
 +pass()
 +{
 +  cd $here
 +  rm -rf $tmp
 +  exit 0
 +}
 +
 +trap "fail" 1 2 3 15
 +
 +
 +# comprobamos que el fichero "main.cpp" contiene
 +# efectivamente el cambio introducido ya que
 +# de lo contrario el test de regresion no '
 +# falla
 +
 +grep 'Cambio realizado por el usuario pruebas2' hello_world/main.cpp
 +
 +if [ $? -ne 0 ]
 +then
 +  fail
 +fi
 +
 +# si llega hasta aquí, es que ha funcionado
 +pass
 +</code>
 +
 +Probamos el test: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aet
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: /bin/sh /home/pruebas2/hello_world.1.0.C011/test/00/t0001a.sh
 +  cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
 +aegis: project "hello_world.1.0": change 11: test/00/t0001a.sh pass
 +aegis: project "hello_world.1.0": change 11: passed 1 tests
 +pruebas2@chupete:~/hello_world.1.0.C011> aet -bl
 +aegis: appending log to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: cd /aegis/hello_world/branch.1/branch.0/baseline
 +aegis: /bin/sh /home/pruebas2/hello_world.1.0.C011/test/00/t0001a.sh
 +FAILED
 +aegis: project "hello_world.1.0": change 11: test/00/t0001a.sh baseline fail,
 +        good
 +aegis: project "hello_world.1.0": change 11: passed 1 tests
 +</code>
 +
 +
 +E indicamos la finalización del cambio: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world.1.0.C011> aed
 +aegis: logging to /home/pruebas2/hello_world.1.0.C011/aegis.log
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas2/hello_world.1.0.C011/test/
 +        00/t0001a.sh > /home/pruebas2/hello_world.1.0.C011/test/00/t0001a.sh,D;
 +        test $? -le 1
 +aegis: project "hello_world.1.0": change 11: difference complete
 +pruebas2@chupete:~/hello_world.1.0.C011> aede
 +aegis: project "hello_world.1.0": change 11: development completed
 +</code>
 +
 +
 +== El segundo desarrollador lleva a cabo el cambio ==
 +
 +El proceso es básicamente el mismo que en el caso anterior: 
 +
 +Nos "apropiamos" del cambio 12: 
 +
 +<code>
 +pruebas3@chupete:~> aedb -p hello_world.1.0 12
 +aegis: project "hello_world.1.0": change 12: development directory
 +        "/home/pruebas3/hello_world.1.0.C012"
 +aegis: logging to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 12: develop begin complete
 +</code>
 +
 +Hacemos "cd" al directorio del proyecto: 
 +
 +<code>
 +pruebas3@chupete:~> aecd -p hello_world.1.0 12
 +aegis: project "hello_world.1.0": change 12: change directory
 +        /home/pruebas3/hello_world.1.0.C012 complete
 +</code>
 +
 +Copiamos el directorio "main.cpp" al directorio del proyecto: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aecp hello_world/main.cpp
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: copied hello_world/main.cpp
 +aegis: project "hello_world.1.0": change 12: copy file complete
 +</code>
 +
 +Y procedemos a realizar el cambio: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> joe hello_world/main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + cambio introducido por pruebas3" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Como Aegis lo exige, creamos un test para este cambio: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aent -p hello_world.1.0 12
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: new test test/00/t0002a.sh
 +        complete
 +</code>
 +
 +Y lo completamos con el siguiente script: 
 +
 +<code>
 +#!/bin/sh
 +
 +tmp=/tmp/$$
 +here=$(pwd)
 +
 +fail()
 +{
 +  echo FAILED 1>&2
 +  cd $here
 +  rm -rf $tmp
 +  exit 1
 +}
 +
 +pass()
 +{
 +  cd $here
 +  rm -rf $tmp
 +  exit 0
 +}
 +
 +trap "fail" 1 2 3 15
 +
 +
 +# comprobamos que el fichero "main.cpp" contiene
 +# efectivamente el cambio introducido ya que
 +# de lo contrario el test de regresion no '
 +# falla
 +
 +
 +grep 'cambio introducido por pruebas3' hello_world/main.cpp
 +
 +if [ $? -ne 0 ]
 +then
 +  fail
 +fi
 +
 +pass
 +</code>
 +
 +
 +Construimos el proyecto (esta fase se sigue manteniendo trivial y en realidad
 +sólo realiza un "exit 0"): 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aeb
 +aegis: logging to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 12: development build started
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 12: development build complete
 +</code>
 +
 +Y ejecutamos los test: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aet
 +aegis: logging to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: /bin/sh /home/pruebas3/hello_world.1.0.C012/test/00/t0002a.sh
 +  cout << "Hello, World! + cambio introducido por pruebas3" << endl;
 +aegis: project "hello_world.1.0": change 12: test/00/t0002a.sh pass
 +aegis: project "hello_world.1.0": change 12: passed 1 tests
 +pruebas3@chupete:~/hello_world.1.0.C012> aet -bl
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: cd /aegis/hello_world/branch.1/branch.0/baseline
 +aegis: /bin/sh /home/pruebas3/hello_world.1.0.C012/test/00/t0002a.sh
 +FAILED
 +aegis: project "hello_world.1.0": change 12: test/00/t0002a.sh baseline fail,
 +        good
 +aegis: project "hello_world.1.0": change 12: passed 1 tests
 +</code>
 +
 +Creamos los ficheros de diferencias: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aed
 +aegis: logging to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: set +e; diff -U10 -a /aegis/hello_world/branch.1/branch.0/baseline/
 +        hello_world/main.cpp /home/pruebas3/hello_world.1.0.C012/hello_world/
 +        main.cpp > /home/pruebas3/hello_world.1.0.C012/hello_world/main.cpp,D;
 +        test $? -le 1
 +aegis: set +e; diff -U10 -a /dev/null /home/pruebas3/hello_world.1.0.C012/test/
 +        00/t0002a.sh > /home/pruebas3/hello_world.1.0.C012/test/00/t0002a.sh,D;
 +        test $? -le 1
 +aegis: project "hello_world.1.0": change 12: difference complete
 +</code>
 +
 +Y finalizamos el desarrollo: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aede
 +aegis: project "hello_world.1.0": change 12: file "hello_world/main.cpp" locked
 +        for change 11
 +aegis: project "hello_world.1.0": change 12: develop end fail
 +</code>
 +
 +Pero no nos deja finalizar el desarrollo porque el fichero "main.cpp" estaba 
 +bloqueado por el cambio del usuario anterior: habrá de ser revisado y 
 +aprobado el cambio anterior para poder continuar con nuestro cambio. 
 +
 +
 +== Se revisa e integra el cambio del primer desarrollador ==
 +
 +Debido a que el cambio que hemos hecho en el fichero main.cpp en el 
 +primer cambio ha bloqueado este fichero y nos impide continuar con el 
 +cambio del segundo desarrollador, procederemos a revisar e integrar 
 +el primer cambio antes de continuar. 
 +
 +NOTA: El usuario "pruebas3" que se ha usado en estos ejemplos es a 
 +la vez desarrollador y revisor: en este apartado actúa como revisor. 
 +
 +**Revisión del cambio **
 +
 +Comenzamos por ver los cambios pendientes de revisión que tenemos: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aerpass -l -p hello_world.1.0
 +
 +Project "hello_world.1.0"                                                Page 1
 +List of Changes being_reviewed                         Sat Jul 30 13:06:13 2005
 +
 +Change  State           Description
 +------- -------         -------------
 +  11    being_reviewed  Change the 'hello world' line of main.cpp.
 +</code>
 +
 +Y pasamos al directorio del cambio 11: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aecd -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: change directory
 +        /home/pruebas2/hello_world.1.0.C011 complete
 +</code>
 +
 +Y le damos como válido: 
 +
 +<code>
 +pruebas3@chupete:/home/pruebas2/hello_world.1.0.C011> aerpass -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: review pass complete
 +</code>
 +
 +**Integración del cambio**
 +
 +Comenzamos la integración del cambio: 
 +
 +<code>
 +pruebas4@chupete:~> aeib -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: link baseline to integration
 +        directory
 +aegis: project "hello_world.1.0": change 11: apply change to integration
 +        directory
 +aegis: logging to /aegis/hello_world/branch.1/branch.0/delta568.002/aegis.log
 +aegis: project "hello_world.1.0": change 11: integrate begin complete
 +pruebas4@chupete:~> aecd -p hello_world.1.0 11
 +aegis: project "hello_world.1.0": change 11: change directory
 +        /aegis/hello_world/branch.1/branch.0/delta568.002 complete
 +</code>
 +
 +Construimos: 
 +
 +<code>
 +pruebas4@chupete:/aegis/hello_world/branch.1/branch.0/delta568.002> aeb
 +aegis: logging to /aegis/hello_world/branch.1/branch.0/delta568.002/aegis.log
 +aegis: project "hello_world.1.0": change 11: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 11: integration build started
 +aegis: user "pruebas1", group "users"
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 11: integration build complete
 +aegis: project "hello_world.1.0": change 11: removing symbolic links to
 +        baseline
 +</code>
 +
 +Probamos: 
 +
 +<code>
 +pruebas4@chupete:/aegis/hello_world/branch.1/branch.0/delta568.002> aet
 +aegis: appending log to /aegis/hello_world/branch.1/branch.0/delta568.002/
 +        aegis.log
 +aegis: /bin/sh /aegis/hello_world/branch.1/branch.0/delta568.002/test/00/
 +        t0001a.sh
 +  cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
 +aegis: project "hello_world.1.0": change 11: test/00/t0001a.sh pass
 +aegis: project "hello_world.1.0": change 11: passed 1 tests
 +pruebas4@chupete:/aegis/hello_world/branch.1/branch.0/delta568.002> aet -bl
 +aegis: appending log to /aegis/hello_world/branch.1/branch.0/delta568.002/
 +        aegis.log
 +aegis: cd /aegis/hello_world/branch.1/branch.0/baseline
 +aegis: /bin/sh /aegis/hello_world/branch.1/branch.0/delta568.002/test/00/
 +        t0001a.sh
 +FAILED
 +aegis: project "hello_world.1.0": change 11: test/00/t0001a.sh baseline fail,
 +        good
 +aegis: project "hello_world.1.0": change 11: passed 1 tests
 +</code>
 +
 +
 +Generamos los ficheros de diferencias: 
 +
 +<code>
 +pruebas4@chupete:/aegis/hello_world/branch.1/branch.0/delta568.002> aed
 +aegis: appending log to /aegis/hello_world/branch.1/branch.0/delta568.002/
 +        aegis.log
 +aegis: project "hello_world.1.0": change 11: difference complete
 +</code>
 +
 +Y damos la integración por finalizada, constituyendo una nueva "baseline"
 +del cambio: 
 +
 +<code>
 +pruebas4@chupete:/aegis/hello_world/branch.1> aeipass
 +aegis: appending log to /aegis/hello_world/branch.1/branch.0/delta568.002/
 +        aegis.log
 +aegis: cd /aegis/hello_world/history
 +aegis: user "pruebas1", group "users"
 +aegis: ci -u -d -M -m'(1.0.D002) Change the '"'hello world' line of main.cpp."
 +        -wpruebas2 -t/dev/null /tmp/aegis-617-2/main.cpp
 +        /aegis/hello_world/history/hello_world/main.cpp,v; rcs -U
 +        /aegis/hello_world/history/hello_world/main.cpp,v
 +/aegis/hello_world/history/hello_world/main.cpp, <--  /tmp/aegis-617-2/main.cpp
 +new revision: 1.2; previous revision: 1.1
 +done
 +RCS file: /aegis/hello_world/history/hello_world/main.cpp,v
 +done
 +aegis: ( rlog -r /aegis/hello_world/history/hello_world/main.cpp,v | awk
 +        '/^head:/ {print $2}' ) > /tmp/aegis-617-3
 +aegis: ci -u -d -M -m'(1.0.D002) Change the '"'hello world' line of main.cpp."
 +        -wpruebas2 -t/dev/null /tmp/aegis-617-2/main.cpp
 +        /aegis/hello_world/history/hello_world/main.cpp,v; rcs -U
 +        /aegis/hello_world/history/hello_world/main.cpp,v
 +/aegis/hello_world/history/hello_world/main.cpp, <--  /tmp/aegis-617-2/main.cpp
 +new revision: 1.2; previous revision: 1.1
 +done
 +RCS file: /aegis/hello_world/history/hello_world/main.cpp,v
 +done
 +aegis: ( rlog -r /aegis/hello_world/history/hello_world/main.cpp,v | awk
 +        '/^head:/ {print $2}' ) > /tmp/aegis-617-3
 +aegis: ci -u -d -M -m'(1.0.D002) Change the '"'hello world' line of main.cpp."
 +        -wpruebas2 -t/dev/null /tmp/aegis-617-2/t0001a.sh
 +        /aegis/hello_world/history/test/00/t0001a.sh,v; rcs -U
 +        /aegis/hello_world/history/test/00/t0001a.sh,v
 +/aegis/hello_world/history/test/00/t0001a.sh, <--  /tmp/aegis-617-2/t0001a.sh
 +initial revision: 1.1
 +done
 +RCS file: /aegis/hello_world/history/test/00/t0001a.sh,v
 +done
 +aegis: ( rlog -r /aegis/hello_world/history/test/00/t0001a.sh,v | awk '/^head:/
 +        {print $2}' ) > /tmp/aegis-617-4
 +aegis: project "hello_world.1.0": change 11: adjust file modification times
 +aegis: project "hello_world.1.0": change 11: discard old directories
 +aegis: warning: file times in future
 +aegis: project "hello_world.1.0": change 11: integrate pass complete
 +</code>
 +
 +Ahora es el momento de que el segundo desarrollador pueda completar 
 +el cambio que estaba haciendo y que no pudo continuar. 
 +
 +== El segundo desarrollador completa el cambio  ==
 +
 +Al intentar finalizar el cambio, se nos comunica que la baseline del proyecto
 +ha cambiado: 
 +
 +<code>
 +pruebas3@chupete:/home/pruebas2/hello_world.1.0.C011> aede
 +aegis: project "hello_world.1.0": change 12: baseline hello_world/main.cpp
 +        changed
 +aegis: project "hello_world.1.0": change 12: develop end fail
 +</code>
 +
 +Por lo que es necesario re-copiar main.cpp: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aecpu hello_world/main.cpp
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: hello_world/main.cpp gone
 +pruebas3@chupete:~/hello_world.1.0.C012> aecp hello_world/main.cpp
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: copied hello_world/main.cpp
 +aegis: project "hello_world.1.0": change 12: copy file complete
 +</code>
 +
 +Y ahora, al realizar el cambio, ya vemos el cambio que el otro 
 +desarrollador hizo: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Insertaremos nuestro propio cambio: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
 +  cout << "cambio introducido por pruebas3" << endl;
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Y ahora sí que podremos finalizar nuestro desarrollo. Construir, probar y 
 +diferencias: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aeb
 +aegis: logging to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: project "hello_world.1.0": change 12: creating symbolic links to
 +        baseline
 +aegis: project "hello_world.1.0": change 12: development build started
 +aegis: exit 0
 +aegis: project "hello_world.1.0": change 12: development build complete
 +pruebas3@chupete:~/hello_world.1.0.C012> aet
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: /bin/sh /home/pruebas3/hello_world.1.0.C012/test/00/t0002a.sh
 +  cout << "cambio introducido por pruebas3" << endl;
 +aegis: project "hello_world.1.0": change 12: test/00/t0002a.sh pass
 +aegis: project "hello_world.1.0": change 12: passed 1 tests
 +pruebas3@chupete:~/hello_world.1.0.C012> aet -bl
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: cd /aegis/hello_world/branch.1/branch.0/baseline
 +aegis: /bin/sh /home/pruebas3/hello_world.1.0.C012/test/00/t0002a.sh
 +FAILED
 +aegis: project "hello_world.1.0": change 12: test/00/t0002a.sh baseline fail,
 +        good
 +aegis: project "hello_world.1.0": change 12: passed 1 tests
 +pruebas3@chupete:~/hello_world.1.0.C012> aed
 +aegis: appending log to /home/pruebas3/hello_world.1.0.C012/aegis.log
 +aegis: set +e; diff -U10 -a /aegis/hello_world/branch.1/branch.0/baseline/
 +        hello_world/main.cpp /home/pruebas3/hello_world.1.0.C012/hello_world/
 +        main.cpp > /home/pruebas3/hello_world.1.0.C012/hello_world/main.cpp,D;
 +        test $? -le 1
 +aegis: project "hello_world.1.0": change 12: difference complete
 +</code>
 +
 +Y finalizar el desarrollo: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world.1.0.C012> aede
 +aegis: project "hello_world.1.0": change 12: development completed
 +</code>
 +
 +=== Conclusiones  ===
 +
 +Más que un sistema de control de código fuente, Aegis permite implantar una 
 +metodología de desarrollo en un equipo de trabajo. El sistema exige que todos
 +los cambios que se realicen en el software vayan acompañados de un test que 
 +los compruebe (y que deberá fallar en las versiones anteriores del mismo
 +software). De esta forma, se pretende mejorar la calidad del software 
 +producido. 
 +
 +El sistema además establece tres perfiles en el proceso de desarrollo de 
 +software: el desarrollador, que se ocupa fundamentalmente de realizar los 
 +cambios y los test que los acompañan, el revisor, que se ocupa de verificar
 +la calidad de los desarrollos y los test realizados y el integrador, que 
 +da la revisión final antes de incorporarlos definitivamente al proyecto. 
 +
 +Aegis se encarga de mantener siempre una "línea base" de proyecto que contiene
 +todos los cambios realizados, probados, revisados e integrados, de tal modo que 
 +se trataría en todo momento de una versión "que funciona" y que de esta forma
 +está siempre disponible para trabajar. 
 +
 +Sin embargo, la metodología de trabajo que propone Aegis parece más adecuada 
 +para un entorno empresarial que para grupos de donde no hay una división tan 
 +clara del trabajo. Igualmente, se trata de un software ideal para aquellos 
 +grupos de trabajo que deseen implantar un método de trabajo así, pero en 
 +absoluto es adecuado para aquellos que tengan otros modelos de desarrollo. 
 +Es decir, que la herramienta exige de un cambio en la organización y forma 
 +de hacer el trabajo, y este hecho ha de ser tenido en cuenta a la hora de 
 +adoptarlo. 
 +
 +Por otra parte, el hecho de que todo el trabajo deba hacerse on-line, tampoco
 +favorece la dispersión geográfica de los miembros del equipo ya que implicaría
 +que éstos debieran estar conectados al servidor continuamente para poder 
 +realizar los cambios.
 +
 +Como hecho muy favorable hay que comentar que, al exigir que siempre se realice
 +un test para cada cambio que se haga, mejora enormemente la calidad del 
 +software que se produce; aunque por otra parte debería haber una forma de 
 +saltarse esta comprobación ya que hay errores que se detectan (un fallo de 
 +memoria, por ejemplo) que no siempre tienen un test que lo pueda evidenciar.
 +
 +La documentación no me ha parecido especialmente sencilla de leer, ya que 
 +hay que leersela prácticamente al completo para hacerse una idea de lo que 
 +el programa hace, y cómo hacerlo. Hay aspectos -como la creación del fichero 
 +de configuración- que son realmente oscuros, y no estaría de más un asistente
 +o algo así para facilitar su creación. 
 +
 +Este hecho se evidencia en que la curva de aprendizaje del programa es alta; 
 +por una parte todo se maneja en línea de comandos y los comandos para su uso 
 +están más pensados en el día a día que en facilitar la vida al que está 
 +aprendiendo a usar la herramienta. 
 +
 +Además, la portabilidad hacia otras plataformas no es posible: no hay versión
 +para otros sistemas operativos que no sea linux. 
 +
 +==== CVS ====
 +
 +=== Instalación ===
 +
 +CVS ya está instalado en el sistema debian: 
 +
 +<code>
 +chupete:~# apt-get install cvs
 +Reading Package Lists... Done
 +Building Dependency Tree... Done
 +Sorry, cvs is already the newest version.
 +0 packages upgraded, 0 newly installed, 0 to remove and 1  not upgraded.
 +</code>
 +
 +== Creación del repositorio ==
 +
 +Para ello seguiremos las instrucciones dadas en la documentación de esta 
 +asignatura, con la única diferencia que el repositorio lo crearemos en 
 +''/var'' en lugar de en ''/var/lib'': 
 +
 +<code>
 +chupete:/var# umask
 +0022
 +chupete:/var# mkdir cvs
 +chupete:/var# chgrp src /var/cvs
 +chupete:/var# chmod 3775 /var/cvs
 +chupete:/var# cvs -d /var/cvs init
 +</code>
 +
 +== Haciendo una prueba sencilla  ==
 +
 +A fin de comprobar que el acceso en modo local al repositorio ya es posible, 
 +realizaremos la prueba de importar un fichero sólamente. Esta prueba la 
 +realizará el usuario "pruebas2", para lo cual habrá que añadirle al 
 +grupo de desarrolladores (src): 
 +
 +<code>
 +chupete:~# usermod -G src pruebas2
 +</code>
 +
 +Una vez hecho esto, el import funcionará perfectamente: 
 +
 +<code>
 +pruebas2@chupete:~> rm -r prueba/
 +pruebas2@chupete:~> mkdir prueba
 +pruebas2@chupete:~> echo "Esto es una prueba" > prueba/prueba.txt
 +pruebas2@chupete:~>›> cd prueba
 +pruebas2@chupete:~/prueba> setenv CVSROOT :local:/var/cvs
 +pruebas2@chupete:~/prueba> setenv CVSEDITOR /usr/bin/joe
 +pruebas2@chupete:~/prueba> cvs import prueba company_prueba start
 +File /tmp/cvsMA4ExH saved.
 +N prueba/prueba.txt
 +
 +No conflicts created by this import
 +</code>
 +
 +
 +Comprobamos que efectivamente se obtienen bien los fuentes: 
 +
 +<code>
 +pruebas2@chupete:~> mkdir prueba2
 +pruebas2@chupete:~> cd prueba2
 +pruebas2@chupete:~/prueba2> cvs co prueba
 +cvs checkout: warning: cannot write to history file /var/cvs/CVSROOT/history: Permission denied
 +cvs checkout: Updating prueba
 +U prueba/prueba.txt
 +pruebas2@chupete:~/prueba2> ls
 +prueba
 +pruebas2@chupete:~/prueba2> cd prueba
 +pruebas2@chupete:~/prueba2/prueba> ls
 +CVS  prueba.txt
 +pruebas2@chupete:~/prueba2/prueba> diff prueba.txt ../../prueba
 +prueba/  prueba2/
 +pruebas2@chupete:~/prueba2/prueba> diff prueba.txt ../../prueba/prueba.txt
 +</code>
 +
 +El mensaje de error que aparece es porque los permisos de escritura para el 
 +fichero "history" no están adecuadamente puestos: 
 +
 +<code>
 +chupete:/var/cvs/CVSROOT# ll
 +total 92
 +drwxr-sr-x    2 root     src          4096 Aug  2 20:38 Emptydir
 +-rw-r--r--    1 root     src             0 Aug  2 20:38 history
 +-rw-r--r--    1 root     src             0 Aug  2 20:38 val-tags
 +chupete:/var/cvs/CVSROOT# chmod 664 history
 +</code>
 +
 +Tras este cambio de permisos, ya funciona correctamente.
 +
 +
 +=== Realizando el primer cambio ===
 +
 +Al igual que en los otros casos, comenzaremos por realizar una importación 
 +del proyecto "hello world" que vamos a usar para los ejemplos y luego 
 +realizar un cambio en el mismo para reflejarlo en el repositorio. 
 +
 +
 +<code>
 +pruebas2@chupete:~/hello_world> cvs import -m "Creacion de directorio inicial" hello_world hello_world_company start
 +N hello_world/Makefile.am
 +N hello_world/Makefile.dist
 +N hello_world/configure.in.in
 +N hello_world/AUTHORS
 +N hello_world/COPYING
 +N hello_world/ChangeLog
 +N hello_world/INSTALL
 +N hello_world/README
 +N hello_world/TODO
 +N hello_world/hello_world.lsm
 +N hello_world/hello_world.kdevprj
 +N hello_world/hello_world/.deps/main.Po
 +[....]
 +cvs import: Importing /var/cvs/hello_world/autom4te.cache
 +N hello_world/autom4te.cache/requests
 +N hello_world/autom4te.cache/output.0
 +N hello_world/autom4te.cache/traces.0
 +</code>
 +
 +No conflicts created by this import
 +
 +Ahora ya podemos eliminar el directorio "hello_world" y realizar el 
 +primer check-out directamente del repositorio de código fuente: 
 +
 +<code>
 +pruebas2@chupete:~> rm -r hello_world
 +pruebas2@chupete:~> ls
 +aegis.conf  hello_world.tgz  prueba_orig
 +pruebas2@chupete:~> cvs co hello_world
 +cvs checkout: Updating hello_world
 +U hello_world/AUTHORS
 +U hello_world/COPYING
 +U hello_world/ChangeLog
 +[...]
 +</code>
 +
 +Ha funcionado correctamente. Ahora realizaremos un cambio en el 
 +fichero main.cpp: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +  cout << "Change made by pruebas2" << endl;
 +  cout << "Hello, World!" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Y lo guardamos en el repositorio: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world/hello_world> cvs ci
 +Checking in main.cpp;
 +/var/cvs/hello_world/hello_world/main.cpp, <--  main.cpp
 +new revision: 1.2; previous revision: 1.1
 +done
 +</code>
 +
 +=== Realizando un cambio concurrente ===
 +
 +Ahora vamos a realizar un cambio en el repositorio usando dos usuarios 
 +que se conectarán simulando una conexión remota. Estos usuarios realizarán
 +un cambio en el proyecto hello_world sobre una misma línea de código y 
 +veremos cómo trata cvs los cambios.
 +
 +== Haciendo que el repositorio sea visible públicamente ==
 +
 +El acceso remoto de los desarrolladores se realizará a través de ssh, 
 +por lo que únicamente es necesario disponer de cuenta en la máquina. 
 +Para el propósito de esta evaluación hemos creado cuatro cuentas 
 +-''pruebas1''..''pruebas4''- a las que hemos asociado al grupo ''src'': 
 +
 +<code>
 +chupete:~# usermod -G src pruebas1
 +chupete:~# usermod -G src pruebas2
 +chupete:~# usermod -G src pruebas3
 +chupete:~# usermod -G src pruebas4
 +chupete:~# more /etc/group | grep src
 +src:x:40:pruebas2,pruebas1,pruebas3,pruebas4
 +</code>
 +
 +No nos ocuparemos de habilitar el acceso público al repositorio porque
 +no aportaría nada a la presente evaluación. 
 +
 +Como el acceso que vamos a realizar es a través de ssh, ya no hay nada 
 +más que hacer porque el repositorio ya es accesible a través de estas 
 +cuentas de usuario.
 +
 +== Descargando el repositorio para trabajar con él ==
 +
 +A continuación estableceremos en la máquina remota dos variables de 
 +entorno para informar a CVS de la ubicación del repositorio, y el 
 +editor que queremos usar para editar los archivos de log: 
 +
 +<code>
 +pruebas1@chupete:~> setenv CVSROOT :ext:pruebas1@localhost:/var/cvs
 +pruebas1@chupete:~> setenv CVSEDITOR /usr/bin/joe
 +</code>
 +
 +Y seguidamente haremos el "checkout":
 +
 +<code>
 +pruebas1@chupete:~> cvs co hello_world
 +The authenticity of host 'localhost (127.0.0.1)' can't be established.
 +RSA key fingerprint is 7b:cc:f5:ce:a7:ca:5e:3c:37:50:f9:f7:a3:59:4f:0a.
 +Are you sure you want to continue connecting (yes/no)?
 +Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
 +pruebas1@localhost's password:
 +U hello_world/AUTHORS
 +U hello_world/COPYING
 +U hello_world/ChangeLog
 +U hello_world/INSTALL
 +[...]
 +</code>
 +
 +Aunque este ejemplo se ha realizado localmente por entero, la comunicación 
 +se ha realizado a través de ssh y podría haberse realizado igualmente por 
 +un usuario cualquiera accediendo remotamente a esta máquina. 
 +
 +La misma seguencia de acciones la realizaremos para el usuario pruebas3. 
 +Hemos incorporado al fichero .cshrc las variables de entorno para que 
 +no haya que introducirlas en cada sesión: 
 +
 +<code>
 +pruebas3@chupete:~> echo $CVSROOT
 +:ext:pruebas3@localhost:/var/cvs
 +pruebas3@chupete:~> echo $CVSEDITOR
 +/usr/bin/joe
 +pruebas3@chupete:~> cvs co hello_world
 +The authenticity of host 'localhost (127.0.0.1)' can't be established.
 +RSA key fingerprint is 7b:cc:f5:ce:a7:ca:5e:3c:37:50:f9:f7:a3:59:4f:0a.
 +Are you sure you want to continue connecting (yes/no)?
 +Warning: Permanently added 'localhost' (RSA) to the list of known hosts.
 +pruebas3@localhost's password:
 +cvs server: Updating hello_world
 +U hello_world/AUTHORS
 +U hello_world/COPYING
 +U hello_world/ChangeLog
 +U hello_world/INSTALL
 +[....]
 +</code>
 +
 +
 +== Efectuando un cambio concurrente ==
 +
 +Tanto el usuario pruebas3 como el usuario pruebas4 harán un cambio en la 
 +misma línea de código del fichero main.cpp, y comprobaremos lo que ocurre
 +al guardar los cambios en el repositorio. 
 +
 +<code>
 +pruebas2@chupete:~/hello_world/hello_world> joe main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + change made by pruebas2" << endl;
 +
 +  return EXIT_SUCCESS;
 +</code>
 +
 +
 +<code>
 +pruebas3@chupete:~/hello_world/hello_world> joe main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS3" << endl;
 +
 +  return EXIT_SUCCESS;
 +</code>
 +
 +el usuario pruebas2 guarda sus cambios: 
 +
 +<code>
 +pruebas2@chupete:~/hello_world/hello_world> cvs ci -m "Change on main.cpp"
 +cvs commit: Examining .
 +cvs commit: Examining .deps
 +cvs commit: Examining docs
 +cvs commit: Examining docs/en
 +cvs commit: Examining templates
 +Checking in main.cpp;
 +/var/cvs/hello_world/hello_world/main.cpp, <--  main.cpp
 +new revision: 1.5; previous revision: 1.4
 +done
 +</code>
 +
 +el usuario pruebas3 guarda sus cambios: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world/hello_world> cvs ci -m "Change on main.cpp"
 +cvs commit: Examining .
 +cvs commit: Examining .deps
 +cvs commit: Examining docs
 +cvs commit: Examining docs/en
 +cvs commit: Examining templates
 +pruebas3@localhost's password:
 +cvs server: Up-to-date check failed for 'main.cpp'
 +cvs [server aborted]: correct above errors first!
 +</code>
 +
 +e informa que se ha producido un error al actualizar el fichero "main.cpp"
 +Al ejecutar update para que actualice su contenido: 
 +
 +<code>
 +pruebas3@chupete:~/hello_world/hello_world> cvs update
 +pruebas3@localhost's password:
 +cvs server: Updating .
 +M Makefile
 +RCS file: /var/cvs/hello_world/hello_world/main.cpp,v
 +retrieving revision 1.4
 +retrieving revision 1.5
 +Merging differences between 1.4 and 1.5 into main.cpp
 +rcsmerge: warning: conflicts during merge
 +cvs server: conflicts found in main.cpp
 +C main.cpp
 +cvs server: Updating .deps
 +cvs server: Updating docs
 +M docs/Makefile
 +cvs server: Updating docs/en
 +M docs/en/Makefile
 +cvs server: Updating templates
 +</code>
 +
 +Nos informa que ha habido conflictos en el fichero y que se han realizado 
 +mezclas de código. Al examinar el código podemos observar que cvs ha puesto
 +los cambios del otro usuario y los nuestros juntos: 
 +
 +<code>
 +int main(int argc, char *argv[])
 +{
 +<<<<<<< main.cpp
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS3" << endl;
 +=======
 +  cout << "Hello, World! + change made by pruebas2" << endl;
 +>>>>>>> 1.5
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +=== Conclusiones ===
 +
 +
 +CVS es uno de los sistemas de control de código fuente más utilizados en 
 +la actualidad. Quizá su mayor virtud es la estabilidad, y la gran cantidad 
 +de herramientas disponibles para utilizarlo. También cuenta con una comunidad
 +de usuarios muy activa y con versiones portadas a diferentes plataformas. 
 +
 +Sin embargo, quizá su mayor defecto es la poca flexibilidad que ofrece 
 +en el manejo del repositorio, ya que el borrado de directorios no está 
 +permitido, y el soporte que ofrece para los ficheros binarios es deficiente. 
 +
 +No obstante, se trata de un sistema muy interesante por lo ampliamente probado
 +que está, y la posibilidad que ofrece de trabajar desde otras plataformas.
 +
 +En el terreno de la documentación, el documento clásico sobre cvs 
 +-el Cederqvist- me ha parecido bastante arduo de leer, ya que exige leerlo 
 +por completo para encontrar cosas que deberían estar explicadas al principio.
 +Se hace necesario otro manual que explique el uso y administración de la 
 +herramienta de una forma más fácil. 
 +
 +
 +==== Subversion ====
 +
 +
 +=== Instalación ===
 +
 +Subversion está disponible en paquete debian sólamente para la versión "sarge", 
 +que es la versión estable en la actualidad. Aunque existía para woody (Debian
 +3.0 r2), su autor ha descontinuado el producto una vez que "sarge" pasó a 
 +estable. 
 +
 +Por lo tanto, la única opción que queda es compilar el paquete y realizar una 
 +instalación manual. 
 +
 +Hemos descargado el la versión 1.2.1, que funciona sin Berkeley DB. Tras 
 +ejecutar configure -hemos de tener en cuenta que configure "llama" a otros
 +"configure" de sub-proyectos que se subversion incorpora para que pueda 
 +funcionar- pasamos a ejecutar make; make install (este último comando 
 +como root), y el proceso de instalación finaliza sin problemas. 
 +
 +== Creación del repositorio ==
 +
 +El siguiente paso consiste en la creación del repositorio, que ubicaremos 
 +en el directorio ''/var/svn'': 
 +
 +<code>
 +chupete:~# svnadmin create /var/svn/
 +chupete:~# ls /var/svn
 +README.txt  conf  dav  db  format  hooks  locks
 +chupete:~# more /var/svn/README.txt
 +This is a Subversion repository; use the 'svnadmin' tool to examine
 +it.  Do not add, delete, or modify files here unless you know how
 +to avoid corrupting the repository.
 +
 +If the directory "db" contains a Berkeley DB environment,
 +you may need to tweak the values in "db/DB_CONFIG" to match the
 +requirements of your site.
 +
 +Visit http://subversion.tigris.org/ for more information.
 +</code>
 +
 +**Securización de subversion**
 +
 +Nuestra intención al utilizar subversion en modo local es que un grupo de
 +usuarios determinado tuviera acceso al repositorio y a la operativa de
 +subversion, mientras que otro grupo de usuarios, aunque tuviera cuenta en
 +el sistema, no pudiera utilizarlo.
 +
 +Bajo estas premisas, procedimos a asignar permisos de forma que sólo aquellos
 +usuarios que pertenecieran al grupo de usuarios ''svn'' tuvieran acceso al 
 +repositorio. Para ello, creamos un grupo ''svn'', al que añadimos a dos 
 +usuarios que nos servirían para hacer las pruebas: 
 +
 +<code shell>
 +chupete:/var# groupadd svn
 +chupete:/var# usermod -G svn rluna
 +chupete:/var# usermod -G svn pruebas1
 +</code>
 +
 +Los permisos sobre el directorio serán 2770 -el "2" es el bit group-id, 
 +y tiene por objeto que cada fichero o directorio que se cree dentro de 
 +ese directorio pertenezca al grupo al que pertenece el directorio-, 
 +y el grupo será svn:
 +
 +<code shell>
 +chupete:~# chmod 2770 /var/svn    
 +chupete:~# chgrp svn /var/svn
 +</code>
 +
 +Los comandos relacionados con subversion sólo podrán ser ejecutados por 
 +root o los miembros del grupo svn: 
 +
 +<code shell>
 +chupete:~# ls -la /usr/local/bin/svn*
 +-rwxr-xr-x    1 root     root       360019 Aug  8 17:22 /usr/local/bin/svn
 +-rwxr-xr-x    1 root     root       101569 Aug  8 17:23 /usr/local/bin/svnadmin
 +-rwxr-xr-x    1 root     root        89589 Aug  8 17:23 /usr/local/bin/svndumpfilter
 +-rwxr-xr-x    1 root     root       121222 Aug  8 17:23 /usr/local/bin/svnlook
 +-rwxr-xr-x    1 root     root       150360 Aug  8 17:23 /usr/local/bin/svnserve
 +-rwxr-xr-x    1 root     root        77564 Aug  8 17:23 /usr/local/bin/svnversion
 +chupete:~# chgrp svn /usr/local/bin/svn*
 +chupete:~# chmod 550 /usr/local/bin/svn*
 +# el comando "svn" debe tener permisos de ejecución globales (555) debido 
 +# a que todos los usuarios deberán tener acceso al mismo (por si quieren 
 +# utilizar Subversion como cliente)
 +chupete:~# chmod 555 /usr/local/bin/svn 
 +chupete:~# ls -la /usr/local/bin/svn*
 +-r-xr-xr-x    1 root     svn        360019 Aug  8 17:22 /usr/local/bin/svn
 +-r-xr-x---    1 root     svn        101569 Aug  8 17:23 /usr/local/bin/svnadmin
 +-r-xr-x---    1 root     svn         89589 Aug  8 17:23 /usr/local/bin/svndumpfilter
 +-r-xr-x---    1 root     svn        121222 Aug  8 17:23 /usr/local/bin/svnlook
 +-r-xr-x---    1 root     svn        150360 Aug  8 17:23 /usr/local/bin/svnserve
 +-r-xr-x---    1 root     svn         77564 Aug  8 17:23 /usr/local/bin/svnversion
 +</code>
 +
 +A continuación, crearemos el repositorio mediante el comando svnadmin. El 
 +repositorio lo creará el usuario root también, para que los permisos sean 
 +lo más uniformes posible: 
 +
 +<code shell>
 +chupete:/var/svn# svnadmin create /var/svn
 +chupete:/var/svn# ls
 +README.txt  conf  dav  db  format  hooks  locks
 +chupete:/var/svn# more README.txt
 +This is a Subversion repository; use the 'svnadmin' tool to examine
 +it.  Do not add, delete, or modify files here unless you know how
 +to avoid corrupting the repository.
 +
 +If the directory "db" contains a Berkeley DB environment,
 +you may need to tweak the values in "db/DB_CONFIG" to match the
 +requirements of your site.
 +</code>
 +
 +A continuación, y para permitir que cualquier miembro del grupo "svn" tenga
 +acceso al mismo, cambiaremos el grupo de acceso para todos los ficheros 
 +del repositorio: 
 +
 +<code shell>
 +chupete:/var/svn# chgrp svn * -R
 +chupete:/var/svn# ls -la
 +total 36
 +drwxrwx---    7 root     svn          4096 Aug  8 20:16 .
 +drwxr-xr-x   19 root     root         4096 Aug  8 19:04 ..
 +-rw-r--r--    1 root     svn           379 Aug  8 20:16 README.txt
 +drwxr-xr-x    2 root     svn          4096 Aug  8 20:16 conf
 +drwxr-xr-x    2 root     svn          4096 Aug  8 20:16 dav
 +drwxr-sr-x    5 root     svn          4096 Aug  8 20:16 db
 +-r--r--r--    1 root     svn             2 Aug  8 20:16 format
 +drwxr-xr-x    2 root     svn          4096 Aug  8 20:16 hooks
 +drwxr-xr-x    2 root     svn          4096 Aug  8 20:16 locks
 +</code>
 +
 +Cambios de permisos: 
 +
 +<code shell>
 +chupete:/var/svn# chmod g+w conf dav db hooks/ locks/
 +chupete:~# chmod 600 /var/svn/conf/passwd
 +chupete:~# chmod 600 /var/svn/conf/svnserve.conf
 +chupete:/var/svn/db# chmod g+w transactions/ revprops/ revs/
 +chupete:/var/svn/db# chmod 664 current fs-type uuid write-lock
 +chupete:/var/svn/db/revprops# chmod 664 0
 +chupete:/var/svn/db/revs# chmod 664 0
 +chupete:/var/svn/hooks# chmod 664 *tmpl
 +chupete:/var/svn/locks# chmod 664 *lock
 +</code>
 +
 +Seguidamente comprobaremos que el acceso al repositorio está permitido 
 +haciendo que uno de los usuarios haga una importación de un proyecto: 
 +
 +<code shell>
 +pruebas1@chupete:~> svn import hello_world file:///var/svn/hello_world -m 'Initial release'
 +Adding         hello_world/Makefile.dist
 +Adding         hello_world/configure
 +Adding         hello_world/Makefile.in
 +Adding         hello_world/configure.files
 +[....]
 +Adding         hello_world/aclocal.m4
 +Adding         hello_world/Makefile
 +Adding         hello_world/hello_world.kdevses
 +Adding         hello_world/hello_world.kdevprj
 +Adding         hello_world/stamp-h.in
 +
 +Committed revision 1.
 +</code>
 +
 +Se ha comprobado que el usuario pruebas1 como otro usuario que pertenezca 
 +al grupo ''svn'' tiene ahora permisos para importar proyectos, hacer 
 +check-out y check-in de modificaciones, borrar entradas, etc, por lo 
 +que los permisos que se han asignado funcionan con normalidad y permiten 
 +la operacion del sistema. 
 +
 +Quedaría comprobar que aquellos usuarios que no pertenecen a ese grupo 
 +especial no disponen de los permisos adecuados para ello: 
 +
 +<code shell>
 +pruebas2@chupete:~> svnadmin create /home/pruebas2/myrepo
 +/usr/local/bin/svnadmin: Permission denied.
 +pruebas2@chupete:~> svn import prueba_orig/ file:///var/svn/prueba_orig -m 'prueba de permisos'
 +svn: Unable to open an ra_local session to URL
 +svn: Unable to open repository 'file:///var/svn/prueba_orig'
 +svn: Can't open file '/var/svn/prueba_orig/format': Permission denied
 +</code>
 +
 +
 +=== Realizando del primer cambio ===
 +
 +Nuestro primer paso será, como viene siendo habitual, realizar una importación
 +del proyecto "hello_world" que estamos usando en todos los ejemplos y 
 +posteriormente realizaremos un cambio en el mismo para reflejarlo en el 
 +repositorio. 
 +
 +<code shell>
 +pruebas1@chupete:~> svn import hello_world file:///var/svn/hello_world -m 'Initial release'
 +Adding         hello_world/Makefile.dist
 +Adding         hello_world/configure
 +[....]
 +Adding         hello_world/hello_world.kdevses
 +Adding         hello_world/hello_world.kdevprj
 +Adding         hello_world/stamp-h.in
 +
 +Committed revision 1.
 +</code>
 +
 +El siguiente paso será hacer el checkout inicial: 
 +
 +<code shell>
 +pruebas1@chupete:~> rm -r hello_world/
 +pruebas1@chupete:~> svn co file:///var/svn/hello_world hello_world/
 +A    hello_world/Makefile.dist
 +A    hello_world/configure
 +A    hello_world/Makefile.in
 +A    hello_world/configure.files
 +[...]
 +A    hello_world/subdirs
 +A    hello_world/aclocal.m4
 +A    hello_world/Makefile
 +A    hello_world/hello_world.kdevses
 +A    hello_world/hello_world.kdevprj
 +A    hello_world/stamp-h.in
 +Checked out revision 1.
 +</code>
 +
 +Realizar un cambio en uno de los archivos: 
 +
 +<code shell>
 +pruebas1@chupete:~> joe hello_world/AUTHORS
 +Raul Luna Rodriguez <rluna@tas.raulluna.net>
 +pruebas1 <pruebas1@tas.raulluna.net>
 +</code>
 +
 +Y hacer un commit: 
 +
 +<code shell>
 +pruebas1@chupete:~/hello_world> svn ci -m 'Changed AUTORS file to include myself'
 +Sending        AUTHORS
 +Transmitting file data .
 +Committed revision 2.
 +</code>
 +
 +Y posteriormente actualizar los cambios del servidor: 
 +
 +<code shell>
 +pruebas1@chupete:~/hello_world> svn update
 +At revision 2.
 +</code>
 +
 +
 +=== Realizando un cambio concurrente ===
 +
 +Igual que en las pruebas anteriores, comprobaremos cómo resuelve subversion
 +las colisiones en caso de que dos usuarios realicen un cambio sobre la 
 +misma línea de un archivo de código fuente. Para realizar las pruebas 
 +simularemos que el acceso se está realizando remotamente al repositorio, 
 +aunque las pruebas se seguirán haciendo en local. 
 +
 +== Haciendo que el repositorio sea visible públicamente ==
 +
 +El acceso público lo efectuaremos habilitando el protocolo propietario 
 +para el acceso remoto a la aplicación; svnserve, desde el super-demonio
 +de internet, xinetd. 
 +
 +**Creando un usuario no privilegiado**
 +
 +Nuestro primer paso -que no lo dice la documentación- será crear un
 +usuario no privilegiado para que sea quien acceda al repositorio: 
 +
 +<code shell>
 +chupete:~# useradd -c 'Subversion unpriviledged user' -d /var/svn/ -g svn -G svn -s /bin/false svn
 +chupete:~# passwd svn
 +Enter new UNIX password:
 +Retype new UNIX password:
 +passwd: password updated successfully
 +</code>
 +
 +Y procederemos a cambiar el propietario de todos los ficheros del 
 +repositorio: 
 +
 +<code shell>
 +chupete:~# chown svn /var/svn -Rv
 +changed ownership of /var/svn' to svn
 +changed ownership of /var/svn/dav' to svn
 +changed ownership of /var/svn/locks' to svn
 +changed ownership of /var/svn/locks/db.lock' to svn
 +changed ownership of /var/svn/locks/db-logs.lock' to svn
 +changed ownership of /var/svn/hooks' to svn
 +[...]
 +</code>
 +
 +**Configurando el super-demonio de internet**
 +
 +El siguiente paso será configurar el super-demonio de internet para que 
 +ejecute el servidor de subversion, svnserve. Para ello comenzaremos por 
 +añadir en /etc/services los puertos asignados por la IANA para 
 +subversion: 
 +
 +<code shell>
 +chupete:~# joe /etc/services
 +[....] 
 +mysql           3306/tcp                        # MySQL
 +mysql           3306/udp                        # MySQL
 +svn             3690/tcp                        # Subversion
 +svn             3690/udp                        # Subversion
 +rfe             5002/tcp                        # Radio Free Ethernet
 +rfe             5002/udp                        # Actually uses UDP only
 +[....]
 +</code>
 +
 +Para luego configurar xinetd para que ejecute el servidor svnserve 
 +cuando se le requiera: 
 +
 +<code shell>
 +chupete:~# joe /etc/xinetd.conf
 +# Subversion
 +service svn
 +{
 +        socket_type     = stream
 +        protocol        = tcp
 +        wait            = no
 +        user            = svn
 +        server          = /usr/local/bin/svnserve
 +        server_args     = -i
 +}
 +</code>
 +
 +**Configurar el servidor de subversion**
 +
 +
 +Para probar que nuestra configuración de acceso funciona, habilitaremos 
 +el acceso anónimo de lectura al repositorio editando el fichero 
 +''/var/svn/conf/svnserve.conf'': 
 +
 +<code shell>
 +chupete:/var/svn/conf# joe svnserve.conf
 +[general]
 +### These options control access to the repository for unauthenticated
 +### and authenticated users.  Valid values are "write", "read",
 +### and "none" The sample settings below are the defaults.
 +anon-access = read
 +auth-access = write
 +### The password-db option controls the location of the password
 +### database file.  Unless you specify a path starting with a /,
 +### the file's location is relative to the conf directory.
 +### Uncomment the line below to use the default password file.
 +# password-db = passwd
 +### This option specifies the authentication realm of the repository.
 +### If two repositories have the same authentication realm, they should
 +### have the same password database, and vice versa.  The default realm
 +### is repository's uuid.
 +realm = Project TAS repository
 +
 +También es necesario configurar adecuadamente el fichero passwd que se
 +encuentra en el mismo directorio: 
 +
 +chupete:/var/svn/conf# more passwd
 +### The name and password for each user follow, one account per line.
 +
 +[users]
 +rluna=pepinillo
 +</code>
 +
 +**Una prueba sencilla**
 +
 +Y finalmente, haremos una prueba sencilla para conectarnos a través 
 +de la red, pero de modo local: 
 +
 +<code shell>
 +rluna@chupete:~$ svn co svn://localhost/var/svn/hello_world hello_world
 +A    hello_world/Makefile.dist
 +A    hello_world/configure
 +A    hello_world/Makefile.in
 +A    hello_world/configure.files
 +[....]
 +A    hello_world/Makefile
 +A    hello_world/hello_world.kdevses
 +A    hello_world/hello_world.kdevprj
 +A    hello_world/stamp-h.in
 +Checked out revision 2.
 +</code>
 +
 +**Afinando la configuración **
 +
 +Se puede mejorar la configuración del super-demonio de internet estableciendo
 +la máscara adecuada para el usuario que lo ejecuta (002) y un grupo por
 +defecto -svn, que ya venía configurado para el usuario-. 
 +
 +También es interesante pasar el parámetro -r al servidor, para que haga un 
 +chroot al repositorio y de esta forma en la url no es necesario indicar 
 +la ruta al repositorio. Es decir, que en vez de referirnos a nuestro 
 +proyecto como ''svn://localhost/var/svn/hello_world'' ahora nos podemos referir
 +como ''svn://localhost/hello_world'', que es mucho más intuitivo. 
 +
 +La configuración ''xinetd.conf'' quedará como sigue:
 +
 +<code shell>
 +chupete:/etc# joe xinetd.conf
 +# Subversion
 +service svn
 +{
 +        socket_type     = stream
 +        protocol        = tcp
 +        wait            = no
 +        user            = svn
 +        group           = svn
 +        umask           = 002
 +        server          = /usr/local/bin/svnserve
 +        server_args     = -i -r /var/svn
 +}
 +</code>
 +
 +Y podremos comprobar que la prueba sigue funcionando (tras reiniciar el 
 +super-demonio de internet):
 +
 +<code>
 +rluna@chupete:~$ svn co svn://localhost/hello_world hello_world
 +A    hello_world/Makefile.dist
 +A    hello_world/configure
 +A    hello_world/Makefile.in
 +A    hello_world/configure.files
 +A    hello_world/AUTHORS
 +[....]
 +A    hello_world/Makefile
 +A    hello_world/hello_world.kdevses
 +A    hello_world/hello_world.kdevprj
 +A    hello_world/stamp-h.in
 +Checked out revision 2.
 +</code>
 +
 +== Descargando el repositorio para trabajar con él ==
 +
 +Ahora efectuaremos una descarga del repositorio para el usuario "pruebas1"
 +y para el usuario "pruebas2" (este último se ha añadido al repositorio de 
 +subversion):
 +
 +<code shell>
 +pruebas2@chupete:~> svn co svn://localhost/hello_world hello_world
 +A    hello_world/Makefile.dist
 +A    hello_world/configure
 +A    hello_world/Makefile.in
 +A    hello_world/configure.files
 +[....]
 +A    hello_world/hello_world.kdevses
 +A    hello_world/hello_world.kdevprj
 +A    hello_world/stamp-h.in
 +Checked out revision 6.
 +</code>
 +
 +== Efectuando un cambio concurrente==
 +
 +
 +Ahora, tanto el usuario pruebas1 como pruebas2 modificarán el fichero main.cpp: 
 +
 +<code shell>
 +pruebas1@chupete:~/hello_world/hello_world> joe main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + change made by pruebas1" << endl;
 +
 +  return EXIT_SUCCESS;
 +<code>
 +
 +<code shell>
 +pruebas2@chupete:~/hello_world/hello_world> joe main.cpp
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS2" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +Primero pruebas1 hace el commit: 
 +
 +<code shell>
 +pruebas1@chupete:~/hello_world/hello_world> svn ci -m 'change made by pruebas1'
 +Authentication realm: <svn://localhost:3690> Project TAS repository
 +Password for 'pruebas1':
 +Sending        hello_world/main.cpp
 +Transmitting file data .
 +Committed revision 7.
 +</code>
 +
 +La contraseña que nos ha solicitado no es la de acceso al sistema de 
 +pruebas1, sino la de acceso al repositorio, que es diferente. A continuación
 +efectuaremos el commit para el usuario pruebas2: 
 +
 +<code shell>
 +pruebas2@chupete:~/hello_world/hello_world> svn ci -m 'change made by pruebas2'
 +
 +Authentication realm: <svn://localhost:3690> Project TAS repository
 +Password for 'pruebas2':
 +Sending        hello_world/main.cpp
 +Transmitting file data .svn: Commit failed (details follow):
 +svn: Out of date: '/hello_world/hello_world/main.cpp' in transaction '7-1'
 +</code>
 +
 +Subversion informa que el fichero main.cpp está "out of date", es decir, 
 +está obsoleto según la última revisión. Procedemos entonces a actualizarlo: 
 +
 +<code shell>
 +pruebas2@chupete:~/hello_world/hello_world> svn update
 +C    main.cpp
 +Updated to revision 7.
 +</code>
 +
 +y nos informa que ha habido un conflicto con main.cpp. Subversion crea 
 +entonces tres archivos con extensiones .mine, .rANTERIOR y .rACTUAL. 
 +El primero es el fichero original, el siguiente es el fichero tal y 
 +como estaba en el repositorio antes del cambio y finalmente el fichero 
 +.rACTUAL es el fichero como está actualmente en el repositorio. 
 +
 +Lo ilustraremos con el propio contenido del fichero: 
 +
 +<code shell>
 +pruebas2@chupete:~/hello_world/hello_world> tail main.cpp
 +int main(int argc, char *argv[])
 +{
 +<<<<<<< .mine
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS2" << endl;
 +=======
 +  cout << "Hello, World! + change made by pruebas1" << endl;
 +>>>>>>> .r7
 +
 +  return EXIT_SUCCESS;
 +}
 +pruebas2@chupete:~/hello_world/hello_world> tail main.cpp.mine
 +
 +#include <iostream.h>
 +#include <stdlib.h>
 +
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + CHANGE MADE BY PRUEBAS2" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +pruebas2@chupete:~/hello_world/hello_world> tail main.cpp.r6
 +
 +#include <iostream.h>
 +#include <stdlib.h>
 +
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World!" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +pruebas2@chupete:~/hello_world/hello_world> tail main.cpp.r7
 +
 +#include <iostream.h>
 +#include <stdlib.h>
 +
 +int main(int argc, char *argv[])
 +{
 +  cout << "Hello, World! + change made by pruebas1" << endl;
 +
 +  return EXIT_SUCCESS;
 +}
 +</code>
 +
 +En este caso, haremos prevalecer nuestro cambio sobre el del otro usuario: 
 +
 +<code shell>
 +pruebas2@chupete:~/hello_world/hello_world> mv main.cpp.mine main.cpp
 +pruebas2@chupete:~/hello_world/hello_world> rm main.cpp.r*
 +pruebas2@chupete:~/hello_world/hello_world> svn commit -m 'change made by pruebas2 that superseedes change made by pruebas1'
 +Sending        hello_world/main.cpp
 +Transmitting file data .
 +Committed revision 8.
 +</code>
 +
 +=== Conclusiones ===
 +
 +La razón de ser de Subversion es ofrecer a la comunidad de Open Source 
 +"un CVS mejorado". En este sentido, ofrece características que CVS no 
 +tiene, como son: 
 +
 +  * posibilidad de versionar directorios
 +  * posibilidad de incluir enlaces simbólicos 
 +  * ahora es posible renombrar un fichero sin que éste pierda su historia
 +  * ahora es posible crear un fichero a partir de la copia de otro sin que éste pierda su historia
 +  * ahora se pueden borrar directorios
 +  * un mejor control sobre los formatos binarios 
 +
 +Quizá los requerimientos de seguridad, tanto en el acceso al repositorio
 +a través de un acceso local al propio sistema como a través del protocolo
 +svnserve sea la parte que presenta más carencias. Por la primera parte, 
 +el procedimiento de instalación no establece un conjunto de permisos claro 
 +que permita que un grupo de usuarios de la máquina pueda trabajar con el 
 +repositorio y otros no, por otra parte el acceso remoto no puede limitar 
 +el acceso a partes del repositorio a no ser que se utilicen los "hooks"
 +que provee la aplicación. Los permisos sí que resultan válidos en el 
 +segundo caso para proyectos únicos, pero no para el caso en que un servidor
 +aloje a varios proyectos.
 +
 +La documentación que se ha consultado, "Version Control with Subversion", 
 +es muy completa y amena de leer; incluye una guía para hacerse rápidamente 
 +con la aplicación, y también un capítulo dedicado a la administración, 
 +aparte de una guía de referencia. Como dicen los autores, responde a las 
 +necesidades de conocimiento de subversion que han ido surgiendo en las 
 +listas de correo de esta aplicación a lo largo del tiempo. 
 +
 +==== Conclusión final ====
 +
 +Como conclusión final de este análisis de varios sistemas de control de 
 +código fuente, puede decirse que a pesar de lo superficial del análisis, 
 +he podido detectar diferencias importantes entre las aplicaciones analizadas. 
 +
 +Quizá los análisis expuestos aquí no hayan sido de la profundidad deseable; 
 +quedarían por tratar numerosos puntos como son la facilidad de administración,
 +un análisis en profundidad de la seguridad de la solución presentada, 
 +la estabilidad del repositorio ante posibles daños en el mismo, la 
 +disponibilidad de API's o librerías para crear aplicaciones cliente. Incluso 
 +sería muy interesante analizar la disponibilidad de estas herramientas para 
 +otras plataformas -Windows o Mac-.
 +
 +No obstante, para poder realizar una mínima operativa con los repositorios, 
 +ha sido necesario en muchos casos leerse completamente el manual del programa, 
 +por lo que sí que se ha obtenido una visión en profundidad de los puntos 
 +fuertes y débiles de cada programa. 
 +
 +Antes de continuar con el análisis definitivo, quisiera dar un breve repaso
 +de las características más importantes de cada uno de estos sistemas de 
 +control de código fuente: 
 +
 +En primer lugar, decir que **GNU arch** y **Open CM** no se pudieron instalar 
 +en la versión de debian que estoy utilizando en la actualidad -Debian 3.0 Woody 1-, 
 +por lo que quedaron fuera de la evaluación. No obstante, OpenCM 
 +tiene características muy interesantes respecto a seguridad y robustez 
 +de la solución que merecería la pena valorar. 
 +
 +**Respecto a darcs**, los dos aspectos más destacables son por un lado la sencillez de su funcionamiento: el repositorio se crea en el propio directorio de desarrollo, con lo que tenemos control de código fuente desde las fases primarias del desarrollo. 
 +
 +Más adelante, cuando el proyecto va avanzando y necesitamos 
 +disponer de un repositorio en internet con acceso público, el manual nos
 +dice cómo hacerlo de la forma más fácil posible. Finalmente, cuando otros
 +desarrolladores crean parches que nos interesan -también con darcs- podemos
 +fácilmente pasar a formar una comunidad de desarrolladores sincronizando 
 +nuestros respectivos repositorios. 
 +
 +Esta naturaleza distribuida de los repositorios es un concepto interesante, 
 +que simplifica mucho la tarea de compartir parches y desarrollos entre 
 +desarrolladores, sin embargo encontramos que la carencia de un unico 
 +punto centralizado donde se guarden los fuentes hace que se pierda -o quede
 +dificultada enormemente- el desarrollo de una versión "oficial", cuando 
 +cualquiera de los desarrolladores puede tener la suya propia en su máquina, 
 +mejor o peor parcheada en función de su conexión a internet. 
 +
 +**Respecto a Aegis** puede considerarse que es algo más que un sistema de 
 +control de código fuente. Además de ello, permite organizar el trabajo al 
 +establecer varios perfiles de acceso a la aplicación: desarrollador, revisor,
 +integrador y administrador. Por otra parte, en el proceso de desarrollo 
 +se implanta la obligatoriedad de incluir un script que compruebe cada
 +cambio que se implanta, con lo que se mejora la calidad del software 
 +que se produce. 
 +
 +Las ventajas que añade Aegis en el desarrollo de su solución se transforman 
 +en inconvenientes en un sistema de fuente abierta, donde los desarrolladores
 +realizan su labor "por amor al arte", y es difícil que encajen favorablemente
 +tener a alguien por encima que les asigne las tareas y que revise su trabajo. 
 +
 +Sin embargo sí que parece un sistema muy apropiado para un entorno empresarial, 
 +donde esas jerarquías suelen estar fuertemente establecidas, y donde por otra
 +parte la disponibilidad de acceso a la máquina de desarrollo es total, hecho 
 +que se hace necesario en este tipo de herramientas y que me parece un 
 +requisito muy exigente para llevarlo a la práctica en el mundo del software 
 +libre. 
 +
 +**La opción de CVS** nos parece altamente interesante por varios motivos: 
 +1) se trata de un software ampliamente probado, y del que se puede disponer 
 +incluso de empresas que ofrecen soporte; 2) hay una comunidad de usuarios 
 +muy activa que pueden ofrecernos su ayuda, y abundante documentación; 3) 
 +la disponbilidad en otras plataformas -por lo menos en Windows- está 
 +ampliamente consolidada, con multitud de herramientas tanto cliente como 
 +servidor. 
 +
 +Sin embargo, las carencias que tiene en la gestión del repositorio son 
 +destacables: la dificultad para almacenar binarios, la prácticamente 
 +imposibilidad de borrar directorios, o de almacenar elementos como los 
 +enlaces simbólicos, el que la historia de un fichero se mantenga aunque 
 +borremos completamente ese fichero y creemos uno nuevo con el mismo 
 +nombre.... Se trata de carencias que han alentado la creación del 
 +siguiente competidor en la lucha, Subversion. 
 +
 +**Subversion nace** --y así lo declaran abiertamente sus creadores-- como 
 +''un CVS mejorado''. Es decir; funcionar más o menos igual que CVS, pero con 
 +la posibilidad de hacer aquello que CVS nunca permitió: guardar historia 
 +de un directorio, permitir cambiar de nombre elementos como directorios, 
 +gestión eficiente de archivos binarios mediante una herramienta "diff" que 
 +soporta comparación en modo binario; contemplar la posibilidad de tratar 
 +las colisiones para los ficheros binarios también, permitir reemplazar 
 +ficheros.... 
 +
 +Precísamente esta característica de "caballo ganador" que presenta Subversion,
 +ha hecho que nos tomemos algún tiempo más en su análisis, leyendo en detalle
 +el manual de la aplicación. Hemos encontrado que, por lo menos en la versión
 +que se instala en Debian 3.0 (woody1) existen importantes deficiencias de 
 +seguridad: no está adecuadamente cubierto el acceso a través del sistema 
 +de ficheros. Por otra parte, el programa servidor, svnserve debería ejecutarse
 +como root, comprometiendo la seguridad del sistema en caso de que se produjera 
 +una deficiencia de seguridad como un buffer overflow. 
 +
 +**Finalmente**
 +
 +Si tuviera que decidirme por un sistema de control de código fuente para 
 +un proyecto de software libre, optaría por Subversion, haciendo las 
 +modificaciones de controles de acceso oportunas. En su favor obra la 
 +gran difusión que está teniendo, la calidad del repositorio que implementan, 
 +la fortaleza de su diseño que le va a permitir un crecimiento estable 
 +en los próximos años, y el hecho innegable que es el reemplazo para 
 +CVS, por lo que va a tener una gran difusión en los próximos años, 
 +y eso garantiza la buena salud del proyecto. 
 +
 +