Práctica: Introducción a los Sistemas de Control de Versiones. Ejecución Controlada de Un Programa

Objetivos

Escriba un módulo Batch::Simple que provee:

  1. Un constructor new que recibe
    1. El comando: una cadena como 'sort'
    2. Posibles opciones para el comando, por ejemplo -n, etc.
    3. Un posible manejador de fichero para STDIN
    Si el fichero input no se especifica se entenderá que es STDIN. Por ejemplo:

                  my $c = Batch::Simple->new(command => 'sort', stdin => $infile, options => $options);
    
    La llamada a new retorna un objeto de la clase Batch::Simple.
  2. Los objetos Batch::Simple disponen de un método run que ejecutará el programa. El método run devuelve un objeto Batch::Simple::Result que tiene los siguientes atributos:
    1. stdout: La salida por STDOUT del programa
    2. stderr: La salida por STDERR del programa
    3. status: El contenido de $?

Una posible implementación de run consiste en redirigir los flujos de salida y de error durante la ejecución a ficheros utilizando la metodología vista en la sección Salvando Manejadores de Fichero 1.5. Se usarán ficheros temporales para STDOUT y STDERR (veanse File::Temp y File::Spec).

OOP

Para repasar la programación orientada a objetos en Perl puede repasar el capítulo Programación Orientada a Objetos de los apuntes de LHP o mejor aún, usar Moose y leer los siguientes artículos:

Creación del Proyecto para la Elaboración del Módulo

Para repasar la creación de módulos véase el capítulo Módulos de los apuntes de LHP.

Cree la estructura de directorios usando h2xs o Module::Install (Véase Module::Install::Philosophy):

pp2@nereida:~/src$ h2xs -X -A -n Batch::Simple
Defaulting to backwards compatibility with perl 5.8.8
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing Batch-Simple/lib/Batch/Simple.pm
Writing Batch-Simple/Makefile.PL
Writing Batch-Simple/README
Writing Batch-Simple/t/Batch-Simple.t
Writing Batch-Simple/Changes
Writing Batch-Simple/MANIFEST
Añada un directorio para el ejecutable e incluya su presencia dándolo de alta en el fichero Batch-Simple/MANIFEST y en la entrada EXE_FILES en la llamada a WriteMakefile en el fichero Batch-Simple/Makefile.PL:
pp2@nereida:~/src/Batch-Simple$ cat -n MANIFEST
     1  Changes
     2  Makefile.PL
     3  MANIFEST
     4  README
     5  script/batch.pl
     6  t/Batch-Simple.t
     7  lib/Batch/Simple.pm
pp2@nereida:~/src/Batch-Simple$ cat -n Makefile.PL
     1  use ExtUtils::MakeMaker;
     2  WriteMakefile(
     3      NAME              => 'Batch::Simple',
     4      VERSION_FROM      => 'lib/Batch/Simple.pm', # finds $VERSION
     5      PREREQ_PM         => {}, # e.g., Module::Name => 1.1
     6      EXE_FILES         => [ qw{script/batch.pl} ],
     7  );
Hay varias formas de garantizar que durante el periodo de desarrollo de una librería los ejecutables encuentran la librería. Uno es añadir use blib en el ejecutable:
pp2@nereida:~/src/Batch-Simple/script$ cat -n batch.pl
     1  #!/usr/local/bin/perl -w
     2   use strict;
     3   use Carp;
     4   use Getopt::Long;
     5   use Pod::Usage;
     6   use blib;
     7   use Batch::Simple;
     .   ....................
el módulo blib busca por un directorio de tipo blib hasta 5 niveles por encima del actual. Observa que eso significa que trabajas con la copia en blib y no el original en lib. Por tanto deberás hacer make para asegurarte la actualización de la copia. Otra forma de garantizar que la librería se encuentra es incluirla en la variable PERL5LIB.

Aplicación para Las Pruebas

Puedes usar como ejemplo de aplicación el tar.gz que implementa un producto de matrices en C en la página asociada con estos apuntes (fichero matrix.tar.gz)

Use Subversion: Creación de un Repositorio

Revision control, also known as version control, source control or (source) code management (SCM), is the management of changes to documents, programs, and other information stored as computer files. It is most commonly used in software development, where a team of people may change the same files. Changes are usually identified by a number or letter code, termed the 'revision number', 'revision level', or simply 'revision'.

Software tools for revision control are essential for the organization of multi-developer projects.

Subversion es un programa para el control de versiones.

Esta imagen del libro de subversion ilustra su funcionamiento:

1793

You can think of every object in the repository as existing in a sort of two-dimensional coordinate system. The first coordinate is a particular revision tree, and the second coordinate is a path within that tree

Parece que en banot esta instalado subversion. Para crear un repositorio emita el comando svnadmin create:

-bash-3.1$ uname -a
Linux banot.etsii.ull.es 2.6.24.2 #3 SMP Fri Feb 15 10:39:28 WET 2008 i686 i686 i386 GNU/Linux
-bash-3.1$ svnadmin create /home/loginname/repository/
-bash-3.1$ ls -l repository/
total 28
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 conf
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 dav
drwxr-sr-x 5 loginname apache 4096 feb 28 12:09 db
-r--r--r-- 1 loginname apache    2 feb 28 11:58 format
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 hooks
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 locks
-rw-r--r-- 1 loginname apache  229 feb 28 11:58 README.txt

Una alternativa a considerar es ubicar el repositorio en un dispositivo de almacenamiento portable (pendriver)

Añadiendo Proyectos

Ahora esta en condiciones de añadir proyectos al repositorio creado usando svn import:

[loginname@tonga]~/src/perl/> uname -a
Linux tonga 2.6.24.2 #1 SMP Thu Feb 14 15:37:31 WET 2008 i686 i686 i386 GNU/Linux
[loginname@tonga]~/src/perl/> pwd
/home/loginname/src/perl
[loginname@tonga]~/src/perl/> ls -ld /home/loginname/src/perl/Grammar-0.02
drwxr-xr-x 5 loginname Profesor 4096 feb 28  2008 /home/loginname/src/perl/Grammar-0.02
[loginname@tonga]~/src/perl/> svn import -m 'Grammar Extended Module' \
                                        Grammar-0.02/ \
                                        svn+ssh://banot/home/loginname/repository/Grammar
Añadiendo      Grammar-0.02/t
Añadiendo      Grammar-0.02/t/Grammar.t
Añadiendo      Grammar-0.02/lib
Añadiendo      Grammar-0.02/lib/Grammar.pm
Añadiendo      Grammar-0.02/MANIFEST
Añadiendo      Grammar-0.02/META.yml
Añadiendo      Grammar-0.02/Makefile.PL
Añadiendo      Grammar-0.02/scripts
Añadiendo      Grammar-0.02/scripts/grammar.pl
Añadiendo      Grammar-0.02/scripts/Precedencia.yp
Añadiendo      Grammar-0.02/scripts/Calc.yp
Añadiendo      Grammar-0.02/scripts/aSb.yp
Añadiendo      Grammar-0.02/scripts/g1.yp
Añadiendo      Grammar-0.02/Changes
Añadiendo      Grammar-0.02/README

Commit de la revisión 2.

En general, los pasos para crear un nuevo proyecto son:

* mkdir /tmp/nombreProyecto
* mkdir /tmp/nombreProyecto/branches
* mkdir /tmp/nombreProyecto/tags
* mkdir /tmp/nombreProyecto/trunk
* svn mkdir file:///var/svn/nombreRepositorio/nombreProyecto -m 'Crear el proyecto nombreProyecto'
* svn import /tmp/nombreProyecto \
            file:///var/svn/nombreRepositorio/nombreProyecto \
            -m "Primera versión del proyecto nombreProyecto"

Obtener una Copia de Trabajo

La copia en Grammar-0.02 ha sido usada para la creación del proyecto, pero no pertenece aún al proyecto. Es necesario descargar la copia del proyecto que existe en el repositorio. Para ello usamos svn checkout:

