====== 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:
keycode 41 = masculine ordfeminine backslash nul
alt keycode 41 = Meta_grave
y será sustituida por:
keycode 41 = AltGr
# keycode 41 = masculine ordfeminine backslash nul
# alt keycode 41 = Meta_grave
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:
keycode 8 = seven slash braceleft Control_underscore
alt keycode 8 = Meta_seven
y al tercer valor que es el valor que tiene la tecla cuando se pulsa AltGr, lo reemplazamos por backslash:
keycode 8 = seven slash backslash Control_underscore
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:
keycode 9 = eight parenleft masculine Delete
alt keycode 9 = Meta_eight
keycode 10 = nine parenright ordfeminine
alt keycode 10 = Meta_nine
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:
keycode 44 = z
keycode 45 = x
ahora quedará como:
keycode 44 = z
altgr keycode 44 = less
keycode 45 = x
altgr keycode 45 = greater
====== 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'':
deb ftp://ftp.rediris.es/sites/ftp.es.debian.org/debian oldstable main contrib non-free
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" a
"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:
# General Settings
domain:
innflags:
mailcmd: /usr/lib/news/bin/innmail
server: localhost
Quedara así:
# General Settings
domain: raulluna.net
innflags:
mailcmd: /usr/lib/news/bin/innmail
server: tas
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:
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
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:
chupete:/# ls -lad floppy cdrom
drwxrwx--- 2 root cdrom 4096 Nov 19 2004 cdrom
drwxrwx--- 3 root floppy 1024 Jul 23 18:58 floppy
==== 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:
deb http://security.debian.org/ stable/updates main
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:
deb http://security.debian.org/ oldstable/updates main
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 ====
chupete:~/opencm-0.1.2alpha8/base# ./configure --prefix /tmp/opencm --with-ssl-dir=/usr/lib
OpenCM dice que no tiene instalada la librería openSSL a pesar de que sí
está instalada:
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#
==== GNU arch ====
GNU arch parece que adolece del mismo problema: la comprobación de configure
dice que no está instalado "diff3":
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.)
Sin embargo, sí que lo está:
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.
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:
deb http://abridgegame.org/debian woody/
Hemos ejecutado ''apt-get update'' para que actualice los repositorios de
paquetes:
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#
Y a continuacion apt-get install darcs para que efectúe la instalación:
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
Es aconsejable instalar también el programa wget, que darcs necesita
pero no comprueba su existencia:
chupete:~# apt-get install wget
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:
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
El siguiente paso consiste en añadir todos los ficheros del proyecto
al repositorio del mismo:
pruebas2@chupete:~/hello_world> darcs add * -r
Y luego se efectúa un checkout de todos los ficheros, que en el lenguaje
de darcs implica grabar todos los cambios:
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 '. 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
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--
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'':
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
[...]
}
Igual que en el caso anterior, simplemente ejecutando ''darcs record''
guardaremos los datos relativos al cambio en el repositorio de código
fuente:
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'
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:
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
== 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:
pruebas1@chupete:~> mkdir hello_world
pruebas1@chupete:~> cd hello_world
pruebas1@chupete:~/hello_world> darcs initialize
Y mediante el comando darcs pull ha solicitado una copia completa de todos
parches que tiene el usuario pruebas2 en su repositorio:
pruebas1@chupete:~/hello_world> darcs pull http://localhost/repos/hello_world
Sat Jul 23 11:47:38 CEST 2005 Pruebas Darcs
* Initial Release
Shall I pull this patch? (1/2) [ynWvpxqadjk], or ? for help: y
Sat Jul 23 18:23:02 CEST 2005 Pruebas Darcs
* 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
* 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
== 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:
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
Bien, el cambio ya está hecho. Ahora lo que haremos será guardar los cambios
en nuestro repositorio local:
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'
Ahora el usuario pruebas1 hará un cambio en su propio repositorio:
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;
}
Y lo guardará en darcs:
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 '. 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
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'
Y ahora integramos los cambios de pruebas2 en el repositorio de pruebas1, lo
que provocará una colisión:
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
* 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.
El cambio aparece en el código fuente así:
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;
}
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:
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;
=== Asignacion de las figuras de desarrollador, revisor e integrador ===
Áhora añadimos al proyecto las figuras de desarrollador -pruebas2-,
revisor -pruebas3- e integrador -pruebas4-:
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
=== 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:
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
=== Damos de alta el primer cambio del proyecto ===
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",
];
=== 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:
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
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:
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
Y a continuación informamos a Aegis que pasamos a trabajar en el
directorio del proyecto mediante el comando aecd:
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
Nuestro paso siguiente será hacer una copia completa del proyecto
"hello_world" tal y como lo hemos creado con kdeveloper.
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
Añadiremos al proyecto todos los ficheros como nuevos:
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>
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:
pruebas2@chupete:/usr/doc/aegis/examples/config.example> cat make rcs diff >~/aegis.conf
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:
/*
* 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"; */
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:
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
El mensaje que salía puede verse aquí:
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>
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:
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
El siguiente paso es indicar a Aegis que el desarrollo a finalizado. Primero
generamos los ficheros de diferencias, que se usan para las revisiones.
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
[....]
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.
pruebas2@chupete:~/hello_world.1.0.C010> ls
AUTHORS README config.h,D hello_world.kdevprj
AUTHORS,D README,D config.h.in hello_world.kdevprj,D
COPYING TODO config.h.in,D hello_world.kdevses
COPYING,D TODO,D config.log hello_world.kdevses,D
ChangeLog acinclude.m4 config.log,D hello_world.lsm
ChangeLog,D acinclude.m4,D config.status hello_world.lsm,D
INSTALL aclocal.m4 config.status,D libtool
INSTALL,D aclocal.m4,D configure libtool,D
Makefile admin configure,D stamp-h.in
Makefile,D aegis.conf configure.files stamp-h.in,D
Makefile.am aegis.conf,D configure.files,D stamp-h1
Makefile.am,D aegis.log configure.in stamp-h1,D
Makefile.dist autom4te.cache configure.in,D subdirs
Makefile.dist,D config configure.in.in subdirs,D
Makefile.in config,D configure.in.in,D
Makefile.in,D config.h hello_world
Y finalmente damos por finalizado el cambio:
pruebas2@chupete:~/hello_world.1.0.C010> aede
aegis: project "hello_world.1.0": change 10: development completed
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:
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
El siguiente paso será cambiar al directorio donde se ha realizado el
cambio, que está dentro del directorio "home" del usuario "pruebas2":
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>
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:
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
--More--(Next file: ./COPYING,D)
Y finalmente, si estimamos que el cambio se ha realizado correctamente,
damos por válido el mismo:
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
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:
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
Pasamos al directorio donde están los ficheros del cambio:
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
Comenzamos por informar a aegis que la integración del cambio ha comenzado:
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
El integrador debe construir y probar cada cambio:
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>
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:
pruebas4@chupete:/home/pruebas2> aeipass -p hello_world.1.0 10
[un montón de información sobre la integración]
Como efecto "colateral" el directorio del cambio se ha borrado, y este cambio
figura ya como completado:
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
=== 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 ==
pruebas1@chupete:~> aend pruebas3 -p hello_world.1.0
aegis: project "hello_world.1.0": new developer pruebas3 complete
== El administrador crea dos cambios nuevos ==
Se crearán dos cambios nuevos, uno para el desarrollador pruebas2
y otro para el desarrollador pruebas3:
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
Y el cambio para el usuario pruebas3:
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
== El primer desarrollador lleva a cabo el cambio ==
Igual que en otros casos, comenzamos por visualizar los cambios pendientes:
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
Y a continuación nos apropiamos del cambio 11:
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
Y comenzamos el proceso de desarrollo haciendo un "cd" al directorio con
los cambios que se nos ha creado:
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>
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:
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
[....]
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-.
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
Y ahora sí procedemos a realizar el cambio:
int main(int argc, char *argv[])
{
cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
return EXIT_SUCCESS;
}
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:
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
Que cree los ficheros de diferencias:
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
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:
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
Probamos el test:
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
E indicamos la finalización del cambio:
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
== 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:
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
Hacemos "cd" al directorio del proyecto:
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
Copiamos el directorio "main.cpp" al directorio del proyecto:
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
Y procedemos a realizar el cambio:
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;
}
Como Aegis lo exige, creamos un test para este cambio:
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
Y lo completamos con el siguiente script:
#!/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
Construimos el proyecto (esta fase se sigue manteniendo trivial y en realidad
sólo realiza un "exit 0"):
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
Y ejecutamos los test:
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
Creamos los ficheros de diferencias:
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
Y finalizamos el desarrollo:
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
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:
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.
Y pasamos al directorio del cambio 11:
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
Y le damos como válido:
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
**Integración del cambio**
Comenzamos la integración del cambio:
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
Construimos:
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
Probamos:
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
Generamos los ficheros de diferencias:
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
Y damos la integración por finalizada, constituyendo una nueva "baseline"
del cambio:
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,v <-- /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,v <-- /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,v <-- /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
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:
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
Por lo que es necesario re-copiar main.cpp:
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
Y ahora, al realizar el cambio, ya vemos el cambio que el otro
desarrollador hizo:
int main(int argc, char *argv[])
{
cout << "Hello, World! + Cambio realizado por el usuario pruebas2 " << endl;
return EXIT_SUCCESS;
}
Insertaremos nuestro propio cambio:
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;
}
Y ahora sí que podremos finalizar nuestro desarrollo. Construir, probar y
diferencias:
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
Y finalizar el desarrollo:
pruebas3@chupete:~/hello_world.1.0.C012> aede
aegis: project "hello_world.1.0": change 12: development completed
=== 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:
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.
== 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'':
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
== 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):
chupete:~# usermod -G src pruebas2
Una vez hecho esto, el import funcionará perfectamente:
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
Comprobamos que efectivamente se obtienen bien los fuentes:
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
El mensaje de error que aparece es porque los permisos de escritura para el
fichero "history" no están adecuadamente puestos:
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
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.
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
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:
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
[...]
Ha funcionado correctamente. Ahora realizaremos un cambio en el
fichero main.cpp:
int main(int argc, char *argv[])
{
cout << "Change made by pruebas2" << endl;
cout << "Hello, World!" << endl;
return EXIT_SUCCESS;
}
Y lo guardamos en el repositorio:
pruebas2@chupete:~/hello_world/hello_world> cvs ci
Checking in main.cpp;
/var/cvs/hello_world/hello_world/main.cpp,v <-- main.cpp
new revision: 1.2; previous revision: 1.1
done
=== 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'':
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
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:
pruebas1@chupete:~> setenv CVSROOT :ext:pruebas1@localhost:/var/cvs
pruebas1@chupete:~> setenv CVSEDITOR /usr/bin/joe
Y seguidamente haremos el "checkout":
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
[...]
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:
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
[....]
== 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.
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;
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;
el usuario pruebas2 guarda sus cambios:
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,v <-- main.cpp
new revision: 1.5; previous revision: 1.4
done
el usuario pruebas3 guarda sus cambios:
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!
e informa que se ha producido un error al actualizar el fichero "main.cpp".
Al ejecutar update para que actualice su contenido:
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
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:
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;
}
=== 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'':
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.
**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:
chupete:/var# groupadd svn
chupete:/var# usermod -G svn rluna
chupete:/var# usermod -G svn pruebas1
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:
chupete:~# chmod 2770 /var/svn
chupete:~# chgrp svn /var/svn
Los comandos relacionados con subversion sólo podrán ser ejecutados por
root o los miembros del grupo svn:
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
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:
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.
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:
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
Cambios de permisos:
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
Seguidamente comprobaremos que el acceso al repositorio está permitido
haciendo que uno de los usuarios haga una importación de un proyecto:
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.
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:
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
=== 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.
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.
El siguiente paso será hacer el checkout inicial:
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.
Realizar un cambio en uno de los archivos:
pruebas1@chupete:~> joe hello_world/AUTHORS
Raul Luna Rodriguez
pruebas1
Y hacer un commit:
pruebas1@chupete:~/hello_world> svn ci -m 'Changed AUTORS file to include myself'
Sending AUTHORS
Transmitting file data .
Committed revision 2.
Y posteriormente actualizar los cambios del servidor:
pruebas1@chupete:~/hello_world> svn update
At revision 2.
=== 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:
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
Y procederemos a cambiar el propietario de todos los ficheros del
repositorio:
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
[...]
**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:
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
[....]
Para luego configurar xinetd para que ejecute el servidor svnserve
cuando se le requiera:
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
}
**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'':
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
**Una prueba sencilla**
Y finalmente, haremos una prueba sencilla para conectarnos a través
de la red, pero de modo local:
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.
**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:
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
}
Y podremos comprobar que la prueba sigue funcionando (tras reiniciar el
super-demonio de internet):
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.
== 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):
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.
== Efectuando un cambio concurrente==
Ahora, tanto el usuario pruebas1 como pruebas2 modificarán el fichero main.cpp:
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;
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;
}
Primero pruebas1 hace el commit:
pruebas1@chupete:~/hello_world/hello_world> svn ci -m 'change made by pruebas1'
Authentication realm: Project TAS repository
Password for 'pruebas1':
Sending hello_world/main.cpp
Transmitting file data .
Committed revision 7.
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:
pruebas2@chupete:~/hello_world/hello_world> svn ci -m 'change made by pruebas2'
Authentication realm: 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'
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:
pruebas2@chupete:~/hello_world/hello_world> svn update
C main.cpp
Updated to revision 7.
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:
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
#include
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
#include
int main(int argc, char *argv[])
{
cout << "Hello, World!" << endl;
return EXIT_SUCCESS;
}
pruebas2@chupete:~/hello_world/hello_world> tail main.cpp.r7
#include
#include
int main(int argc, char *argv[])
{
cout << "Hello, World! + change made by pruebas1" << endl;
return EXIT_SUCCESS;
}
En este caso, haremos prevalecer nuestro cambio sobre el del otro usuario:
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.
=== 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.