Pipes

Se puede utilizar open para lanzar un proceso en pipe con el programa actual:
open $DATE, "date|" or die "Falló la creación del pipe desde date: $!";
open $MAIL, "|mail alu1324@csi.ull.es" or die "Falló el pipe hacia mail: $!";
La barra al final indica que la salida del proceso lanzado alimenta la entrada a nuestro programa a través del manipulador. Del mismo modo, la barra al principio indica que la entrada del proceso lanzado se alimenta de las salidas de nuestro programa hacia el correspondiente manipulador.

Para leer desde el proceso ejecutando date basta usar el operador diamante sobre el manejador:

my $now = <$DATE>;
y para escribir al proceso $MAIL hacemos:
print $MAIL "Estimado alumno, ha obtenido usted un sobresaliente.\n";

Valor Retornado por open

Cuando se abre un pipe, open devuelve el identificador del proceso del comando al otro lado del pipe. Este entero puede ser utilizado para monitorizar o enviar posteriormente señales al proceso.

Asegúrese de comprobar los valores retornados por open y close cuando utilice un pipe.

Para entender la razón, piense que sucede cuando se arranca un pipe a un comando que no existe. El éxito del comando open refleja el hecho de que el fork pudo hacerse con éxito. Pero en cuanto intentemos la escritura en el pipe se producirá el error y se recibirá una señal PIPE que deberemos manejar. Si se trata de lectura Perl interpretará que se ha producido un final de entrada.

Ejercicio 1.6.1   Explique la diferencia de conducta entre estos dos ejemplos

Sincronización mediante close

Al cerrar un filehandle:

close(MAIL);
die "mail: salida errónea, $?" if $?;
nuestro proceso se sincroniza con el proceso lanzado, esperando a que este termine y obtener el código de salida, el cual queda almacenado en la variable $?.

Esqueleto de un Pipe

En general el esquema de uso para leer desde un programa en pipe es