[loginname@tonga]~/src/perl/> rm -fR Grammar-0.02
[loginname@tonga]~/src/perl/> svn checkout svn+ssh://banot/home/loginname/repository/Grammar Grammar
A    Grammar/t
A    Grammar/t/Grammar.t
A    Grammar/MANIFEST
A    Grammar/META.yml
A    Grammar/lib
A    Grammar/lib/Grammar.pm
A    Grammar/Makefile.PL
A    Grammar/scripts
A    Grammar/scripts/grammar.pl
A    Grammar/scripts/Calc.yp
A    Grammar/scripts/Precedencia.yp
A    Grammar/scripts/aSb.yp
A    Grammar/scripts/g1.yp
A    Grammar/Changes
A    Grammar/README
Revisión obtenida: 2
Ahora disponemos de una copia de trabajo del proyecto en nuestra máquina local:
[loginname@tonga]~/src/perl/> tree Grammar
Grammar
|-- Changes
|-- MANIFEST
|-- META.yml
|-- Makefile.PL
|-- README
|-- lib
|   `-- Grammar.pm
|-- scripts
|   |-- Calc.yp
|   |-- Precedencia.yp
|   |-- aSb.yp
|   |-- g1.yp
|   `-- grammar.pl
`-- t
    `-- Grammar.t

3 directories, 12 files
[loginname@tonga]~/src/perl/>       
[loginname@tonga]~/src/perl/> cd Grammar
[loginname@tonga]~/src/perl/Grammar/> ls -la
total 44
drwxr-xr-x 6 loginname Profesor 4096 feb 28  2008 .
drwxr-xr-x 5 loginname Profesor 4096 feb 28  2008 ..
-rw-r--r-- 1 loginname Profesor  150 feb 28  2008 Changes
drwxr-xr-x 3 loginname Profesor 4096 feb 28  2008 lib
-rw-r--r-- 1 loginname Profesor  614 feb 28  2008 Makefile.PL
-rw-r--r-- 1 loginname Profesor  229 feb 28  2008 MANIFEST
-rw-r--r-- 1 loginname Profesor  335 feb 28  2008 META.yml
-rw-r--r-- 1 loginname Profesor 1196 feb 28  2008 README
drwxr-xr-x 3 loginname Profesor 4096 feb 28  2008 scripts
drwxr-xr-x 6 loginname Profesor 4096 feb 28  2008 .svn
drwxr-xr-x 3 loginname Profesor 4096 feb 28  2008 t
Observe la presencia de los subdirectorios de control .svn.

Actualización del Proyecto

Ahora podemos modificar el proyecto y hacer públicos los cambios mediante svn commit:

loginname@tonga]~/src/perl/Grammar/> svn rm META.yml
D         META.yml
[loginname@tonga]~/src/perl/Grammar/> ls -la
total 40
drwxr-xr-x 6 loginname Profesor 4096 feb 28  2008 .
drwxr-xr-x 5 loginname Profesor 4096 feb 28 12:34 ..
-rw-r--r-- 1 loginname Profesor  150 feb 28 12:34 Changes
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 lib
-rw-r--r-- 1 loginname Profesor  614 feb 28 12:34 Makefile.PL
-rw-r--r-- 1 loginname Profesor  229 feb 28 12:34 MANIFEST
-rw-r--r-- 1 loginname Profesor 1196 feb 28 12:34 README
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 scripts
drwxr-xr-x 6 loginname Profesor 4096 feb 28  2008 .svn
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 t
[loginname@tonga]~/src/perl/Grammar/> echo "Modifico README" >> README
[loginname@tonga]~/src/perl/Grammar/> svn commit -m 'Just testing ...'
Eliminando     META.yml
Enviando       README
Transmitiendo contenido de archivos .
Commit de la revisión 3.
Observe que ya no es necesario especificar el lugar en el que se encuentra el repositorio: esa información esta guardada en los subdirectorios de administración de subversion .svn

El servicio de subversion parece funcionar desde fuera de la red del centro. Véase la conexión desde una maquina exterior:

pp2@nereida:/tmp$  svn checkout svn+ssh://loginname@banot.etsii.ull.es/home/loginname/repository/Grammar Grammar
loginname@banot.etsii.ull.es's password:
loginname@banot.etsii.ull.es's password:
A    Grammar/t
A    Grammar/t/Grammar.t
A    Grammar/MANIFEST
A    Grammar/lib
A    Grammar/lib/Grammar.pm
A    Grammar/Makefile.PL
A    Grammar/scripts
A    Grammar/scripts/grammar.pl
A    Grammar/scripts/Calc.yp
A    Grammar/scripts/Precedencia.yp
A    Grammar/scripts/aSb.yp
A    Grammar/scripts/g1.yp
A    Grammar/Changes
A    Grammar/README
Revisión obtenida: 3

Comandos Básicos

Autentificación Automática

Para evitar la solicitud de claves cada vez que se comunica con el repositorio establezca autentificación SSH automática. Para ver como hacerlo puede consultar las instrucciones en la sección 2.1.3 o en:

http://search.cpan.org/~casiano/GRID-Machine/lib/GRID/Machine.pod#INSTALLATION

Consulte también las páginas del manual Unix de ssh, ssh-key-gen, ssh_config, scp, ssh-agent, ssh-add, sshd

Especificadores de Revisión

Véase la sección Revision Specifiers en el libro de Subversion

Repasando la Historia

Véase la sección Examining History en el libro de Subversion

Deshaciendo Cambios en la Copia de Trabajo

Véase la sección Undoing Working Changes en el libro de Subversion

Resolución de Conflictos

Ejercicio 1.9.1  

Usando vimdiff como programa de diferencias para subversion

Side by side diffs are much more legible and useful than those in unified format or any other linear diff. By default, the svn diff command presents output in the unified format, though it has an option, --diff-cmd, which allows you to specify the program that will perform the diff. Passing vimdiff as the diff command doesn't work as the options passed by svn diff are a bit complicated:

Veamos que es así. Escribimos el siguiente programa de prueba:

generaciondecodigos@nereida:~/bin$ cat -n ./foo.pl
     1  #!/usr/bin/perl
     2
     3  print "'$_' " for @ARGV;
     4  print "\n";

Ejecución:

$ svn diff --diff-cmd=/home/generaciondecodigos/bin/foo.pl overloading.tex -rPREV
Index: overloading.tex
===================================================================
'-u' '-L' 'overloading.tex      (revisión: 5112)' '-L' 'overloading.tex (copia de trabajo)' '.svn/tmp/tempfile.tmp' 'overloading.tex'
El argumento -u indica que se debe usar unified format y el argumento -L especifica la etiqueta que describe la correspondiente versión.

Es necesario escribir un wrapper que prescinda de esas opciones y que se quede con los nombres de los dos ficheros:

pp2@nereida:~$ cat -n bin/diffwrap.sh
 1  #!/bin/sh
 2
 3  # Configure your favorite diff program here.
 4  DIFF="/usr/bin/vimdiff"
 5
 6  # Subversion provides the paths we need as the sixth and seventh
 7  # parameters.
 8  LEFT=${6}
 9  RIGHT=${7}
10
11  # Call the diff command (change the following line to make sense for
12  # your merge program).
13  $DIFF $LEFT $RIGHT
14
15  # Return an errorcode of 0 if no differences were detected, 1 if some were.
16  # Any other errorcode will be treated as fatal.
Ahora podemos establecer que este programa sea nuestro comando diff para svn editando el fichero de configuración ~/.subversion/config:
pp2@nereida:~/Lbook$ grep 'diff' ~/.subversion/config
### Set diff-cmd to the absolute path of your 'diff' program.
###   Subversion's internal diff implementation.
# diff-cmd = diff_program (diff, gdiff, etc.)
### Set diff3-cmd to the absolute path of your 'diff3' program.
###   Subversion's internal diff3 implementation.
# diff3-cmd = diff3_program (diff3, gdiff3, etc.)
### Set diff3-has-program-arg to 'true' or 'yes' if your 'diff3'
###   program accepts the '--diff-program' option.
# diff3-has-program-arg = [true | false]
diff-cmd = /home/pp2/bin/diffwrap.sh

Tracking Systems

Protocolos y Esquemas

Subversion admite una variedad de protocolos de red para conectarse con el repositorio. Con subversion viene el programa svnserve que escucha por conexiones de red y soporta un modo de autentificación simple. Sólo debe usarse si esta en una LAN privada. En el siguiente ejemplo arranco el daemon svnserve en banot:

bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i386 GNU/Linux
-bash-3.2$ svnserve --listen-port=4040 -d -r repository
-bash-3.2$ ps -fA | grep svn
casiano  11876     1  0 11:16 ?        00:00:00 svnserve --listen-port=4040 -d -r repository
casiano  12036 11698  0 11:22 pts/0    00:00:00 grep svn

