projects:uocmaster
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
projects:uocmaster [2011/12/27 12:05] – rlunaro | projects:uocmaster [2022/12/02 21:02] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Master of the UOC (Universidad Oberta de Catalunya) ====== | ||
+ | |||
+ | ===== Reconfigurar teclado en linux ===== | ||
+ | |||
+ | Con el teclado del sistema Toshiba 430CDT que estamos manejando | ||
+ | se dan unas particularidades que lo hacen peculiar: | ||
+ | |||
+ | - La tecla " | ||
+ | - 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 ''/ | ||
+ | encargado de cargar la configuración del teclado, que se encuentra | ||
+ | en ''/ | ||
+ | |||
+ | 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 ''/ | ||
+ | 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 " | ||
+ | |||
+ | 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 " | ||
+ | * 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, | ||
+ | * 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 | ||
+ | alt keycode | ||
+ | </ | ||
+ | |||
+ | y será sustituida por: | ||
+ | |||
+ | < | ||
+ | keycode | ||
+ | # keycode | ||
+ | # | ||
+ | </ | ||
+ | |||
+ | 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 (/ | ||
+ | |||
+ | < | ||
+ | keycode | ||
+ | alt keycode | ||
+ | </ | ||
+ | |||
+ | y al tercer valor que es el valor que tiene la tecla cuando se pulsa AltGr, lo reemplazamos por backslash: | ||
+ | |||
+ | < | ||
+ | keycode | ||
+ | </ | ||
+ | |||
+ | En el caso de las teclas 9 y 10, que corresponden a los caracteres | ||
+ | 8 y 9 respectivamente, | ||
+ | retornen " | ||
+ | |||
+ | < | ||
+ | keycode | ||
+ | alt keycode | ||
+ | keycode | ||
+ | alt keycode | ||
+ | </ | ||
+ | |||
+ | |||
+ | 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 | ||
+ | keycode | ||
+ | </ | ||
+ | |||
+ | ahora quedará como: | ||
+ | |||
+ | < | ||
+ | keycode | ||
+ | altgr keycode 44 = less | ||
+ | keycode | ||
+ | altgr | ||
+ | </ | ||
+ | |||
+ | ====== Actualización del sistema ====== | ||
+ | |||
+ | Para actualizar el sistema el primer paso que hemos realizado ha sido | ||
+ | añadir la siguiente línea en ''/ | ||
+ | |||
+ | < | ||
+ | deb ftp:// | ||
+ | </ | ||
+ | |||
+ | Y ejecutar el comando '' | ||
+ | de paquetes. Hemos de hacer notar que woody ha pasado de ser " | ||
+ | " | ||
+ | |||
+ | A continuación hemos ejecutado " | ||
+ | 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 | ||
+ | ''/ | ||
+ | que no habían sido configurados correctamente. Se modificó el fichero para | ||
+ | que el contenido: | ||
+ | |||
+ | <code properties> | ||
+ | # General Settings | ||
+ | |||
+ | domain: | ||
+ | innflags: | ||
+ | mailcmd: | ||
+ | server: | ||
+ | </ | ||
+ | |||
+ | Quedara así: | ||
+ | |||
+ | <code properties> | ||
+ | # General Settings | ||
+ | |||
+ | domain: | ||
+ | innflags: | ||
+ | mailcmd: | ||
+ | server: | ||
+ | </ | ||
+ | |||
+ | Este error provocaba un mensaje de error que llegaba por correo electrónico | ||
+ | cada vez que se ejecutaba el crontab ''/ | ||
+ | |||
+ | ==== 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, | ||
+ | tienen acceso a los mismos una vez montados: | ||
+ | |||
+ | < | ||
+ | chupete:/# ls -lad floppy cdrom | ||
+ | drwxr-xr-x | ||
+ | drwxr-xr-x | ||
+ | </ | ||
+ | |||
+ | Resulta más adecuado hacer un uso racional de los grupos " | ||
+ | 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 " | ||
+ | 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--- | ||
+ | drwxrwx--- | ||
+ | </ | ||
+ | |||
+ | ==== Instalación de paquetes complementarios ==== | ||
+ | |||
+ | Se han instalado los siguientes paquetes complementarios: | ||
+ | |||
+ | ==== 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:// | ||
+ | </ | ||
+ | |||
+ | Cuando debian pasó " | ||
+ | " | ||
+ | paquetes de versiones posteriores a las que funcionaban en woody, creando | ||
+ | colisiones. Bastó corregirla línea en cuestión: | ||
+ | |||
+ | < | ||
+ | deb http:// | ||
+ | </ | ||
+ | |||
+ | 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, | ||
+ | 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; | ||
+ | cuál es el proceso para realizarlo y ver las respuestas que se producen | ||
+ | con este modo de acceso. | ||
+ | |||
+ | ==== OpenCM ==== | ||
+ | |||
+ | < | ||
+ | chupete: | ||
+ | </ | ||
+ | |||
+ | OpenCM dice que no tiene instalada la librería openSSL a pesar de que sí | ||
+ | está instalada: | ||
+ | |||
+ | < | ||
+ | chupete: | ||
+ | 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: | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== GNU arch ==== | ||
+ | |||
+ | GNU arch parece que adolece del mismo problema: la comprobación de configure | ||
+ | dice que no está instalado " | ||
+ | |||
+ | < | ||
+ | chupete: | ||
+ | |||
+ | The configured versions of diff and diff3 do not handle files | ||
+ | not ending in newline correctly. | ||
+ | |||
+ | configured diff = diff. | ||
+ | configured 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. | ||
+ | the native diff tools on your system, but you do need to | ||
+ | | ||
+ | have made a poor decision about their native diffs. | ||
+ | |||
+ | | ||
+ | </ | ||
+ | |||
+ | |||
+ | Sin embargo, sí que lo está: | ||
+ | |||
+ | < | ||
+ | chupete: | ||
+ | diff - File comparison utilities | ||
+ | chupete: | ||
+ | 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: | ||
+ | diff diff3 | ||
+ | chupete: | ||
+ | diff3: missing operand | ||
+ | diff3: Try 'Diff3 --help' | ||
+ | </ | ||
+ | |||
+ | 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:// | ||
+ | |||
+ | Su página web[1] presenta a darcs como "otro sistema de control de código | ||
+ | fuente", | ||
+ | Como base teórica el autor presenta una " | ||
+ | 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:// | ||
+ | </ | ||
+ | |||
+ | |||
+ | Hemos ejecutado '' | ||
+ | paquetes: | ||
+ | |||
+ | < | ||
+ | chupete:/ | ||
+ | Hit http:// | ||
+ | Hit http:// | ||
+ | Get:1 http:// | ||
+ | Ign http:// | ||
+ | Fetched 1100B in 1s (763B/s) | ||
+ | Reading Package Lists... Done | ||
+ | Building Dependency Tree... Done | ||
+ | chupete:/ | ||
+ | </ | ||
+ | |||
+ | Y a continuacion apt-get install darcs para que efectúe la instalación: | ||
+ | |||
+ | < | ||
+ | chupete:/ | ||
+ | [....] | ||
+ | Official i386 Binary-1 (20031201)' | ||
+ | |||
+ | Get:1 http:// | ||
+ | Selecting previously deselected package libgmp3. | ||
+ | (Reading database ... 34955 files and directories currently installed.) | ||
+ | Unpacking libgmp3 (from .../ | ||
+ | Selecting previously deselected package darcs. | ||
+ | Unpacking darcs (from .../ | ||
+ | |||
+ | 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; | ||
+ | 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", | ||
+ | el propio subdirectorio del proyecto: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | AUTHORS | ||
+ | COPYING | ||
+ | ChangeLog | ||
+ | INSTALL | ||
+ | Makefile | ||
+ | Makefile.am | ||
+ | Makefile.dist | ||
+ | Makefile.in | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | AUTHORS | ||
+ | COPYING | ||
+ | ChangeLog | ||
+ | INSTALL | ||
+ | Makefile | ||
+ | Makefile.am | ||
+ | Makefile.dist | ||
+ | </ | ||
+ | |||
+ | El siguiente paso consiste en añadir todos los ficheros del proyecto | ||
+ | al repositorio del mismo: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | Y luego se efectúa un checkout de todos los ficheros, que en el lenguaje | ||
+ | de darcs implica grabar todos los cambios: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | Darcs needs to know what name (conventionally an email address) to use as the | ||
+ | patch author, e.g. 'Fred Bloggs < | ||
+ | now it will be stored in the file ' | ||
+ | in the future. | ||
+ | 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 | ||
+ | '' | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | { | ||
+ | hunk ./Makefile 42 | ||
+ | -build_triplet = i686-pc-linux-gnu | ||
+ | -host_triplet = i686-pc-linux-gnu | ||
+ | -target_triplet = i686-pc-linux-gnu | ||
+ | -ACLOCAL = ${SHELL} / | ||
+ | +build_triplet = i586-pc-linux-gnu | ||
+ | +host_triplet = i586-pc-linux-gnu | ||
+ | +target_triplet = i586-pc-linux-gnu | ||
+ | +ACLOCAL = ${SHELL} / | ||
+ | hunk ./Makefile 48 | ||
+ | -AMTAR = ${SHELL} / | ||
+ | -AUTOCONF = ${SHELL} / | ||
+ | +AMTAR = ${SHELL} / | ||
+ | +AUTOCONF = ${SHELL} / | ||
+ | hunk ./Makefile 51 | ||
+ | [...] | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Igual que en el caso anterior, simplemente ejecutando '' | ||
+ | guardaremos los datos relativos al cambio en el repositorio de código | ||
+ | fuente: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | 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 '' | ||
+ | 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 / | ||
+ | que darcs crea en esta máquina, a continuación hemos hecho un enlace | ||
+ | simbólico a nuestro proyecto desde el mismo: | ||
+ | |||
+ | < | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | total 8 | ||
+ | drwxr-xr-x | ||
+ | drwxr-xr-x | ||
+ | lrwxrwxrwx | ||
+ | </ | ||
+ | |||
+ | == 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: | ||
+ | pruebas1@chupete: | ||
+ | pruebas1@chupete: | ||
+ | </ | ||
+ | |||
+ | Y mediante el comando darcs pull ha solicitado una copia completa de todos | ||
+ | parches que tiene el usuario pruebas2 en su repositorio: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | |||
+ | Sat Jul 23 11:47:38 CEST 2005 Pruebas Darcs < | ||
+ | * Initial Release | ||
+ | Shall I pull this patch? (1/2) [ynWvpxqadjk], | ||
+ | |||
+ | Sat Jul 23 18:23:02 CEST 2005 Pruebas Darcs < | ||
+ | * Tras ejecutar configure | ||
+ | Shall I pull this patch? (2/2) [ynWvpxqadjk], | ||
+ | pruebas1@chupete: | ||
+ | |||
+ | Sat Jul 23 11:47:38 CEST 2005 Pruebas Darcs < | ||
+ | * Initial Release | ||
+ | Shall I pull this patch? (1/2) [ynWvpxqadjk], | ||
+ | pruebas1@chupete: | ||
+ | AUTHORS | ||
+ | COPYING | ||
+ | ChangeLog | ||
+ | INSTALL | ||
+ | Makefile | ||
+ | Makefile.am | ||
+ | Makefile.dist | ||
+ | </ | ||
+ | |||
+ | == 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: | ||
+ | pruebas2@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | 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: | ||
+ | { | ||
+ | binary ./ | ||
+ | hunk ./ | ||
+ | - cout << " | ||
+ | + cout << " | ||
+ | } | ||
+ | pruebas2@chupete: | ||
+ | binary ./ | ||
+ | Shall I record this patch? (1/2) [ynWsfqadjk], | ||
+ | hunk ./ | ||
+ | - cout << " | ||
+ | + cout << " | ||
+ | Shall I record this patch? (2/2) [ynWsfqadjk], | ||
+ | What is the patch name? Change on the line hello world | ||
+ | Do you want to add a long comment? [yn] n | ||
+ | Finished recording patch ' | ||
+ | </ | ||
+ | |||
+ | Ahora el usuario pruebas1 hará un cambio en su propio repositorio: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y lo guardará en darcs: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | { | ||
+ | hunk ./ | ||
+ | - cout << " | ||
+ | + cout << " | ||
+ | } | ||
+ | pruebas1@chupete: | ||
+ | Darcs needs to know what name (conventionally an email address) to use as the | ||
+ | patch author, e.g. 'Fred Bloggs < | ||
+ | now it will be stored in the file ' | ||
+ | in the future. | ||
+ | this file. | ||
+ | |||
+ | What is your email address? Pruebas Darcs Uno < | ||
+ | hunk ./ | ||
+ | - cout << " | ||
+ | + cout << " | ||
+ | Shall I record this patch? (1/1) [ynWsfqadjk], | ||
+ | What is the patch name? change on the hello world line | ||
+ | Do you want to add a long comment? [yn] n | ||
+ | |||
+ | Finished recording patch ' | ||
+ | </ | ||
+ | |||
+ | |||
+ | Y ahora integramos los cambios de pruebas2 en el repositorio de pruebas1, lo | ||
+ | que provocará una colisión: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | Pulling from " | ||
+ | |||
+ | Fri Jul 29 12:14:21 CEST 2005 Pruebas Darcs < | ||
+ | * Change on the line hello world | ||
+ | Shall I pull this patch? (1/1) [ynWvpxqadjk], | ||
+ | We have conflicts in the following files: | ||
+ | ./ | ||
+ | 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 << " | ||
+ | ************* | ||
+ | cout << " | ||
+ | ^ ^ ^ ^ ^ ^ ^ | ||
+ | |||
+ | 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: | ||
+ | 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 " | ||
+ | que hacer nada especial para informar a aegis que éste es el usuario | ||
+ | administrador; | ||
+ | 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!" | ||
+ | necesarios para autoconf y automake. | ||
+ | |||
+ | Mediante el comando aenpr crearemos el proyecto y a continuación | ||
+ | establecemos las características del mismo mediante aepa: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | pruebas1@chupete: | ||
+ | |||
+ | /* | ||
+ | ** Project hello_world | ||
+ | */ | ||
+ | description = "The \" | ||
+ | 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, | ||
+ | |||
+ | Áhora añadimos al proyecto las figuras de desarrollador -pruebas2-, | ||
+ | revisor -pruebas3- e integrador -pruebas4-: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | === 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: | ||
+ | |||
+ | List of Projects | ||
+ | Sat Jul 16 23:34:10 2005 | ||
+ | |||
+ | Project | ||
+ | --------- | ||
+ | hello_world | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | "/ | ||
+ | aegis: project " | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | "/ | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Damos de alta el primer cambio del proyecto === | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | aegis: project " | ||
+ | |||
+ | /* | ||
+ | ** nc dflt hint | ||
+ | */ | ||
+ | brief_description = " | ||
+ | description = " | ||
+ | cause = internal_bug; | ||
+ | test_exempt = true; | ||
+ | test_baseline_exempt = true; | ||
+ | regression_test_exempt = true; | ||
+ | architecture = | ||
+ | [ | ||
+ | " | ||
+ | ]; | ||
+ | </ | ||
+ | |||
+ | === 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: | ||
+ | |||
+ | Project " | ||
+ | List of Changes awaiting_development | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 10 awaiting_ | ||
+ | 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: | ||
+ | aegis: project " | ||
+ | "/ | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y a continuación informamos a Aegis que pasamos a trabajar en el | ||
+ | directorio del proyecto mediante el comando aecd: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | |||
+ | Nuestro paso siguiente será hacer una copia completa del proyecto | ||
+ | " | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | hello_world | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | aegis.log | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | Añadiremos al proyecto todos los ficheros como nuevos: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | AUTHORS | ||
+ | COPYING | ||
+ | ChangeLog | ||
+ | INSTALL | ||
+ | Makefile | ||
+ | Makefile.am | ||
+ | Makefile.dist | ||
+ | pruebas2@chupete: | ||
+ | foreach? aenf $i -p hello_world.1.0 -c 10 | ||
+ | foreach? end | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | .... | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | completed | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | 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 / | ||
+ | que pueden sernos de utilidad: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete:/ | ||
+ | </ | ||
+ | |||
+ | En principio, daremos los valores por defecto como válidos. El uso de make | ||
+ | con aegis requiere de algunos " | ||
+ | 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 " | ||
+ | sin construir: | ||
+ | |||
+ | < | ||
+ | /* | ||
+ | * The build_command field of the project config file is used to invoke the | ||
+ | * relevant build command. | ||
+ | * The ${s Makefile} expands to a path into the baseline during development | ||
+ | * if the file is not in the change. | ||
+ | * 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, | ||
+ | aegis espera para este fichero no es " | ||
+ | procedemos a renombrarlo e informar a Aegis del nuevo fichero que hay en | ||
+ | el proyecto: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | El mensaje que salía puede verse aquí: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | |||
+ | 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: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | El siguiente paso es indicar a Aegis que el desarrollo a finalizado. Primero | ||
+ | generamos los ficheros de diferencias, | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | [....] | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | hello_world/ | ||
+ | main.cpp,D; test $? -le 1 | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | hello_world/ | ||
+ | main.o,D; test $? -le 1 | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | hello_world/ | ||
+ | / | ||
+ | D; test $? -le 1 | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | hello_world/ | ||
+ | / | ||
+ | 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: | ||
+ | AUTHORS | ||
+ | AUTHORS, | ||
+ | COPYING | ||
+ | COPYING, | ||
+ | ChangeLog | ||
+ | ChangeLog, | ||
+ | INSTALL | ||
+ | INSTALL, | ||
+ | Makefile | ||
+ | Makefile, | ||
+ | Makefile.am | ||
+ | Makefile.am, | ||
+ | Makefile.dist | ||
+ | Makefile.dist, | ||
+ | Makefile.in | ||
+ | Makefile.in, | ||
+ | </ | ||
+ | |||
+ | Y finalmente damos por finalizado el cambio: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | |||
+ | Project " | ||
+ | List of Changes being_reviewed | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 10 being_reviewed | ||
+ | </ | ||
+ | |||
+ | El siguiente paso será cambiar al directorio donde se ha realizado el | ||
+ | cambio, que está dentro del directorio " | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | pruebas3@chupete:/ | ||
+ | </ | ||
+ | |||
+ | |||
+ | 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 ' | ||
+ | equipo de desarrollo, que en este caso es " | ||
+ | |||
+ | El comando '' | ||
+ | realizado de una forma rápida: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete:/ | ||
+ | :::::::::::::: | ||
+ | ./AUTHORS,D | ||
+ | :::::::::::::: | ||
+ | --- / | ||
+ | +++ / | ||
+ | @@ -0,0 +1 @@ | ||
+ | +Raul Luna Rodriguez < | ||
+ | --More--(Next file: ./ | ||
+ | </ | ||
+ | |||
+ | |||
+ | Y finalmente, si estimamos que el cambio se ha realizado correctamente, | ||
+ | damos por válido el mismo: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete:/ | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Si consideraramos que el cambio no cumple algún requisito y quisieramos | ||
+ | rechazarlo, utilizaríamos el comando " | ||
+ | |||
+ | == La integración del cambio == | ||
+ | |||
+ | Para ello entraremos como integrador -usuario " | ||
+ | revisar qué cambios están pendientes de integración: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete: | ||
+ | |||
+ | Project " | ||
+ | List of Changes awaiting_integration | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 10 awaiting_ | ||
+ | integration | ||
+ | </ | ||
+ | |||
+ | Pasamos al directorio donde están los ficheros del cambio: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | Comenzamos por informar a aegis que la integración del cambio ha comenzado: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: project " | ||
+ | directory | ||
+ | aegis: project " | ||
+ | directory | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | El integrador debe construir y probar cada cambio: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: cd / | ||
+ | aegis: user " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | pruebas4@chupete:/ | ||
+ | pruebas4@chupete:/ | ||
+ | </ | ||
+ | |||
+ | Y finalmente ya podemos dar la fase de integración como " | ||
+ | el cambio formará parte de la nueva línea base del proyecto: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | [un montón de información sobre la integración] | ||
+ | </ | ||
+ | |||
+ | Como efecto " | ||
+ | figura ya como completado: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | |||
+ | Project " | ||
+ | List of Changes | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 10 completed | ||
+ | </ | ||
+ | |||
+ | === 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: | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | == 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: | ||
+ | /* | ||
+ | ** Project hello_world.1.0 | ||
+ | ** nc dflt hint | ||
+ | */ | ||
+ | brief_description = " | ||
+ | description = " | ||
+ | cause = internal_bug; | ||
+ | test_exempt = false; | ||
+ | test_baseline_exempt = false; | ||
+ | regression_test_exempt = true; | ||
+ | architecture = | ||
+ | [ | ||
+ | " | ||
+ | ]; | ||
+ | ~ | ||
+ | ~ | ||
+ | ~ | ||
+ | / | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y el cambio para el usuario pruebas3: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | /* | ||
+ | ** Project hello_world.1.0 | ||
+ | ** nc dflt hint | ||
+ | */ | ||
+ | brief_description = " | ||
+ | description = " | ||
+ | cause = internal_bug; | ||
+ | test_exempt = false; | ||
+ | test_baseline_exempt = false; | ||
+ | regression_test_exempt = true; | ||
+ | architecture = | ||
+ | [ | ||
+ | " | ||
+ | ]; | ||
+ | ~ | ||
+ | / | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | == El primer desarrollador lleva a cabo el cambio | ||
+ | |||
+ | Igual que en otros casos, comenzamos por visualizar los cambios pendientes: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | |||
+ | Project " | ||
+ | List of Changes awaiting_development | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 11 awaiting_ | ||
+ | development | ||
+ | 12 awaiting_ | ||
+ | development | ||
+ | </ | ||
+ | |||
+ | Y a continuación nos apropiamos del cambio 11: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | "/ | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y comenzamos el proceso de desarrollo haciendo un " | ||
+ | los cambios que se nos ha creado: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | total 24 | ||
+ | drwxr-sr-x | ||
+ | drwxr-xr-x | ||
+ | lrwxrwxrwx | ||
+ | lrwxrwxrwx | ||
+ | lrwxrwxrwx | ||
+ | lrwxrwxrwx | ||
+ | [....] | ||
+ | </ | ||
+ | |||
+ | Aquellos ficheros que vayan a ser modificados han de ser " | ||
+ | local mediante el comando aecp -obviamente, | ||
+ | y aemv, además de sus correspondientes comandos " | ||
+ | para deshacerlos-. | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y ahora sí procedemos a realizar el cambio: | ||
+ | |||
+ | < | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | 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: | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | |||
+ | Que cree los ficheros de diferencias: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: set +e; diff -U10 -a / | ||
+ | hello_world/ | ||
+ | main.cpp > / | ||
+ | test $? -le 1 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | complete | ||
+ | pruebas2@chupete: | ||
+ | #!/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 " | ||
+ | |||
+ | |||
+ | # comprobamos que el fichero " | ||
+ | # efectivamente el cambio introducido ya que | ||
+ | # de lo contrario el test de regresion no ' | ||
+ | # falla | ||
+ | |||
+ | grep ' | ||
+ | |||
+ | if [ $? -ne 0 ] | ||
+ | then | ||
+ | fail | ||
+ | fi | ||
+ | |||
+ | # si llega hasta aquí, es que ha funcionado | ||
+ | pass | ||
+ | </ | ||
+ | |||
+ | Probamos el test: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: /bin/sh / | ||
+ | cout << " | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | pruebas2@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: cd / | ||
+ | aegis: /bin/sh / | ||
+ | FAILED | ||
+ | aegis: project " | ||
+ | good | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | |||
+ | E indicamos la finalización del cambio: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | aegis: logging to / | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | 00/ | ||
+ | test $? -le 1 | ||
+ | aegis: project " | ||
+ | pruebas2@chupete: | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | |||
+ | == El segundo desarrollador lleva a cabo el cambio == | ||
+ | |||
+ | El proceso es básicamente el mismo que en el caso anterior: | ||
+ | |||
+ | Nos " | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | "/ | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Hacemos " | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | Copiamos el directorio " | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y procedemos a realizar el cambio: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Como Aegis lo exige, creamos un test para este cambio: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | 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 " | ||
+ | |||
+ | |||
+ | # comprobamos que el fichero " | ||
+ | # efectivamente el cambio introducido ya que | ||
+ | # de lo contrario el test de regresion no ' | ||
+ | # falla | ||
+ | |||
+ | |||
+ | grep ' | ||
+ | |||
+ | 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: | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y ejecutamos los test: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: logging to / | ||
+ | aegis: /bin/sh / | ||
+ | cout << " | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: cd / | ||
+ | aegis: /bin/sh / | ||
+ | FAILED | ||
+ | aegis: project " | ||
+ | good | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Creamos los ficheros de diferencias: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: logging to / | ||
+ | aegis: set +e; diff -U10 -a / | ||
+ | hello_world/ | ||
+ | main.cpp > / | ||
+ | test $? -le 1 | ||
+ | aegis: set +e; diff -U10 -a /dev/null / | ||
+ | 00/ | ||
+ | test $? -le 1 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y finalizamos el desarrollo: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | for change 11 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Pero no nos deja finalizar el desarrollo porque el fichero " | ||
+ | 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, | ||
+ | el primer cambio antes de continuar. | ||
+ | |||
+ | NOTA: El usuario " | ||
+ | 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: | ||
+ | |||
+ | Project " | ||
+ | List of Changes being_reviewed | ||
+ | |||
+ | Change | ||
+ | ------- ------- | ||
+ | 11 being_reviewed | ||
+ | </ | ||
+ | |||
+ | Y pasamos al directorio del cambio 11: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | Y le damos como válido: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete:/ | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | **Integración del cambio** | ||
+ | |||
+ | Comenzamos la integración del cambio: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete: | ||
+ | aegis: project " | ||
+ | directory | ||
+ | aegis: project " | ||
+ | directory | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | pruebas4@chupete: | ||
+ | aegis: project " | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | Construimos: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: user " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | </ | ||
+ | |||
+ | Probamos: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: appending log to / | ||
+ | aegis.log | ||
+ | aegis: /bin/sh / | ||
+ | t0001a.sh | ||
+ | cout << " | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: appending log to / | ||
+ | aegis.log | ||
+ | aegis: cd / | ||
+ | aegis: /bin/sh / | ||
+ | t0001a.sh | ||
+ | FAILED | ||
+ | aegis: project " | ||
+ | good | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | |||
+ | Generamos los ficheros de diferencias: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: appending log to / | ||
+ | aegis.log | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y damos la integración por finalizada, constituyendo una nueva " | ||
+ | del cambio: | ||
+ | |||
+ | < | ||
+ | pruebas4@chupete:/ | ||
+ | aegis: appending log to / | ||
+ | aegis.log | ||
+ | aegis: cd / | ||
+ | aegis: user " | ||
+ | aegis: ci -u -d -M -m' | ||
+ | -wpruebas2 -t/dev/null / | ||
+ | / | ||
+ | / | ||
+ | / | ||
+ | new revision: 1.2; previous revision: 1.1 | ||
+ | done | ||
+ | RCS file: / | ||
+ | done | ||
+ | aegis: ( rlog -r / | ||
+ | '/ | ||
+ | aegis: ci -u -d -M -m' | ||
+ | -wpruebas2 -t/dev/null / | ||
+ | / | ||
+ | / | ||
+ | / | ||
+ | new revision: 1.2; previous revision: 1.1 | ||
+ | done | ||
+ | RCS file: / | ||
+ | done | ||
+ | aegis: ( rlog -r / | ||
+ | '/ | ||
+ | aegis: ci -u -d -M -m' | ||
+ | -wpruebas2 -t/dev/null / | ||
+ | / | ||
+ | / | ||
+ | / | ||
+ | initial revision: 1.1 | ||
+ | done | ||
+ | RCS file: / | ||
+ | done | ||
+ | aegis: ( rlog -r / | ||
+ | {print $2}' ) > / | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | aegis: warning: file times in future | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | 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:/ | ||
+ | aegis: project " | ||
+ | changed | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Por lo que es necesario re-copiar main.cpp: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y ahora, al realizar el cambio, ya vemos el cambio que el otro | ||
+ | desarrollador hizo: | ||
+ | |||
+ | < | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Insertaremos nuestro propio cambio: | ||
+ | |||
+ | < | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | cout << " | ||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y ahora sí que podremos finalizar nuestro desarrollo. Construir, probar y | ||
+ | diferencias: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: logging to / | ||
+ | aegis: project " | ||
+ | baseline | ||
+ | aegis: project " | ||
+ | aegis: exit 0 | ||
+ | aegis: project " | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: /bin/sh / | ||
+ | cout << " | ||
+ | aegis: project " | ||
+ | aegis: project " | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: cd / | ||
+ | aegis: /bin/sh / | ||
+ | FAILED | ||
+ | aegis: project " | ||
+ | good | ||
+ | aegis: project " | ||
+ | pruebas3@chupete: | ||
+ | aegis: appending log to / | ||
+ | aegis: set +e; diff -U10 -a / | ||
+ | hello_world/ | ||
+ | main.cpp > / | ||
+ | test $? -le 1 | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | Y finalizar el desarrollo: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | aegis: project " | ||
+ | </ | ||
+ | |||
+ | === 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, | ||
+ | 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 " | ||
+ | todos los cambios realizados, probados, revisados e integrados, de tal modo que | ||
+ | se trataría en todo momento de una versión "que funciona" | ||
+ | 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 | ||
+ | ''/ | ||
+ | |||
+ | < | ||
+ | chupete:/ | ||
+ | 0022 | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | </ | ||
+ | |||
+ | == 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 " | ||
+ | grupo de desarrolladores (src): | ||
+ | |||
+ | < | ||
+ | chupete:~# usermod -G src pruebas2 | ||
+ | </ | ||
+ | |||
+ | Una vez hecho esto, el import funcionará perfectamente: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | File / | ||
+ | N prueba/ | ||
+ | |||
+ | No conflicts created by this import | ||
+ | </ | ||
+ | |||
+ | |||
+ | Comprobamos que efectivamente se obtienen bien los fuentes: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | cvs checkout: warning: cannot write to history file / | ||
+ | cvs checkout: Updating prueba | ||
+ | U prueba/ | ||
+ | pruebas2@chupete: | ||
+ | prueba | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | CVS prueba.txt | ||
+ | pruebas2@chupete: | ||
+ | prueba/ | ||
+ | pruebas2@chupete: | ||
+ | </ | ||
+ | |||
+ | El mensaje de error que aparece es porque los permisos de escritura para el | ||
+ | fichero " | ||
+ | |||
+ | < | ||
+ | chupete:/ | ||
+ | total 92 | ||
+ | drwxr-sr-x | ||
+ | -rw-r--r-- | ||
+ | -rw-r--r-- | ||
+ | chupete:/ | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | [....] | ||
+ | cvs import: Importing / | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | N hello_world/ | ||
+ | </ | ||
+ | |||
+ | No conflicts created by this import | ||
+ | |||
+ | Ahora ya podemos eliminar el directorio " | ||
+ | primer check-out directamente del repositorio de código fuente: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | aegis.conf | ||
+ | pruebas2@chupete: | ||
+ | cvs checkout: Updating hello_world | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | [...] | ||
+ | </ | ||
+ | |||
+ | Ha funcionado correctamente. Ahora realizaremos un cambio en el | ||
+ | fichero main.cpp: | ||
+ | |||
+ | < | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y lo guardamos en el repositorio: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | Checking in 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 | ||
+ | -'' | ||
+ | |||
+ | < | ||
+ | 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: | ||
+ | </ | ||
+ | |||
+ | 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, | ||
+ | editor que queremos usar para editar los archivos de log: | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | pruebas1@chupete: | ||
+ | </ | ||
+ | |||
+ | Y seguidamente haremos el " | ||
+ | |||
+ | < | ||
+ | pruebas1@chupete: | ||
+ | The authenticity of host ' | ||
+ | RSA key fingerprint is 7b: | ||
+ | Are you sure you want to continue connecting (yes/no)? | ||
+ | Warning: Permanently added ' | ||
+ | pruebas1@localhost' | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | [...] | ||
+ | </ | ||
+ | |||
+ | 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: | ||
+ | : | ||
+ | pruebas3@chupete: | ||
+ | / | ||
+ | pruebas3@chupete: | ||
+ | The authenticity of host ' | ||
+ | RSA key fingerprint is 7b: | ||
+ | Are you sure you want to continue connecting (yes/no)? | ||
+ | Warning: Permanently added ' | ||
+ | pruebas3@localhost' | ||
+ | cvs server: Updating hello_world | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | U hello_world/ | ||
+ | [....] | ||
+ | </ | ||
+ | |||
+ | |||
+ | == 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: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | </ | ||
+ | |||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | </ | ||
+ | |||
+ | el usuario pruebas2 guarda sus cambios: | ||
+ | |||
+ | < | ||
+ | pruebas2@chupete: | ||
+ | cvs commit: Examining . | ||
+ | cvs commit: Examining .deps | ||
+ | cvs commit: Examining docs | ||
+ | cvs commit: Examining docs/en | ||
+ | cvs commit: Examining templates | ||
+ | Checking in main.cpp; | ||
+ | / | ||
+ | new revision: 1.5; previous revision: 1.4 | ||
+ | done | ||
+ | </ | ||
+ | |||
+ | el usuario pruebas3 guarda sus cambios: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | cvs commit: Examining . | ||
+ | cvs commit: Examining .deps | ||
+ | cvs commit: Examining docs | ||
+ | cvs commit: Examining docs/en | ||
+ | cvs commit: Examining templates | ||
+ | pruebas3@localhost' | ||
+ | cvs server: Up-to-date check failed for ' | ||
+ | cvs [server aborted]: correct above errors first! | ||
+ | </ | ||
+ | |||
+ | e informa que se ha producido un error al actualizar el fichero " | ||
+ | Al ejecutar update para que actualice su contenido: | ||
+ | |||
+ | < | ||
+ | pruebas3@chupete: | ||
+ | pruebas3@localhost' | ||
+ | cvs server: Updating . | ||
+ | M Makefile | ||
+ | RCS file: / | ||
+ | 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/ | ||
+ | cvs server: Updating docs/en | ||
+ | M docs/ | ||
+ | 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[]) | ||
+ | { | ||
+ | <<<<<<< | ||
+ | cout << " | ||
+ | ======= | ||
+ | cout << " | ||
+ | >>>>>>> | ||
+ | |||
+ | 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, | ||
+ | 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, | ||
+ | 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 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 " | ||
+ | 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 " | ||
+ | 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 " | ||
+ | " | ||
+ | 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, | ||
+ | en el directorio ''/ | ||
+ | |||
+ | < | ||
+ | chupete:~# svnadmin create /var/svn/ | ||
+ | chupete:~# ls /var/svn | ||
+ | README.txt | ||
+ | chupete:~# more / | ||
+ | This is a Subversion repository; use the ' | ||
+ | it. Do not add, delete, or modify files here unless you know how | ||
+ | to avoid corrupting the repository. | ||
+ | |||
+ | If the directory " | ||
+ | you may need to tweak the values in " | ||
+ | requirements of your site. | ||
+ | |||
+ | Visit http:// | ||
+ | </ | ||
+ | |||
+ | **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 '' | ||
+ | repositorio. Para ello, creamos un grupo '' | ||
+ | usuarios que nos servirían para hacer las pruebas: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | </ | ||
+ | |||
+ | Los permisos sobre el directorio serán 2770 -el " | ||
+ | y tiene por objeto que cada fichero o directorio que se cree dentro de | ||
+ | ese directorio pertenezca al grupo al que pertenece el directorio-, | ||
+ | y el grupo será svn: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# chmod 2770 / | ||
+ | chupete:~# chgrp svn /var/svn | ||
+ | </ | ||
+ | |||
+ | Los comandos relacionados con subversion sólo podrán ser ejecutados por | ||
+ | root o los miembros del grupo svn: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# ls -la / | ||
+ | -rwxr-xr-x | ||
+ | -rwxr-xr-x | ||
+ | -rwxr-xr-x | ||
+ | -rwxr-xr-x | ||
+ | -rwxr-xr-x | ||
+ | -rwxr-xr-x | ||
+ | chupete:~# chgrp svn / | ||
+ | chupete:~# chmod 550 / | ||
+ | # el comando " | ||
+ | # a que todos los usuarios deberán tener acceso al mismo (por si quieren | ||
+ | # utilizar Subversion como cliente) | ||
+ | chupete:~# chmod 555 / | ||
+ | chupete:~# ls -la / | ||
+ | -r-xr-xr-x | ||
+ | -r-xr-x--- | ||
+ | -r-xr-x--- | ||
+ | -r-xr-x--- | ||
+ | -r-xr-x--- | ||
+ | -r-xr-x--- | ||
+ | </ | ||
+ | |||
+ | A continuación, | ||
+ | repositorio lo creará el usuario root también, para que los permisos sean | ||
+ | lo más uniformes posible: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | README.txt | ||
+ | chupete:/ | ||
+ | This is a Subversion repository; use the ' | ||
+ | it. Do not add, delete, or modify files here unless you know how | ||
+ | to avoid corrupting the repository. | ||
+ | |||
+ | If the directory " | ||
+ | you may need to tweak the values in " | ||
+ | requirements of your site. | ||
+ | </ | ||
+ | |||
+ | A continuación, | ||
+ | acceso al mismo, cambiaremos el grupo de acceso para todos los ficheros | ||
+ | del repositorio: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | total 36 | ||
+ | drwxrwx--- | ||
+ | drwxr-xr-x | ||
+ | -rw-r--r-- | ||
+ | drwxr-xr-x | ||
+ | drwxr-xr-x | ||
+ | drwxr-sr-x | ||
+ | -r--r--r-- | ||
+ | drwxr-xr-x | ||
+ | drwxr-xr-x | ||
+ | </ | ||
+ | |||
+ | Cambios de permisos: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | chupete:~# chmod 600 / | ||
+ | chupete:~# chmod 600 / | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | chupete:/ | ||
+ | </ | ||
+ | |||
+ | Seguidamente comprobaremos que el acceso al repositorio está permitido | ||
+ | haciendo que uno de los usuarios haga una importación de un proyecto: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | [....] | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | |||
+ | Committed revision 1. | ||
+ | </ | ||
+ | |||
+ | Se ha comprobado que el usuario pruebas1 como otro usuario que pertenezca | ||
+ | al grupo '' | ||
+ | check-out y check-in de modificaciones, | ||
+ | que los permisos que se han asignado funcionan con normalidad y permiten | ||
+ | la operacion del sistema. | ||
+ | |||
+ | Quedaría comprobar que aquellos usuarios que no pertenecen a ese grupo | ||
+ | especial no disponen de los permisos adecuados para ello: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | / | ||
+ | pruebas2@chupete: | ||
+ | svn: Unable to open an ra_local session to URL | ||
+ | svn: Unable to open repository ' | ||
+ | svn: Can't open file '/ | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Realizando del primer cambio === | ||
+ | |||
+ | Nuestro primer paso será, como viene siendo habitual, realizar una importación | ||
+ | del proyecto " | ||
+ | posteriormente realizaremos un cambio en el mismo para reflejarlo en el | ||
+ | repositorio. | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | Adding | ||
+ | Adding | ||
+ | [....] | ||
+ | Adding | ||
+ | Adding | ||
+ | Adding | ||
+ | |||
+ | Committed revision 1. | ||
+ | </ | ||
+ | |||
+ | El siguiente paso será hacer el checkout inicial: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | pruebas1@chupete: | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | [...] | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | Checked out revision 1. | ||
+ | </ | ||
+ | |||
+ | Realizar un cambio en uno de los archivos: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | Raul Luna Rodriguez < | ||
+ | pruebas1 < | ||
+ | </ | ||
+ | |||
+ | Y hacer un commit: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | Sending | ||
+ | Transmitting file data . | ||
+ | Committed revision 2. | ||
+ | </ | ||
+ | |||
+ | Y posteriormente actualizar los cambios del servidor: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | 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; | ||
+ | de internet, xinetd. | ||
+ | |||
+ | **Creando un usuario no privilegiado** | ||
+ | |||
+ | Nuestro primer paso -que no lo dice la documentación- será crear un | ||
+ | usuario no privilegiado para que sea quien acceda al repositorio: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# useradd -c ' | ||
+ | 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: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# chown svn /var/svn -Rv | ||
+ | changed ownership of / | ||
+ | changed ownership of / | ||
+ | changed ownership of / | ||
+ | changed ownership of / | ||
+ | changed ownership of / | ||
+ | changed ownership of / | ||
+ | [...] | ||
+ | </ | ||
+ | |||
+ | **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 / | ||
+ | subversion: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# joe / | ||
+ | [....] | ||
+ | mysql | ||
+ | mysql | ||
+ | svn | ||
+ | svn | ||
+ | rfe | ||
+ | rfe | ||
+ | [....] | ||
+ | </ | ||
+ | |||
+ | Para luego configurar xinetd para que ejecute el servidor svnserve | ||
+ | cuando se le requiera: | ||
+ | |||
+ | <code shell> | ||
+ | chupete:~# joe / | ||
+ | # Subversion | ||
+ | service svn | ||
+ | { | ||
+ | socket_type | ||
+ | protocol | ||
+ | wait = no | ||
+ | user = svn | ||
+ | server | ||
+ | server_args | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **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 | ||
+ | ''/ | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | [general] | ||
+ | ### These options control access to the repository for unauthenticated | ||
+ | ### and authenticated users. | ||
+ | ### and " | ||
+ | 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. | ||
+ | ### is repository' | ||
+ | realm = Project TAS repository | ||
+ | |||
+ | También es necesario configurar adecuadamente el fichero passwd que se | ||
+ | encuentra en el mismo directorio: | ||
+ | |||
+ | chupete:/ | ||
+ | ### 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: | ||
+ | |||
+ | <code shell> | ||
+ | rluna@chupete: | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | [....] | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | 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 '' | ||
+ | como '' | ||
+ | |||
+ | La configuración '' | ||
+ | |||
+ | <code shell> | ||
+ | chupete:/ | ||
+ | # Subversion | ||
+ | service svn | ||
+ | { | ||
+ | socket_type | ||
+ | protocol | ||
+ | wait = no | ||
+ | user = svn | ||
+ | group = svn | ||
+ | umask = 002 | ||
+ | server | ||
+ | server_args | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Y podremos comprobar que la prueba sigue funcionando (tras reiniciar el | ||
+ | super-demonio de internet): | ||
+ | |||
+ | < | ||
+ | rluna@chupete: | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | [....] | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | Checked out revision 2. | ||
+ | </ | ||
+ | |||
+ | == Descargando el repositorio para trabajar con él == | ||
+ | |||
+ | Ahora efectuaremos una descarga del repositorio para el usuario " | ||
+ | y para el usuario " | ||
+ | subversion): | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | [....] | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | A hello_world/ | ||
+ | Checked out revision 6. | ||
+ | </ | ||
+ | |||
+ | == Efectuando un cambio concurrente== | ||
+ | |||
+ | |||
+ | Ahora, tanto el usuario pruebas1 como pruebas2 modificarán el fichero main.cpp: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | < | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Primero pruebas1 hace el commit: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas1@chupete: | ||
+ | Authentication realm: < | ||
+ | Password for ' | ||
+ | Sending | ||
+ | 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, | ||
+ | efectuaremos el commit para el usuario pruebas2: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | |||
+ | Authentication realm: < | ||
+ | Password for ' | ||
+ | Sending | ||
+ | Transmitting file data .svn: Commit failed (details follow): | ||
+ | svn: Out of date: '/ | ||
+ | </ | ||
+ | |||
+ | Subversion informa que el fichero main.cpp está "out of date", es decir, | ||
+ | está obsoleto según la última revisión. Procedemos entonces a actualizarlo: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | 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: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | <<<<<<< | ||
+ | cout << " | ||
+ | ======= | ||
+ | cout << " | ||
+ | >>>>>>> | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | pruebas2@chupete: | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | pruebas2@chupete: | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | pruebas2@chupete: | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | cout << " | ||
+ | |||
+ | return EXIT_SUCCESS; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | En este caso, haremos prevalecer nuestro cambio sobre el del otro usuario: | ||
+ | |||
+ | <code shell> | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | pruebas2@chupete: | ||
+ | Sending | ||
+ | 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" | ||
+ | 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 " | ||
+ | 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, " | ||
+ | es muy completa y amena de leer; incluye una guía para hacerse rápidamente | ||
+ | con la aplicació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: | ||
+ | |||
+ | 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, | ||
+ | punto centralizado donde se guarden los fuentes hace que se pierda -o quede | ||
+ | dificultada enormemente- el desarrollo de una versión " | ||
+ | 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: | ||
+ | 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, | ||
+ | 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; | ||
+ | la disponbilidad en otras plataformas -por lo menos en Windows- está | ||
+ | ampliamente consolidada, | ||
+ | servidor. | ||
+ | |||
+ | Sin embargo, las carencias que tiene en la gestión del repositorio son | ||
+ | destacables: | ||
+ | imposibilidad de borrar directorios, | ||
+ | enlaces simbólicos, | ||
+ | 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 | ||
+ | '' | ||
+ | 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 " | ||
+ | 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 " | ||
+ | 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. | ||
+ | |||
+ | |||