my $pid = open(my $README, "program arguments | ") or die("Couldn't fork: $|\n";
while ($input = <$README>) {
 ...
}
close($README);
# Check $?

La variable $?

La variable $? ($CHILD_ERROR si se usa el módulo English) contiene el estatus retornado por el cierre de un pipe (close), la ejecución de un comando bajo comillas, una llamada a wait o una llamada a system. Un ejemplo de uso, comprobando el estatus es:

 if ($? == -1) { # $? es -1 si no se pudo ejecutar
     print "No se pudo ejecutar: $!\n";
 }
 elsif ($? & 127) {
     printf "El proceso hijo a muerto con señal %d, %s coredump\n",
         ($? & 127),  ($? & 128) ? 'con' : 'sin';
 }
 else { # El valor de salida del proceso es $? >> 8
     printf "Proceso hijo termina con estatus %d\n", $? >> 8;
 }
Si quiere obtener mas información sobre $? lea perldoc perlvar.

Pipes con IO::File

Una alternativa es usar objetos IO::File:

pp2@nereida:~/LGRID_Machine/lib/GRID$ perl -wde 0
main::(-e:1):   0
  DB<1>  use IO::File
  DB<2> $DATE = IO::File->new
  DB<3> $DATE->open("date|") or die "Falló la creación del pipe desde date: $!"
  DB<4> p <$DATE>
lun mar  3 12:17:57 WET 2008

Ejecución de un Proceso Mediante Open con Pipe

Veamos un ejemplo:

lhp@nereida:~/Lperl/src$ cat -n rwho.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3
 4  my $host = shift;
 5
 6  my $pid = open(my $WHOFH, "ssh $host who |") or die "No se pudo abrir who: $!";
 7  print "PID of pipe process: $pid\n";
 8  system(<<"EOC");
 9    ssh $host ps -f  &&
10    echo --------------------------- &&
11    pstree -p $$ &&
12    echo ---------------------------
13  EOC
14
15  while (<$WHOFH>) {
16    print $_;
17  }
18
19  close($WHOFH) or die "error al cerrar: $!";
La opción -p de pstree hace que se muestren los PID de los procesos en el árbol. La opción -f de ps indica que queremos un listado completo.

Ejercicio 1.6.2   El programa anterior tiene tres comandos
  1. ssh $host who
  2. ssh $host ps -f
  3. pstree -p
¿En que orden ocurrirán las salidas a STDOUT de este programa? ¿Que procesos son mostrados por ssh $host ps -f? ¿Que procesos son mostrados por pstree -p?

Al ejecutar el programa obtenemos una salida como esta:

lhp@nereida:~/Lperl/src$ rwho.pl orion | cat -n
 1  PID of pipe process: 26100
 2  UID        PID  PPID  C STIME TTY          TIME CMD
 3  casiano  10885 10883  0 Mar02 ?        00:00:00 sshd: casiano@notty
 4  casiano  10886 10885  0 Mar02 ?        00:00:00 perl
 5  casiano  25679 25677  0 12:42 ?        00:00:00 sshd: casiano@pts/3
 6  casiano  26136 26134  0 13:13 ?        00:00:00 sshd: casiano@notty
 7  casiano  26137 26131  0 13:13 ?        00:00:00 sshd: casiano@notty
 8  casiano  26138 26136  0 13:13 ?        00:00:00 ps -f
 9  ---------------------------
10  rwho.pl(26098)-+-sh(26101)---pstree(26103)
11                 `-ssh(26100)
12  ---------------------------
13  cleon    pts/0        2008-02-28 17:05 (miranda.deioc.ull.es)
14  cleon    pts/1        2008-02-28 17:11 (miranda.deioc.ull.es)
15  boriel   pts/2        2008-02-28 18:37 (localhost.localdomain)
16  casiano  pts/3        2008-03-03 12:42 (nereida.deioc.ull.es)
Las líneas 2-8 corresponden a la llamada ssh $host ps -f) en la máquina remota. Las líneas 10-11 a la ejecución en local pstree -p. Por último, las líneas 13-16 se corresponden con la lectura y volcado a través del manejador $WHOFH del proceso remoto ejecutando el comando who.

Usando Pipes para Replicar la Salida de Nuestro Programa

Ejercicio 1.6.3   ¿Cómo se puede modificar la conducta de unas librerías existentes de manera que todas las salidas (mediante print u otras funciones) a STDOUT se replique en un grupo de ficheros?.

Los pipes nos dan una solución sencilla al problema de replicar la salida de nuestro programa a varios ficheros. Basta con hacer un pipe con el programa tee. El programa tee copia la entrada estandar a cada uno de los ficheros que se le pasan como argumentos y también a la salida estándar:

lhp@nereida:/tmp$ uname -a | tee /tmp/one.txt
Linux nereida.deioc.ull.es 2.4.20-perfctr #6 SMP vie abr 2 
lhp@nereida:/tmp$ cat one.txt
Linux nereida.deioc.ull.es 2.4.20-perfctr #6 SMP vie abr 2

En el siguiente código la subrutina tee abre un pipe del $stream especificado contra la aplicación tee:

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n tee.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use IO::File;
 4  use File::Spec;
 5
 6  sub tee {
 7    my ($stream, $echo, @files) = @_;
 8
 9    my $devnull = File::Spec->devnull();
10    my $redirect = $echo?"":"> $devnull";
11    open $stream, "| tee @files $redirect" or die "can't tee. $!\n";
12  }
13
14  #### main
15  die "Provide some file names\n" unless @ARGV;
16
17  my $file = IO::File->new();
18  open $file, ">& STDOUT";
19  tee(\*STDOUT, 1, @ARGV);
20    # Now any print goes to all the @files plus STDOUT
21    print "1) Hola Mundo\n";
22  close(STDOUT);
23
24
25  open STDOUT,">&", $file;
26    # STDOUT only
27    print "2) Hola Mundo\n";
Sigue un ejemplo de ejecución:
lhp@nereida:~/Lperl/src/perl_networking/ch2$ tee.pl one two three
1) Hola Mundo
2) Hola Mundo
lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat one two three
1) Hola Mundo
1) Hola Mundo
1) Hola Mundo

Transparencia en Open

La sentencia open actúa sobre procesos y ficheros de manera trasparente.

Supongamos un programa que espera una secuencia de ficheros en @ARGV como este:

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n toupper
 1  #!/usr/bin/perl -w
 2  while (<>) {
 3    print "\U$_";
 4  }
Este programa usa el operador diamante para leer los ficheros pasados en @ARGV y pasar sus contenidos a mayúsculas.

Entonces podemos pasarle al programa en vez de una lista de ficheros una lista en la que se mezclan pipes y ficheros arbitrariamente:

 1 $ toupper toupper "sort toupper|" - "cat -n system.pl|" 
 2 #!/USR/BIN/PERL -W
 3 WHILE (<>) {
 4   PRINT "\U$_";
 5 }
 6 }
 7   PRINT "\U$_";
 8 #!/USR/BIN/PERL -W
 9 WHILE (<>) {
10 Esta linea la escribí interactivamente
11 ESTA LINEA LA ESCRIBí INTERACTIVAMENTE
12      1  SYSTEM('PS -FU LHP');
13      2  SYSTEM('ECHO -----------------');
14      3  SYSTEM('PS -FU LHP | GREP SYSTEM');

Las líneas 2-5 contienen el resultado de pasar el fichero toupper a mayúsculas. Las líneas 6-9 contienen el resultado de ordenar el fichero alfabéticamente y pasarlo a mayúsculas (sort toupper|"). Después vienen una línea leida interactivamente, correspondiente al "-" en la línea de argumentos y el resultado de filtrar el proceso "cat -n system.pl|".

Pipes versus Backticks

En muchos casos ocurre que lo que puedes hacer con un pipe lo puedes hacer con backticks:

pp2@nereida:~/LGRID_Machine/lib/GRID$ perl -wde 0
main::(-e:1):   0
  DB<1> print grep { /www/ } `netstat -a 2>&1`
tcp        0      0 *:www                   *:*                     LISTEN
tcp        0      0 nereida:56001           mg-in-f83.google.co:www ESTABLISHED
tcp        1      0 nereida:56003           mg-in-f83.google.co:www CLOSE_WAIT

  DB<2> p $?
0

En general la versión pipe consume menos memoria, ya que se procesa una línea de cada vez y nos da la oportunidad, si conviene, de matar el proceso hijo antes de su terminación.

Por otro lado, hay que reconocer la comodidad de usar backticks. Si además se está cronometrando el programa lanzado, la versión pipe da lugar a sincronizaciones que pueden perturbar el proceso de medición de tiempos.

Cierre Prematuro de un Pipe

Si un proceso cierra un pipe de lectura antes de leer el EOF, el programa al otro lado recibirá una señal PIPE en su siguiente intento de escribir en la salida estandard.

En el siguiente ejemplo, tomado de [1] el siguiente proceso intenta escribir 10 líneas al proceso al otro lado del pipe:

lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n write_ten.pl
 1  #!/usr/bin/perl -w
 2  use strict;
 3  use IO::Handle;
 4
 5  open (my $PIPE,"| read_three.pl") or die "Can't open pipe: $!";
 6  $PIPE->autoflush(1);
 7
 8  my $count = 0;
 9  for (1..10) {
10    warn "Writing line $_\n";
11    print $PIPE "This is line number $_\n" and $count++;
12    sleep 1;
13  }
14  close $PIPE or die "Can't close pipe: $!";
15
16  print "Wrote $count lines of text\n"
pero el proceso al otro lado sólo lee las tres primeras:
lhp@nereida:~/Lperl/src/perl_networking/ch2$ cat -n read_three.pl
   1  #!/usr/bin/perl -w
   2  use strict;
   3
   4  my $line;
   5  for (1..3) {
   6    last unless defined($line = <>);
   7    warn "Read_three got: $line";
   8  }
Al ejecutar write_ten.pl se produce la muerte prematura del proceso, el cual recibe un señal PIPE:
lhp@nereida:~/Lperl/src/perl_networking/ch2$ write_ten.pl
Writing line 1
Read_three got: This is line number 1
Writing line 2
Read_three got: This is line number 2
Writing line 3
Read_three got: This is line number 3
Writing line 4
lhp@nereida:~/Lperl/src/perl_networking/ch2$
La solución al problema está en instalar un manejador para la señal PIPE. Veremos como hacerlo en la sección 3.4.11.



Subsecciones
Casiano Rodríguez León
2010-03-22
h 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

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

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/ SVN/
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=lhp",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 lhp 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

Véanse:

Locking

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
2010-04-20
>Conjuntos de Cambios en Subversion
  • Mezclas en svnbook
  • Hooks
  • Enviando Mails via Hooks
  • Controlando los Permisos via Hooks
  • Locking
  • Referencias
    Casiano Rodríguez León
    2010-04-21
    ME="tex2html9" HREF="http://www.etsii.ull.es/">etsiiullpcgull
    Sig: Repaso Sup: Ejecucion de Programas Ant: Repaso
    Casiano Rodríguez León
    2010-04-26
    ="0" SRC="gv.jpeg" ALT="ps">modulosperlmonksperldocgoogleetsiiullpcgull
    Sig: Repaso Sup: Ejecucion de Programas Ant: Repaso
    Casiano Rodríguez León
    2010-04-26
    gle.gif" ALT="google">etsiiullpcgull
    Sig: Repaso Sup: Ejecucion de Programas Ant: Repaso
    Casiano Rodríguez León
    2010-04-28
    siano Rodríguez León
    2010-10-19 RC="pp2.jpg" ALT="PP2">PP2 moodlepsmodulosperlmonksperldocgoogleetsiiullpcgull
    Sig: Repaso Sup: Ejecucion de Programas Ant: Repaso
    Casiano Rodríguez León
    2010-11-02
    >