Ahora puedo acceder al proyecto vía svn desde otra máquina:

casiano@tonga:~$ svn co svn://banot:4040/acme-svn/trunk chuchu
A    chuchu/t
A    chuchu/t/00.load.t
A    chuchu/t/perlcritic.t
A    chuchu/t/pod.t
A    chuchu/t/pod-coverage.t
A    chuchu/MANIFEST
A    chuchu/lib
A    chuchu/lib/Acme
A    chuchu/lib/Acme/SVN.pm
A    chuchu/Makefile.PL
A    chuchu/Changes
A    chuchu/Build.PL
A    chuchu/README
Revisión obtenida: 6

Aunque no tengo permisos de ejecución:

casiano@tonga:~$ cd chuchu
casiano@tonga:~/chuchu$ echo prueba > prueba.txt
casiano@tonga:~/chuchu$ svn add prueba.txt
A         prueba.txt
casiano@tonga:~/chuchu$ svn commit prueba.txt -m 'added prueba.txt'
svn: Falló el commit (detalles a continuación):
svn: falló la autorización

Habría que dar permisos de escritura al repositorio y crear usuarios (véase svnserve, a custom server y svnserve.conf para los detalles)

El Comando blame

El comando blame permite responder a la pregunta ¿En que revisión se introdujo esta línea?

La salida de svn blame es una versión formateada del fichero en el que cada línea es prefijada con la revisión en que la línea fué introducida y el autor de esa revisión. Un sinónimo para blame es annotate. Sigue un ejemplo de salida:

pp2@nereida:~/LBench-Test/lib/Bench$ svn annotate Test.pm | tail -25
  3063    casiano     for my $exp_name ( @{ $self->{SELECTED} } ) {
  3064    lgforte         $self->{EXPERIMENTS}{$exp_name}->connect;
  3063    casiano         $self->{EXPERIMENTS}{$exp_name}->execute_preamble;
  3063    casiano     }
  2248    casiano 
  3063    casiano     # Tomamos el array TEST como array para mantener el orden
  3063    casiano     # Por ello, no recorremos con for (keys @array)
  3063    casiano     # Usamos entonces while (@array) { $key, $value = splice ... }
  3063    casiano     #
  3063    casiano     while ( @{ $self->{TESTS} } ) {
  3063    casiano         my ( $test, $params ) = splice @{ $self->{TESTS} }, 0, 2;
  2248    casiano 
  3063    casiano         for my $exp_name ( @{ $self->{SELECTED} } ) {
  3129    lgforte            $self->{EXPERIMENTS}{$exp_name}->save_result( $params, $test );
  3063    casiano         }
  3063    casiano     }
  3063    casiano 
  3063    casiano     for my $exp_name ( @{ $self->{SELECTED} } ) {
  3063    casiano         $self->{EXPERIMENTS}{$exp_name}->execute_postamble;
  3063    casiano     }
  2248    casiano }
  2248    casiano 
  2248    casiano 1;
  2248    casiano 
  2248    casiano __END__

Propiedades

Las propiedades son metadatos asociados con los ficheros o directorios en el repositorio. Las propiedades pueden ser modificadas y actualizadas por los usuarios de la misma forma que los ficheros. Del mismo modo esta actividad puede dar lugar a conflictos. Las propiedades se usan para asociar datos extra con un fichero. Por ejemplo cada imagen en un repositorio conteniendo imágenes puede tener asociada una propiedad binaria que sea un thumbnail.

Propiedades Subversion

Las propiedades cuyo nombre comienzan por svn: estan reservadas para subversion. Por ejemplo:

Sustitución de Palabras Clave

Véase la sección Keyword Substitution en el libro de Subversion

Autopropiedades

El directorio ~/.subversion contiene algunos ficheros de control:

pp2@nereida:~/Lbook$ tree /home/pp2/.subversion/
/home/pp2/.subversion/
|-- README.txt
|-- auth
|   |-- svn.simple
|   |   |-- 0538d14359bc5c0
|   |   |-- 16dbf53b0205461
|   |   `-- 7cb71bc67c219b9
|   |-- svn.ssl.server
|   `-- svn.username
|-- config
`-- servers

4 directories, 6 files
Fichero ~/.subversion/config establece la configuración por defecto. Utiliza el formato de configuración INI. Para establecer propiedades en términos de las extensiones de los ficheros debe rellenarse la sección auto-props:
### Set enable-auto-props to 'yes' to enable automatic properties
### for 'svn add' and 'svn import', it defaults to 'no'.
### Automatic properties are defined in the section 'auto-props'.
enable-auto-props = yes

### Section for configuring automatic properties.
[auto-props]
### The format of the entries is:
###   file-name-pattern = propname[=value][;propname[=value]...]
### The file-name-pattern can contain wildcards (such as '*' and
### '?').  All entries which match will be applied to the file.
### Note that auto-props functionality must be enabled, which
### is typically done by setting the 'enable-auto-props' option.
*.c = svn:eol-style=native
*.cpp = svn:eol-style=native
*.h = svn:eol-style=native
# *.dsp = svn:eol-style=CRLF
# *.dsw = svn:eol-style=CRLF
*.sh = svn:eol-style=native;svn:executable
*.pl = svn:eol-style=native;svn:executable
# *.txt = svn:eol-style=native
*.png = svn:mime-type=image/png
*.jpg = svn:mime-type=image/jpeg
Makefile = svn:eol-style=native

Propiedades y Compartición de Documentos entre Proyectos: svn:externals

Conforme evolucionan los proyectos descubrimos que existen áreas comunes entre ellos que pueden factorizarse y ser reutilizadas en varios proyectos.

Una forma de gestionar la compartición de subproyectos entre proyectos es mediante la propiedad svn:externals, la cual nos permite incluir contenidos en otro repositorio en nuestra copia de trabajo.

Por ejemplo, en la mayoría de los artículos y apuntes que escribo en LATEX comparto la bibliografía. En vez de tener múltiples ficheros .bib de bibliografía por artículo prefiero tener uno garantizando así la consistencia. Del mismo modo comparto los ficheros de estilo LATEX y las definiciones de comandos LATEX. Así en el proyecto svn de estos apuntes que lee tenemos:

pp2@nereida:~/Lbook$ svn proplist .
Propiedades en '.':
  svn:externals
pp2@nereida:~/Lbook$ svn propget svn:externals
perlbib svn+ssh://casiano@arlom.pcg.ull.es/var/svn/casiano/BIBTEX/PERLBIB/trunk
booktt svn+ssh://casiano@arlom.pcg.ull.es/var/svn/casiano/booktt/trunk
lhplabels svn+ssh://casiano@arlom.pcg.ull.es/var/svn/casiano/LHP/perlexamples/labels.pl
Subversion no hace automáticamente commit de los cambios en las copias de los subproyectos externos. Es necesario cambiar al directorio en cuestión y ejecutar svn commit.
pp2@nereida:~/Lbook$ svn status -qu
Estado respecto a la revisión:   5463

Averiguando el estado del recurso externo en 'perlbib'
Estado respecto a la revisión:   5463

Averiguando el estado del recurso externo en 'booktt'
Estado respecto a la revisión:   5463

He aquí un ejemplo de como establecer svn:externals:

MacBookdeCasiano:LPLDI2011 casiano$ svn propset svn:externals \
   'code svn+ssh://casiano@orion.pcg.ull.es/var/svn/casiano/PL/PLconferences/softwarepracticeandexperience/code' .
property 'svn:externals' set on '.'
Obsérvese el uso de las comillas simples protegiendo al segundo argumento de svn propset. Ahora un update cargará el subproyecto externo:
MacBookdeCasiano:LPLDI2011 casiano$ svn update

Fetching external item into 'code'
A    code/MyopicPPCR.eyp
A    code/pascalnestedeyapp2.eyp
A    code/noPackratSolvedExpRG2.eyp
A    code/pascalnestedeyapp3.eyp
A    code/pascalenumeratedvsrangePPCR.eyp
A    code/reducereduceconflictnamefirst.eyp
A    code/Cplusplus.eyp
A    code/pascalenumeratedvsrangesolvedviadyn.eyp
A    code/dynamicgrammar.eyp
A    code/Range.eyp
A    code/nopackratSolved.eyp
A    code/reducereduceconflictPPCRwithAction.eyp
A    code/Myopic.eyp
A    code/noLRk_exp.eyp
A    code/Calc.eyp
A    code/input_for_dynamicgrammar.txt
A    code/pascalenumeratedvsrange.eyp
A    code/noPackratSolvedExpRG.eyp
A    code/pascalenumeratedvsrangenested.eyp
A    code/reducereduceconflictPPCR.eyp
A    code/nopackratPPCR.eyp
A    code/noPackratSolvedExp.eyp
A    code/Cplusplus2.eyp
A    code/MyopicSolved.eyp
A    code/pascalenumeratedvsrangesolvedviadyn2.eyp
A    code/noLRk_expSolved.eyp
A    code/noPackratSolvedExpRGconcept.eyp
A    code/reducereduceconflict.eyp
A    code/nopackrat.eyp
Updated external to revision 6318.

Updated to revision 6318.
MacBookdeCasiano:LPLDI2011 casiano$

Consejo tomado del libro de subversión:

You should seriously consider using explicit revision numbers in all of your externals definitions. Doing so means that you get to decide when to pull down a different snapshot of external information, and exactly which snapshot to pull. Besides avoiding the surprise of getting changes to third-party repositories that you might not have any control over, using explicit revision numbers also means that as you backdate your working copy to a previous revision, your externals definitions will also revert to the way they looked in that previous revision, which in turn means that the external working copies will be updated to match they way they looked back when your repository was at that previous revision. For software projects, this could be the difference between a successful and a failed build of an older snapshot of your complex codebase.

svn export

El comando svn export es similar a svn checkout y nos permite crear una copia del proyecto que no contiene los directorios de administración .svn

pp2@nereida:~$ svn help export
export: Crea una copia no versionada de un árbol.
uso: 1. export [-r REV] URL[@REVPEG] [RUTA]
     2. export [-r REV] RUTA1[@REVPEG] [RUTA2]

  1. Exporta un árbol de directorios limpio del repositorio a RUTA,
     especificado por RUTA, en la revisión REV si se especifica, de
     otro modo se exporta HEAD. Si se omite la RUTA, se usa el último
     componente del URL para el nombre del directorio local creado.

  2. Exporta un árbol de directorios limpio a RUTA2 a partir de la copia
     de trabajo especificada por RUTA1, en la revisión REV si especificada,
     si no en WORKING.  Si se omite RUTA2, se usa el último componente de
     RUTA1 para el nombre del directorio local creado. Si no se especifica
     REV se preservarán todos los cambios locales.  Los archivos que no
     estén bajo control de versiones no se copiarán.

  Si se especifica, REVPEG determina la revisión en la que el objetivo
  se busca primero.

Completando comandos de subversion en bash

El comando shopt permite establecer y ver las opciones de la bash:

casiano@exthost:~$ help shopt
shopt: shopt [-pqsu] [-o] [optname ...]
    Set and unset shell options.
    
    Change the setting of each shell option OPTNAME.  Without any option
    arguments, list all shell options with an indication of whether or not each
    is set.
    
    Options:
      -o    restrict OPTNAMEs to those defined for use with `set -o'
      -p    print each shell option with an indication of its status
      -q    suppress output
      -s    enable (set) each OPTNAME
      -u    disable (unset) each OPTNAME
    
    Exit Status:
    Returns success if OPTNAME is enabled; fails if an invalid option is
    given or OPTNAME is disabled.
Sin argumentos muestra los valores actuales de las opciones:
casiano@exthost:~$ shopt
autocd          off
cdable_vars     off
cdspell         off
checkhash       off
checkjobs       off
checkwinsize    on
cmdhist         on
compat31        off
compat32        off
dirspell        off
dotglob         off
execfail        off
expand_aliases  on
extdebug        off
extglob         on
extquote        on
failglob        off
force_fignore   on
globstar        off
gnu_errfmt      off
histappend      off
histreedit      off
histverify      off
hostcomplete    on
huponexit       off
interactive_comments    on
lithist         off
login_shell     on
mailwarn        off
no_empty_cmd_completion off
nocaseglob      off
nocasematch     off
nullglob        off
progcomp        on
promptvars      on
restricted_shell    off
shift_verbose   off
sourcepath      on
xpg_echo        off

Las opciones extglob y progcomp gobiernan la forma en la que se completan los comandos cuando se presiona la tecla TAB:

$shopt -s extglob progcomp
Por otro lado el comando complete permite especificar como se completa un comando:
complete: complete [-abcdefgjksuv] [-pr] [-o option] [-A action] [-G globpat] \
                   [-W wordlist]  [-F function] [-C command] [-X filterpat] \
                   [-P prefix] [-S suffix] [name ...]
    Specify how arguments are to be completed by Readline.
    
    For each NAME, specify how arguments are to be completed.  If no options
    are supplied, existing completion specifications are printed in a way that
    allows them to be reused as input.
    
    Options:
      -p    print existing completion specifications in a reusable format
      -r    remove a completion specification for each NAME, or, if no
        NAMEs are supplied, all completion specifications
    
    When completion is attempted, the actions are applied in the order the
    uppercase-letter options are listed above.
    
    Exit Status:
    Returns success unless an invalid option is supplied or an error occurs.

Así, si hacemos:

complete -W 'add blame praise annotate cat checkout co cleanup commit ci copy delete del \
             remove rm diff di export help h import info list ls log merge mkdir move mv \
             rename ren propdel pdel pd propedit pedit pe propget pget pg proplist plist pl \
             propset pset ps resolved revert status stat st switch sw update up' svn

Un comando como svn h<TAB> se completrá a svn help.

Mejor aún, localize el fichero bash_completion que viene con la distribución de subversion. Puede encontrarlo en:

hágale un source a dicho script:

$ . bash_completion
Así podrá completar también las opciones de los subcomandos.

Copia de un Repositorio

Para copiar un repositorio es necesario asegurarse que el repositorio no es modificado durante la copia. Si eso ocurriera la copia podría no ser válida. Además, para garantizar la consistencia, los distintos ficheros que constituyen la Berkeley DB deben ser copiados en un cierto orden.

El comando svnadmin hotcopy

svnadmin hotcopy RUTA_REPOS NUEVA_RUTA_REPOS
permite realizar una copia consistente de un repositorio. No es necesario detener la actividad de los clientes durante el proceso.

Volcado y Carga de los contenidos de un Repositorio

Si queremos migrar un repositorio en una versión mas antigua de subversion a una mas nueva o queremos hacer una copia del repositorio que sea mas independiente de la versión de Subversion utilizada podemos usar los comandos svnadmin dump y svnadmin load. Estos dos comandos usan un sencillo formato de volcado consistente en cabeceras RFC 822 (como los headers en e-mail) que son sencillos de analizar y en los contenidos en bruto de los ficheros del repositorio. Una copia por volcado y carga nos protege también contra posibles cambios en la versión de la librería DBD subyacente.

-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i386 GNU/Linux
-bash-3.2$ svnlook youngest repository/
6
-bash-3.2$ svnadmin dump repository/ > dumprep.6
* Revisión 0 volcada.
* Revisión 1 volcada.
* Revisión 2 volcada.
* Revisión 3 volcada.
* Revisión 4 volcada.
* Revisión 5 volcada.
* Revisión 6 volcada.
-bash-3.2$ ls -ltr | tail -1
-rw-r--r-- 1 casiano apache 14962 abr  3 18:29 dumprep.6

Para restaurar el repositorio en otra máquina debemos primero crear el repositorio y a continuación cargarcon svnadmin load el fichero volcado en la operación anterior:

pp2@nereida:~$ ssh banot cat dumprep.6 | svnadmin load mietsiirep
<<< Nueva transacción iniciada, basada en la revisión original 1
     * añadiendo ruta : acme-svn ... hecho.
     * añadiendo ruta : acme-svn/branches ... hecho.
     * añadiendo ruta : acme-svn/trunk ... hecho.
     * añadiendo ruta : acme-svn/trunk/Build.PL ... hecho.
     * añadiendo ruta : acme-svn/trunk/Changes ... hecho.
     * añadiendo ruta : acme-svn/trunk/MANIFEST ... hecho.
     * añadiendo ruta : acme-svn/trunk/Makefile.PL ... hecho.
     * añadiendo ruta : acme-svn/trunk/README ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib/Acme ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib/Acme/SVN.pm ... hecho.
     * añadiendo ruta : acme-svn/trunk/t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/00.load.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/perlcritic.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/pod-coverage.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/pod.t ... hecho.

------- Commit de la revisión 1 >>>

<<< Nueva transacción iniciada, basada en la revisión original 2
     * añadiendo ruta : acme-svn/branches/myacme-svn ...COPIED... hecho.

------- Commit de la revisión 2 >>>

<<< Nueva transacción iniciada, basada en la revisión original 3
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 3 >>>

<<< Nueva transacción iniciada, basada en la revisión original 4
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 4 >>>

<<< Nueva transacción iniciada, basada en la revisión original 5
     * editando ruta : acme-svn/branches/myacme-svn/Makefile.PL ... hecho.

------- Commit de la revisión 5 >>>

<<< Nueva transacción iniciada, basada en la revisión original 6
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 6 >>>

Ahora el nuevo repositorio puede ser accedido desde cualquier otra máquina:

casiano@orion:~$ svn ls svn+ssh://pp2/home/pp2/mietsiirep
acme-svn/
casiano@orion:~$ svn ls svn+ssh://pp2/home/pp2/mietsiirep/acme-svn
branches/
trunk/
casiano@orion:~$ svn ls svn+ssh://pp2/home/pp2/mietsiirep/acme-svn/trunk
Build.PL
Changes
MANIFEST
Makefile.PL
README
lib/
t/

Copias Incrementales

El comando svnadmin dump admite las opciones --incremental y --revision que permiten producir copias mas pequeñas.

Guardemos primero las versiones de la 1 a la 4:

-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i386 GNU/Linux

-bash-3.2$ svnadmin dump --incremental --revision 1:4 repository > dumprep.1to4
* Revisión 1 volcada.
* Revisión 2 volcada.
* Revisión 3 volcada.
* Revisión 4 volcada.
y después las de la 5 a la 6:
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i386 GNU/Linux

-bash-3.2$ svnadmin dump --incremental --revision 5:6 repository > dumprep.5to6
* Revisión 5 volcada.
* Revisión 6 volcada.

Podemos ahora restaurar el repositorio en otra máquina. Primero creamos el repositorio:

pp2@nereida:~$ svnadmin create mietsiiincrep
A continuación restauramos la primera parte:
pp2@nereida:~$ ssh banot cat dumprep.1to4 | svnadmin load mietsiiincrep
<<< Nueva transacción iniciada, basada en la revisión original 1
     * añadiendo ruta : acme-svn ... hecho.
     * añadiendo ruta : acme-svn/branches ... hecho.
     * añadiendo ruta : acme-svn/trunk ... hecho.
     * añadiendo ruta : acme-svn/trunk/Build.PL ... hecho.
     * añadiendo ruta : acme-svn/trunk/Changes ... hecho.
     * añadiendo ruta : acme-svn/trunk/MANIFEST ... hecho.
     * añadiendo ruta : acme-svn/trunk/Makefile.PL ... hecho.
     * añadiendo ruta : acme-svn/trunk/README ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib/Acme ... hecho.
     * añadiendo ruta : acme-svn/trunk/lib/Acme/SVN.pm ... hecho.
     * añadiendo ruta : acme-svn/trunk/t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/00.load.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/perlcritic.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/pod-coverage.t ... hecho.
     * añadiendo ruta : acme-svn/trunk/t/pod.t ... hecho.

------- Commit de la revisión 1 >>>

<<< Nueva transacción iniciada, basada en la revisión original 2
     * añadiendo ruta : acme-svn/branches/myacme-svn ...COPIED... hecho.

------- Commit de la revisión 2 >>>

<<< Nueva transacción iniciada, basada en la revisión original 3
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 3 >>>

<<< Nueva transacción iniciada, basada en la revisión original 4
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 4 >>>

A continuación restauramos la segunda parte:

pp2@nereida:~$ ssh banot cat dumprep.5to6 | svnadmin load mietsiiincrep
<<< Nueva transacción iniciada, basada en la revisión original 5
     * editando ruta : acme-svn/branches/myacme-svn/Makefile.PL ... hecho.

------- Commit de la revisión 5 >>>

<<< Nueva transacción iniciada, basada en la revisión original 6
     * editando ruta : acme-svn/trunk/Makefile.PL ... hecho.

------- Commit de la revisión 6 >>>

pp2@nereida:~$

Podemos acceder al nuevo repositorio desde una tercera máquina:

casiano@orion:~$ svn ls svn+ssh://pp2/home/pp2/mietsiiincrep            
acme-svn/
casiano@orion:~$ svn log svn+ssh://pp2/home/pp2/mietsiiincrep/acme-svn/trunk/
------------------------------------------------------------------------
r6 | lgforte | 2009-04-23 11:54:54 +0100 (jue, 23 abr 2009) | 1 line

lgforte modification
------------------------------------------------------------------------
r4 | casiano | 2009-03-05 15:56:20 +0000 (jue, 05 mar 2009) | 1 line

sally in trunk: list of final comments
------------------------------------------------------------------------
r3 | casiano | 2009-03-05 15:56:02 +0000 (jue, 05 mar 2009) | 1 line

sally in trunk: list of comments
------------------------------------------------------------------------
r1 | casiano | 2009-03-05 15:53:05 +0000 (jue, 05 mar 2009) | 1 line

branches
------------------------------------------------------------------------

Si múltiples usuarios estan accediendo al repositorio vía svn+ssh será necesario también garantizar que los permisos del repositorio son los adecuados. Por ejemplo:

bash-2.05b# ls -l | grep svn
drwxrwxr-x   7 svn   svnusers      512 Apr 27 15:06 reponame1
drwxrwxr-x   7 svn   svnusers      512 Apr 27 15:06 reponame2
drwxrwxr-x   7 svn   svnusers      512 Apr 27 15:06 reponame3
bash-2.05b# ls -l reponame1/ | egrep -i "db"
drwxrwsr-x  2 svn  svnusers  512 Apr 27 15:07 db
bash-2.05b#

Ejercicio 1.9.2   Escriba un guión que copie su repositorio de forma incremental en un dispositivo portable o en una máquina remota. Compruebe que el repositorio resultante es utilizable.

Etiquetas

Veamos un ejemplo de creación de un tag. Primero creamos el proyecto:

casiano@exthost:~/src/subversion/BUG-3035$ svn mkdir svn+ssh://banot/home/casiano/repository/ejemplo/
Committed revision 1.
casiano@exthost:~/src/subversion/BUG-3035$ svn mkdir svn+ssh://banot/home/casiano/repository/ejemplo/tags/
Committed revision 2.
casiano@exthost:~/src/subversion/BUG-3035$ svn mkdir svn+ssh://banot/home/casiano/repository/ejemplo/branches/
Committed revision 3.
casiano@exthost:~/src/subversion$ h2xs -XA -n SVN::Example
Defaulting to backwards compatibility with perl 5.10.0
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.

Writing SVN-Example/lib/SVN/Example.pm
Writing SVN-Example/Makefile.PL
Writing SVN-Example/README
Writing SVN-Example/t/SVN-Example.t
Writing SVN-Example/Changes
Writing SVN-Example/MANIFEST

casiano@exthost:~/src/subversion$ svn import SVN-Example/ svn+ssh://banot/home/casiano/repository/ejemplo/trunk/
Adding         SVN-Example/t
Adding         SVN-Example/t/SVN-Example.t
Adding         SVN-Example/lib
Adding         SVN-Example/lib/SVN
Adding         SVN-Example/lib/SVN/Example.pm
Adding         SVN-Example/MANIFEST
Adding         SVN-Example/Makefile.PL
Adding         SVN-Example/Changes
Adding         SVN-Example/README

Committed revision 4.
casiano@exthost:~/src/subversion$ svn ls svn+ssh://banot/home/casiano/repository/ejemplo/trunk/
Changes
MANIFEST
Makefile.PL
README
lib/
t/

Para crear una etiqueta hacemos una copia:

casiano@exthost:~/src/subversion/ejemplo$ svn cp svn+ssh://banot/home/casiano/repository/ejemplo/trunk \
                                          svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-1.0 \
                                              -m 'tagging release 1.0'

Committed revision 12.
casiano@exthost:~/src/subversion/ejemplo$ svn diff svn+ssh://banot/home/casiano/repository/ejemplo/trunk \
                                            svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-1.0

casiano@exthost:~/src/subversion/ejemplo$

Ramas y Mezclas

casiano@exthost:~/src/subversion$ svn cp svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-1.0 \
                         svn+ssh://banot/home/casiano/repository/ejemplo/branches/TRY-MGM-cache-pages

casiano@exthost:~/src/subversion$ svn checkout svn+ssh://banot/home/casiano/repository/ejemplo/branches/TRY-MGM-cache-pages
A    TRY-MGM-cache-pages/t
A    TRY-MGM-cache-pages/t/SVN-Example.t
A    TRY-MGM-cache-pages/MANIFEST
A    TRY-MGM-cache-pages/lib
A    TRY-MGM-cache-pages/lib/SVN
A    TRY-MGM-cache-pages/lib/SVN/Example.pm
A    TRY-MGM-cache-pages/Makefile.PL
A    TRY-MGM-cache-pages/Changes
A    TRY-MGM-cache-pages/README
Checked out revision 7.

Ahora, mientras un grupo trabaja en la rama TRY-MGM-cache-pages ...

casiano@exthost:~/src/subversion$ cd TRY-MGM-cache-pages/
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ vi lib/SVN/Example.pm
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn commit -mm
Sending        lib/SVN/Example.pm
Transmitting file data .
Committed revision 8.
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn diff lib/SVN/Example.pm -r PREV
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm  (revision 7)
+++ lib/SVN/Example.pm  (working copy)
@@ -28,6 +28,12 @@
 our $VERSION = '0.01';


+sub g1{
+}
+
+sub g2{
+}
+
 # Preloaded methods go here.

 1;

otro trabaja en el tronco ...

casiano@exthost:~/src/subversion$ svn checkout svn+ssh://banot/home/casiano/repository/ejemplo/trunk/ ejemplo
A    ejemplo/t
A    ejemplo/t/SVN-Example.t
A    ejemplo/MANIFEST
A    ejemplo/lib
A    ejemplo/lib/SVN
A    ejemplo/lib/SVN/Example.pm
A    ejemplo/Makefile.PL
A    ejemplo/Changes
A    ejemplo/README
Checked out revision 4.
casiano@exthost:~/src/subversion$ cd ejemplo
casiano@exthost:~/src/subversion/ejemplo$ vi lib/SVN/Example.pm
casiano@exthost:~/src/subversion/ejemplo$ svn commit
Sending        lib/SVN/Example.pm
Transmitting file data .
Committed revision 5.
casiano@exthost:~/src/subversion/ejemplo$ svn diff lib/SVN/Example.pm -r PREV
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm  (revision 4)
+++ lib/SVN/Example.pm  (working copy)
@@ -30,6 +30,12 @@

 # Preloaded methods go here.

+sub new_functionality1 {
+}
+
+sub new_functionality2 {
+}
+
 1;
 __END__
 # Below is stub documentation for your module. You'd better edit it!

Supongamos que ahora se crea un tag para la release 2.0:

casiano@exthost:~/src/subversion/ejemplo$ svn cp svn+ssh://banot/home/casiano/repository/ejemplo/trunk \
                                          svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-2.0
Y que queremos mezclar los cambios que se han producido entre las releases 1.0 y 2.0 en la rama RY-MGM-cache-pages:
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn merge svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-1.0 \
                                                                svn+ssh://banot/home/casiano/repository/ejemplo/tags/REL-2.0 \
                                                                .
--- Merging differences between repository URLs into '.':
U    lib/SVN/Example.pm
El estatus nos muestra que el fichero lib/SVN/Example.pm ha sido modificado en la copia de trabajo:
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn status
M       lib/SVN/Example.pm
Veamos cuales son las diferencias:
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn diff lib/SVN/Example.pm -r BASE
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm  (revision 8)
+++ lib/SVN/Example.pm  (working copy)
@@ -36,6 +36,15 @@

 # Preloaded methods go here.

+sub new_functionality1 {
+}
+
+sub new_functionality2 {
+}
+
+sub new_functionality3 {
+}
+
 1;
 __END__
 # Below is stub documentation for your module. You'd better edit it!

Many users (especially those new to version control) are initially perplexed about the proper syntax of the command and about how and when the feature should be used. But fear not, this command is actually much simpler than you think! There's a very easy technique for understanding exactly how svn merge behaves.

The main source of confusion is the name of the command. The term “merge” somehow denotes that branches are combined together, or that some sort of mysterious blending of data is going on. That's not the case. A better name for the command might have been svn diff-and-apply, because that's all that happens: two repository trees are compared, and the differences are applied to a working copy.

Mezcla Usando un Rango de Versiones de una Rama

Supongamos que se sigue trabajando en el tronco:

casiano@exthost:~/src/subversion/ejemplo$ vi lib/SVN/Example.pm
casiano@exthost:~/src/subversion/ejemplo$ svn commit -m 'some bug fixed'
Sending        lib/SVN/Example.pm
Transmitting file data .
Committed revision 11.
Estos fueron los cambios realizados:
casiano@exthost:~/src/subversion/ejemplo$ svn diff lib/SVN/Example.pm -r PREV
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm  (revision 10)
+++ lib/SVN/Example.pm  (working copy)
@@ -37,6 +37,7 @@
 }

 sub new_functionality3 {
+ # some bug fixed here
 }

 1;
Podemos incorporar los cambios realizados en el tronco a la rama

casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn merge -r10:11 svn+ssh://banot/home/casiano/repository/ejemplo/trunk/ 
--- Merging r11 into '.':                                                                                                      
U    lib/SVN/Example.pm                                                                                                        
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn commit
Sending        lib/SVN/Example.pm
Transmitting file data .
Committed revision 13.

Las Mezclas Pueden Producir Conflictos

After performing the merge, you might also need to resolve some conflicts (just as you do with svn update) or possibly make some small edits to get things working properly.

Remember: just because there are no syntactic conflicts doesn't mean there aren't any semantic conflicts!

If you encounter serious problems, you can always abort the local changes by running svn revert . -R (which will undo all local modifications) and start a long what's going on? discussion with your collaborators.

If you don't like the results of the merge, simply run svn revert . -R to revert the changes from your working copy and retry the command with different options. The merge isn't final until you actually svn commit the results.

...

While it's perfectly fine to experiment with merges by running svn merge and svn revert over and over, you may run into some annoying (but easily bypassed) roadblocks.

For example, if the merge operation adds a new file (i.e., schedules it for addition), svn revert won't actually remove the file; it simply unschedules the addition. You're left with an unversioned file. If you then attempt to run the merge again, you may get conflicts due to the unversioned file “being in the way.”

Solution? After performing a revert, be sure to clean up the working copy and remove unversioned files and directories. The output of svn status should be as clean as possible, ideally showing no output.

Gestión de Configuraciones

Véanse los artículos de la Wikipedia sobre Gestión de Configuraciones:

Configuration management (CM) is a field of management that focuses on

throughout its life.

Modelos de Sincronización

In configuration management (CM), one has to control (among other things) changes made to software and documentation. This is called revision control, which manages multiple versions of the same unit of information. Although revision control is important to CM, it is not equal to it.

Synchronization Models, also known as Configuration Management Models, describe methods to enable revision control through allowing simultaneous, concurrent changes to individual files.

In revision control, changesets are a way to group a number of modifications that are relevant to each other in one atomic package, that may be cancelled or propagated as needed.

Los siguientes párrafos están tomados de http://itil.osiatis.es/ITIL_course/it_service_management/configuration_management/introduction_and_objectives_configuration_management/introduction_and_objectives_configuration_management.php.

Funciones de la Gestión de Configuraciones

The four main functions of Configuration Management may be summarised as:

Objetivos de la Gestión de configuraciones (CM)

It is essential to have a detailed knowledge of your organisation's IT infrastructure in order to make best use of it. The main task of Configuration Management is to keep an up-to-date record of all the components in the IT infrastructure configuration and the interrelations between them.

This is not a simple task and requires the cooperation of the people managing other processes, in particular Change Management and Release Management.

The main objectives of Configuration Management are:

Ventajas a la Hora de Usar Gestión de Configuraciones

The benefits of correct Configuration Management include, among other things:

Dificultades a la Hora de Usar Gestión de Configuraciones

The main activities difficulties in Configuration Management are:

Conjuntos de Cambios en Subversion

En subversión la definición de changeset es mas concreta:

A changeset is just a collection of changes with a unique name. The changes might include
In more common speak, a changeset is just a patch with a name you can refer to.

In Subversion, a global revision number N names a tree in the repository: it's the way the repository looked after the Nth commit. It's also the name of an implicit changeset: if you compare tree N with tree N−1, you can derive the exact patch that was committed. For this reason, it's easy to think of revision N as not just a tree, but a changeset as well.

If you use an issue tracker to manage bugs, you can use the revision numbers to refer to particular patches that fix bugs—for example, 'this issue was fixed by r9238.' Somebody can then run

 svn log -r 9238

to read about the exact changeset that fixed the bug, and run

 svn diff -c 9238  #  La opción -c REV es equivalente a -r REV-1:REV

to see the patch itself.

Subversion's svn merge command is able to use revision numbers. You can merge specific changesets from one branch to another by naming them in the merge arguments: passing -c 9238 to svn merge would merge changeset r9238 into your working copy.

Mezclas en svnbook

The general act of replicating changes from one branch to another is called merging, and it is performed using various invocations of the svn merge command.

Véase:

Hooks

A hook script is a program triggered by some repository event, such as the creation of a new revision or the modification of an unversioned property. Each hook is handed enough information to tell what that event is, what target(s) it's operating on, and the username of the person who triggered the event. Depending on the hook's output or return status, the hook program may continue the action, stop it, or suspend it in some way.

To actually install a working hook, you need only place some executable program or script into the repos/hooks directory, which can be executed as the name (such as start-commit or post-commit) of the hook.

Veamos el directorio hooks/. El fichero pre-commit tiene permisos de ejecución:

pp2@nereida:~$ ls -l svnrep/hooks/
total 40
-rw-r--r-- 1 pp2 pp2 2000 2010-04-12 10:33 post-commit.tmpl
-rw-r--r-- 1 pp2 pp2 1690 2010-04-12 10:33 post-lock.tmpl
-rw-r--r-- 1 pp2 pp2 2307 2010-04-12 10:33 post-revprop-change.tmpl
-rw-r--r-- 1 pp2 pp2 1606 2010-04-12 10:33 post-unlock.tmpl
-rwxr-xr-x 1 pp2 pp2  110 2010-04-19 08:30 pre-commit
-rw-r--r-- 1 pp2 pp2 2982 2010-04-19 07:45 pre-commit.tmpl
-rw-r--r-- 1 pp2 pp2 2038 2010-04-12 10:33 pre-lock.tmpl
-rw-r--r-- 1 pp2 pp2 2764 2010-04-12 10:33 pre-revprop-change.tmpl
-rw-r--r-- 1 pp2 pp2 1980 2010-04-12 10:33 pre-unlock.tmpl
-rw-r--r-- 1 pp2 pp2 2758 2010-04-12 10:33 start-commit.tmpl

Estos son los contenidos de svnrep/hooks/pre-commit:

pp2@nereida:~$ cat -n svnrep/hooks/pre-commit
     1  #!/bin/sh
     2  
     3  REPOS="$1"
     4  TXN="$2"
     5  
     6  /home/pp2/src/perl/subversion/pre-commit.pl "$REPOS" "$TXN" || exit 1
     7  
     8  exit 0

El programa Perl simplemente comprueba que el mensaje de log es suficientemente largo:

pp2@nereida:~$ cat -n /home/pp2/src/perl/subversion/pre-commit.pl
     1  #!/usr/bin/perl -w
     2  use strict;
     3  # creating scalar variables that holds some values
     4  
     5  open my $file, '> /tmp/mylog';
     6  print $file "executing hook\n";
     7  close($file);
     8  
     9  my $min = 8;
    10  my $svnlook = '/usr/bin/svnlook';
    11  #--------------------------------------------
    12  my $repos = shift;
    13  my $txn   = shift;
    14  
    15  unless (defined($repos) and defined($txn)) {
    16    warn "Error: Expected repos and txn args\n";
    17    exit(3);
    18  }
    19  
    20  my $msg;
    21  eval {
    22    $msg = `$svnlook log -t "$txn" "$repos" 2>&1`;
    23  };
    24  
    25  if ($@ or $?) {
    26    warn qq{Error executing '$svnlook log -t "$txn" "$repos"'\nmessage:\n$msg\n};
    27    exit(2);
    28  }
    29  warn "repos=$repos txn=$txn msg=$msg\n";
    30  chomp($msg);
    31  
    32  if (length($msg) < $min) { 
    33    warn "Message should be at least $min characters in length\n"; 
    34    exit(1); 
    35  } 
    36  
    37  exit(0);

Ahora modificamos un fichero en un proyecto y hacemos un commit con un mensaje corto:

pp2@nereida:~/src/perl/subversion/project$ svn commit -mm
Enviando       trunk/Makefile.PL
Transmitiendo contenido de archivos .svn: Falló el commit (detalles a continuación):
svn: Commit bloqueado por hook pre-commit (código de salida 1) con salida:
repos=/home/pp2/svnrep txn=16-j msg=m

Message should be at least 8 characters in length

El commit es aceptado si el mensaje es suficientemente largo:

pp2@nereida:~/src/perl/subversion/project$ svn commit -m 'longer message'
Enviando       trunk/Makefile.PL
Transmitiendo contenido de archivos .
Commit de la revisión 17.

Enviando Mails via Hooks

El programa commit-email.pl puede ser usado como post-commit hook para enviar emails:
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686 i386 GNU/Linux
-bash-3.2$ /usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl                            
/usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl: too few arguments.                   
usage (commit mode):                                                                                     
  /usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...
usage: (revprop-change mode):                                                                                                
  /usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl --revprop-change REPOS REVNUM USER PROPNAME [-d diff_file] \
    [[-m regex] [options] [email_addr ...]] ...
options are:
  --from email_address  Email address for 'From:' (overrides -h)
  -h hostname           Hostname to append to author for 'From:'
  -l logfile            Append mail contents to this log file
  -m regex              Regular expression to match committed path
  -r email_address      Email address for 'Reply-To:'
  -s subject_prefix     Subject line prefix
  --diff y|n            Include diff in message (default: y)
                        (applies to commit mode only)

This script supports a single repository with multiple projects,
where each project receives email only for actions that affect that
project.  A project is identified by using the -m command line
option with a regular expression argument.  If the given revision
contains modifications to a path that matches the regular
expression, then the action applies to the project.

Any of the following -h, -l, -r, -s and --diff command line options
and following email addresses are associated with this project.  The
next -m resets the -h, -l, -r, -s and --diff command line options
and the list of email addresses.

To support a single project conveniently, the script initializes
itself with an implicit -m . rule that matches any modifications
to the repository.  Therefore, to use the script for a single-
project repository, just use the other command line options and
a list of email addresses on the command line.  If you do not want
a rule that matches the entire repository, then use -m with a
regular expression before any other command line options or email
addresses.

'revprop-change' mode:
The message will contain a copy of the diff_file if it is provided,
otherwise a copy of the (assumed to be new) property value.

Estos son los contenidos del ejecutable post-commit en el subdirectorio hooks:

-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686 i386 GNU/Linux
bash-3.2$ pwd
/home/casiano/newrepository/hooks
-bash-3.2$ ls -ltra post-commit
-rwxr-xr-x 1 casiano apache 280 abr 20 14:08 post-commit
-bash-3.2$ cat post-commit
#!/bin/sh
REPOS="$1"
REV="$2"

/usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl "$REPOS" "$REV" --from 'aluXXX@ull.es' -r  'aluXXX@ull.es' 'aluXXX@gmail.com'
Supongamos que modificamos nuestra copia de trabajo y hacemos un commit:
pp2@nereida:~/src/perl/subversion/banotnewrepproject2$ svn info
Ruta: .
URL: svn+ssh://banot/home/casiano/newrepository/project2/trunk
Raíz del repositorio: svn+ssh://banot/home/casiano/newrepository
UUID del repositorio: faf63038-71ca-4861-8080-6a701f9d687f
Revisión: 11
Tipo de nodo: directorio
Agendado: normal
Autor del último cambio: casiano
Revisión del último cambio: 11
Fecha de último cambio: 2010-04-20 09:17:16 +0100 (mar 20 de abr de 2010)

pp2@nereida:~/src/perl/subversion/banotnewrepproject2$ vi Makefile.PL
pp2@nereida:~/src/perl/subversion/banotnewrepproject2$ svn commit -m 'checking post-commit'
Enviando       Makefile.PL
Transmitiendo contenido de archivos .
Commit de la revisión 20.
Ahora en nuestra cuenta de correo tenemos un mensaje:
from        aluXXX@ull.es
reply-to    aluXXX@ull.es
to          aluXXX@gmail.com
date        20 April 2010 14:09
subject     r20 - project2/trunk
mailed-by   ull.es
    
hide details 14:09 (21 minutes ago)
    
Author: aluXXX
Date: 2010-04-20 14:09:35 +0100 (Tue, 20 Apr 2010)
New Revision: 20

Modified:
  project2/trunk/Makefile.PL
Log:
checking post-commit

Modified: project2/trunk/Makefile.PL
===================================================================
--- project2/trunk/Makefile.PL  2010-04-20 08:32:45 UTC (rev 19)
+++ project2/trunk/Makefile.PL  2010-04-20 13:09:35 UTC (rev 20)
@@ -1,11 +1,4 @@
 use ExtUtils::MakeMaker;
-
-
-#
-#
-#
-#
-#
 WriteMakefile(
    NAME              => 'project2',
    VERSION_FROM      => 'lib/project2.pm', # finds $VERSION

Controlando los Permisos via Hooks

Ejercicio 1.9.3   Restrinja los accesos de un compañero a un proyecto situado en su repositorio. Para ello siga estos pasos:

  1. Ejemplo de un ejecutable pre-commit:
    -bash-3.2$ uname -a
    Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686 i386 GNU/Linux
    -bash-3.2$ pwd
    /home/casiano/newrepository/hooks
    -bash-3.2$ ls -l pre-commit
    -rwxr-xr-x 1 casiano apache 281 abr 20 17:17 pre-commit
    -bash-3.2$
    -bash-3.2$ cat pre-commit
    #!/bin/sh
    
    REPOS="$1"
    TXN="$2"
    
    perl -I/home/casiano/perl5/lib/perl5/site_perl/5.8.8/ /home/casiano/newrepository/hooks/commit-access-control.pl \
                               "$REPOS" "$TXN" /home/casiano/newrepository/hooks/commit-access-control.cfg || exit 1
    
    # All checks passed, so allow the commit.
    exit 0
    -bash-3.2$
    
    En /home/casiano/perl5/lib/perl5/site_perl/5.8.8/ se encuentra la librería Config::IniFiles usada por commit-access-control.pl para parsear el fichero de configuración.

  2. Asegúrese que otro compañero puede acceder a su repositorio usando el protocolo svn+ssh. Para ello, si no lo ha hecho ya, genere una pareja de claves y publique la clave en el servidor subversion. Recuerde el formato en el fichero authorized_keys para identificarle:
    -bash-3.2$ cat ~/.ssh/authorized_keys                                        
    .............................................................
    
    # key for subversion
    command="/usr/bin/svnserve -t -r /home/casiano/newrepository/ --tunnel-user=aluXXXX",no-port-forwarding ssh-dss AAAAB...................= myfriend key
    
  3. Restrinja los accesos de ese compañero editando el fichero de configuración:
    -bash-3.2$ cat commit-access-control.cfg
    [Make everything read-only for all users]
            match   = .*
            access  = read-only
    
    [project1 aluXXXX permissions]
            match  = ^project1/trunk
            users  = myfriend
            access = read-write
    
    [casiano permissions]
            match  = .*
            users  = casiano
            access = read-write
    
  4. Compruebe que su compañero tiene el acceso limitado a las correspondientes partes del proyecto. El compañero crea una entrada en su configuración ssh para facilitar el acceso via svn al repositorio:
    aluXXXX@nereida:/tmp$ sed -ne '/svn/,//p' /home/aluXXXX/.ssh/config 
    Host svn                                                    
    HostName banot.etsii.ull.es                                 
    user casiano                                                
    IdentityFile /home/aluXXXX/.ssh/id_dsa_svn
    
    A continuación descarga los proyectos en los que está interesado:
    aluXXXX@nereida:/tmp$ svn ls svn+ssh://svn/
    project1/                              
    project2/                              
    aluXXXX@nereida:/tmp$ svn checkout svn+ssh://svn/
    A    svn/project1                            
    A    svn/project1/trunk                      
    A    svn/project1/trunk/t                    
    A    svn/project1/trunk/t/project1.t         
    A    svn/project1/trunk/MANIFEST             
    A    svn/project1/trunk/lib                  
    A    svn/project1/trunk/lib/project1.pm      
    A    svn/project1/trunk/Makefile.PL          
    A    svn/project1/trunk/Changes              
    A    svn/project1/trunk/README               
    A    svn/project1/branches
    A    svn/project1/branches/branch1
    A    svn/project1/branches/branch1/t
    A    svn/project1/branches/branch1/t/project1.t
    A    svn/project1/branches/branch1/MANIFEST
    A    svn/project1/branches/branch1/lib
    A    svn/project1/branches/branch1/lib/project1.pm
    A    svn/project1/branches/branch1/Makefile.PL
    A    svn/project1/branches/branch1/Changes
    A    svn/project1/branches/branch1/README
    A    svn/project2
    A    svn/project2/trunk
    A    svn/project2/trunk/t
    A    svn/project2/trunk/t/project2.t
    A    svn/project2/trunk/MANIFEST
    A    svn/project2/trunk/lib
    A    svn/project2/trunk/lib/project2.pm
    A    svn/project2/trunk/Makefile.PL
    A    svn/project2/trunk/Changes
    A    svn/project2/trunk/README
    Revisión obtenida: 24
    
    Hace modificaciones e intenta un commit en la zona prohibida:
    aluXXXX@nereida:/tmp$ cd svn/project1/branches/branch1
    aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ echo '# comentario'>>Makefile.PL
    aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ svn commit -m 'checking permits'
    Enviando       branch1/Makefile.PL
    Transmitiendo contenido de archivos .svn: Falló el commit (detalles a continuación):
    svn: El hook 'pre-commit' falló con la siguiente salida de error:
    /home/casiano/newrepository/hooks/commit-access-control.pl: user `aluXXXX' does not have permission to commit to these paths:
      project1/branches/branch1
      project1/branches/branch1/Makefile.PL
    

    Veamos que ocurre en la zona en la que tiene permisos de escritura:

    aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ cd /tmp/svn/project1/trunk/
    aluXXXX@nereida:/tmp/svn/project1/trunk$ echo '# comentario'>>Makefile.PL
    aluXXXX@nereida:/tmp/svn/project1/trunk$ svn commit -m 'checking permits'
    Enviando       trunk/Makefile.PL
    Transmitiendo contenido de archivos .
    Commit de la revisión 25.
    aluXXXX@nereida:/tmp/svn/project1/trunk$
    

Véanse:

Locking

Búsqueda Binaria

It's a common developer practice to track down a bug by looking for the change that introduced it. This is most efficiently done by performing a binary search between the last known working commit and the first known broken commit in the commit history.

At each step of the binary search, the bisect method checks out the source code at the commit chosen by the search. The user then has to test to see if the software is working or not. If it is, the user performs a svn-bisect good, otherwise they do a svn-bisect bad, and the search proceeds accordingly.

Ejercicio 1.9.4   Haga un checkout del proyecto parse-eyapp en google-code. Utilize el método de la bisección con svn-bisect para averiguar en que versión del proyecto se introdujo la funcionalidad que comprueba el test t/73dynamicshiftreduceconflictresolution.t

Replicación de Repositorios

Referencias

Consulte

  1. http://svnbook.red-bean.com/en/1.5/ .
  2. Vea la página de la ETSII http://cc.etsii.ull.es/svn .
  3. Vea los capítulos disponibles del libro Subversion in Action de la editorial Manning
  4. Practical Subversion de APress
  5. La hoja de referencia en http://www.digilife.be/quickreferences/QRC/Subversion%20Quick%20Reference%20Card.pdf

En KDE puede instalar el cliente gráfico KDEsvn.



Subsecciones
Casiano Rodríguez León
2012-02-29