Control de versiones con Subversion

Revision 3658

Ben Collins-Sussman

Brian W. Fitzpatrick

C. Michael Pilato

Este trabajo se publica bajo la licencia Creative Commons Attribution License. Para ver una copia de esta licencia, visite http://creativecommons.org/licenses/by/2.0/ o envíe una carta a Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.

(TBA)


Tabla de contenidos

Prólogo
Prefacio
Audiencia
Cómo leer este libro
Convenciones empleadas en este libro
Convenciones tipográficas
Iconos
Organización de este libro
Este libro es libre
Agradecimientos
De parte de Ben Collins-Sussman
De parte de Brian W. Fitzpatrick
De parte de C. Michael Pilato
1. Introducción
¿Qué es Subversion?
Historia de Subversion
Características de Subversion
Arquitectura de Subversion
Instalando Subversion
Componentes de Subversion
Un comienzo rápido
2. Conceptos básicos
El repositorio
Modelos de versionado
El problema de compartir archivos
La solución bloqueo-modificación-desbloqueo
La solución copiar-modificar-mezclar
Subversion en acción
Copias de trabajo
Revisiones
Cómo las copias de trabajo siguen la pista al repositorio
Las limitaciones de las revisiones mixtas
Resumen
3. Recorrido guiado
¡Ayuda!
Import
Revisiones: Números, Palabras Clave, y Fechas, ¡Dios Mío!
Números de revisión
Palabras clave de la revisión
Fechas de revisión
Descarga inicial
Ciclo básico de trabajo
Actualizar su copia de trabajo local
Hacer cambios en su copia de trabajo local
Examine sus cambios
svn status
svn diff
svn revert
Resolver conflictos (fusionando los cambios de otros)
Fusionando conflictos a mano
Copiando un fichero en su fichero de trabajo
Punting: Usando svn revert
Enviar sus cambios
Examinando el historial
svn log
svn diff
Examinando cambios locales
Comparando copia de trabajo con repositorio
Comparando repositorio con repositorio
svn cat
svn list
Una palabra final en el historial
Otros comandos útiles
svn cleanup
svn import
Sumario
4. Crear ramas y fusionarlas
¿Qué es una rama?
Usando ramas
Creando una rama
Trabajando con su rama
Conceptos clave sobre las ramas
Copiando cambios entre ramas
Copiando cambios específicos
Procedimientos ideales de fusionado
Realizar fusiones de forma manual
Visualización previa de fusiones
Teniendo en cuenta o ignorando ascendencia
Casos habituales de fusionado
Fusionando una rama completa con otra
Deshaciendo cambios
Resucitando elementos borrados
Cambiando la copia local de trabajo
Etiquetas
Creando una etiqueta simple
Creando una etiqueta compleja
Mantenimiento de ramas
Estructura del repositorio
Longevidad de los datos
Sumario
5. Administración del Repositorio
Cuestiones básicas acerca de el repositorio
Entendiendo las Transacciones y Revisiones
Propiedades no versionadas
Base de datos Berkeley
Creación y Configuración de Repositorios
Scripts de enganche
Configuración de la base de datos Berkeley
Mantenimiento del Repositorio
Una caja de herramientas del Administrador
svnlook
svnadmin
svndumpfilter
svnshell.py
Utilidades de la base de datos Berkeley
Limpieza del repositorio
Gestionando el espacio de almacenamiento
Restauración del repositorio
Migrando un repositorio
Copias de seguridad del repositorio
Añadiendo proyectos
Escogiendo el esquema de repositorio
Creando el esquema, importando los datos iniciales
Sumario
6. Configuración del servidor
Introducción
Modelo de red
Solicitudes y Respuestas
Client Credentials Caching
svnserve, un servidor personalizado
Invocando el Servidor
Autenticación y autorización integradas
Crear un fichero 'users' y realm
Activar control de acceso
Autenticación y autorización SSH
httpd, el servidor HTTP Apache
Requisitos previos
Configuración básica de Apache
Opciones de autenticación
Autenticación HTTP básica
Gestión de certificados SSL
Opciones de autorización
Control de acceso simple
Control de acceso por directorio
Regalitos extra
Navegar por el repositorio
Otras características
Ofrecer múltiples métodos de acceso al repositorio
7. Tópicos avanzados
Área de configuración de parámetros de ejecución
Estructura del área de configuración
La configuración y el registro de Windows
Opciones de configuración
Servers
Config
Propiedades
¿Por qué propiedades?
Manipulando propiedades
Propiedades especiales
svn:executable
svn:mime-type
svn:ignore
svn:keywords
svn:eol-style
svn:externals
Ajuste automático de propiedades
Repositorios externos
Ramas de proveedores
Procedimiento general de gestión de ramas de proveedor
svn_load_dirs.pl
8. Información para desarrolladores
Diseño de librería por capas
Capa de repositorio
Capa de acceso al repositorio
RA-DAV (Acceso al repositorio usando HTTP/DAV)
RA-SVN (Acceso al repositorio usando protocolo propio)
RA-Local (Acceso directo al repositorio)
Su librería RA aquí
Capa cliente
Usando las APIs
La librería Apache Portable Runtime
Requisitos de URL y ruta
Usando lenguajes distintos de C y C++
Dentro del área de administración de la copia local de trabajo
El fichero de entradas
Copias prístinas y propiedades de ficheros
WebDAV
Programando con áreas de memoria
Contribuyendo a Subversion
Únase a la comunidad
Obtenga el código fuente
Familiarícese con las reglas de la comunidad
Realizando y verificando sus cambios
Donar sus cambios
9. Referencia completa de Subversion
El cliente de línea de comando de Subversion: svn
Parámetros de svn
Subcomandos de svn
svn add
svn blame
svn cat
svn checkout
svn cleanup
svn commit
svn copy
svn delete
svn diff
svn export
svn help
svn import
svn info
svn list
svn log
svn merge
svn mkdir
svn move
svn propdel
svn propedit
svn propget
svn proplist
svn propset
svn resolved
svn revert
svn status
svn switch
svn update
svnadmin
Parámetros de svnadmin
Subcomandos de svnadmin
svnadmin create
svnadmin deltify
svnadmin dump
svnadmin help
svnadmin hotcopy
svnadmin list-dblogs
svnadmin list-unused-dblogs
svnadmin load
svnadmin lstxns
svnadmin recover
svnadmin rmtxns
svnadmin setlog
svnadmin verify
svnlook
Parámetros de svnlook
svnlook
svnlook author
svnlook cat
svnlook changed
svnlook date
svnlook diff
svnlook dirs-changed
svnlook help
svnlook history
svnlook info
svnlook log
svnlook propget
svnlook proplist
svnlook tree
svnlook uuid
svnlook youngest
svnserve
Parámetros de svnserve
A. Subversion para usuarios de CVS
Los números de revisión son diferentes ahora
Versiones de directorios
Más operaciones estando desconectado
Distinciones entre estado (status) y actualización (update)
Ramas y etiquetas
Propiedades de los metadatos
Resolución de conflictos
Ficheros binarios y traducción
Versionado de módulos
Autenticación
Convirtiendo un repositorio de CVS a Subversion
B. Solución de problemas
Problemas comunes
Problemas usando Subversion
Cada vez que intento acceder a mi repositorio, mi cliente de Subversion se cuelga
Cada vez que intento ejecutar svn, dice que mi copia de trabajo está bloqueada.
Estoy teniendo errores encontrando o abriendo un repositorio, pero sé que mi URL del repositorio es correcta.
Cómo puedo especificar una letra de un dispositivo Windows en una URL file://?
Estoy teniendo problemas haciendo operaciones de escritura en un repositorio de Subversion sobre una red.
Bajo Windows XP, a veces el servidor de Subversion parece enviar datos corruptos.
¿Cual es el mejor método de hacer un trazo de red de la conversación entre un cliente de Subversion y un servidor Apache?
Acabo de construir la distribución binaria, y cuando intento descargar Subversion, consigo un error sobre un "Esquema desconocido de URL."
¿Por qué el comando 'svn revert' requiere un target explícito? ¿Por qué no es recursivo por defecto? Este comportamiento difiere de casi el resto de subcomandos.
Cuando arranco Apache, mod_dav_svn se queja por una "mala versión de la base de datos", que encontró db-3.X, en vez de db-4.X.
Estoy obteniendo errores de "Función no implementada" en RedHat 9, y nada funciona. ¿Cómo arreglo esto?
¿Por qué el informe de cambios dice "(no author)" para los ficheros enviados o importados vía Apache (ra_dav)?
Estoy obteniendo errores ocasionales de "Acceso Denegado" en Windows. Parece que sucede al azar.
En FreeBSD, ciertas operaciones (especialmente svnadmin create) a veces se cuelgan.
Puedo ver mi repositorio en un navegador web, pero 'svn checkout' me da un error sobre "301 Movido Permanentemente".
Estoy intentando mirar una versión antigua de mi fichero, pero svn dice algo sobre "path no encontrado".
C. WebDAV y autoversionado
Conceptos básicos de WebDAV
WebDAV sencillo
Extensiones DeltaV
Subversion y DeltaV
Mapping Subversion to DeltaV
Soporte de autoversionado
La Alternativa mod_dav_lock
Interoperabilidad de autoversionado
WebFolders Win32
Mac OS X
Unix: Nautilus 2
Linux davfs2
D. Herramientas de terceras partes
Clientes y módulos
Language Bindings
Conversores de repositorios
Herramientas de mayor nivel
Herramientas de exploración de repositorios
E. Sobre esta traducción
Origen del proyecto
Quienes somos
Listas de correo
F. Copyright

Lista de figuras

1.1. Arquitectura de Subversion
2.1. Un sistema cliente/servidor típico
2.2. El problema a evitar
2.3. La solución bloqueo-modificación-desbloqueo
2.4. La solución copiar-modificar-mezclar
2.5. La solución copiar-modificar-mezclar (continuación)
2.6. El sistema de archivos del repositorio
2.7. El repositorio
4.1. Ramas de desarrollo
4.2. Estructura inicial del repositorio
4.3. Repositorio con nueva copia
4.4. Bifurcación de la historia de un fichero
8.1. Ficheros y directorios en dos dimensiones
8.2. Versionando el tiempo—¡la tercera dimensión!

Lista de tablas

2.1. URLs de Acceso al Repositorio
6.1. Comparación de tipos de servidores de red
8.1. Un corto inventario de las librerías de Subversion

Lista de ejemplos

5.1. Usando svnshell para navegar por el repositorio
5.2. txn-info.sh (Informe de transacciones pendientes)
6.1. A sample configuration for anonymous access.
6.2. A sample configuration for authenticated access.
6.3. A sample configuration for mixed authenticated/anonymous access.
7.1. Fichero ejemplo de registro (.reg).
8.1. Usando la capa de repositorio
8.2. Usando la capa de repositorio con Python
8.3. Un script simple para obtener una copia de trabajo local.
8.4. Contenido de un fichero .svn/entries típico.
8.5. Uso efectivo de áreas de memoria

Prólogo

Una mala lista de preguntas y respuestas frecuentes (FAQ[1].) es aquella compuesta a partir de las preguntas que el autor de la FAQ desearía que la gente hiciese, en lugar de las preguntas que la gente hace realmente. Quizás haya visto una de estas antes:

P: ¿Cómo puedo usar Glorbosoft XZY para maximizar la productividad de mi equipo?

R: Muchos de nuestros clientes desean saber cómo maximizar su productividad a través de nuestras innovaciones patentadas de software para trabajo en grupo. La respuesta es simple: primero, haga clic en el menú Archivo, baje hasta Incrementar productividad, entonces…

El problema con FAQs de este estilo es que no son, en un sentido literal, FAQs en absoluto. Nadie llamó nunca a la línea de soporte técnico y preguntó, ¿Cómo podemos maximizar la productividad?. En su lugar, la gente hizo preguntas altamente específicas, como, ¿Cómo podemos modificar el sistema de calendario para enviar recordatorios con dos días de antelación en lugar de uno? y similares. Pero es mucho más fácil inventar preguntas y respuestas frecuentes que descubrirlas de verdad. Compilar una verdadera hoja de preguntas y respuestas frecuentes requiere una esfuerzo sostenido y organizado: durante el tiempo de vida del software las preguntas realizadas deben ser rastreadas, las respuestas monitorizadas, y todo ello recogido en un todo coherente e indexado que refleje la experiencia colectiva de los usuarios en ambientes de producción. Requiere de la actitud paciente y observadora de un naturalista de campo. Sin grandes hipótesis ni discursos visionarios—ojos abiertos y tomar notas con detalle es lo que más se necesita.

Lo que me encanta de este libro es que creció justo de un proceso similar, y se puede apreciar en cada página. Es el resultado directo de los encuentros que tuvieron los autores con los usuarios. Comenzó con la observación de Ben Collins-Sussman, que las mismas preguntas básicas estaban siendo realizadas una y otra vez en la lista de correo de Subversion: ¿Cuál es el flujo de trabajo estándar recomendado con Subversion? ¿Funcionan las etiquetas y ramas del mismo modo que en otros sistemas de control de versiones? ¿Cómo puedo averiguar quién ha hecho un cambio en particular?

Frustrado por ver las mismas preguntas día tras otro, Ben trabajó intensamente durante un mes en verano del 2002 para escribir El Manual de Subversion [2], un manual de sesenta páginas que cubría el uso básico de Subversion. El manual no tenía pretensiones de ser una obra completa, pero fue distribuido con Subversion y ayudó a los usuarios a superar ese bache inicial en su curva de aprendizaje. Cuando O'Reilly y Asociados decidió publicar un libro completo sobre Subversion, el camino del mínimo esfuerzo era obvio: expandir el manual de Subversion.

A los tres coautores del nuevo libro se les presentó una oportunidad sin igual. Oficialmente, su tarea consistía en escribir un libro de arriba hacia abajo, comenzando a partir de una tabla de contenidos y borrador inicial. Pero también tenían acceso a un flujo constante—de hecho, un géiser incontrolable—de material práctico. Subversion ya estaba en manos de miles de usuarios, y éstos estaban proporcionando toneladas de retroalimentación, no sólo sobre Subversion, sino también sobre la documentación existente.

Durante el tiempo que llevó escribir este libro, Ben, Mike y Brian frecuentaron las listas de correo y sesiones de chat de Subversion incesantemente, anotando cuidadosamente los problemas que estaban teniendo los usuarios en situaciones de la vida real. Monitorizar tal respuesta es de todos modos parte de la descripción de su puesto de trabajo en CollabNet, y les dio una gran ventaja cuando comenzaron a documentar Subversion. El libro producido está firmemente establecido sobre unos cimientos de experiencia, no en las arenas movedizas de las puras ilusiones; combina las mejores características de un manual de usuario y una lista de preguntas y respuestas frecuentes. Esta dualidad quizás no sea advertida durante una primera lectura. Leído en orden, de principio a fin, el libro es simplemente una descripción directa de una pieza de software. Hay una visión general, el obligado tour guiado, un capítulo sobre administración, algunos temas avanzados, y por supuesto una referencia de los comandos y una guía para resolver problemas. Sólo cuando se vuelve a revisar se ve brillar su autenticidad: los detalles que sólo pueden resultar de encuentros con lo inesperado, ejemplos extraídos de casos de uso genuinos, y sobre todo la sensibilidad hacia las necesidades del usuario y su punto de vista.

Por supuesto, nadie puede garantizar que este libro responderá cualquier pregunta que tenga sobre Subversion. A veces, la precisión con la que se anticipa a sus dudas le parecerá misteriosamente telepática; pero ocasionalmente, se topará con un agujero en el conocimiento de la comunidad de usuarios y se quedará con las manos vacías. Cuando esto ocurra, lo mejor que puede hacer es mandar un correo electrónico a y exponer su problema. Los autores aun siguen ahí, observando, y esto no sólo incluye a los tres que aparecen en la portada, sino muchos otros que contribuyeron correcciones al material original. Desde el punto de vista de la comunidad de usuarios, solucionar su problema es meramente un agradable efecto colateral de un proyecto mucho mayor—en concreto, ajustar ligeramente este libro, y en definitiva Subversion, para que se adapte mejor a la forma en que la gente realmente lo usa. Están deseosos de escucharle, no únicamente porque le pueden ayudar, sino porque usted puede ayudarles a ellos. Con Subversion, como con todos los proyectos de software libre en activo, usted no está solo.

Deje que este libro sea su primer compañero.

Karl Fogel, Chicago, 14 de Marzo, 2004




[1] N.T.: El acrónimo proviene del inglés Frequently Asked Questions.

[2] N.T.: The Subversion Handbook.

Prefacio

Si C le da suficiente cuerda para ahorcarse, piense en Subversion como una especie de almacén de cuerdas. —Brian W. Fitzpatrick

En el mundo del software de código fuente abierto, Concurrent Versions System (CVS[3]) ha sido durante mucho tiempo la herramienta seleccionada para el control de versiones. Y con razón. CVS es software libre, y su modus operandi sin restricciones y soporte para trabajar en red—lo que permite a docenas de programadores dispersos geográficamente compartir su trabajo—se adapta muy bien a la naturaleza colaborativa del mundo de código fuente abierto. CVS y su modelo de desarrollo semi caótico se han convertido en la piedra angular de la cultura del código fuente abierto.

Pero al igual que muchas herramientas, CVS está haciéndose viejo. Subversion es un sistema de control de versiones relativamente nuevo diseñado para ser el sucesor de CVS. Los diseñadores se marcaron el objetivo de ganarse el corazón de los usuarios de CVS de dos modos: creando un sistema de código fuente abierto con un diseño (y look and feel) similar a CVS, e intentando corregir los defectos más notables de CVS. A pesar de que el resultado no representa necesariamente la siguiente gran evolución en diseño de los sistemas de control de versiones, Subversion es muy potente, muy fácil de usar y muy flexible.

Este libro documenta la serie 1.0 del sistema de control de versiones Subversion. Nos hemos esforzado por ser meticulosos. No obstante, Subversion cuenta con una próspera y energética comunidad de desarrolladores, por lo que, sin duda, ya habrá algunas características y mejoras planeadas para futuras versiones de Subversion que puedan modificar algunos comandos y notas específicas en este libro.

Audiencia

Este libro está orientado a los usuarios que saben manejar ordenadores y quieren usar Subversion para gestionar sus datos. Aunque Subversion puede ejecutarse en varios sistemas operativos diferentes, su interfaz de usuario principal es la línea de comandos. Es esta herramienta de línea de comandos (svn) la que será explicada y usada en este libro. Por consistencia, los ejemplos de este libro asumen que el lector está usando un sistema operativo tipo Unix y se siente relativamente cómodo con Unix y las interfaces de línea de comandos.

Dicho ésto, el programa svn también funciona en otras plataformas además de Unix como Microsoft Windows. A excepción de algunos detalles, como el uso de contra barras (\) en lugar de barras de dividir (/) como separadores en la ruta de los ficheros, los datos de entrada y de salida de esta herramienta cuando se ejecuta bajo Windows son idénticos a los de la versión para Unix. No obstante, es posible que los usuarios de Windows obtengan mejores resultados ejecutando los ejemplos bajo el entorno de emulación Unix Cygwin.

Probablemente, la mayoría de los lectores serán programadores o administradores de sistemas que necesiten seguir la pista a los cambios realizados sobre el código fuente. Éste es el uso más común de Subversion y, por lo tanto, el escenario que subyace en todos los ejemplos del libro. Pero Subversion puede usarse para administrar los cambios en cualquier tipo de información: imágenes, música, bases de datos, documentación, etc. Para Subversion, todos los datos no son más que datos.

A pesar de que este libro asume que el lector nunca ha usado un sistema de control de versiones, también hemos intentado facilitar a los usuarios de CVS realizar un cambio indoloro a Subversion. Pequeñas notas secundarias mencionarán CVS de vez en cuando, y un apéndice especial resume la mayoría de las diferencias entre CVS y Subversion.

Cómo leer este libro

Este libro intenta ser útil a personas con diferentes grados de experiencia—desde personas sin experiencia previa en el control de versiones hasta administradores de sistemas experimentados. Dependiendo de su propia experiencia, algunos capítulos le resultarán más o menos importantes. A continuación se detalla una lista de lecturas recomendadas para varios tipos de lectores:

Administradores de sistemas experimentados

Aquí asumimos que probablemente ya ha usado CVS antes, y que se muere por instalar y ejecutar un servidor Subversion lo antes posible. Los capítulos 5 y 6 le mostrarán cómo crear su primer repositorio y ofrecer acceso al mismo por red. Después de realizar estas tareas, el capítulo 3 y el apéndice A son las rutas más rápidas para aprender a manejar el programa cliente de Subversion aprovechando su experiencia con CVS.

Nuevos usuarios

Su administrador probablemente ya ha configurado el servidor de Subversion, así que necesita aprender a usar el cliente. Si nunca ha usado un sistema de control de versiones (como CVS), entonces los capítulos 2 y 3 son una introducción vital. Si ya tiene experiencia con CVS, el capítulo 3 y el apéndice A son los mejores sitios para comenzar.

Usuarios avanzados

Independientemente de que sea usuario o administrador, su proyecto acabará por crecer. Le interesará aprender cómo hacer cosas más avanzadas con Subversion, como por ejemplo usar ramas de desarrollo y realizar mezclas (capítulo 4), cómo usar la característica de propiedades de Subversion, cómo configurar las opciones de tiempo de ejecución (capítulo 7), y otras cosas. Los capítulos 4 y 7 no son vitales al principio, pero no olvide leerlos una vez se haya familiarizado con los conceptos básicos.

Desarrolladores

Asumimos que ya está familiarizado con Subversion y que ahora desea extender su funcionalidad o crear nuevo software usando sus múltiples APIs. El capítulo 8 está dedicado a usted.

El libro termina con material de referencia—el capítulo 9 es una guía de referencia de todos los comandos de Subversion, y los apéndices cubren varios temas interesantes. Una vez haya terminado de leer este libro, estos capítulos serán posiblemente los que vuelva a usar.

Convenciones empleadas en este libro

Esta sección cubre las convenciones empleadas en este libro.

Convenciones tipográficas

Anchura constante

Usada para comandos, salida de comandos y parámetros

Anchura constante en cursiva

Usada para elementos que se pueden reemplazar tanto en código fuente como texto

Cursiva

Usada para nombres de ficheros y directorios

Iconos

[Nota]Nota

Este icono señala una nota relacionada con el texto al que acompaña.

[Sugerencia]Sugerencia

Este icono señala una sugerencia útil relacionada con el texto al que acompaña.

[Aviso]Aviso

Este icono señala un aviso relacionado con el texto al que acompaña.

Tenga en cuenta que los ejemplos de código fuente no son más que eso—ejemplos. Aunque puedan ser compilados con los comandos apropiados, su objetivo es el de ilustrar el problema expuesto, y no tienen por qué ser ejemplos de un buen estilo de programación.

Organización de este libro

Aquí tiene un listado de los siguientes capítulos y sus contenidos:

Capítulo 1, Introducción

Cubre la historia de Subversion, sus características, arquitectura, componentes y métodos de instalación. También incluye una guía rápida para comenzar.

Capítulo 2, Conceptos básicos

Explica los conceptos básicos del control de versiones y los diferentes modelos de versionado, así como el repositorio de Subversion, las copias de trabajo y las revisiones.

Capítulo 3, Recorrido guiado

Da un repaso a un día cualquiera en la vida de un usuario de Subversion. Muestra cómo usar Subversion para obtener, modificar y enviar cambios al repositorio.

Capítulo 4, Crear ramas y fusionar cambios

Explica las ramas, fusiones y etiquetado, incluyendo los mejores métodos para crear ramas y fusionarlas, casos de uso comunes, cómo deshacer cambios y cómo alternar fácilmente entre ramas de desarrollo.

Capítulo 5, Administración de repositorios

Describe los elementos básicos de un repositorio Subversion, cómo crear, configurar y mantener un repositorio, y las herramientas que puede usar para hacer todo esto.

Capítulo 6, Configuración del servidor

Explica cómo configurar su servidor Subversion y tres métodos de acceso: HTTP, el protocolo svn y el acceso local. También cubre los detalles de autenticación, autorización y acceso anónimo.

Capítulo 7, Temas avanzados

Explora los ficheros de configuración del cliente de Subversion, las propiedades de ficheros y directorios, cómo ignorar ficheros en su copia local, cómo incluir árboles externos en su copia local, y finalmente, cómo manejar ramas de desarrollo.

Capítulo 8, Información para desarrolladores

Describe detalles internos de Subversion, el sistema de ficheros de Subversion, y las áreas administrativas de una copia local desde el punto de vista de un programador. Muestra cómo usar las APIs públicas para escribir un programa que use Subversion, y sobre todo, cómo contribuir al desarrollo de Subversion.

Capítulo 9, Referencia completa de Subversion

Explica con detalle todo subcomando de svn, svnadmin, y svnlook junto con una gran cantidad de ejemplos para todos los gustos.

Apéndice A, Subversion para usuarios de CVS

Cubre las similitudes y diferencias entre Subversion y CVS, con numerosas sugerencias para deshacerse de los malos hábitos adquiridos tras años de uso de CVS. Incluye descripciones de los números de revisión de Subversion, versionado de directorios, operaciones offline, update vs. status, ramas, etiquetas, metadatos, resolución de conflictos y autenticación.

Apéndice B, Solución de problemas

Dedicado a las dificultades y problemas habituales usando y compilando Subversion.

Apéndice C, WebDAV y el auto versionado

Describe los detalles de WebDAV y DeltaV, y cómo configurar su repositorio Subversion para que pueda ser montado como una recurso DAV compartido en modo lectura/escritura.

Apéndice D, Herramientas de terceros

Trata las herramientas que soportan o usan Subversion, incluyendo programas cliente alternativos, navegadores de repositorio y similares.

Este libro es libre

Este libro comenzó con fragmentos de documentación escritos por los desarrolladores del proyecto Subversion, los cuales fueron reescritos y agrupados en una única obra. Como tal, siempre ha estado bajo una licencia libre. (Lea Apéndice F, Copyright.) De hecho, el libro fue escrito bajo escrutinio público como parte de Subversion. Ésto significa dos cosas:

  • Siempre encontrará la última versión de este libro en el propio árbol de código fuente de Subversion.

  • Puede distribuir y realizar cambios en el libro a su gusto—está bajo una licencia libre. Por supuesto, en lugar de distribuir su propia versión privada de este libro, preferiríamos que enviase cualquier comentario o parche a la comunidad de desarrolladores Subversion. Lea “Contribuyendo a Subversion” para aprender cómo tomar parte en la comunidad.

Puede enviar comentarios y preguntas sobre la publicación a O'Reilly aquí: ###insert boilerplate.

Una versión online reciente de este libro puede encontrarse en http://svnbook.red-bean.com.

Agradecimientos

Este libro no hubiese sido posible (y no muy útil) si Subversion no existiese. Por esta razón, los autores quieren agradecer a Brian Behlendorf de CollabNet por su visión y apoyo económico a tan peligroso y ambicioso nuevo proyecto de código fuente abierto; Jim Blandy por el nombre original Subversion y diseño; te queremos, Jim; Karl Fogel por ser tan buen amigo y excelente líder de la comunidad, en ese orden.[4]

Gracias a O'Reilly y nuestros editores, Linda Mui y Tatiana Diaz por su paciencia y apoyo.

Finalmente, queremos dar gracias a las innumerables personas que contribuyeron al libro con revisiones informales, sugerencias y correcciones: A pesar de ser sin lugar a dudas una lista incompleta, este libro sería incompleto e incorrecto sin la ayuda de Jani Averbach, Ryan Barrett, Francois Beausoleil, Jennifer Bevan, Matt Blais, Zack Brown, Martin Buchholz, Brane Cibej, John R. Daily, Peter Davis, Olivier Davy, Robert P. J. Day, Mo DeJong, Brian Denny, Joe Drew, Nick Duffek, Ben Elliston, Justin Erenkrantz, Shlomi Fish, Julian Foad, Chris Foote, Martin Furter, Dave Gilbert, Eric Gillespie, Matthew Gregan, Art Haas, Greg Hudson, Alexis Huxley, Jens B. Jorgensen, Tez Kamihira, David Kimdon, Mark Benedetto King, Andreas J. Koenig, Nuutti Kotivuori, Matt Kraai, Scott Lamb, Vincent Lefevre, Morten Ludvigsen, Paul Lussier, Bruce A. Mah, Philip Martin, Feliciano Matias, Patrick Mayweg, Gareth McCaughan, Jon Middleton, Tim Moloney, Mats Nilsson, Joe Orton, Amy Lyn Pilato, Kevin Pilch-Bisson, Dmitriy Popkov, Michael Price, Mark Proctor, Steffen Prohaska, Daniel Rall, Tobias Ringstrom, Garrett Rooney, Joel Rosdahl, Christian Sauer, Larry Shatzer, Russell Steicke, Sander Striker, Erik Sjoelund, Johan Sundstroem, John Szakmeister, Mason Thomas, Eric Wadsworth, Colin Watson, Alex Waugh, Chad Whitacre, Josef Wolf, Blair Zajac, y toda la comunidad de Subversion.

De parte de Ben Collins-Sussman

Gracias a mi mujer Frances, quien, tras muchos meses llegó a oír, Pero cariño, todavía estoy trabajando en el libro, en lugar del habitual, Pero cariño, todavía estoy escribiendo emails. ¡No tengo ni idea de dónde saca toda su paciencia! Ella es mi contrapeso ideal.

Gracias a mi familia adquirida por su sincero apoyo, a pesar de no tener interés real en el tema. (Ya sabe, aquellos que cuando dicen, Oh, ¿estás escribiendo un libro?, y les respondes que es sobre ordenadores, desvían su mirada.)

Gracias a todos mis amigos íntimos, quienes enriquecen mi vida. No me miréis de ese modo—sabéis quienes sois.

De parte de Brian W. Fitzpatrick

Muchísimas gracias a mi mujer Marie por ser increíblemente comprensiva, apoyarme, y sobre todo, ser paciente. Gracias a mi hermano Eric quien me introdujo a la programación UNIX hace tiempo. Gracias a mi madre y abuela por todo su apoyo, sin olvidar aquellas vacaciones de navidades en las que tuvieron que soportarme porque nada más llegar a casa me dediqué a mi portátil para trabajar en el libro.

A Mike y Ben: ha sido un placer trabajar con vosotros en el libro. Vaya, ¡es un placer trabajar con vosotros en el trabajo!

A todas las personas de la comunidad Subversion y la Apache Software Foundation, gracias por aceptarme. No pasa un solo día sin que aprenda algo de al menos uno de vosotros.

Por último, gracias a mi abuelo, que siempre me decía la libertad es responsabilidad. No podría estar más de acuerdo.

De parte de C. Michael Pilato

Gracias en especial a mi mujer, Amy, por su amor y paciente apoyo, por soportar largas noches, e incluso por revisar secciones enteras de este libro—siempre das lo mejor, y lo haces con increíble elegancia. Gavin, cuando seas suficientemente mayor para leer, espero que estés tan orgulloso de tu papá como él lo está de ti. A mamá y papá (y el resto de la familia), gracias por vuestro constante apoyo y entusiasmo.

Me quito el sombrero ante Shep Kendall, a través de quien descubrí el mundo de los ordenadores; Ben Collins-Sussman, mi guía turístico por el mundo del código fuente abierto; Karl Fogel—tu eres mi .emacs; Greg Stein, por transmitirme conocimientos prácticos de programación. Brian Fitzpatrick—por compartir esta experiencia literaria conmigo. A todas las numerosas personas de las que siempre estoy aprendiendo cosas nuevas—¡continuad enseñando!

Por último, a Aquél que demuestra perfectamente excelencia creativa—gracias.




[3] N.T.: Sistema concurrente de versiones. Debido a que el comando principal de este software se llama cvs, se usa la abreviación tanto para referirse al comando como al sistema completo y por ello no se traduce.

[4] Oh, y gracias Karl, por tener demasiado trabajo como para haber escrito este libro.

Capítulo 1. Introducción

El control de versiones es el arte del manejo de los cambios en la información. Ha sido durante mucho tiempo una herramienta crítica para los programadores, quienes normalmente empleaban su tiempo haciendo pequeños cambios en el software y después deshaciendo esos cambios al día siguiente. Pero la utilidad del software de control de versiones se extiende más allá de los límites del mundo del desarrollo de software. Allá donde pueda encontrarse a gente usando ordenadores para manejar información que cambia a menudo, hay un hueco para el control de versiones. Y aquí es donde entra en juego Subversion.

Este capítulo contiene una introducción general a Subversion — qué es; qué hace; cómo conseguirlo.

¿Qué es Subversion?

Subversion es un sistema de control de versiones libre y de código fuente abierto. Es decir, Subversion maneja ficheros y directorios a través del tiempo. Hay un árbol de ficheros en un repositorio central. El repositorio es como un servidor de ficheros ordinario, excepto porque recuerda todos los cambios hechos a sus ficheros y directorios. Ésto le permite recuperar versiones antiguas de sus datos, o examinar el historial de cambios de los mismos. En este aspecto, mucha gente piensa en los sistemas de versiones como en una especie de máquina del tiempo .

Subversion puede acceder al repositorio a través de redes, lo que le permite ser usado por personas que se encuentran en distintos ordenadores. A cierto nivel, la capacidad para que varias personas puedan modificar y administrar el mismo conjunto de datos desde sus respectivas ubicaciones fomenta la colaboración. Se puede progresar mas rápidamente sin un único conducto por el cual deban pasar todas las modificaciones. Y puesto que el trabajo se encuentra bajo el control de versiones, no hay razón para temer por que la calidad del mismo vaya a verse afectada por la pérdida de ese conducto único—si se ha hecho un cambio incorrecto a los datos, simplemente deshaga ese cambio.

Algunos sistemas de control de versiones son también sistemas de administración de configuración de software. Estos sistemas son diseñados específicamente para la administración de árboles de código fuente, y tienen muchas características que son específicas del desarrollo de software— tales como el entendimiento nativo de lenguajes de programación, o el suministro de herramientas para la construcción de software. Sin embargo, Subversion no es uno de estos sistemas. Subversion es un sistema general que puede ser usado para administrar cualquier conjunto de ficheros. Para usted, esos ficheros pueden ser código fuente— para otros, cualquier cosa desde la lista de la compra de comestibles hasta combinaciones de vídeo digital y más allá.

Historia de Subversion

A principios del 2000, CollabNet, Inc. (http://www.collab.net) comenzó a buscar desarrolladores para escribir un sustituto para CVS. CollabNet ofrece un conjunto de herramientas de software colaborativo llamado SourceCast, del cual un componente es el control de versiones. Aunque SourceCast usaba CVS como su sistema de control de versiones inicial, las limitaciones de CVS se hicieron evidentes desde el principio, y CollabNet sabía que tendría que encontrar algo mejor. Desafortunadamente, CVS se había convertido en el estándar de facto en el mundo del código abierto porque no había nada mejor, al menos no bajo una licencia libre. Así CollabNet decidió escribir un nuevo sistema de control de versiones desde cero, manteniendo las ideas básicas de CVS, pero sin sus fallos y defectos.

En febrero del 2000, contactaron con Karl Fogel, autor de Open Source Development with CVS (Coriolis, 1999), y le preguntaron si le gustaría trabajar en este nuevo proyecto. Casualmente, por aquel entonces Karl ya se encontraba discutiendo sobre el diseño de un nuevo sistema de control de versiones con su amigo Jim Blandy. En 1995, los dos habían fundado Cyclic Software, compañía que hacía contratos de soporte de CVS, y aunque después vendieron el negocio, seguían usando CVS todos los días en sus trabajos. La frustración de ambos con CVS había conducido a Jim a pensar cuidadosamente acerca de mejores vías para administrar datos versionados , y no sólo tenía ya el nombre de Subversion, sino también el diseño básico del repositorio de Subversion. Cuando CollabNet llamó, Karl aceptó inmediatamente trabajar en el proyecto, y Jim consiguió que su empresa, RedHat Software, básicamente lo donara al proyecto por un período de tiempo indefinido. Collabnet contrató a Karl y a Ben Collins-Sussman, y el trabajo detallado de diseño comenzó en mayo. Con la ayuda de algunos ajustes bien colocados de Brian Behlendorf y Jason Robbins de CollabNet, y Greg Stein (por aquel entonces un activo desarrollador independiente del proceso de especificación de WebDAV/DeltaV), Subversion atrajo rápidamente a una comunidad activa de desarrolladores. Ésto vino a demostrar que era mucha la gente que había tenido las mismas frustrantes experiencias con CVS, y que había recibido con agrado la oportunidad de hacer algo al respecto.

El equipo de diseño original estableció algunos objetivos simples. No querían abrir nuevos caminos en la metodología del control de versiones, sólo querían corregir CVS. Decidieron que Subversion incorporaría las características de CVS, y que preservarían el mismo modelo de desarrollo, pero sin duplicar los defectos obvios de CVS. Y aunque no necesitaba ser un reemplazo exacto de CVS, debía ser lo bastante similar para que cualquier usuario de CVS pudiera hacer el cambio con poco esfuerzo.

Después de catorce meses de codificación, Subversion pasó a ser auto-hospedado el 31 de agosto del 2001. Es decir, los desarrolladores de Subversion dejaron de usar CVS para la administración del propio código fuente de Subversion, y en su lugar empezaron a usar Subversion.

Si bien fue CollabNet quien inició el proyecto, y todavía financia una gran parte del trabajo (paga el salario de unos pocos desarrolladores a tiempo completo de Subversion), Subversion funciona como la mayoría de proyectos de código abierto, dirigido por un conjunto informal de reglas transparentes que fomentan el mérito. La licencia copyright de CollabNet es completamente compatible con las Directrices de Software Libre de Debian. En otras palabras, cualquier persona es libre de descargar, modificar, y redistribuir Subversion como desee; no se requiere ningún permiso de CollabNet o de cualquier otra persona.

Características de Subversion

Al discutir acerca de las características que Subversion aporta al mundo del control de versiones, a menudo es útil hablar de ellas en términos de cómo han mejorado sobre el diseño de CVS. Si no está familiarizado con CVS, quizás no entienda todas estas características. Y si no está familiarizado con el control de versiones en absoluto, se le pueden nublar los ojos a menos que lea primero Capítulo 2, Conceptos básicos, donde proporcionamos una leve introducción al control de versiones en general.

Subversion proporciona:

Versionado de directorios

CVS solamente lleva el historial de ficheros individuales, pero Subversion implementa un sistema de ficheros versionado virtual que sigue los cambios sobre árboles de directorios completos a través del tiempo. Ambos, ficheros y directorios, se encuentran bajo el control de versiones.

Verdadero historial de versiones

Dado que CVS está limitado al versionado de ficheros, operaciones como copiar y renombrar—las cuales pueden ocurrir sobre ficheros, pero que realmente son cambios al contenido del directorio en el que se encuentran—no son soportadas por CVS. Adicionalmente, en CVS no puede reemplazar un fichero versionado con algo nuevo que lleve el mismo nombre sin que el nuevo elemento herede el historial del fichero antiguo—que quizás sea completamente distinto al anterior. Con Subversion, usted puede añadir, borrar, copiar, y renombrar ficheros y directorios. Y cada fichero nuevo añadido comienza con un historial nuevo, limpio y completamente suyo.

Envíos atómicos

Una colección cualquiera de modificaciones o bien entra por completo al repositorio, o bien no lo hace en absoluto. Ésto permite a los desarrolladores construir y enviar los cambios como fragmentos lógicos e impide que ocurran problemas cuando sólo una parte de los cambios enviados lo hace con éxito.

Versionado de metadatos

Cada fichero y directorio tiene un conjunto de propiedades —claves y sus valores —asociado a él. Usted puede crear y almacenar cualquier par arbitrario de clave/valor que desee. Las propiedades son versionadas a través del tiempo, al igual que el contenido de los ficheros.

Elección de las capas de red

Subversion tiene una noción abstracta del acceso al repositorio, facilitando a las personas implementar nuevos mecanismos de red. Subversion puede conectarse al servidor HTTP Apache como un módulo de extensión. Ésto proporciona a Subversion una gran ventaja en estabilidad e interoperabilidad, y acceso instantáneo a las características existentes que ofrece este servidor—autenticación, autorización, compresión de la conexión, etcétera. También tiene disponible un servidor de Subversion independiente, y más ligero. Este servidor habla un protocolo propio, el cual puede ser encaminado fácilmente a través de un túnel SSH.

Manipulación consistente de datos

Subversion expresa las diferencias del fichero usando un algoritmo de diferenciación binario, que funciona idénticamente con ficheros de texto (legibles para humanos) y ficheros binarios (ilegibles para humanos). Ambos tipos de ficheros son almacenados igualmente comprimidos en el repositorio, y las diferencias son transmitidas en ambas direcciones a través de la red.

Ramificación y etiquetado eficientes

El coste de ramificación y etiquetado no necesita ser proporcional al tamaño del proyecto. Subversion crea ramas y etiquetas simplemente copiando el proyecto, usando un mecanismo similar al enlace duro. De este modo estas operaciones toman solamente una cantidad de tiempo pequeña y constante.

Hackability

Subversion no tiene un equipaje histórico; está implementado como una colección de bibliotecas compartidas en C con APIs bien definidas. Ésto hace a Subversion extremadamente fácil de mantener y reutilizable por otras aplicaciones y lenguajes.

Arquitectura de Subversion

Figura 1.1, “Arquitectura de Subversion” ilustra lo que uno podría titular una visión panorámica del diseño de Subversion.

Figura 1.1. Arquitectura de Subversion

Arquitectura de Subversion


En un extremo se encuentra un repositorio de Subversion que conserva todos los datos versionados. Al otro lado, hay un programa cliente Subversion que administra réplicas parciales de esos datos versionados (llamadas copias de trabajo). Entre estos extremos hay múltiples rutas a través de varias capas de acceso al repositorio (AR). Algunas de estas rutas incluyen redes de ordenadores y servidores de red que después acceden al repositorio. Otras pasan por alto la red y acceden al repositorio directamente.

Instalando Subversion

Subversion está construido sobre una capa de portabilidad llamada APR (la biblioteca Apache Portable Runtime), lo cual significa que Subversion debería funcionar en cualquier sistema operativo donde lo haga el servidor httpd Apache: Windows, Linux, todos los sabores de BSD, Mac OS X, Netware y otros.

La manera más sencilla de obtener Subversion es descargando un paquete binario construido para su sistema operativo. El sitio web de Subversion (http://subversion.tigris.org) dispone a menudo de estos paquetes disponibles para su descarga, publicados por voluntarios. El sitio web contiene generalmente paquetes que incluyen instaladores gráficos para los usuarios de los sistemas operativos de Microsoft. Si usted usa un sistema operativo Unix o similar, puede usar el sistema nativo de distribución de paquetes de su sistema (RPMs, DEBs, el árbol de ports, etc.) para obtener Subversion.

Alternativamente, usted puede compilar Subversion directamente a partir del código fuente. Del sitio web de Subversion, descargue la ultima versión del código fuente. Después de desempaquetarlo, siga las instrucciones del fichero INSTALL para compilarlo. Observe que cada paquete de código fuente que se publica contiene todo lo necesario para construir un cliente de línea de comandos capaz de comunicarse con un repositorio remoto (en particular, las bibliotecas apr, apr-util y neon). Sin embargo, las partes opcionales de Subversion tienen otras muchas dependencias, tales como la base de datos Berkeley DB y posiblemente el servidor web Apache. Si usted quiere hacer una compilación completa, asegúrese de tener todos los paquetes documentados en el fichero INSTALL. Si planea trabajar en el propio Subversion, puede usar su programa cliente para obtener la última y más reciente versión del código fuente. Este procedimiento está documentado en “Obtenga el código fuente”.

Componentes de Subversion

Una vez instalado, Subversion se compone de un número diferente de piezas. A continuación se presenta una visión general de estos componentes. No se alarme si las descripciones breves no le dejan las cosas muy claras —hay páginas de sobra en este libro dedicadas a aliviarle esa confusión.

svn

El programa cliente de línea de comandos.

svnversion

Programa para informar del estado (en términos de revisiones de los elementos presentes) de una copia de trabajo.

svnlook

Una herramienta para inspeccionar un repositorio de Subversion.

svnadmin

Herramienta para crear, modificar o reparar un repositorio de Subversion.

svndumpfilter

Un programa para filtrar el formato de salida de volcado de repositorios Subversion.

mod_dav_svn

Un módulo para el servidor HTTP Apache usado para hacer que su repositorio esté disponible a otros a través de una red.

svnserve

Un servidor independiente, ejecutable como proceso demonio o invocable por SSH; otra manera de hacer que su repositorio esté disponible para otros a través de una red.

Suponiendo que ha instalado Subversion correctamente, debería estar preparado para comenzar. Los próximos dos capítulos le guiarán a través del uso de svn, el programa cliente de Subversion de línea de comandos.

Un comienzo rápido

Algunas personas tienen problemas para absorber una nueva tecnología leyendo un enfoque del tipo "arriba a abajo" como el que ofrece este libro. Esta sección es una introducción muy breve a Subversion, y está pensada para dar a los principiantes algo con lo que defenderse. Si usted es de los que prefiere aprender experimentando, la siguiente demostración le pondrá en marcha. A lo largo del camino, le iremos dando enlaces a los capítulos relevantes de este libro.

Si a usted le resulta completamente nuevo el concepto de control de versiones o el modelo copiar-modificar-mezclar usado tanto por CVS como por Subversion, debería leer Capítulo 2, Conceptos básicos antes de seguir adelante.

[Nota]Nota

El siguiente ejemplo asume que usted tiene preparados tanto el cliente de línea de comandos de Subversion svn, como la herramienta administrativa svnadmin. También asume que su cliente svn ha sido compilado con soporte para la base de datos Berkeley DB. Puede comprobarlo ejecutando svn --version y asegurándose de que el modulo ra_local está disponible. Sin este módulo, el cliente no podrá acceder a URLs del tipo file://

Subversion almacena todos los datos versionados en un repositorio central. Para comenzar, cree un nuevo repositorio:

$ svnadmin create /path/to/repos 
$ ls /path/to/repos
conf/  dav/  db/  format  hooks/  locks/  README.txt

Este comando crea un nuevo directorio /path/to/repos que contiene un repositorio de Subversion. Asegúrese de que este directorio reside en un disco local y no compartido en red. Este nuevo directorio contiene principalmente una colección de ficheros de la base de datos Berkeley DB. Para más información sobre la creación y mantenimiento de repositorios, vea Capítulo 5, Administración del Repositorio.

A continuación, cree un árbol de ficheros y directorios para importar dentro del repositorio. Por razones que se aclararán más tarde (vea Capítulo 4, Crear ramas y fusionarlas), su estructura debería tener tres directorios en el primer nivel de la jerarquía llamados branches,tags, y trunk:

/tmp/project/branches/
/tmp/project/tags/
/tmp/project/trunk/
               foo.c
               bar.c
               Makefile
               …

Una vez tenga un árbol de datos listo para continuar, impórtelo dentro del repositorio con el comando svn import (vea svn import):

$ svn import /tmp/project file:///path/to/repos -m "initial import"
Adding         /tmp/project/branches
Adding         /tmp/project/tags
Adding         /tmp/project/trunk
Adding         /tmp/project/trunk/foo.c
Adding         /tmp/project/trunk/bar.c
Adding         /tmp/project/trunk/Makefile
…
Committed revision 1.
$ 

Ahora el repositorio contiene este árbol de datos. Observe que el directorio original /tmp/project no se ha modificado; Subversion no se preocupa por él (de hecho, puede incluso borrar ese directorio si lo desea). Para comenzar a manipular los datos del repositorio, necesitará crear una nueva copia de trabajo de los datos, una especie de entorno de trabajo privado. Pida a Subversion que obtenga [5] una copia de trabajo del directorio trunk del repositorio:

$ svn checkout file:///path/to/repos/trunk project
A  project/foo.c
A  project/bar.c
A  project/Makefile
…
Checked out revision 1.

Ahora usted dispone de una copia personal de parte del repositorio en un nuevo directorio llamado project. Puede editar los ficheros en su copia de trabajo y después depositar esos cambios de nuevo en el repositorio.

  • Entre en su copia de trabajo y edite el contenido de un fichero.

  • Ejecute svn diff para ver las diferencias introducidas por sus cambios en formato diff unificado.

  • Ejecute svn commit para depositar la nueva versión de su fichero en el repositorio.

  • Ejecute svn update para sincronizar su copia de trabajo con el repositorio.

Para un recorrido completo por todas las operaciones que puede realizar con su copia de trabajo, vea Capítulo 3, Recorrido guiado.

Llegado este punto, usted tiene la opción de hacer que su repositorio Subversion esté disponible a otros a través de una red. Vea Capítulo 6, Configuración del servidor para aprender acerca de los diferentes tipos de procesos servidor disponibles y cómo configurarlos.




[5] N.T.: En la bibliografía sobre control de versiones se suele utilizar el vocablo inglés «check out» para referirse a la operación usada para obtener una copia (parcial) de un repositorio centralizado. En ocasiones, la obtención de dicha copia implica la conexión a un servidor remoto, por lo que en la traducción es común emplear indistintamente los términos «obtener» y «descargar» para referirse a esta operación.

Capítulo 2. Conceptos básicos

Este capítulo es una introducción breve e informal a Subversion. Si es nuevo en el tema del control de versiones, este capítulo es definitivamente para usted. Empezaremos tratando los conceptos generales en el control de versiones, seguiremos con las ideas específicas detrás de Subversion, y mostraremos algunos ejemplos simples de Subversion en acción.

Aunque los ejemplos de este capítulo muestran a gente compartiendo colecciones de archivos de código fuente, tenga en mente que Subversion puede manejar cualquier tipo de colección de archivos—no está limitado a asistir a programadores de ordenadores.

El repositorio

Subversion es un sistema centralizado para compartir información. La parte principal de Subversion es el repositorio, el cual es un almacén central de datos. El repositorio guarda información en forma de árbol de archivos—una típica jerarquía de archivos y directorios. Cualquier número de clientes puede conectarse al repositorio y luego leer o escribir en esos archivos. Al escribir datos, un cliente pone a disposición de otros la información; al leer datos, el cliente recibe información de otros. La figura Figura 2.1, “Un sistema cliente/servidor típico” ilustra ésto.

Figura 2.1. Un sistema cliente/servidor típico

Un sistema cliente/servidor típico


Entonces, ¿qué tiene ésto de interesante?. Hasta ahora, suena como la definición del típico servidor de archivos. Y, de hecho, el repositorio es una especie de servidor de archivos, pero no del tipo habitual. Lo que hace especial al repositorio de Subversion es que recuerda todos los cambios hechos sobre él: cada cambio a cada archivo, e inclusive cambios al propio árbol de directorios, tales como la adición, borrado y reubicación de archivos y directorios.

Cuando un cliente lee datos del repositorio, normalmente sólo ve la ultima versión del árbol de archivos. Sin embargo, el cliente también tiene la posibilidad de ver estados previos del sistema de archivos. Por ejemplo, un cliente puede hacer consultas históricas como, ¿Qué contenía este directorio el miércoles pasado? Esta es la clase de preguntas que resulta esencial en cualquier sistema de control de versiones: sistemas que están diseñados para registrar y seguir los cambios en los datos a través del tiempo.

Modelos de versionado

La misión principal de un sistema de control de versiones es permitir la edición colaborativa y la compartición de los datos. Sin embargo, existen diferentes sistemas que utilizan diferentes estrategias para alcanzar este objetivo.

El problema de compartir archivos

Todos los sistemas de control de versiones tienen que resolver un problema fundamental: ¿Cómo permitirá el sistema a los usuarios el compartir información, pero al mismo tiempo impedirá que se pisen los callos mutuamente de forma accidental? Es muy sencillo para los usuarios el sobreescribir accidentalmente los cambios de los demás en el repositorio.

Considere el escenario mostrado en Figura 2.2, “El problema a evitar”. Suponga que tenemos dos colaboradores, Juan y Carmen. Cada uno de ellos decide editar el mismo archivo del repositorio al mismo tiempo. Si Juan guarda sus cambios en el repositorio en primer lugar, es posible que (unos momentos más tarde) Carmen los sobreescriba accidentalmente con su propia versión del archivo. Si bien es cierto que la versión de Juan no se ha perdido para siempre (porque el sistema recuerda cada cambio), cualquier cambio que Juan haya hecho no estará presente en la versión más reciente de Carmen porque, para empezar, ella nunca vio los cambios de Juan. El trabajo de Juan sigue efectivamente perdido—o al menos ausente en la última versión del archivo—y probablemente por accidente. ¡Esta es definitivamente una situación que queremos evitar!

Figura 2.2. El problema a evitar

El problema a evitar


La solución bloqueo-modificación-desbloqueo

Muchos sistemas de control de versiones utilizan un modelo de bloqueo-modificación-desbloqueo para atacar este problema. En un sistema como éste, el repositorio sólo permite a una persona modificar un archivo al mismo tiempo. Juan debe bloquear primero el archivo para luego empezar a hacerle cambios. Bloquear un archivo se parece mucho a pedir prestado un libro de la biblioteca; si Juan ha bloqueado el archivo, entonces Carmen no puede hacerle cambios. Por consiguiente, si ella intenta bloquear el archivo, el repositorio rechazará la petición. Todo lo que puede hacer es leer el archivo y esperar a que Juan termine sus cambios y deshaga el bloqueo. Tras desbloquear Juan el archivo, Carmen puede aprovechar su turno bloqueando y editando el archivo. La figura Figura 2.3, “La solución bloqueo-modificación-desbloqueo” demuestra esta sencilla solución.

Figura 2.3. La solución bloqueo-modificación-desbloqueo

La solución bloqueo-modificación-desbloqueo


El problema con el modelo bloqueo-modificación-desbloqueo es que es un tanto restrictivo y a menudo se convierte en un obstáculo para los usuarios:

  • Bloquear puede causar problemas administrativos. En ocasiones Juan bloqueará un archivo y se olvidará de él. Mientras tanto, como Carmen está aún esperando para editar el archivo, sus manos están atadas. Y luego Juan se va de vacaciones. Ahora Carmen debe conseguir que un administrador deshaga el bloqueo de Juan. La situación termina causando muchas demoras innecesarias y pérdida de tiempo.

  • Bloquear puede causar una serialización innecesaria. ¿Qué sucede si Juan está editando el inicio de un archivo de texto y Carmen simplemente quiere editar el final del mismo archivo? Estos cambios no se solapan en absoluto. Ambos podrían editar el archivo simultáneamente sin grandes perjuicios, suponiendo que los cambios se combinaran correctamente. No hay necesidad de turnarse en esta situación.

  • Bloquear puede causar una falsa sensación de seguridad. Imaginemos que Juan bloquea y edita el archivo A, mientras que Carmen bloquea y edita el archivo B al mismo tiempo. Pero suponga que A y B dependen uno del otro y que los cambios hechos a cada uno de ellos son semánticamente incompatibles. Súbitamente A y B ya no funcionan juntos. El sistema de bloqueo se mostró ineficaz a la hora de evitar el problema—sin embargo, y de algún modo, ofreció una falsa sensación de seguridad. Es fácil para Juan y Carmen imaginar que al bloquear archivos, cada uno está empezando una tarea segura y aislada, lo cual les inhibe de discutir sus cambios incompatibles desde un principio.

La solución copiar-modificar-mezclar

Subversion, CVS y otros sistemas de control de versiones utilizan un modelo del tipo copiar-modificar-mezclar como alternativa al bloqueo. En este modelo, el cliente de cada usuario se conecta al repositorio del proyecto y crea una copia de trabajo personal—una réplica local de los archivos y directorios del repositorio. Los usuarios pueden entonces trabajar en paralelo, modificando sus copias privadas. Finalmente, todas las copias privadas se combinan (o mezclan) en una nueva versión final. El sistema de control de versiones a menudo ayuda con la mezcla, pero en última instancia es un ser humano el responsable de hacer que ésto suceda correctamente.

He aquí un ejemplo. Digamos que Juan y Carmen crean sendas copias de trabajo del mismo proyecto, extraídas del repositorio. Ambos trabajan concurrentemente y hacen cambios a un mismo archivo A dentro de sus copias. Carmen guarda sus cambios en el repositorio primero. Cuando Juan intenta guardar sus cambios más tarde, el repositorio le informa de que su archivo A está desactualizado. En otras palabras, que el archivo A en el repositorio ha sufrido algún cambio desde que lo copió por última vez. Por tanto, Juan le pide a su cliente que mezcle cualquier cambio nuevo del repositorio con su copia de trabajo del archivo A. Es probable que los cambios de Carmen no se solapen con los suyos; así que una vez que tiene ambos juegos de cambios integrados, Juan guarda su copia de trabajo de nuevo en el repositorio. Las figuras Figura 2.4, “La solución copiar-modificar-mezclar” y Figura 2.5, “La solución copiar-modificar-mezclar (continuación)” muestran este proceso.

Figura 2.4. La solución copiar-modificar-mezclar

La solución copiar-modificar-mezclar


Figura 2.5. La solución copiar-modificar-mezclar (continuación)

La solución copiar-modificar-mezclar (continuación)


¿Pero qué ocurre si los cambios de Carmen se solapan con los de Juan? ¿Entonces qué? Esta situación se conoce como conflicto y no suele suponer un gran problema. Cuando Juan le pide a su cliente que mezcle los últimos cambios del repositorio en su copia de trabajo, su copia del archivo A se marca de algún modo para indicar que está en estado de conflicto: Juan podrá ver ambos conjuntos de cambios conflictivos y escoger manualmente entre ellos. Observe que el programa no puede resolver automáticamente los conflictos; sólo los humanos son capaces de entender y tomar las decisiones inteligentes oportunas. Una vez que Juan ha resuelto manualmente los cambios solapados—posiblemente después de discutirlos con Carmen—ya podrá guardar con seguridad el archivo mezclado en el repositorio.

La solución copiar-modificar-mezclar puede sonar un tanto caótica, pero en la práctica funciona extremadamente bien. Los usuarios pueden trabajar en paralelo, sin tener que esperarse el uno al otro. Cuando trabajan en los mismos archivos, sucede que la mayoría de sus cambios concurrentes no se solapan en absoluto; los conflictos son poco frecuentes. El tiempo que toma resolver los conflictos es mucho menor que el tiempo perdido por un sistema de bloqueos.

Al final, todo desemboca en un factor crítico: la comunicación entre los usuarios. Cuando los usuarios se comunican pobremente, los conflictos tanto sintácticos como semánticos aumentan. Ningún sistema puede forzar a los usuarios a comunicarse perfectamente, y ningún sistema puede detectar conflictos semánticos. Por consiguiente, no tiene sentido dejarse adormecer por la falsa promesa de que un sistema de bloqueos evitará de algún modo los conflictos; en la práctica, el bloqueo parece inhibir la productividad más que otra cosa.

Subversion en acción

Es hora de movernos de lo abstracto a lo concreto. En esta sección mostraremos ejemplos reales de Subversion en la práctica.

Copias de trabajo

Ya ha leído acerca de las copias de trabajo; ahora demostraremos cómo las crea y las usa el cliente de Subversion.

Una copia de trabajo de Subversion es un árbol de directorios corriente de su sistema de archivos local, conteniendo una colección de archivos. Usted puede editar estos archivos del modo que prefiera y si se trata de archivos de código fuente, podrá compilar su programa a partir de ellos de la manera habitual. Su copia de trabajo es su área de trabajo privada: Subversion nunca incorporará los cambios de otra gente o pondrá a disposición de otros sus cambios hasta que usted le indique explícitamente que lo haga.

Tras hacer algunos cambios a los archivos en su copia de trabajo y verificar que funcionan correctamente, Subversion le proporciona comandos para publicar sus cambios al resto de personas que trabajan con usted en su proyecto (escribiendo en el repositorio). Si las demás personas publican sus propios cambios, Subversion le proporciona comandos para mezclar estos cambios en su directorio de trabajo (leyendo del repositorio).

Una copia de trabajo también contiene algunos archivos extra, creados y mantenidos por Subversion para ayudarle a ejecutar estos comandos. En particular, cada directorio de su copia de trabajo contiene un subdirectorio llamado .svn, también conocido como el directorio administrativo de la copia de trabajo. Los archivos en cada directorio administrativo ayudan a Subversion a reconocer qué archivos contienen cambios no publicados y qué archivos están desactualizados con respecto al trabajo hecho por los demás.

Un repositorio típico de Subversion contiene a menudo los archivos (o el código fuente) de varios proyectos; normalmente, cada proyecto es un subdirectorio en el árbol del sistema de archivos del repositorio. En esta disposición, la copia de trabajo de un usuario se corresponde habitualmente con un subárbol particular del repositorio.

Por ejemplo, suponga que usted tiene un repositorio que contiene dos proyectos de software, paint y calc. Cada proyecto reside en su propio subdirectorio dentro del directorio raíz, tal como se muestra en Figura 2.6, “El sistema de archivos del repositorio”.

Figura 2.6. El sistema de archivos del repositorio

El sistema de archivos del repositorio


Para conseguir una copia de trabajo, debe ejecutar primero un check out de algún subárbol del repositorio. (El término inglés check out puede sonar como si tuviera algo que ver con bloquear o reservar recursos, pero no es así; tan sólo crea una copia privada del proyecto para usted). Por ejemplo, si usted hace un check out de /calc, obtendrá una copia de trabajo como ésta:

$ svn checkout http://svn.example.com/repos/calc
A  calc
A  calc/Makefile
A  calc/integer.c
A  calc/button.c

$ ls -A calc
Makefile  integer.c  button.c  .svn/

La lista de letras A indica que Subversion está añadiendo una serie de elementos a su copia de trabajo. Usted ahora tiene una copia personal del directorio /calc del repositorio, con una entrada adicional—.svn—que contiene la información extra que Subversion necesita, tal y como se mencionó anteriormente.

Suponga que hace cambios a button.c. Puesto que el directorio .svn recuerda la fecha de modificación del archivo y su contenido original, Subversion es capaz de darse cuenta de que el archivo ha cambiado. Sin embargo, Subversion no hará públicos sus cambios hasta que usted no le diga explícitamente que lo haga. El acto de publicar sus cambios es conocido comúnmente como consignar (o registrar) los cambios al repositorio.

Para publicar sus cambios a otros, usted puede utilizar el comando commit de Subversion:

$ svn commit button.c
Sending        button.c
Transmitting file data .
Committed revision 57.

Ahora sus cambios a button.c han sido consignados al repositorio; si otro usuario obtiene una copia de trabajo de /calc, podrá ver sus cambios en la última versión del archivo.

Suponga que tiene un colaborador, Carmen, quien obtuvo una copia de trabajo de /calc al mismo tiempo que usted. Cuando usted envía sus cambios sobre button.c, la copia de trabajo de Carmen se deja sin cambios; Subversion solo modifica las copias de trabajo a petición del usuario.

Para tener su proyecto actualizado, Carmen puede pedir a Subversion que proceda a actualizar su copia de trabajo, usando para ello el comando update de Subversion. Ésto incorporará los cambios hechos por usted en la copia de trabajo de Carmen, así como otros cambios consignados desde que ella hizo el check out.

$ pwd
/home/sally/calc

$ ls -A 
.svn/ Makefile integer.c button.c

$ svn update
U button.c

La salida del comando svn update indica que Subversion actualizó el contenido de button.c. Observe que Carmen no necesitó especificar qué archivos actualizar; Subversion usa la información del directorio .svn, junto con información adicional del repositorio, para decidir qué archivos necesitan una actualización.

Revisiones

Una operación svn commit puede publicar los cambios sobre cualquier número de ficheros y directorios como una única transacción atómica. En su copia privada, usted puede cambiar el contenido de los ficheros, crear, borrar, renombrar y copiar ficheros y directorios, y luego enviar el conjunto entero de cambios como si se tratara de una unidad.

En el repositorio, cada cambio es tratado como una transacción atómica: o bien se realizan todos los cambios, o no se realiza ninguno. Subversion trata de conservar esta atomicidad para hacer frente a posibles fallos del programa, fallos del sistema, problemas con la red, y otras acciones del usuario.

Cada vez que el repositorio acepta un envío, éste da lugar a un nuevo estado del árbol de ficheros llamado revisión. A cada revisión se le asigna un número natural único, una unidad mayor que el número de la revisión anterior. La revisión inicial de un repositorio recién creado se numera con el cero, y consiste únicamente en un directorio raíz vacío.

La Figura 2.7, “El repositorio” ilustra una manera interesante de ver el repositorio. Imagine un array de números de revisión, comenzando por el 0, que se extiende de izquierda a derecha. Cada número de revisión tiene un árbol de ficheros colgando debajo de él, y cada árbol es una instantánea del aspecto del repositorio tras cada envío.

Figura 2.7. El repositorio

El repositorio


Es importante observar que las copias de trabajo no siempre se corresponden con una revisión en particular del repositorio; pueden contener ficheros de varias revisiones diferentes. Por ejemplo, suponga que obtiene una copia de trabajo de un repositorio cuya revisión más reciente es la 4:

calc/Makefile:4
     integer.c:4
     button.c:4

Por el momento, esta copia de trabajo se corresponde exactamente con la revisión 4 del repositorio. Sin embargo, suponga que realiza un cambio a button.c y lo publica. Suponiendo que no se han realizado otros envíos, el suyo creará la revisión 5 del repositorio, y su copia de trabajo aparecerá ahora así:

calc/Makefile:4
     integer.c:4
     button.c:5

Suponga que, en este punto, Carmen envía un cambio a integer.c, creando la revisión 6. Si usted usa svn update para actualizar su copia de trabajo, ésta se verá ahora como:

calc/Makefile:6
     integer.c:6
     button.c:6

Los cambios de Carmen sobre integer.c aparecerán en su copia de trabajo y las modificaciones hechas por usted seguirán presentes en button.c. En este ejemplo, el texto de Makefile es idéntico en las revisiones 4, 5 y 6, aunque Subversion marcará su copia de trabajo de Makefile con la revisión 6 para indicar que ya está actualizada. Por lo tanto, después de hacer una actualización limpia en el directorio raíz de su copia de trabajo, ésta se corresponderá generalmente con una revisión del repositorio exactamente.

Cómo las copias de trabajo siguen la pista al repositorio

Para cada fichero de una copia de trabajo, Subversion registra dos datos esenciales en el área administrativa .svn/:

  • revisión en la que está basado el fichero de la copia de trabajo (ésto se llama la revisión de trabajo del fichero), y

  • una marca de tiempo con la fecha de la última actualización del fichero desde el repositorio.

Con esta información, y comunicándose con el repositorio, Subversion puede conocer en cuál de los cuatro estados siguientes se encuentra el fichero de la copia de trabajo:

Sin cambios y actualizado

El fichero no ha sido modificado en la copia de trabajo ni se ha enviado ningún cambio sobre ese fichero al repositorio desde su revisión de trabajo. Un svn commit de ese fichero no hará nada, y un svn update del fichero tampoco hará nada.

Modificado localmente y actualizado

El fichero ha sido modificado en la copia de trabajo pero no se ha enviado ningún cambio sobre ese fichero al repositorio desde su revisión base. Hay cambios locales que no han sido enviados al repositorio, por lo que un svn commit del fichero publicará con éxito sus cambios, y un svn update del fichero no hará nada.

Sin cambios y desactualizado

El fichero no ha sido modificado en la copia de trabajo, pero sí en el repositorio. El fichero debería ser actualizado para sincronizarlo con la revisión pública. Un svn commit del fichero no hará nada, y un svn update del fichero introducirá los últimos cambios en su copia de trabajo.

Modificado localmente y desactualizado

El fichero ha sido modificado tanto en la copia de trabajo como en el repositorio. Un svn commit del fichero fallará dando un error de desactualizado. El fichero debe ser actualizado primero; un svn update intentará mezclar los cambios públicos con los cambios locales. Si Subversion no puede combinar los cambios de manera convincente automáticamente, dejará que sea el usuario el que resuelva el conflicto.

Todo ésto puede parecer un montón de cosas a tener en cuenta, pero el comando svn status le mostrará el estado de cualquier elemento de su copia de trabajo. Para obtener más información acerca de ese comando, vea svn status.

Las limitaciones de las revisiones mixtas

Por norma general, Subversion trata de ser tan flexible como sea posible. Un tipo especial de flexibilidad es la habilidad para tener dentro de una copia de trabajo números de revisión mixtos.

Para comenzar, puede que no esté completamente claro el por qué este tipo de flexibilidad se considera una característica y no un problema. Después de completar un envío al repositorio, los ficheros y directorios recién enviados se encuentran en una revisión de trabajo más reciente que el resto de la copia de trabajo. Parece un poco lioso. Tal como se mostró anteriormente, siempre se puede dejar una copia de trabajo en una única revisión de trabajo ejecutando svn update. ¿Por qué querría alguien deliberadamente tener una mezcla de revisiones de trabajo?

Suponiendo que su proyecto es lo suficientemente complejo, descubrirá que a veces es conveniente forzar la desactualización de ciertas partes de su copia de trabajo a una versión anterior; aprenderá cómo hacer ésto en el capítulo 3. Quizás quiera probar una versión anterior de un submódulo contenido en un subdirectorio, o tal vez quiera examinar una serie de versiones previas de un fichero en el contexto del último árbol.

Por mucho que usted haga uso de revisiones mixtas en su copia de trabajo, hay ciertas limitaciones asociadas a esta flexibilidad.

Primero, usted no puede publicar la eliminación de un fichero o directorio que no esté completamente actualizado. Si existe una versión más reciente en el repositorio, su intento de eliminación será rechazado para impedir que destruya accidentalmente cambios que aún no ha visto.

Segundo, usted no puede publicar los cambios en los metadatos de un directorio a menos que esté completamente actualizado. Aprenderá cómo adjuntar propiedades a elementos en el capítulo 6. Una revisión de trabajo de un directorio define un conjunto específico de entradas y propiedades, y por tanto enviar un cambio a una propiedad de un directorio desactualizado puede destruir las propiedades que no haya visto todavía.

Resumen

A lo largo de este capítulo hemos tratado una serie de conceptos fundamentales acerca de Subversion:

  • Hemos introducido las nociones de repositorio central, la copia de trabajo del cliente, y el array de árboles de revisiones del repositorio.

  • Hemos visto algunos ejemplos sencillos de cómo dos colaboradores pueden usar Subversion para publicar y recibir cambios uno del otro usando el modelo 'copiar-modificar-mezclar'.

  • Hemos hablado un poco sobre la manera en que Subversion sigue y maneja la información de una copia de trabajo.

A estas alturas usted ya debería tener una idea más o menos clara de cómo funciona Subversion a nivel general. Armado con este conocimiento, debería estar listo para pasar al siguiente capítulo, el cual es un recorrido detallado por los comandos y características de Subversion.

Capítulo 3. Recorrido guiado

Ahora entraremos en los detalles de usar Subversion. Para el momento en que usted alcance el final de este capítulo, podrá realizar casi todas las tareas necesarias para usar Subversion de manera cotidiana. Comenzará con una descarga inicial de su código, e irá haciendo cambios y examinando esos cambios. Usted también verá como introducir cambios hechos por otros en su copia de trabajo, examinarlos, y resolver cualquier conflicto que pudiera surgir.

Observe que este capítulo no pretende ser una lista exhaustiva de los comandos de Subversion —más bien es una introducción informal a las tareas más comunes de Subversion que se encontrará. Este capítulo asume que usted ha leído y entendido Capítulo 2, Conceptos básicos y está familiarizado con el modelo general de Subversion. Para una referencia completa de todos los comandos, vea Capítulo 9, Referencia completa de Subversion.

¡Ayuda!

Antes de seguir leyendo, aquí está el comando más importante que usted necesitará cuando esté usando Subversion: svn help. El cliente de línea de comandos Subversion está auto-documentado— en cualquier momento, un svn help <subcomando> rápido describirá la sintaxis, las opciones y el comportamiento del subcomando.

Import

Use svn import para importar un nuevo proyecto dentro de un repositorio Subversion. Mientras que esto es muy probablemente lo primero que usted hará cuando instale su servidor Subversion, no es algo que pase muy a menudo. Para una descripción detallada de import, vea svn import más adelante en este capítulo.

Revisiones: Números, Palabras Clave, y Fechas, ¡Dios Mío!

Antes de continuar, usted debería saber un poco acerca de como identificar una revisión particular en su repositorio. Como aprendió en “Revisiones”, una revisión es una instantánea de su repositorio en un momento particular en el tiempo. A medida que continúe depositando y haciendo crecer su repositorio, usted necesita un mecanismo para identificar estas instantáneas.

Usted puede especificar estas revisiones usando --revision (-r) junto con la revisión que desee (svn --revision REV) o puede especificar un rango separando dos revisiones con dos puntos (svn --revision REV1:REV2). Y Subversion le permite referirse a estas revisiones por número, palabra clave, o fecha.

Números de revisión

Cuando usted crea un nuevo repositorio de Subversion, este comienza su vida en la revisión cero y cada envío sucesivo incrementa el número de revisión en uno. Después de completar su envío, el cliente de Subversion le informa del nuevo número de revisión:

$ svn commit --message "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

Si en cualquier punto del futuro usted quiere referirse a esa revisión (veremos cómo y por qué podríamos querer hacer esto más adelante en este capítulo), puede referirse a ella como 3.

Palabras clave de la revisión

El cliente de Subversion entiende una cantidad de palabras clave de la revisión. Estas palabras clave pueden ser usadas en vez del número entero como argumento a la opción --revision, y son resueltos como números específicos de revisión por Subversion:

[Nota]Nota

Cada directorio en su copia de trabajo contiene un subdirectorio administrativo llamado .svn. Para todos los ficheros en un directorio, Subversion mantiene una copia de cada fichero en el área administrativa. Esta copia es una copia no modificada (ninguna expansión de palabra clave, ninguna traducción del fin de línea, nada de nada) del fichero tal como existió en la última revisión (llamada la revisión BASE) que usted actualizó a su copia de trabajo. Nos referimos a este fichero como copia prístina o versión basada en texto de su fichero, y siempre es una copia byte-a-byte exacta del fichero tal como existe en el repositorio.

HEAD

La última revisión en el repositorio.

BASE

La revisión prístina de un elemento en la copia de trabajo.

COMMITTED

La última revisión en la que un elemento cambió antes (o en) BASE.

PREV

La revisión inmediatamente anterior a la última revisión en la cual un elemento cambió. (Técnicamente, COMMITTED - 1.)

[Nota]Nota

PREV, BASE, y COMMITTED pueden ser usadas para referirse a rutas locales, pero no a URLs.

Aquí hay algunos ejemplos de palabras clave de revisión en acción. No se preocupe si los comandos no tienen sentido todavía; estaremos explicando estos comandos a medida que avancemos por el capítulo:

$ svn diff --revision PREV:COMMITTED foo.c
# shows the last change committed to foo.c

$ svn log --revision HEAD
# shows log message for the latest repository commit

$ svn diff --revision HEAD
# compares your working file (with local mods) to the latest version
# in the repository.

$ svn diff --revision BASE:HEAD foo.c
# compares your pristine foo.c (no local mods) with the 
# latest version in the repository

$ svn log --revision BASE:HEAD
# shows all commit logs since you last updated

$ svn update --revision PREV foo.c
# rewinds the last change on foo.c.
# (foo.c's working revision is decreased.)

Estas palabras clave le permiten realizar muchas operaciones comunes (y útiles) sin tener que buscar números específicos de revisión o recordar la revisión exacta de su copia de trabajo.

Fechas de revisión

Dondequiera que usted especifique un número de revisión o palabra clave de revisión, usted también puede especificar una fecha dentro de llaves {}. ¡Puede incluso tener acceso a un rango de cambios en el repositorio usando fechas y revisiones juntas!

Aquí hay ejemplos de los formatos de fecha que admite Subversion. Recuerde usar comillas alrededor de cualquier fecha que contenga espacios.

$ svn checkout --revision {2002-02-17}
$ svn checkout --revision {15:30}
$ svn checkout --revision {15:30:00.200000}
$ svn checkout --revision {"2002-02-17 15:30"}
$ svn checkout --revision {"2002-02-17 15:30 +0230"}
$ svn checkout --revision {2002-02-17T15:30}
$ svn checkout --revision {2002-02-17T15:30Z}
$ svn checkout --revision {2002-02-17T15:30-04:00}
$ svn checkout --revision {20020217T1530}
$ svn checkout --revision {20020217T1530Z}
$ svn checkout --revision {20020217T1530-0500}
…

Cuando usted especifica una fecha como revisión, Subversion encuentra la revisión más reciente en el repositorio a esa fecha:

$ svn log --revision {2002-11-28}
------------------------------------------------------------------------
r12 | ira | 2002-11-27 12:31:51 -0600 (Wed, 27 Nov 2002) | 6 lines
…

También puede especificar un rango de fechas. Subversion encontrará todas las revisiones entre ambas fechas, inclusive:

$ svn log --revision {2002-11-20}:{2002-11-29}
…

Como señalamos, también puede mezclar fechas y revisiones:

$ svn log --revision {2002-11-20}:4040

Los usuarios deberían estar enterados de una sutileza que puede convertirse en un obstáculo al tratar con fechas en Subversion. Como la marca de tiempo de una revisión es guardada como una propiedad de la revisión— una propiedad sin versionar, modificable — las marcas de tiempo de las revisiones pueden cambiarse para representar una falsificación completa de la verdadera cronología, o incluso borrarla totalmente. Esto causará estragos en la conversión interna fecha-a-revisión que realiza Subversion.

Descarga inicial

La mayor parte del tiempo, usted empezará a usar un repositorio de Subversion haciendo un checkout de su proyecto . Descargar un repositorio crea una copia de éste en en su máquina local. Esta copia contiene el HEAD (última revisión) del repositorio de Subversion que usted especifica en la línea de comandos:

$ svn checkout http://svn.collab.net/repos/svn/trunk
A  trunk/subversion.dsw
A  trunk/svn_check.dsp
A  trunk/COMMITTERS
A  trunk/configure.in
A  trunk/IDEAS
…
Checked out revision 2499.

Aunque el ejemplo de arriba descarga el directorio trunk, usted puede descargar fácilmente cualquier subdirectorio más profundo de un repositorio especificando el subdirectorio en la URL de descarga:

$ svn checkout http://svn.collab.net/repos/svn/trunk/doc/book/tools
A  tools/readme-dblite.html
A  tools/fo-stylesheet.xsl
A  tools/svnbook.el
A  tools/dtd
A  tools/dtd/dblite.dtd
…
Checked out revision 2499.

Como Subversion usa un modelo copie-modifique-fusione en vez de bloquear-modificar-desbloquear (vea Capítulo 2, Conceptos básicos), usted ya puede empezar a realizar cambios a los ficheros y directorios en su copia de trabajo local. Su copia local es justo como cualquier otra colección de ficheros y directorios en su sistema. Usted puede editarlos y cambiarlos, moverlos, usted puede incluso borrar la copia local entera y olvidarse de ella.

[Nota]Nota

Mientras que su copia de trabajo local es justo como cualquier otra colección de ficheros y directorios en su sistema, usted necesita hacer saber a Subversion si va a reacomodar cualquier cosa dentro de su copia local. Si desea copiar o mover un elemento en una copia local, debe usar svn copy o svn move en vez de los comandos para copiar y mover proporcionados por su sistema operativo. Hablaremos más acerca de ellos al avanzar en este capítulo.

A menos que esté listo para enviar al repositorio un fichero o directorio nuevo, o cambios a unos existentes, no hay necesidad de indicarle al servidor de Subversion que usted haya hecho algo más.

Mientras que usted puede descargar una copia de trabajo local con la URL del repositorio como único argumento, también puede especificar un directorio después de su URL del repositorio. Esto pone su copia de trabajo local dentro del nuevo directorio que usted nombra. Por ejemplo:

$ svn checkout http://svn.collab.net/repos/svn/trunk subv
A  subv/subversion.dsw
A  subv/svn_check.dsp
A  subv/COMMITTERS
A  subv/configure.in
A  subv/IDEAS
…
Checked out revision 2499.

Esto pondrá su copia de trabajo local en un directorio llamado subv en vez de en un directorio llamado trunk como hicimos previamente.

Ciclo básico de trabajo

Subversion tiene numerosas características, opciones, campanas y silbidos, pero bajo uso cotidiano lo más probable es que use solamente algunas de ellas. En esta sección veremos las cosas más comunes que usted puede encontrarse haciendo con Subversion en el transcurso de un día de trabajo.

El ciclo de trabajo típico se parece a esto:

  • Actualizar su copia de trabajo local

    • svn update

  • Hacer cambios

    • svn add

    • svn delete

    • svn copy

    • svn move

  • Examinar sus cambios

    • svn status

    • svn diff

    • svn revert

  • Fusionar los cambios de otros en su copia de trabajo

    • svn merge

    • svn resolved

  • Enviar sus cambios

    • svn commit

Actualizar su copia de trabajo local

Cuando se trabaja en un proyecto con un equipo, usted querrá actualizar su copia de trabajo local para recibir cualquier cambio hecho desde su última actualización por otros desarrolladores en el proyecto. Use svn update para poner a su copia de trabajo local en sincronía con la última revisión en el repositorio.

$ svn update
U  foo.c
U  bar.c
Updated to revision 2.

En este caso, alguien envió modificaciones a foo.c y bar.c desde la última vez que usted actualizó, y Subversion ha actualizado su copia de trabajo local para incluir estos cambios.

Vamos a examinar la salida de svn update un poco más. Cuando el servidor envía cambios a su copia de trabajo local, un código de letras es mostrado al lado de cada elemento para hacerle saber qué acciones realizó Subversion para actualizar su copia de trabajo local:

U foo

El fichero foo fue actualizado[6] (recibidos cambios del servidor).

A foo

El fichero o directorio foo fue Añadido a su copia de trabajo local.

D foo

El fichero o directorio foo fue borrado [7] de su copia de trabajo local.

R foo

El fichero o directorio foo fue Reemplazado en su copia de trabajo local; esto es, foo fue borrado, y un nuevo objeto con el mismo nombre fue añadido. Aunque pueden tener el mismo nombre, el repositorio los considera objetos distintos con historiales distintos.

G foo

El fichero foo recibió nuevos cambios del repositorio, pero su copia local del fichero tenía las modificaciones que ud. ya había hecho. O los cambios no se intersectaron , o los cambios eran exactamente iguales que sus modificaciones locales, así que Subversion ha fusionado [8] satisfactoriamente los cambios del repositorio en el fichero sin ningún problema.

C foo

El fichero foo recibió cambios Conflicting del servidor. Los cambios del servidor directamente se superpusieron sobre sus propios cambios en el fichero. Aunque no hay necesidad de aterrarse. Esta superposición necesita ser resuelta por un humano (usted); tratamos esta situación más tarde en este capítulo.

Hacer cambios en su copia de trabajo local

Ahora puede conseguir trabajar y hacer cambios en su copia de trabajo local. Generalmente es más conveniente decidir un cambio particular (o un conjunto de cambios) a hacer, por ejemplo escribir una nueva característica, corregir un fallo, etc. Los comandos de Subversion que puede usar aquí son svn add, svn delete, svn copy, y svn move. Sin embargo, si usted está simplemente corrigiendo los archivos que están ya en Subversion, puede no necesitar utilizar ninguno de estos comandos hasta que envíe los cambios. Cambios que puede hacer a su copia de trabajo local:

Cambios en los ficheros

Esta es la clase más simple de cambio. No necesita decirle a Subversion que se propone a cambiar un fichero; simplemente haga los cambios. Subversion será capaz de detectar automáticamente qué archivos han sido cambiados.

Cambios en el árbol

Puede preguntar a Subversion que marque ficheros y directorios para el borrado planificado, la adición, la copia, o moverlos. Mientras estos cambios pueden ocurrir inmediatamente en su copia de trabajo local, ninguna adición o borrado sucederá en el repositorio hasta que envíe los cambios.

Para hacer cambios en los ficheros, use su editor de textos, procesador de textos, programa de gráficos , o cualquier herramienta que usaría normalmente. Subversion maneja ficheros binarios tan fácilmente como maneja archivos de texto—y tan eficientemente también.

Aquí hay una descripción de los cuatro subcomandos de Subversion que usted usará más a menudo para hacer cambios del árbol (cubriremos svn import y svn mkdir después.

svn add foo

Programa añadir foo al repositorio. Cuando haga su próximo envío, foo se convertirá en hijo de su directorio padre. Fíjese que si foo es un directorio, todo por debajo de foo será programado para la adición . Si solo quiere añadir el propio foo, pase la opción --non-recursive (-N) .

svn delete foo

Programa borrar foo del repositorio. Si foo es un fichero, se borrará inmediatamente de su copia de trabajo local. Si foo es un directorio, este no es borrado, pero Subversion lo programa para borrarlo. Cuando envíe sus cambios, foo será borrado de su copia de trabajo y del repositorio.[9]

svn copy foo bar

Crea un nuevo objeto bar como duplicado de foo. bar es automáticamente programado para la adición. Cuando bar es añadido al repositorio en el siguiente envío de cambios, su historia de copia es registrada (como que originalmente viene de foo).svn copy no crea directorios intermedios.

svn move foo bar

Este comando funciona exactamente igual que svn copy foo bar; svn delete foo. Esto es, se programa bar para la adición como una copia de foo, y se programa foo para la eliminación. svn move no crea directorios intermedios.

Examine sus cambios

Una vez que haya terminado de hacer cambios, necesita enviarlos al repositorio, pero antes de hacerlo, generalmente es buena idea echar un vistazo a lo que ha cambiado exactamente. Examinando sus cambios antes de que los envíe, puede hacer un mensaje de informe de cambios más exacto. También puede descubrir que ha cambiado inadvertidamente un fichero, y esto le da la posibilidad de invertir esos cambios antes de enviarlos. Además, esta es una buena oportunidad para revisar y escudriñar cambios antes de publicarlos. Usted puede ver exactamente qué cambios ha hecho usando svn status, svn diff, y svn revert. Generalmente usará los primeros dos comandos para descubrir qué ficheros han cambiado en su copia de trabajo local, y después quizá el tercero para invertir algunos (o todos) de esos cambios.

Subversion ha sido optimizado para ayudarle con esta tarea, y es capaz de hacer muchas cosas sin comunicarse con el repositorio. En particular, su copia de trabajo local contiene una copia prístina secreta almacenada de cada fichero de versión controlado dentro del área .svn. Debido a esto, Subversion puede rápidamente mostrarle cómo sus ficheros de trabajo han cambiado, o incluso permitirle deshacer sus cambios sin contactar con el repositorio.

svn status

Probablemente usará el comando svn status más que cualquier otro comando de Subversion.

Si ejecuta svn status en lo alto de su copia de trabajo sin argumentos, detectará todos los cambios de fichero y árbol que usted ha hecho. Este ejemplo está diseñado para mostrar todos los códigos de estado diferentes que svn status puede devolver. (Observe que el texto que sigue a # en el siguiente ejemplo no es impreso realmente por svn status.)

$ svn status
  L    abc.c               # svn has a lock in its .svn directory for abc.c
M      bar.c               # the content in bar.c has local modifications
 M     baz.c               # baz.c has property but no content modifications
X      3rd_party           # this dir is part of an externals definition
?      foo.o               # svn doesn't manage foo.o
!      some_dir            # svn manages this, but it's either missing or incomplete
~      qux                 # versioned as dir, but is file, or vice versa
I      .screenrc           # this file is ignored
A  +   moved_dir           # added with history of where it came from
M  +   moved_dir/README    # added with history and has local modifications
D      stuff/fish.c        # this file is scheduled for deletion
A      stuff/loot/bloo.h   # this file is scheduled for addition
C      stuff/loot/lump.c   # this file has conflicts from an update
    S  stuff/squawk        # this file or dir has been switched to a branch
…

En este formato de salida svn status impresa cinco columnas de caracteres, seguidos por varios caracteres de espacio en blanco, seguido por un nombre de fichero o directorio. La primera columna dice el estado de un fichero o directorio y/o su contenido. Los códigos impresos aquí son:

A file_or_dir

El fichero o directorio file_or_dir ha sido programado para la adición en el repositorio.

C file

El fichero file está en un estado de conflicto. Esto es, los cambios recibidos del servidor durante una actualización se solapan con cambios locales que usted tiene en su copia de trabajo. Debe resolver este conflicto antes de enviar sus cambios al repositorio.

D file_or_dir

El fichero o directorio file_or_dir ha sido programado para la supresión del repositorio.

M file

El contenido del fichero file ha sido modificado.

X dir

El directorio dir está sin versionar, pero está relacionado con una definición externa de Subversion. Para descubrir más acerca de definiciones externas, vea “Repositorios externos”.

? file_or_dir

El fichero o directorio file_or_dir no está bajo control de versiones. Puede silenciar la marca de pregunta pasando la opción --quiet (-q) a svn status, o o poniendo la característica svn:ignore en el directorio padre. Para más información sobre ficheros ignorados, vea svn:ignore.

! file_or_dir

El fichero o directorio file_or_dir está bajo el control de versiones pero falta o está de alguna manera incompleto. El objeto puede faltar si se ha borrado usando un comando ajeno a Subversion. En el caso de un directorio, puede estar incompleto si ha interrumpido una descarga o una actualización. Un rápido svn update repondrá el fichero o el directorio desde el repositorio, o svn revert file restaurará un archivo que falta.

~ file_or_dir

El fichero o directorio file_or_dir está en el repositorio como un tipo de objeto, pero actualmente está en su copia de trabajo como otro tipo. Por ejemplo, Subversion pudo tener un fichero en el repositorio, pero usted borró el fichero y creó un directorio en su lugar, sin usar los comandos svn delete o svn add.

I file_or_dir

Subversion está ignorando el fichero o directorio file_or_dir, probablemente porque usted se lo dijo. Para más información sobre ficheros ignorados, vea svn:ignore. Observe que este símbolo solo aparece si le pasa la opción --no-ignore a svn status.

La segunda columna dice el estado de las propiedades de un fichero o un directorio (vea “Propiedades” para más información sobre propiedades). Si aparece una M en la segunda columna, entonces las propiedades han sido modificadas, si no un espacio en blanco será impreso.

La tercera columna solo mostrará un espacio en blanco o una L la cual significa que Subversion ha bloqueado el objeto en el área de trabajo .svn. Usted verá una L si ejecuta svn status en un directorio donde un svn commit esté en progreso—quizás cuando esté editando el informe de cambios. Si Subversion no se está ejecutando, entonces probablemente Subversion fue interrumpido y el bloqueo necesita ser eliminado ejecutando svn cleanup (más sobre eso más adelante en este capítulo).

La cuarta columna solo mostrará un espacio blanco o un + el cual significa que el fichero o directorio está programado para ser añadido o modificado con historial adicional adjunto. Esto ocurre típicamente cuando usted svn move o svn copy un fichero o directorio. Si usted ve A  +, esto significa que el objeto está programado para la adición-con-historial. Este puede ser un fichero, o la raíz de un directorio copiado. + significa que el objeto es parte de un subárbol programado para la adición-con-historial, p.e.algún padre fue copiado,and it's just coming along for the ride. M  + significa que el objeto es parte de un subárbol programado para la adición-con-historial, y este tiene modificaciones locales. Cuando envíe los cambios, primero el padre será añadido-con-historial (copiado), lo que significa que este fichero existirá automáticamente en la copia. Entonces las modificaciones locales serán enviadas al repositorio.

La quinta columna solo mostrará un espacio en blanco o una S. Esto significa que el fichero o directorio ha sido movido de la ruta del resto de la copia de trabajo (usando svn switch) a una rama.

Si usted pasa una ruta específica a svn status, este le dará información acerca de ese objeto solamente:

$ svn status stuff/fish.c
D      stuff/fish.c

svn status también tiene una opción --verbose (-v), el cuál le mostrará el estado de todos los objetos en su copia de trabajo, incluso si este no ha sido cambiado:

$ svn status --verbose
M               44        23    sally     README
                44        30    sally     INSTALL
M               44        20    harry     bar.c
                44        18    ira       stuff
                44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
                44        21    sally     stuff/things
A                0         ?     ?        stuff/things/bloo.h
                44        36    harry     stuff/things/gloo.c

Esta es la forma larga de salida de svn status. La primera columna permanece igual, pero la segunda columna muestra la revisión de trabajo del fichero. La tercera y cuarta columna muestra la revisión en la cuál el objeto cambió por última vez, y quién lo cambió.

Ninguna de las invocaciones anteriores a svn status contactaban con el repositorio, trabajan solo localmente comparando los metadatos en el directorio .svn con la copia de trabajo local. Finalmente está la opción --show-updates (-u), la cual contacta con el repositorio y añade información acerca de las cosas que están fuera-de-fecha:

$ svn status --show-updates --verbose
M      *        44        23    sally     README
M               44        20    harry     bar.c
       *        44        35    harry     stuff/trout.c
D               44        19    ira       stuff/fish.c
A                0         ?     ?        stuff/things/bloo.h
Status against revision:   46

Observe los dos asteriscos: si usted ejecutara svn update en este punto, usted podría recibir cambios para README y trout.c. Esto le dice cierta información muy útilmdash;necesitará actualizar y coger los cambios del servidor para README antes de enviar sus cambios, o el repositorio rechazará su envío por estar fuera-de-fecha. (Más de este tema más adelante.)

svn diff

Otra manera de examinar sus cambios es con el comando svn diff. Puede descubrir exactamente cómo ha modificado cosas ejecutando svn diff sin argumentos, el cual imprime los cambios de los ficheros en formato unificado del diff:[10]

$ svn diff
Index: bar.c
===================================================================
--- bar.c	(revision 3)
+++ bar.c	(working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>

 int main(void) {
-  printf("Sixty-four slices of American Cheese...\n");
+  printf("Sixty-five slices of American Cheese...\n");
 return 0;
 }

Index: README
===================================================================
--- README	(revision 3)
+++ README	(working copy)
@@ -193,3 +193,4 @@ 
+Note to self:  pick up laundry.

Index: stuff/fish.c
===================================================================
--- stuff/fish.c	(revision 1)
+++ stuff/fish.c	(working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.

Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h	(revision 8)
+++ stuff/things/bloo.h	(working copy)
+Here is a new file to describe
+things about bloo.

El comando svn diff produce esta salida comparando sus ficheros de copia de trabajo contra la copia prístina almacenada en el área .svn. Los ficheros programados para la adición se visualizan como texto-añadido, y los ficheros programados para la eliminación son visualizados como texto eliminado.

La salida es visualizada en formato unificado del diff. Esto es, las lineas quitadas son empezadas con un - y las lineas añadidas son empezadas con un +. svn diff también imprime el nombre del fichero e información útil para el programa patch, así que puede generar parches redireccionando la salida del diff a un fichero:

$ svn diff > patchfile

Usted puede, por ejemplo, mandar por email el fichero de parche a otro desarrollador para la revisión o testeo antes de enviarlo.

svn revert

Ahora suponga que usted ve la salida del diff anterior, y se da cuenta que sus cambios a README son un error; quizás accidentalmente tecleó ese texto en el fichero equivocado en su editor

Esta es una oportunidad perfecta para usar svn revert.

$ svn revert README
Reverted 'README'

Subversion invierte el fichero a un estado pre-modificado reescribiéndolo con la copia prístina almacenada en el área .svn. Pero también observar que svn revert puede deshacer cualquier operación programada—por ejemplo, usted puede decidir que no quiere añadir un nuevo fichero después de todo:

$ svn status foo
?      foo

$ svn add foo
A         foo

$ svn revert foo
Reverted 'foo'

$ svn status foo
?      foo
[Nota]Nota

svn revert ITEM tiene exactamente el mismo efecto que suprimiendo ITEM de su copia de trabajo local y después ejecutando svn update -r BASE ITEM. Sin embargo, si está revirtiendo un fichero, svn revert tiene una diferencia muy considerable—no tiene que comunicarse con el repositorio para reponer su fichero.

O quizás usted quito equivocadamente un fichero del control de versión:

$ svn status README 
       README

$ svn delete README 
D         README

$ svn revert README
Reverted 'README'

$ svn status README
       README

Resolver conflictos (fusionando los cambios de otros)

Ya hemos visto cómo svn status -u puede predecir conflictos. Suponga que ejecuta svn update y ocurren algunas cosas interesantes

$ svn update
U  INSTALL
G  README
C  bar.c
Updated to revision 46.

Los códigos U y G no son causa para la inquietud; esos ficheros absorbieron limpiamente los cambios del repositorio. Los ficheros marcados con una U no contienen cambios locales pero fueron actUalizados con cambios del repositorio. La G representa merGed, lo que significa que el fichero tenía para comenzar cambios locales, pero los cambios que venían del repositorio no se solaparon de ninguna manera.

Pero la C representa conflicto. Esto significa que los cambios del servidor se solapan con los suyos propios, y ahora tiene que elegir manualmente entre ellos.

Siempre que ocurra un conflicto, ocurren tres cosas para ayudarle a notar y resolver ese conflicto:

  • Subversion imprime una C durante la actualización, y recuerda que el fichero está en un estado de conflicto.

  • Subversion coloca marcas de conflicto—secuencias especiales de texto que delimitan los lados del conflicto—en el fichero para demostrar visualmente las áreas solapadas.

  • Para cada fichero en conflicto, Subversion coloca tres ficheros extra en su copia de trabajo local:

    filename.mine

    Este es su fichero como existió en su copia de trabajo antes de que usted actualizara su copia de trabajo—esto es, sin marcas de conflicto. Este fichero tiene su últimos cambios y nada más.

    filename.rOLDREV

    Este es el fichero que era la revisión BASE antes de que usted actualizará su copia de trabajo. Esto es, el fichero que usted descargó antes de que hiciera su última edición.

    filename.rNEWREV

    Este es el fichero que su cliente de Subversion recibió del servidor justo cuando usted actualizó su copia de trabajo. Este fichero corresponde con la revisión HEAD del repositorio.

    Aquí OLDREV es el número de revisión del fichero en su directorio .svn y NEWREV es el número de revisión del HEAD del repositorio.

Por ejemplo, Sally hace cambios al fichero sandwich.txt en el repositorio. Harry acaba de cambiar el fichero en su copia de trabajo y lo ha enviado. Sally actualiza su copia de trabajo antes de enviarlo y recibe un conflicto:

$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2

En este punto, Subversion no le permitirá enviar el fichero sandwich.txt hasta que los tres ficheros temporales sean borrados.

$ svn commit --message "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict

Si obtiene un conflicto, necesita hacer una de tres cosas:

  • Fusionar el texto en conflicto a mano (examinando y editando las marcas de conflicto dentro del fichero).

  • Copiar uno de los ficheros temporales sobre su fichero de trabajo.

  • Ejecutar svn revert <filename> para eliminar todos sus cambios locales.

Una vez que usted haya resuelto el conflicto, necesita dejar que Subversion lo sepa ejecutando svn resolved. Esto borrará los tres ficheros temporales y Subversion no considerará por más tiempo que el fichero está en estado de conflicto.[11]

$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'

Fusionando conflictos a mano

Fusionar conflictos a mano puede ser absolutamente intimidatorio la primera vez que lo haga, pero con un poco de practica, puede llegar a ser tan fácil como caerse de una bici.

Aquí tiene un ejemplo. Debido a una falta de comunicación, usted y Sally, su colaboradora, editan el fichero sandwich.txt en el mismo momento. Sally envía sus cambios, y cuando usted va a actualizar su copia de trabajo, obtiene un conflicto y vamos a tener que editar sandwich.txt para resolver los conflictos. Primero, echemos una hojeada al fichero:

$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread

Las líneas de signos menor-que, y signos mayor-que son marcas de conflictos, y no son parte de los datos en conflicto actuales. Generalmente usted querrá asegurarse que estén borrados del fichero antes de su próximo envío. El texto entre las dos primeras marcas están compuestas por los cambios que usted hizo en el área conflictiva:

<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======

El texto entre el segundo y tercer conjunto de marcas de conflicto es el texto del envío de Sally:

=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2

Generalmente usted no deseará borrar las marcas de conflicto y los cambios de Sally—ella se sorprenderá terriblemente cuando llegue el sandwich y no sea lo que ella quería. Aquí es donde usted coge el teléfono o anda a través de la oficina y le explica a Sally que no puede tomar sauerkraut de un deli Italiano.[12]Una vez usted esté de acuerdo con los cambios deberá comprobarlos, editar su fichero y borrar las marcas de conflicto

Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread

Ahora ejecute svn resolved, y está listo para enviar sus cambios:

$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."

Recuerde, si alguna vez está confuso mientras edita el fichero conflictivo, siempre puede consultar los tres ficheros que Subversion crea para usted en su copia de trabajo—incluyendo su fichero como estaba antes de que usted actualizara. Incluso puede usar una herramienta interactiva de fusión de una third-party para examinar esos tres fichero.

Copiando un fichero en su fichero de trabajo

Si obtiene un conflicto y decide que quiere rechazar sus cambios, simplemente usted puede copiar uno de los ficheros temporales creados por Subversion sobre el fichero en su copia de trabajo:

$ svn update
C  sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt  sandwich.txt.mine  sandwich.txt.r2  sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt

Punting: Usando svn revert

Si obtiene un conflicto, y al examinarlo decide que quiere rechazar sus cambios y empezar su edición de nuevo, simplemente invierta sus cambios:

$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt

Observe que cuando usted invierte un fichero conflictivo, no tiene que ejecutar svn resolved.

Ahora usted está listo para enviar sus cambios. Observe que svn resolved, al contrario de la mayoría de los otros comandos que nos hemos ocupado en este capitulo, requiere un argumento. En cualquier caso, debe tener cuidado y solo ejecutar svn resolved cuando esté seguro que ha arreglado el conflicto en su fichero—una vez los ficheros temporales son borrados, Subversion le dejará enviar el fichero incluso si todavía tiene marcas de conflicto.

Enviar sus cambios

¡Finalmente! Su edición está terminada, ha fusionado todos los cambios del servidor, y está listo para enviar sus cambios al repositorio.

El comando svn commit envía todos sus cambios al repositorio. Cuando usted envía un cambio, necesita proveer un mensaje de registro, describiendo su cambio. Su mensaje de registro será adjuntado a la nueva revisión que ha creado. Si su mensaje de registro es breve, puede querer proveerlo en la línea de comando usando la opción --message (o -m):

$ svn commit --message "Corrected number of cheese slices."
Sending        sandwich.txt
Transmitting file data .
Committed revision 3.

Sin embargo, si ha estado componiendo su mensaje de registro mientras trabaja, puede querer decirle a Subversion que coja el mensaje de un fichero pasando el nombre de fichero con la opción --file :

$ svn commit --file logmsg 
Sending        sandwich
Transmitting file data .
Committed revision 4.

Si usted falla en especificar cualquiera de las opciones --message o --file, entonces Subversion lanzará automáticamente su editor favorito (según lo definido en la variable de entorno $EDITOR) para redactar un mensaje de registro.

[Sugerencia]Sugerencia

Si está en su editor escribiendo un mensaje de registro y decide que quiere cancelar su envío, usted puede quitar su editor sin guardar los cambios. Si ya ha guardado su mensaje de registro, simplemente borre el texto y salve otra vez.

$ svn commit
Waiting for Emacs...Done

Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$

El repositorio no sabe ni cuida si sus cambios tienen algún sentido en su totalidad; solo comprueba para asegurarse que nadie haya cambiado cualquiera de los mismos ficheros que usted mientras usted no miraba. Si alguien ha hecho esto, el envío entero fallará con un mensaje informándole que uno o más de sus ficheros está fuera-de-fecha:

$ svn commit --message "Add another rule"
Sending        rules.txt
svn: Commit failed (details follow):
svn: Out of date: 'rules.txt' in transaction 'g'

En este punto, necesita ejecutar svn update, ocupándose con cualquier fusión o conflicto que resulte y procure enviarlo otra vez.

Eso cubre el ciclo básico de trabajo para usar Subversion. Hay muchas otras características en Subversion que usted puede usar para administrar su repositorio y copia de trabajo, pero puede pasar fácilmente usando solo los comandos que hemos visto hasta ahora en este capítulo.

Examinando el historial

Como hemos mencionado anteriormente, el repositorio es como una máquina del tiempo. Este mantiene un expediente de cada cambio enviado, y le permite explorar este historial examinando versiones anteriores de ficheros y directorios así como los metadatos que los acompañan. Con un único comando de Subversion, puede descargar el repositorio (o restaurar una copia de trabajo existente) exactamente como era en cualquier fecha o número de revisión en el pasado. Sin embargo, a veces solo desea mirar al pasado en vez de ir al pasado.

Hay varios comandos que pueden proporcionarle datos históricos del repositorio:

svn log

Le muestra amplia información: mensajes de registro unidos a las revisiones, y que ruta de fichero cambió en cada revisión.

svn diff

Le muestra los detalles específicos de cómo cambió un fichero en un cierto plazo.

svn cat

Este se utiliza para recuperar cualquier fichero tal como existió en un un número de revisión particular y lo muestra en su pantalla.

svn list

Muestra los ficheros en un directorio para cualquier revisión dada.

svn log

Para descubrir información sobre la historia de un fichero o directorio, use el comando svn log. svn log le proporcionará un registro de quién hizo cambios a un fichero o directorio, en qué revisión cambió, la hora y fecha de esa revisión, y, si fue proporcionado, el mensaje de registro que acompañaba al envío.

$ svn log
------------------------------------------------------------------------
r3 | sally | Mon, 15 Jul 2002 18:03:46 -0500 | 1 line

Added include lines and corrected # of cheese slices.
------------------------------------------------------------------------
r2 | harry | Mon, 15 Jul 2002 17:47:57 -0500 | 1 line

Added main() methods.
------------------------------------------------------------------------
r1 | sally | Mon, 15 Jul 2002 17:40:08 -0500 | 1 line

Initial import
------------------------------------------------------------------------

Observe que los mensajes de registro son impresos en orden cronológico inverso por defecto. Si desea ver un rango diferente de revisiones en un orden particular, o solo una única revisión, pase la opción --revision (-r):

$ svn log --revision 5:19    # shows logs 5 through 19 in chronological order

$ svn log -r 19:5            # shows logs 5 through 19 in reverse order

$ svn log -r 8               # shows log for revision 8

También puede examinar el historial de registro de un único fichero o directorio. Por ejemplo:

$ svn log foo.c
…
$ svn log http://foo.com/svn/trunk/code/foo.c
…

Ésto mostrará los mensajes de registro solo para esas revisiones en las cuales el fichero de trabajo (o URL) cambió.

Si desea aún más información sobre un fichero o directorio, svn log también toma una opción --verbose (-v). Porque Subversion le permite mover y copiar ficheros y directorios, es importante poder seguir cambios de la ruta del fichero en el sistema de ficheros, así en modo detallado, svn log incluirá una lista de rutas de fichero cambiadas en una revisión en su salida:

$ svn log -r 8 -v
------------------------------------------------------------------------
r8 | sally | 2002-07-14 08:15:29 -0500 | 1 line
Changed paths:
M /trunk/code/foo.c
M /trunk/code/bar.h
A /trunk/code/doc/README

Frozzled the sub-space winch.

------------------------------------------------------------------------

svn diff

Ya hemos visto svn diff antes—éste muestra las diferencias de fichero en un formato unificado del diff; fue utilizado para mostrar las modificaciones locales hechas a nuestra copia de trabajo antes de enviarlas al repositorio.

De hecho, resulta que hay tres usos distintos para svn diff:

  • Examinar cambios locales

  • Comparar su copia de trabajo con la del repositorio

  • Comparar repositorio con repositorio

Examinando cambios locales

Como hemos visto, invocando svn diff sin argumentos comparará sus ficheros de trabajo con las copias prístinas almacenadas en el área .svn:

$ svn diff
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

Comparando copia de trabajo con repositorio

Si se pasa un único --revision (-r) número, entonces su copia de trabajo es comparada con la revisión especificada del repositorio.

$ svn diff --revision 3 rules.txt 
Index: rules.txt
===================================================================
--- rules.txt	(revision 3)
+++ rules.txt	(working copy)
@@ -1,4 +1,5 @@
 Be kind to others
 Freedom = Responsibility
 Everything in moderation
-Chew with your mouth open
+Chew with your mouth closed
+Listen when others are speaking
$

Comparando repositorio con repositorio

Si dos números de revisión, separados por una coma, son pasados vía --revision (-r), entonces las dos revisiones son comparadas directamente.

$ svn diff --revision 2:3 rules.txt 
Index: rules.txt
===================================================================
--- rules.txt	(revision 2)
+++ rules.txt	(revision 3)
@@ -1,4 +1,4 @@
 Be kind to others
-Freedom = Chocolate Ice Cream
+Freedom = Responsibility
 Everything in moderation
 Chew with your mouth closed 
$

No solo puede usar svn diff para comparar ficheros en su copia de trabajo con el repositorio, sino que si suministra una URL como argumento, usted puede examinar las diferencias entre elementos en el repositorio incluso sin tener una copia de trabajo. Esto es especialmente útil si desea inspeccionar cambios en un fichero cuando no tiene una copia de trabajo en su máquina local:

$ svn diff --revision 4:5 http://svn.red-bean.com/repos/example/trunk/text/rules.txt
…
$

svn cat

Si desea examinar una versión anterior de un fichero y no necesariamente las diferencias entre dos ficheros, puede usar svn cat:

$ svn cat --revision 2 rules.txt 
Be kind to others
Freedom = Chocolate Ice Cream
Everything in moderation
Chew with your mouth closed
$

También puede redireccionar la salida directamente a un fichero:

$ svn cat --revision 2 rules.txt > rules.txt.v2
$

Probablemente se esté preguntando por qué no usamos svn update --revision para actualizar el fichero a la revisión más antigua. Hay algunas razones por las que preferimos usar svn cat.

Primero, usted puede querer ver las diferencias entre dos revisiones de un fichero usando un programa diff externo (quizás uno gráfico, o quizás su fichero está en un formato que la salida de un diff unificado es absurdo). En este caso, necesitará coger una copia de la revisión antigua, redireccionarla a un fichero, y pasar este y el fichero de su copia de trabajo a su programa diff externo.

A veces es más fácil mirar una versión más antigua de un fichero en su totalidad en comparación con las diferencias entre esta y otra revisión.

svn list

El comando svn list le muestra qué ficheros están en un directorio de un repositorio sin realmente descargar los ficheros a su máquina local:

$ svn list http://svn.collab.net/repos/svn
README
branches/
clients/
tags/
trunk/

Si desea un listado más detallado, pase la opción --verbose (-v) para obtener una salida como esta.

$ svn list --verbose http://svn.collab.net/repos/svn
   2755 harry          1331 Jul 28 02:07 README
   2773 sally               Jul 29 15:07 branches/
   2769 sally               Jul 29 12:07 clients/
   2698 harry               Jul 24 18:07 tags/
   2785 sally               Jul 29 19:07 trunk/

Las columnas le dicen la revisión en la cual el fichero o directorio fue modificado por última vez, el usuario qué lo modificó, el tamaño si este es un fichero, la fecha de la última modificación, y el nombre del objeto.

Una palabra final en el historial

Además de todos los comandos anteriores, usted puede usar svn update y svn checkout con la opción --revision para tomar una copia de trabajo entera anterior en el tiempo [13]:

$ svn checkout --revision 1729 # Checks out a new working copy at r1729
…
$ svn update --revision 1729 # Updates an existing working copy to r1729
…

Otros comandos útiles

Mientras que no son usados con tanta frecuencia como los comandos discutidos previamente en este capítulo, usted necesitará de vez en cuando estos comandos.

svn cleanup

Cuando Subversion modifica su copia de trabajo (o cualquier información en el interior de .svn), intenta hacerlo tan seguro como sea posible. Antes de cambiar cualquier cosa, escribe sus intenciones en un fichero de registro, ejecuta los comandos en el fichero de registro, entonces borra el fichero de registro (esto es similar en diseño a un sistema de ficheros transaccional). Si una operación de Subversion es interrumpida (si el proceso es matado, o si la máquina se cuelga, por ejemplo), los ficheros de registro permanecen en disco. Ejecutando de nuevo los ficheros de registro, Subversion puede completar la operación anteriormente empezada, y su copia de trabajo puede volver a estar en un estado consistente.

Y esto es exactamente lo que hace svn cleanup: busca en su copia de trabajo y ejecuta cualquier registro de sobra, eliminando bloqueos en el proceso. Si Subversion alguna vez le dice que alguna parte de su copia de trabajo está bloqueada, entonces éste es el comando que debería ejecutar. También, svn status mostrará una L al lado de los objetos bloqueados:

$ svn status
  L    somedir
M      somedir/foo.c 

$ svn cleanup
$ svn status
M      somedir/foo.c

svn import

El comando svn import es una manera rápida de copiar un árbol de ficheros sin versionar en el repositorio, creando directorios intermedios como sea necesario.

$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/some/project
Adding         mytree/foo.c
Adding         mytree/bar.c
Adding         mytree/subdir
Adding         mytree/subdir/quux.h

Committed revision 1.

El ejemplo anterior copia el contenido del directorio mytree debajo del directorio some/project en el repositorio:

$ svn ls file:///usr/local/svn/newrepos/some/project
bar.c
foo.c
subdir/

Observe que después de que la importación esté acabada, el árbol original no se convierte en una copia de trabajo. Para empezar a trabajar, usted todavía necesita hacer svn checkout en una copia de trabajo fresca del árbol.

Sumario

Ahora hemos cubierto la mayoría de los comandos del cliente de Subversion. Las excepciones notables son ésas que se ocupan de la ramificación y de la combinación (vea Capítulo 4, Crear ramas y fusionarlas) y de las propiedades (vea “Propiedades”). Sin embargo, usted puede querer tomarse un momento para echar un ojo a Capítulo 9, Referencia completa de Subversion para tener una idea de todos los muchos comandos diferentes que tiene Subversion—y cómo puede usarlos para hacer su trabajo más fácil.




[6] N. del T.: La inicial usada representa la palabra Updated (actualizado) en el idioma inglés

[7] N. del T.: La inicial usada representa la palabra Deleted (borrado) en el idioma inglés

[8] N. del T.: merGed, fusionado, en inglés

[9] Por supuesto, nada es borrado totalmente del repositorio—solo delHEAD del repositorio. Puede conseguir cualquier cosa que borró descargando (o actualizando su copia de trabajo) a una revisión anterior a la que lo borró.

[10] Subversion usa su motor interno de diff, el cual produce un formato unificado del diff, por defecto. Si quiere la salida del diff en un formato diferente, especifique un programa diff externo usando --diff-cmd y pasando cualquier parámetro que quiera usando la opción --extensions. Por ejemplo, para ver diferencias locales en el fichero foo.c en contexto con el formato de salida mientras ignora cambios en espacios en blanco puede ejecutar svn diff --diff-cmd /usr/bin/diff --extensions '-bc' foo.c.

[11] Siempre puede borrar los ficheros temporales usted mismo, pero ¿realmente querría hacer eso cuando Subversion puede hacerlo para usted? No lo creemos.

[12] Y si les pregunta por esto, pueden echarle de la ciudad en un carril.

[13] ¿Ve? Le dijimos que Subversion era una máquina del tiempo.

Capítulo 4. Crear ramas y fusionarlas

Crear ramas, etiquetar y fusionar, son conceptos comunes en casi todos los sistemas de control de versiones. Si no está familiarizado con estas ideas, le proporcionaremos una buena introducción en este capítulo. Si está familiarizado, esperamos que encuentre interesante descubrir cómo Subversion implementa estas ideas.

Crear ramas es una parte fundamental del control de versiones. Si va a permitir que Subversion gestione sus datos, entonces esta es una característica de la cual acabará dependiendo. Este capítulo asume que ya está familiarizado con los conceptos básicos de Subversion (Capítulo 2, Conceptos básicos).

¿Qué es una rama?

Supongamos que su trabajo es mantener un documento para una de las divisiones de su compañía, una especie de manual. Un día una división diferente le pide ese manual, pero con algunas partes 'retocadas' para ellos, dado que hacen las cosas de forma ligeramente diferente.

¿Qué hace en esta situación? Lo más obvio: realiza una segunda copia de su documento, y comienza a mantener ambas copias de forma separada. A medida que cada departamento solicita pequeños cambios, usted los incorpora en una de las copias o la otra.

A menudo desea realizar el mismo cambio en ambas copias. Por ejemplo, si descubre un error ortográfico en la primera copia, es muy probable que el mismo error exista en la segunda copia. Al fin y al cabo, ambos documentos son casi iguales; sólo se diferencian en pequeños detalles específicos.

Este es el concepto de una rama— una línea de desarrollo que existe de forma independiente a otra, pero comparte una historia común si mira suficientemente atrás en el tiempo. Una rama siempre nace como una copia de algo, y a partir de ahí, pasa a generar su propia historia (vea Figura 4.1, “Ramas de desarrollo”).

Figura 4.1. Ramas de desarrollo

Ramas de desarrollo


Subversion tiene comandos para ayudarle a mantener ramas paralelas de sus ficheros y directorios. Le permite crear ramas copiando sus datos, y recordando que las copias están relacionadas unas a otras. También le ayuda a duplicar cambios de una rama a otra. Finalmente, puede reflejar el contenido de diferentes ramas en partes de su copia de trabajo local, para que pueda mezclar y probar diferentes líneas de desarrollo en su trabajo diario.

Usando ramas

A estas alturas debería entender que todo cambio en el repositorio genera internamente un nuevo árbol de sistema de ficheros (llamado revisión). Si no es así, debería volver atrás y leer información sobre revisiones en “Revisiones”.

En este capítulo, volveremos al ejemplo del segundo capítulo. Recuerde cómo usted y su compañera, Carmen, comparten un repositorio que contiene dos proyectos, paint y calc. Fíjese que en Figura 4.2, “Estructura inicial del repositorio”, no obstante, cada directorio de proyecto contiene subdirectorios llamados trunk y branches. La razón se descubrirá pronto.

Figura 4.2. Estructura inicial del repositorio

Estructura inicial del repositorio


Igual que antes, asuma que tanto Carmen como usted tienen copias locales del proyecto calc. En concreto, ambos tienen una copia local de /calc/trunk. Todos los ficheros del proyecto están en este subdirectorio en lugar de /calc, porque su equipo ha decidido que /calc/trunk es donde tendrá lugar la línea principal de desarrollo.

Digamos que le han encargado la tarea de realizar una reorganización radical del proyecto. Le tomará un largo tiempo realizar la modificación, y ésta afectará a todos los ficheros del proyecto. En esta situación el problema es que no quiere interferir con Carmen, quien sigue en el proceso de corregir pequeños fallos aquí y allá. Ella depende del hecho que la última versión del proyecto (en /calc/trunk) siempre sea usable. Si comienza a enviar cambios al repositorio poco a poco, seguramente fastidiará la tarea de Carmen.

Una estrategia es esconderse en un agujero: usted y Carmen pueden dejar de compartir información durante una o dos semanas. Es decir, comienza a reorganizar los ficheros en su copia local, pero no envía los cambios al servidor o actualiza su copia hasta que ha completado la tarea. Sin embargo, hay varios problemas con esto. En primer lugar, no es muy seguro. La mayoría de las personas prefieren guardar sus cambios en el repositorio con frecuencia, por si algo malo pudiera suceder de forma accidental a su copia local. Segundo, no es muy flexible. Si realiza su trabajo en diferentes ordenadores (quizás tiene una copia de /calc/trunk en dos máquinas diferentes), necesitará copiar manualmente sus cambios de un lado a otro, o realizar todo el trabajo en un solo ordenador. Del mismo modo, es difícil compartir sus cambios a mitad del desarrollo con otras personas. Una práctica ideal común en el desarrollo de software es permitir que sus compañeros puedan revisar su trabajo a medida que progresa. Si nadie ve los cambios que realiza poco a poco, pierde críticas potenciales. Finalmente, si ha finalizado con su tarea, puede encontrarse con que es muy difícil fusionar su trabajo final con el cuerpo principal del repositorio usado por el resto de la compañía. Carmen (y otros) puede haber realizado cambios en el repositorio que son difíciles de incorporar en su copia local—especialmente si ejecuta svn update semanas tras el aislamiento.

La mejor solución es crear su propia rama, o línea de desarrollo, en el repositorio. Esto le permite guardar su trabajo a medio hacer con frecuencia sin interferir con otros, permitiendo a su vez compartir de forma selectiva información con sus colaboradores. Más adelante verá exactamente como funciona esto.

Creando una rama

Crear una rama es muy simple—realice una copia del proyecto en el repositorio usando el comando svn copy. Subversion no sólo es capaz de copiar ficheros individuales, sino también directorios. En este caso, desea realizar una copia del directorio /calc/trunk. ¿Dónde debería colocar la nueva copia? Donde desee—es una cuestión de política de su proyecto. Digamos que su equipo tiene la política de crear ramas en el área de repositorio /calc/branches, y quiere nombrar la rama my-calc-branch. Entonces querrá crear un nuevo directorio /calc/branches/my-calc-branch, el cual comienza su vida como una copia de /calc/trunk.

Hay dos formas diferentes de realizar una copia. Le mostraremos primero el modo engorroso, para asegurarnos de que queda claro el concepto. Para comenzar, obtenga una copia local del directorio raíz del proyecto, /calc:

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

Realizar ahora una copia es cuestión de pasar dos rutas locales al comando svn copy:

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

En este caso, el comando svn copy copia recursivamente el directorio trunk en un nuevo directorio, branches/my-calc-branch. Tal y como puede ver por el comando svn status, el nuevo directorio está ahora a la espera de ser añadido al repositorio. Pero fíjese en el signo + tras la letra A. Esto indica que la adición pendiente es una copia de algo, no algo nuevo. Cuando envíe sus cambios al repositorio, Subversion creará /calc/branches/my-calc-branch en el repositorio como una copia de /calc/trunk, en lugar de reenviar todos los datos de su copia local de nuevo a través de la red:

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

Y ahora el método más sencillo para crear una rama, que deberíamos haberle explicado en primer lugar: svn copy es capaz de operar directamente sobre dos URLs.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.

Realmente no hay diferencia entre estos dos métodos. Ambos procedimientos crean un nuevo directorio en la revisión 341, y el nuevo directorio es una copia de /calc/trunk. Esto se puede ver en Figura 4.3, “Repositorio con nueva copia”. Note cómo el segundo método, no obstante, realiza los cambios en el repositorio inmediatamente. [14] Es un procedimiento más sencillo, porque no requiere obtener primero una gran copia local de todo el repositorio. De hecho, esta técnica ni si quiera requiere que tenga una copia local en absoluto.

Figura 4.3. Repositorio con nueva copia

Repositorio con nueva copia


Trabajando con su rama

Ahora que ha creado una rama del proyecto, puede obtener una nueva copia local para comenzar a usarla:

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

No hay nada especial sobre esta copia local; simplemente refleja un directorio diferente del repositorio. Cuando envíe sus cambios, no obstante, Carmen nunca los verá cuando se actualice. Su copia local es de /calc/trunk. (Asegúrese de leer “Cambiando la copia local de trabajo” más adelante en este capítulo: el comando svn switch permite crear una copia local de una rama de modo diferente.)

Pretendamos que ha pasado una semana, y se realizan los siguientes cambios:

  • Ha realizado un cambio en /calc/branches/my-calc-branch/button.c, creando la revisión 342.

  • Ha realizado un cambio en /calc/branches/my-calc-branch/integer.c, creando la revisión 343.

  • Carmen ha realizado un cambio en /calc/trunk/integer.c, creando la revisión 344.

Ahora hay dos líneas independientes de desarrollo, mostradas en Figura 4.4, “Bifurcación de la historia de un fichero”, sobre el fichero integer.c.

Figura 4.4. Bifurcación de la historia de un fichero

Bifurcación de la historia de un fichero


Las cosas se ponen interesantes cuando mira el historial de cambios realizados a su copia de integer.c:

$ pwd
/home/usuario/my-calc-branch

$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | usuario | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | usuario | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | carmen | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | carmen | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Fíjese como Subversion sigue la historia de integer.c en su rama hacia atrás, traspasando incluso el punto donde el fichero fue copiado. Muestra la creación de la rama como un evento en la historia, porque integer.c fue copiado de forma implícita cuando realizó la copia de /calc/trunk/. Ahora mire qué es lo que ocurre cuando Carmen ejecuta el mismo comando sobre su copia del fichero:

$ pwd
/home/carmen/calc

$ svn log --verbose integer.c
------------------------------------------------------------------------
r344 | carmen | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  fix a bunch of spelling errors.

------------------------------------------------------------------------
r303 | carmen | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | carmen | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------

Carmen ve su propio cambio de la revisión 344, pero no el cambio realizado en la revisión 343. Desde el punto de vista de Subversion, estos dos cambios afectaron ficheros diferentes en distintos lugares del repositorio. No obstante, Subversion muestra que ambos ficheros comparten una historia común. Antes de que la rama fuese creada en la revisión 341, ambos eran el mismo fichero. Por esta razón tanto usted como Carmen ven los cambios realizados en las revisiones 303 y 98.

Conceptos clave sobre las ramas

Hay dos lecciones importantes que debería recordar de esta sección.

  1. A diferencia de muchos otros sistemas de control de versiones, las ramas de Subversion existen como directorios normales del sistema de archivos en el repositorio, no en una dimensión extra. Estos directorios simplemente llevan información histórica adicional.

  2. Subversion no tiene un concepto interno de rama—sólo copias. Cuando copia un directorio, el nuevo directorio sólo es una rama porque usted añade esa connotación. Puede considerar el directorio de forma diferente, o tratarlo de manera diferente, pero para Subversion no es más que un directorio ordinario que simplemente fue creado como resultado de una operación de copiado.

Copiando cambios entre ramas

Ahora usted y Carmen están trabajando en ramas paralelas del proyecto: usted está trabajando en una rama privada, y Carmen está trabajando en el tronco, o línea principal de desarrollo.

En proyectos que tienen grandes cantidades de contribuyentes, es habitual que la mayoría tengan copias locales del tronco. Cuando alguien necesita hacer un cambio a largo plazo que puede molestar en el tronco, un procedimiento estándar es crear una rama privada y realizar ahí los cambios hasta que todo el trabajo esté completo.

Así que las buenas noticias son que usted y Carmen no están interfiriendo en sus trabajos. Las malas noticias son que es muy fácil derivar demasiado lejos. Recuerde que uno de los problemas con la estrategia esconderse en un agujero es que para cuando haya terminado con su rama, puede que sea casi imposible fusionar los cambios con el tronco sin un alto número de conflictos.

En su lugar, usted y Carmen pueden continuar compartiendo cambios a medida que trabajan. Es tarea suya decidir qué cambios merece la pena compartir; Subversion le brinda la posibilidad de copiar cambios entre ramas de forma selectiva. Y para cuando haya finalizado completamente con su rama, su conjunto entero de cambios en la rama puede ser copiado en el tronco.

Copiando cambios específicos

El la sección anterior, mencionamos que tanto usted como Carmen han realizado cambios a integer.c en ramas diferentes. Si observa el informe de cambios de la revisión 344, puede ver que ella realizó algunas correcciones de ortografía. Sin duda, su copia del mismo fichero debe tener todavía los mismos errores de ortografía. Es probable que futuros cambios al fichero afectarán las mismas áreas que tienen estos errores ortográficos, lo que potencialmente generará conflictos cuando fusione su rama algún día. Es mejor, entonces, recibir ahora los cambios de Carmen, antes de que comience a trabajar de forma intensiva en el mismo sitio.

Es el momento de usar el comando svn merge. Este comando, descubrirá que es un primo cercano del comando svn diff (sobre el cual leímos en el tercer capítulo). Ambos comandos son capaces de comparar dos objetos cualquiera del repositorio y describir sus diferencias. Por ejemplo, puede preguntar a svn diff que le muestre el cambio exacto realizado por Carmen en la revisión 344:

$ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk

Index: integer.c
===================================================================
--- integer.c	(revision 343)
+++ integer.c	(revision 344)
@@ -147,7 +147,7 @@
     case 6:  sprintf(info->operating_system, "HPFS (OS/2 or NT)"); break;
     case 7:  sprintf(info->operating_system, "Macintosh"); break;
     case 8:  sprintf(info->operating_system, "Z-System"); break;
-    case 9:  sprintf(info->operating_system, "CPM"); break;
+    case 9:  sprintf(info->operating_system, "CP/M"); break;
     case 10:  sprintf(info->operating_system, "TOPS-20"); break;
     case 11:  sprintf(info->operating_system, "NTFS (Windows NT)"); break;
     case 12:  sprintf(info->operating_system, "QDOS"); break;
@@ -164,7 +164,7 @@
     low = (unsigned short) read_byte(gzfile);  /* read LSB */
     high = (unsigned short) read_byte(gzfile); /* read MSB */
     high = high << 8;  /* interpret MSB correctly */
-    total = low + high; /* add them togethe for correct total */
+    total = low + high; /* add them together for correct total */
 
     info->extra_header = (unsigned char *) my_malloc(total);
     fread(info->extra_header, total, 1, gzfile);
@@ -241,7 +241,7 @@
      Store the offset with ftell() ! */
 
   if ((info->data_offset = ftell(gzfile))== -1) {
-    printf("error: ftell() retturned -1.\n");
+    printf("error: ftell() returned -1.\n");
     exit(1);
   }
 
@@ -249,7 +249,7 @@
   printf("I believe start of compressed data is %u\n", info->data_offset);
   #endif
   
-  /* Set postion eight bytes from the end of the file. */
+  /* Set position eight bytes from the end of the file. */
 
   if (fseek(gzfile, -8, SEEK_END)) {
     printf("error: fseek() returned non-zero\n");

El comando svn merge es casi exactamente idéntico. En lugar de mostrar las diferencias en su terminal, no obstante, las aplica directamente a su copia local como modificaciones locales:

$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

La salida del comando svn merge muestra que su copia de integer.c fue parcheada. Ahora contiene el cambio de Carmen—el cambio ha sido copiado desde el tronco a su copia local de la rama privada, y existe ahora como una modificación local. En este punto, es tarea suya revisar la modificación local para asegurarse de que funciona correctamente.

En otra situación, es posible que las cosas no hayan ido tan bien, y que integer.c haya entrado en un estado de conflicto. Puede tener que resolver el conflicto usando el procedimiento estándar (vea el tercer capítulo), o si decide que la fusión fue una mala idea, simplemente dé se por vencido y use svn revert para revertir el cambio local.

Pero asumiendo que ha revisado el cambio fusionado, puede guardar sus cambios como es habitual con svn commit. En ese punto, el cambio ha sido fusionado en su rama del repositorio. En terminología de control de versiones, este acto de copiar cambios entre ramas se denomina portar cambios.

Cuando guarda su modificación local, asegúrese de que el mensaje de cambios indica que está portando un cambio específico de una rama a otra. Por ejemplo:

$ svn commit -m "integer.c: ported r344 (spelling fixes) from trunk."
Sending        integer.c
Transmitting file data .
Committed revision 360.

Tal y como verá en las siguientes secciones, es muy importante seguir esta práctica recomendada.

Aviso: aunque los comandos svn diff y svn merge son similares en concepto, tienen una diferente sintaxis en muchos casos. Asegúrese de leer en el capítulo 8 los detalles, o pregunte a svn help. Por ejemplo, svn merge requiere una ruta a una copia local como destino, es decir, un lugar donde debe aplicar los cambios del árbol. Si el destino no es especificado, asume que está intentando realizar una de las siguientes operaciones comunes:

  1. Quiere fusionar cambios de directorio directamente en su copia local.

  2. Quiere fusionar los cambios de un fichero específico en un fichero que existe con el mismo nombre en su directorio de trabajo actual.

Si está fusionando un directorio y no ha especificado una ruta destino, svn merge asume el primer caso mencionado e intenta aplicar los cambios en su directorio actual. Si está fusionando un fichero, y ese fichero (o un fichero con el mismo nombre) existe en su directorio actual de trabajo, svn merge asume el segundo caso mencionado e intenta aplicar los cambios al fichero local de mismo nombre.

Si quiere aplicar los cambios en otro lugar, tendrá que especificarlo. Por ejemplo, si todavía está en el directorio padre de su copia local, tendrá que especificar el directorio destino para recibir los cambios:

$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk my-calc-branch
U   my-calc-branch/integer.c

Procedimientos ideales de fusionado

Realizar fusiones de forma manual

Fusionar cambios suena bastante simple, pero en la práctica puede convertirse en un dolor de cabeza. El problema es que si fusiona cambios con frecuencia de una rama a otra, puede acabar fusionando accidentalmente el mismo cambio dos veces. Cuando esto ocurre, a veces las cosas seguirán funcionando bien. Cuando aplica un parche a un fichero, Subversion normalmente se da cuenta de que el fichero ya contiene el cambio, y no hace nada. Pero si el cambio ya existente fue modificado de algún modo, obtendrá un conflicto.

Idealmente, su sistema de control de versiones debería prevenir la doble aplicación de cambios a una rama. Debería recordar automáticamente qué cambios fueron recibidos en la rama, y ser capaz de mostrarle un listado de los mismos. Debería poder usar esta información para automatizar las fusiones tanto como sea posible.

Desafortunadamente, Subversion no es tal sistema. Al igual que CVS, Subversion 1.0 no almacena ninguna información sobre operaciones de fusionado. Cuando envía sus cambios locales, el repositorio no tiene idea si esos cambios vinieron de una ejecución del comando svn merge, o de una modificación manual de los ficheros.

¿Qué significa esto para usted, el usuario? Significa que hasta que llegue el día que Subversion desarrolle esta característica, tendrá que gestionar usted mismo la información de fusionado. El mejor lugar para hacerlo es en el informe de cambios cuando envía cambios al repositorio. Tal y como se demostró en el ejemplo anterior, es recomendable que su informe de cambios mencione el número de revisión específico (o rango de revisiones) que está siendo fusionado en su rama. Más tarde, puede ejecutar svn log para revisar qué cambios contiene ya su rama. Esto le permitirá construir con cuidado siguientes comandos svn merge que no serán redundantes con cambios previamente portados.

En la siguiente sección, le mostraremos algunos ejemplos de esta técnica en acción.

Visualización previa de fusiones

Dado que el fusionado sólo genera modificaciones locales, normalmente no es una operación de alto riesgo. Si se equivoca en el fusionado la primera vez, simplemente ejecute svn revert para ignorar los cambios y pruebe de nuevo.

Es posible, no obstante, que su copia local ya tenga algunas modificaciones. Los cambios aplicados por una fusión se mezclarán con sus modificaciones locales, por lo que ejecutar svn revert ya no es una opción. Los dos conjuntos de cambios quizás sean imposibles de separar.

En casos como este, la gente se reconforta con la idea de ser capaces de predecir o examinar las fusiones antes de que ocurran. Una manera simple de hacerlo es ejecutar svn diff con los mismos argumentos que planea pasar a svn merge, tal y como ya le enseñamos en el primer ejemplo de fusionado. Otro método de visualización previa es pasar la opción --dry-run al comando de fusionado:

$ svn merge --dry-run -r 343:344 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
#  nothing printed, working copy is still unchanged.

La opción --dry-run en realidad no aplica ningún cambio a su copia local. Únicamente muestra los códigos de estado que serían mostrados durante una fusión de verdad. Esto es útil para obtener una visualización previa de alto nivel de la potencial fusión, para aquellos casos en los que ejecutar svn diff proporcionaría demasiados detalles.

Teniendo en cuenta o ignorando ascendencia

Cuando hable con un desarrollador de Subversion, es muy posible que oiga alguna referencia al término ascendencia. Esta palabra es usada para describir la relación entre dos objetos en un repositorio: si ambos están relacionados, entonces se dice que un objeto es el ancestro del otro.

Por ejemplo, suponga que envía cambios al repositorio en la revisión 100, los cuales incluyen un cambio al fichero foo.c. Entonces foo.c@99 es un ancestro de foo.c@100. Por otro lado, suponga que hace efectivo el borrado de foo.c en la revisión 101, y entonces añade un nuevo fichero con el mismo nombre en la revisión 102. En este caso, foo.c@99 y foo.c@102 pueden parecer relacionados (tienen la misma ruta), pero de hecho son objetos completamente diferentes en el repositorio. No comparten historial o ascendencia.

La razón de mencionar esto ahora es para señalar una diferencia importante entre svn diff y svn merge. El primer comando ignora la ascendencia, mientras que el segundo es muy sensitivo a ella. Por ejemplo, si usase svn diff para comparar las revisiones 99 y 102 de foo.c, vería ficheros diferenciales basados en líneas; el comando diff esta comparando a ciegas ambas rutas. Pero si usase svn merge para comparar ambos objetos, se daría cuenta de que no están relacionados y primero intentaría borrar el fichero antiguo, y entonces añadir el nuevo; vería D foo.c seguido por A foo.c.

En la mayoría de las fusiones se comparan árboles que están relacionados por ascendencia, y por esta razón svn merge sigue este comportamiento por defecto. No obstante, ocasionalmente puede querer que el comando de fusionado compare dos árboles no relacionados. Por ejemplo, puede haber importado dos árboles de código fuente representando diferentes versiones de un proyecto de software (lea “Ramas de proveedores”). ¡Si usase svn merge para comparar los dos árboles, vería que el primero es borrado completamente, seguido de una adición completa del segundo!

En estas situaciones, querrá que svn merge realice una comparación basada únicamente en rutas de fichero, ignorando cualquier relación existente entre ficheros y directorios. Añada la opción --ignore-ancestry a su comando de fusionado, y se comportará del mismo modo que svn diff. (Y de modo similar, la opción --notice-ancestry hará que svn diff se comporte como un comando de fusionado.)

Casos habituales de fusionado

Hay muchos usos diferentes para svn merge, y esta sección describe los más comunes que posiblemente usará alguna vez.

Fusionando una rama completa con otra

Para completar el ejemplo que hemos ido mostrando, nos moveremos hacia adelante en el tiempo. Suponga que han pasado varios días, y se han realizado muchos cambios tanto en el tronco como en su rama privada. Suponga que ya ha terminado de trabajar en su rama privada; la característica o corrección está finalmente completada, y ahora quiere fusionar todos los cambios de su rama en el tronco para que otros puedan disfrutarlos.

¿Cómo usamos svn merge en esta situación? Recuerde que este comando compara dos árboles, y aplica las diferencias en una copia local. Así que para recibir los cambios, necesita tener una copia local del tronco. Asumiremos que todavía tiene una copia original del tronco por alguna parte (completamente actualizada), o que recientemente ha obtenido una copia local fresca de /calc/trunk.

¿Pero qué dos árboles deberían ser comparados? A primera vista, la respuesta puede parecer obvia: simplemente compare el último árbol del tronco con el último árbol de su rama. ¡Pero cuidado—esta asunción es incorrecta, y ha confundido a muchos usuarios nuevos! Dado que svn merge funciona como svn diff, comparar el tronco y su rama no describirá meramente el conjunto de cambios realizados en su rama. Tal comparación muestra demasiados cambios: no solo mostraría la adición de los cambios en su rama, sino también el borrado de cambios en el tronco que nunca ocurrieron en su rama.

Para expresar únicamente los cambios que ocurrieron en su rama, necesita comparar el estado inicial de su rama con su estado final. Usando svn log en su rama, puede ver que ésta fue creada en la revisión 341. Y el estado final puede obtenerlo con la revisión HEAD. Esto significa que quiere comparar las revisiones 341 y HEAD del directorio de su rama, y aplicar esas diferencias a una copia local del tronco.

[Sugerencia]Sugerencia

Un buen método para encontrar la revisión en la cual fue creada una rama (la "base" de la misma) es usar la opción --stop-on-copy con svn log. El sub comando log normalmente muestra todo cambio realizado en la rama, incluyendo los de la copia a partir de la cual fue creada. Así que normalmente verá también la historia del tronco. La opción --stop-on-copy detendrá la salida del comando en cuanto svn log detecte que la rama fue copiada o renombrada.

Así que en nuestro ejemplo continuo:

$ svn log --verbose --stop-on-copy \
          http://svn.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
r341 | usuario | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

$

Como esperábamos, el número final de revisión mostrado por este comando es la revisión en la cual my-calc-branch fue creado a raíz de una operación de copiado.

De nuevo, aquí está el procedimiento final de fusionado:

$ cd calc/trunk
$ svn update
At revision 405.

$ svn merge -r 341:HEAD http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn status
M   integer.c
M   button.c
M   Makefile

# ...examine the diffs, compile, test, etc...

$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 406.

De nuevo, fíjese que el mensaje de cambios menciona de forma explícita el rango de cambios que fue fusionado en el tronco. Recuerde siempre hacer esto, porque es información crítica que necesitará más adelante.

Por ejemplo, suponga que decide continuar trabajando en su rama otra semana, para completar una mejora a una de las características originales o corregir un fallo. La revisión HEAD del repositorio es ahora 480, y está listo para realizar otra fusión desde su rama privada al tronco. Pero como se discutió en “Procedimientos ideales de fusionado”, no quiere fusionar los cambios que ya fueron fusionados; sólo quiere fusionar todo lo nuevo en su rama desde la última vez que fue fusionada. El truco es descubrir qué es lo nuevo.

El primer paso es ejecutar svn log en el tronco, y buscar un mensaje de cambios sobre la última vez que realizó una fusión desde su rama:

$ cd calc/trunk
$ svn log
…
------------------------------------------------------------------------
r406 | usuario | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line

Merged my-calc-branch changes r341:405 into the trunk.
------------------------------------------------------------------------
…

¡Ajá! Dado que todos los cambios que ocurrieron en la rama entre las revisiones 341 y 405 fueron fusionados con el tronco como la revisión 406, ahora sabe que sólo quiere fusionar los cambios de la rama posteriores a esto—comparando las revisiones 406 y HEAD.

$ cd calc/trunk
$ svn update
At revision 480.

# We notice that HEAD is currently 480, so we use it to do the merge:

$ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn commit -m "Merged my-calc-branch changes r406:480 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 481.

Ahora el tronco contiene la segunda oleada completa de cambios realizados en la rama. En este punto, puede o bien borrar su rama (discutiremos esto más adelante), o continuar trabajando en su rama y repetir este procedimiento en siguientes operaciones de fusionado.

Deshaciendo cambios

Otro uso común de svn merge es deshacer un cambio que acaba de ser enviado al repositorio. Suponga que está trabajando felizmente en su copia local de /calc/trunk, y descubre que el cambio realizado hace tiempo en la revisión 303, que modificó integer.c, está completamente mal. Nunca debería haber llegado al repositorio. Puede usar svn merge para deshacer el cambio en su copia local, y entonces enviar las modificaciones locales al repositorio. Todo lo que necesita hacer es especificar la diferencia al revés:

$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff
…
# verify that the change is removed
…

$ svn commit -m "Undoing change committed in r303."
Sending        integer.c
Transmitting file data .
Committed revision 350.

Una forma de pensar sobre las revisiones del repositorio es como un grupo de cambios (algunos sistemas de control de versiones los llaman changesets). Al usar el parámetro -r, puede pedirle a svn merge que aplique el changeset, o el rango completo de changesets sobre su copia local de trabajo. En nuestro caso para deshacer un cambio usamos svn merge para aplicar el changeset #303 a nuestra copia local al revés.

Tenga presente que deshacer un cambio de esta manera es como realizar cualquier otra operación svn merge, así que debería usar svn status y svn diff para confirmar que su trabajo está en el estado que usted desea, y entonces usar svn commit para enviar la versión final al repositorio. Tras el envío, este changeset particular ya no se verá reflejado en la revisión HEAD.

De nuevo puede estar preguntándose: vaya, eso no deshizo el cambio anterior, ¿verdad? El cambio todavía existe en la revisión 303. Si alguien obtiene una versión del proyecto calc entre las revisiones 303 y 349, seguirán viendo todavía el cambio malo, ¿verdad?

Si, en efecto. Cuando hablamos sobre eliminar un cambio, estamos hablando sobre eliminarlo de HEAD. El cambio original todavía existe en la historia del repositorio. En la mayoría de las situaciones, esto es suficiente. De todos modos la mayoría de las personas sólo están interesadas en seguir la versión HEAD de un proyecto. No obstante, hay casos especiales en los que realmente desearía destruir toda evidencia de un cambio. (Quizás alguien puso accidentalmente en el repositorio un documento confidencial.) Esto, tal y como verá, no es tan fácil porque Subversion fue diseñado deliberadamente para nunca perder información. Las revisiones son árboles inmutables que se construyen unos sobre otros. Eliminar una revisión de la historia podría causar un efecto dominó, creando caos en todas las revisiones siguientes y posiblemente invalidando todas las copias locales de trabajo. [16]

Resucitando elementos borrados

La mejor característica de los sistemas de control de versiones es que la información nunca se pierde. Incluso cuando borra un fichero o un directorio, puede haberse esfumado de la revisión HEAD, pero el objeto todavía existe en revisiones anteriores. Una de las preguntas más comunes realizadas por nuevos usuarios es, ¿Cómo puedo recuperar mi fichero o directorio antiguo?

El primer paso es definir exactamentequé elemento está intentando resucitar. Aquí tiene una metáfora útil: puede pensar que todo objeto en el repositorio existe en una especie de sistema de coordenadas bidimensional. Las abscisas marcan una revisión particular del árbol, y las ordenadas una ruta de fichero dentro de ese árbol. Así que cualquier versión de su fichero o directorio puede definirse por un par específico de coordenadas.

Subversion no tiene un directorio Attic como CVS, [17] por lo que necesita usar el comando svn log para descubrir la pareja de coordenadas exactas que quiere resucitar. Una buena estrategia es ejecutar svn log --verbose en un directorio que solía contener el elemento borrado. La opción --verbose muestra una lista de todos los elementos modificados en cada revisión; todo lo que necesita hacer es encontrar la revisión en la cual borró el fichero o directorio. Puede hacer esto visualmente, o usando otra herramienta para examinar el informe de cambios (vía grep, o quizás con una búsqueda incremental en un editor.)

$ cd parent-dir
$ svn log --verbose
…
------------------------------------------------------------------------
r808 | jose | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.
…

En el ejemplo, estamos asumiendo que está buscando el fichero borrado real.c. Leyendo los informes de cambios del directorio padre, hemos encontrado que este fichero fue borrado en la revisión 808. Por lo tanto la última versión del fichero existió en la revisión inmediatamente anterior a esa. Conclusión: quiere resucitar la ruta /calc/trunk/real.c de la revisión 807.

Eso fue la parte difícil—la investigación. Ahora que sabe lo que quiere recuperar, tiene dos posibles opciones.

Una de las opciones es usar el comando svn merge para aplicar la revisión 808 al revés. (Ya hemos discutido cómo deshacer cambios, lea “Deshaciendo cambios”.) Esto tendría el efecto de reañadir real.c como modificación local. El fichero se programaría para ser añadido, y tras enviar los cambios, el fichero existiría de nuevo en HEAD.

No obstante, en este ejemplo particular, ésta probablemente no es la mejor estrategia. Aplicar al revés la revisión 808 no solamente programaría la adición de real.c, ya que el mensaje de cambios indica que también se desharían ciertos cambios a integer.c, cosa que no desea. Ciertamente, podría fusionar al revés la revisión 808 y entonces ejecutar svn revert sobre la modificación local de integer.c, pero esta técnica no es muy escalable. ¿Qué pasa si hay 90 ficheros modificados en la revisión 808?

Otra estrategia más precisa es usar svn copy en lugar de svn merge. Simplemente copie la pareja de coordenadas exactas de revisión y ruta del repositorio a su copia de trabajo local:

$ svn copy --revision 807 \
           http://svn.example.com/repos/calc/trunk/real.c ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.

El símbolo de suma en la salida del comando indica que el elemento no sólo se ha programado para ser añadido, sino para ser añadido con historia. Subversion recuerda de dónde fue copiado. En el futuro, ejecutar svn log sobre este fichero recorrerá su historia hasta la resurrección y continuará también con la que tenía antes de la revisión 807. En otras palabras, este nuevo real.c no es realmente nuevo; es un descendiente directo del fichero original que fue borrado.

A pesar de que nuestro ejemplo muestra cómo resucitar un fichero, tenga en cuenta que esta misma técnica proporciona los mismos resultados al resucitar directorios borrados.

Cambiando la copia local de trabajo

El comando svn switch transforma una copia local de trabajo existente en una rama diferente. Aunque este comando no es estrictamente necesario para trabajar con ramas, representa un buen atajo para los usuarios. En nuestro ejemplo anterior, tras crear su rama privada, obtuvo una nueva copia local del nuevo directorio del repositorio. En su lugar, puede pedirle a Subversion que cambie su copia local de /calc/trunk para que refleje la ubicación de la nueva rama

$ cd calc

$ svn info | grep URL
URL: http://svn.example.com/repos/calc/trunk

$ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile
Updated to revision 341.

$ svn info | grep URL
URL: http://svn.example.com/repos/calc/branches/my-calc-branch

Tras cambiar a la rama, su copia local no diferirá en nada a una copia local nueva obtenida del mismo directorio. Y normalmente es más eficiente usar este comando, dado que a menudo las ramas sólo difieren entre sí en pocas cosas. El servidor manda únicamente el conjunto mínimo de cambios necesarios para hacer que su copia local de trabajo refleje el directorio de la rama.

El comando svn switch también permite usar la opción --revision (-r), por lo que no siempre tendrá que cambiar su copia local a la punta de la rama.

Por supuesto, la mayoría de los proyectos son más complicados que nuestro ejemplo calc, y contienen múltiples subdirectorios. Los usuarios de Subversion frecuentemente siguen un algoritmo específico al usar ramas:

  1. Copiar el 'tronco' entero a un nuevo directorio rama.

  2. Cambiar sólo parte de la copia de trabajo del tronco para que refleje esa rama.

En otras palabras, si un usuario sabe que el trabajo que quiere realizar sólo ocurrirá en un subdirectorio específico, usará svn switch para mover sólo ese subdirectorio a la rama. (¡A veces los usuarios cambian un único fichero de su copia local a la rama!) De ese modo, sigue pudiendo recibir modificaciones normales del 'tronco' en su copia local, pero las porciones que cambió a otra rama quedarán inmunes (a no ser que alguien envíe un cambio a esa rama). Esta característica añade una nueva dimensión al concepto de copia local mezclada—no sólo pueden tener las copias locales una mezcla de revisiones, sino también una mezcla de rutas de repositorio.

Si su copia local contiene un número de sub árboles cambiados a otras ubicaciones del repositorio, sigue funcionando de forma normal. Cuando la actualice, recibirá los parches propios de cada sub árbol. Cuando envíe cambios, sus cambios locales seguirán siendo aplicados como un cambio atómico único al repositorio.

Tenga en cuenta que a pesar de que su copia local de trabajo puede reflejar una mezcla de lugares de repositorio, todos estos lugares deben estar dentro del mismo repositorio. Los repositorios de Subversion todavía no son capaces de comunicarse unos con otros; esta característica está planeada para una versión de Subversion posterior a la 1.0.[18]

Dado que svn switch es esencialmente una variante de svn update, comparte el mismo comportamiento; cualquier modificación local en su copia de trabajo es preservada cuando obtiene nuevos datos del repositorio. Esto le permite realizar toda una variedad de trucos ingeniosos.

Por ejemplo, suponga que tiene una copia local de /calc/trunk y realiza un número de cambios sobre ella. De repente se da cuenta de que se suponía que tenía que realizar esos cambios en una rama. ¡No hay problema! Cambie su copia local del tronco a la rama con svn switch, y seguirá manteniendo sus cambios locales. Ahora puede probarlos y enviar los cambios a la rama del repositorio.

Etiquetas

Otro concepto común en el control de versiones es la etiqueta. Una etiqueta no es más que una instantánea del proyecto en el tiempo. En Subversion, esta idea ya está presente en todas partes. Cada revisión del repositorio es exactamente eso—una instantánea del sistema de ficheros tras cada envío al repositorio.

No obstante, las personas prefieren dar nombres más familiares a las etiquetas, como lanzamiento-1.0. Y quieren hacer instantáneas de subdirectorios menores del sistema de archivos. Después de todo, no es tan fácil recordar que lanzamiento-1.0 de una parte del software es un subdirectorio particular de la revisión 4822.

Creando una etiqueta simple

De nuevo, svn copy viene al rescate. Si desea crear una instantánea de /calc/trunk exactamente tal y como se ve en la revisión HEAD, haga una copia:

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/tags/release-1.0 \
      -m "Tagging the 1.0 release of the 'calc' project."

Committed revision 351.

Este ejemplo asume que ya existe un directorio /calc/tags. (Si no existe, vea svn mkdir). Tras finalizar la copia, el nuevo directorio lanzamiento-1.0 será para siempre una instantánea del aspecto que tenía la revisión HEAD del proyecto en el momento que realizó la copia. Por supuesto puede querer ser más preciso sobre qué revisión exacta copiar, en caso de que alguien realice un cambio al proyecto mientras no está observando. Así que si sabe que la revisión 350 de /calc/trunk es exactamente la instantánea que desea, puede especificarla pasando -r 350 al comando svn copy.

Pero espere un momento: ¿no es este procedimiento para crear etiquetas el mismo que usamos para crear una rama? Si, de hecho, así es. En Subversion no hay diferencia entre una etiqueta y una rama. Ambas son directorios ordinarios que son creados a partir de una copia. Igual que con las ramas, la única razón por la que un directorio copiado es una etiqueta es porque los humanos han decidido tratarlo de ese modo: mientras nadie envíe cambios usando ese directorio, seguirá siendo siempre una instantánea. Si la gente comienza a realizar cambios, se convierte en una rama.

Si está administrando un repositorio, hay dos formas de de gestionar etiquetas. La primera es manos fuera: según la política de su proyecto, decida dónde pondrá sus etiquetas, y asegúrese de que todos los usuarios saben cómo tratar los directorios que copian ahí. (En otras palabras, asegúrese de que no cambian nada en esos directorios.) La segunda forma es más paranoica: puede usar uno de los scripts de control de acceso proporcionados con Subversion para prevenir cualquier cosa que no sea la creación de nuevas copias en el área de etiquetado (Vea Capítulo 6, Configuración del servidor.) No obstante, el método paranoico normalmente no es necesario. Si un usuario envía un cambio accidentalmente al directorio de etiquetas, puede simplemente deshacer el cambio tal y como discutimos en la sección anterior. Después de todo, esto es control de versiones.

Creando una etiqueta compleja

A veces quiere que su instantánea sea más complicada que un solo directorio de una revisión concreta.

Por ejemplo, pretendamos que su proyecto es mucho más grande que nuestro ejemplo calc: suponga que contiene un número de subdirectorios y muchos más ficheros. Realizando su trabajo, puede decidir que necesita crear una copia de trabajo local diseñada para contener características o correcciones de fallos específicas. Puede conseguir esto modificando ficheros o directorios de forma selectiva para que reflejen una revisión particular (usando svn update -r liberalmente), o cambiando ficheros y directorios a ramas particulares (haciendo uso de svn switch). Cuando ha finalizado, su copia local es un batiburrillo de ubicaciones del repositorio de diferentes versiones. Pero tras hacer unas pruebas, sabe que es la combinación precisa de datos que necesita.

Hora de tomar una instantánea. Copiar una URL en otra no funcionará esta vez. En este caso, quiere hacer una instantánea de la configuración exacta de su copia local y almacenarla en el repositorio. Afortunadamente, svn copy tiene en realidad cuatro usos diferentes (sobre los cuales puede leer en el noveno capítulo), incluyendo la habilidad para copiar el árbol de una copia local al repositorio:

$ ls
my-working-copy/

$ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag

Committed revision 352.

Ahora existe un nuevo directorio en el repositorio, /calc/tags/mytag, el cual es una instantánea exacta de su copia local de trabajo—revisiones mezcladas, urls, y todo eso.

Otros usuarios han encontrado usos interesantes de esta característica. A veces hay situaciones en las que acaba de enviar al repositorio un grupo de cambios locales, y querría que los viese un colaborador. En lugar de ejecutar svn diff y enviar el fichero de parche (el cual no captura cambios en el árbol), puede usar en su lugar svn copy para subir su copia local a un área privada del repositorio. Entonces su colaborador puede o bien obtener una copia tal cual de su copia local, o bien usar svn merge para recibir sus cambios exactos.

Mantenimiento de ramas

Habrá notado que Subversion es extremadamente flexible. Dado que implementa ramas y etiquetas mediante el mismo mecanismo (copias de directorio), y tanto las ramas como las etiquetas aparecen en el espacio normal del sistema de archivos, mucha gente encuentra Subversion intimidante. Es incluso demasiado flexible. En esta sección le ofrecemos algunas sugerencias para agrupar y gestionar sus datos a lo largo del tiempo.

Estructura del repositorio

Hay maneras estándar recomendadas para organizar un repositorio. La mayoría de las personas crean un directorio trunk que contendrá la línea principal de desarrollo, un directorio branches que contendrá copias de las ramas, y un directorio tags que contendrá copias de las etiquetas. Si un repositorio sólo almacena un proyecto, entonces la gente a menudo crea estos directorios en el directorio raíz:

/trunk
/branches
/tags

Si un repositorio contiene múltiples proyectos, los administradores normalmente indexan la estructura por proyecto (vea “Escogiendo el esquema de repositorio” para leer más sobre raíces de proyecto):

/paint/trunk
/paint/branches
/paint/tags
/calc/trunk
/calc/branches
/calc/tags

Por supuesto, es libre de ignorar estas estructuras comunes. Puede crear cualquier tipo de variación, lo que sea mejor para usted y su equipo. Recuerde que sea lo que sea lo que elija, no es una decisión final. Puede reorganizar su repositorio en cualquier momento. Dado que las ramas y etiquetas son directorios ordinarios, el comando svn move puede moverlos o renombrarlos como desee. Cambiar de una estructura a otra es una cuestión de ejecutar una serie de movimientos en el servidor; si no le gusta cómo se organizan las cosas en el repositorio, simplemente juegue un poco con los directorios.

Recuerde, no obstante, que aunque mover directorios puede ser fácil, necesita ser considerado con sus usuarios. Sus movimientos pueden desorientar a los usuarios que tengan copias locales de trabajo. Si un usuario tiene una copia local de un directorio particular del repositorio, su operación svn move quizás elimine la ruta de la última revisión. Cuando el usuario ejecute svn update, se le informará que su copia local usa una ruta que ya no existe, y el usuario se verá forzado a hacer svn switch a otra ubicación.

Longevidad de los datos

Otra buena característica del modelo de Subversion es que las ramas y las etiquetas pueden tener vidas infinitas, igual que cualquier otro elemento versionado. Por ejemplo, suponga que finalmente termina todo su trabajo en una rama personal del proyecto calc. Tras fusionar todos sus cambios de vuelta en /calc/trunk, ya no hace falta que el directorio de su rama privada permanezca visible:

$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \
             -m "Removing obsolete branch of calc project."

Committed revision 375.

Y desapareció su rama. Por supuesto no ha desaparecido realmente: el directorio simplemente falta en la revisión HEAD, y ya no distrae a nadie más. Si usa svn checkout, svn switch, o svn list para examinar una revisión anterior, seguirá pudiendo ver su antigua rama.

Si navegar por su directorio borrado no es suficiente, siempre puede devolverle la vida. Resucitar datos es muy fácil en Subversion. Si hay un directorio (o fichero) borrado que querría volver a poner en HEAD, simplemente cópielo con svn copy -r de la revisión antigua:

$ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch \
                  http://svn.example.com/repos/calc/branches/my-calc-branch

Committed revision 376.

En nuestro ejemplo, su rama personal tuvo un periodo de actividad relativamente corto: puede haberla creado para corregir un fallo o implementar una nueva característica. Cuando su tarea está terminada, así lo está su rama. No obstante, en el desarrollo de software también es común tener dos ramas principales que viven en paralelo por largos periodos de tiempo. Por ejemplo, suponga que es el momento de hacer público el proyecto estable calc, y sabe que va a necesitar un par de meses corregir posibles fallos del mismo. No quiere que la gente añada nuevas características al proyecto, pero tampoco quiere decirle a los desarrolladores que dejen de programar. Así que crea una rama estable del software que no cambiará mucho:

$ svn copy http://svn.example.com/repos/calc/trunk \
         http://svn.example.com/repos/calc/branches/stable-1.0 \
         -m "Creating stable branch of calc project."

Committed revision 377.

Y ahora los desarrolladores son libres de continuar añadiendo características novísimas (o experimentales) a /calc/trunk, y usted puede declarar como política del proyecto que sólo las correcciones de fallos serán realizadas en /calc/branches/stable-1.0. Es decir, a medida que la gente continúa su trabajo en el tronco, un humano portará selectivamente las correcciones de fallos a la rama estable. Incluso después de haber lanzado la rama estable, probablemente continuará manteniéndola por un largo tiempo—es decir, tanto como desee continuar el soporte de esa versión para sus usuarios.

Sumario

Hemos cubierto mucho terreno en este capítulo. Hemos discutido los conceptos de las etiquetas y ramas, y demostrado como Subversion implementa estos conceptos por medio de la copia de directorios y el comando svn copy. Hemos mostrado cómo usar svn merge para copiar cambios de una rama a otra, o deshacer cambios erróneos. Hemos revisado el uso de svn switch para crear copias de trabajo con mezcla de ubicaciones del repositorio. Y hemos hablado sobre cómo podría gestionar la organización y tiempos de vida de las ramas en un repositorio.

Recuerde el mantra de subversion: las ramas y etiquetas son ligeras. ¡Úselas sin pudor!




[14] Subversion no soporta copias entre diferentes repositorios. Cuando use URLs con svn copy o svn move, sólo puede copiar elementos dentro del mismo repositorio.

[15] En el futuro, el proyecto Subversion planea usar (o inventar) un formato de parche que describe cambios a árboles.

[16] Sin embargo, el proyecto Subversion tiene planes para implementar algún día un comando svnadmin obliterate que realizaría la tarea de borrar información permanentemente. Mientras tanto, lea “svndumpfilter” para una posible solución alternativa.

[17] Dado que CVS no hace versionado de árboles, crea un área Attic dentro de cada directorio del repositorio para poder recordar los ficheros borrados.

[18] Usted puede, no obstante, usar svn switch con el parámetro --relocate si la URL de su servidor cambia y no desea abandonar su copia de trabajo existente. Vea la sección svn switch en Capítulo 9, Referencia completa de Subversion para obtener más información y un ejemplo.

Capítulo 5. Administración del Repositorio

El repositorio de Subversion es el almacén central de datos y versiones para un número indeterminado de proyectos; de esta manera se convierte en el destinatario obvio de todo el amor y atención que un administrador puede ofrecer. Aunque el repositorio es, en general, un elemento de bajo mantenimiento, es importante entender cómo configurarlo y cuidarlo correctamente para evitar problemas potenciales y para solucionar los problemas actuales de forma segura.

En este capítulo, explicaremos cómo crear y configurar un repositorio Subversion, y cómo publicarlo para acceder a él por red. También hablaremos acerca del mantenimiento del repositorio, incluyendo el uso de las herramientas svnlook y svnadmin ( que están disponibles junto con Subversion ). Trataremos también algunas preguntas y errores comunes, y haremos sugerencias sobre la forma de organizar los datos en el repositorio.

Si planea acceder al repositorio Subversion sólo desde el punto de vista de un usuario cuyos datos están bajo control de versiones (es decir, un cliente Subversion), puede saltarse este capítulo en conjunto. Sin embargo, si vd. es, o quiere convertirse en un administrador de repositorios Subversion, [19] debería sin lugar a dudas prestar atención a este capítulo.

Cuestiones básicas acerca de el repositorio

Antes de entrar en el tema principal de la administración del repositorio, vamos a definir con más detalle qué es un repositorio. ¿Qué pinta tiene? ¿Cómo se siente? ¿Se toma el té caliente o helado, dulce, y con limón? Como administrador, necesitará entender la composición de un repositorio desde una perspectiva lógica— entendiendo cómo se representa la información dentro del repositorio—y desde una perspectiva práctica—qué apariencia tiene y cómo actúa un repositorio con respecto a herramientas no pertenecientes a Subversion. La siguiente sección se ocupa de algunos de estos conceptos básicos a un nivel muy alto.

Entendiendo las Transacciones y Revisiones

Hablando conceptualmente, un repositorio Subversion es una secuencia de árboles de directorios. Cada árbol es una fotografía de cómo eran los ficheros y directorios versionados en tu repositorio en un momento determinado. Estas fotografías son generadas como resultado de operaciones de programas cliente, y son llamadas revisiones.

Cada revisión nace como un árbol de transacciones. Cuando se envían cambios al repositorio, el programa cliente construye una transacción de Subversion que copia los cambios locales ( junto a cualquier cambio adicional que haya podido tener lugar desde el comienzo del proceso de envío de datos), y luego pide al repositorio que guarde ese árbol como la próxima fotografía en la secuencia. Si el envío de datos no da error, la transacción se convierte en una nueva revisión del árbol, y se le asigna un nuevo número de revisión. Si el envío de datos fallara por alguna razón, la transacción se destruye, y se le informa al cliente del error.

Las actualizaciones funcionan de una manera parecida. El Cliente prepara un árbol de transacción temporal que copia el estado de la copia de trabajo. El repositorio compara entonces ese árbol de transacción con el árbol de la revisión solicitada (normalmente la más reciente, o el árbol más joven), e informa al cliente acerca de qué cambios son necesario para convertir su copia local de trabajo en una réplica de ese árbol de revisión. Tras completarse la actualización, se borra la transacción temporal.

El uso de árboles de transacción es la única manera de hacer cambio permanentes en un repositorio de sistema de ficheros versionados. De todas maneras, es importante entender que el tiempo de vida de una transacción es completamente flexible. En el caso de actualizaciones, las transacciones con árboles temporales que se destruyen inmediatamente. En el caso de envíos al repositorio, las transacciones son transformadas en revisiones permanentes ( o borradas si el envío falla ). En el caso de un error, es posible que una transacción permanezca accidentalmente suelta en el repositorio ( sin que afecte en realidad a nada, pero ocupando espacio).

En teoría, un día los programas de flujo de trabajo completo deberán girar hacia un control más fino del tiempo de vida de la transacción. Es factible imaginar un sistema por el que cada transacción que se convierta en revisión permanezca en bastante después de que el cliente termine de describir sus cambios al repositorio. Esto permitiría que cada nuevo envío sea revisado por alguna otra persona, quizás un director o , que pueda elegir entre promoverla a una revisión, o cancelarla.

Propiedades no versionadas

Las transacciones y las revisiones en el repositorio Subversion pueden tener propiedades adjuntas. Estas propiedades son relaciones genéricas clave-valor, y generalmente se usan para guardar información acerca del árbol al que están adjuntas. Los nombres y valores de estas propiedades se guardan en el sistema de ficheros del repositorio, junto con el resto de los datos de tu árbol.

Las propiedades de revisiones y transacciones son útiles para asociar información con un árbol que no está estrictamente relacionada con los ficheros y directorios de ese árbol—el tipo de información que no es gestiona por las copias de trabajo de cliente. Por ejemplo, cuando una nueva transacción de envío es creada en el repositorio, Subversion añade una propiedad a dicha transacción llamada svn:date— una marca de tiempo que representa el momento en que la transacción se creó. En el momento que el proceso de envío termina, el árbol también ha recibido una propiedad para guardar el nombre del usuario que es autor de la revisión (svn:author) y una propiedad para guardar el mensaje de informe de cambios adjunto a dicha revisión (svn:log).

Las propiedades de revisiones y transacciones son propiedades no versionada—cuando son modificadas, sus valores previos se descartan definitivamente. Así mismo, mientras los árboles de revisiones en sí son inmutables, las propiedades adjuntas de dichos árboles no lo son. Puedes añadir, borrar, y modificar propiedades de revisiones en cualquier momento más adelante. Si envías al repositorio una nueva revisión y más tarde te das cuenta de alguna información incorrecta o un error sintáctico en tu mensaje de log, puedes simplemente sustituir el valor de la propiedad svn:log con un nuevo y corregido mensaje de log.

Base de datos Berkeley

Los datos almacenados dentro de repositorios Subversion, realmente se encuentran en una base de datos, más concretamente, un fichero de base de datos Berkeley. Durante la fase inicial de diseño de Subversion, los desarrolladores decidieron usar una base de datos Berkeley por una serie de razones, como su licencia open-source, soporte de transacciones, ser de confianza, funcionamiento, simplicidad de su API, soporte de hilos, cursores, y más.

La base de datos Berkeley tiene un soporte real de transacciones —probablemente es su característica más poderosa. Muchos procesos que acceden a sus repositorios Subversion no tienen que preocuparse por pisotear los datos de otros. El aislamiento provisto por el sistema de transacciones es tal que por cada operación dada, el código de repositorio Subversion tiene una vista estática de la base de datos—no una base de datos que está constantemente cambiando de la mano de algunos otros procesos—y puede tomar decisiones basándose en esa vista. Si dicha decisión está en conflicto con lo que otro proceso esté haciendo, la operación completa como si nunca hubiera sucedido, y Subversion reintenta la operación contra una nueva y actualizada ( y estática ) vista de la base de datos.

Otra gran característica de la base de datos Berkeley son las copias de seguridad en caliente— la habilidad para hacer una copia de seguridad del entorno de la base de datos sin que tenga que estar . Hablaremos sobre cómo hacer copias de seguridad de tu repositorio en “Copias de seguridad del repositorio”, pero los beneficios de ser capaz de hacer copias completas y funcionales de tus repositorios sin debería ser obvia.

La base de datos Berkeley también es un sistema de bases de datos de mucha confianza. Subversion utiliza las utilidades de registro de las bases de datos Berkeley, lo que significa que la base datos primero escribe una descripción de cualquier modificación que vaya a hacer en ficheros de registros, para luego hace la propia modificación. Esto es para asegurar que si algo fuese mal, el sistema de base de datos pueda retroceder a un checkpoint—una posición en los ficheros de registro que se sabe que no están corruptas—y repetir transacciones hasta que los datos estén en un estado usable. Ver “Gestionando el espacio de almacenamiento” si quieres más información acerca de los ficheros de registro de las bases de datos de Berkeley.

Pero toda rosa tiene su espina, así que tenemos que hablar sobre algunas conocidas limitaciones de la base de datos Berkeley. Primero, los entornos de base de datos Berkeley no son portables. No puedes copiar simplemente un repositorio Subversion que fue creado en un sistema Unix a un sistema Windows y esperar que funcione. A pesar de que la mayor parte del formato de base de datos Berkeley es independiente de la arquitectura, hay otros aspectos del entorno que no lo son. Segundo, Subversion usa la base de datos Berkeley de una manera que no puede funcionar en sistemas Windows 95/98—si necesita almacenar un repositorio en una máquina Windows, utilice Windows 2000 o Windows XP. Finalmente, no deberías mantener un repositorio Subversion en una unidad compartida por red. Mientras las bases de datos Berkeley prometen un comportamiento correcto en unidades compartidas por red que cumplan un grupo particular de especificaciones, casi ningún sistema de compartición conocido cumple con todas esas especificaciones.

Creación y Configuración de Repositorios

Crear un repositorio Subversion es una tarea increíblemente simple. La utilidad svnadmin, provista con Subversion, tiene un subcomando justo para esto. Para crear un nuevo repositorio, ejecuta:

$ svnadmin create /path/to/repos

Crea un nuevo repositorio en el directorio /path/to/repos. Dicho nuevo repositorio comienza su vida en la revisión 0, que se define como nada excepto el directorio raíz (/) del sistema de ficheros. Inicialmente, la revisión 0 tiene también una única propiedad de revisión, svn:date, que tiene la hora a la que el el repositorio fue creado.

[Aviso]Aviso

No crees tu repositorio en una unidad de red compartida —no puede existir un un sistema de ficheros remoto como NFS, AFS, o Windows SMB. La base de datos Berkeley necesita que el sistema de ficheros subyacente implemente estrictamente la semántica de bloqueo POSIX, y más importante, la habilidad para mapear ficheros directamente Casi ningún sistema de ficheros de red tiene estas características. Si intentas usar una base de datos Berkeley en una unidad compartida de red, los resultados son impredecibles —puede que veas errores misteriosos, o pueden pasar meses hasta que descubras que la base de datos de tu repositorio está sutilmente corrupta.

Si necesitas que varios ordenadores accedan al repositorio, deberías instalar un proceso servidor ( como Apache o svnserve), almacenar el repositorio en un sistema de ficheros local al que el servidor pueda acceder, y hacer que el repositorio esté disponible por la red. Capítulo 6, Configuración del servidor se ocupa de este proceso en detalle.

Te habrás dado cuenta de que el argumento de ruta de svnadmin fue sólo una ruta normal del sistema de ficheros y no una URL como la que el programa cliente svn usa cuando se refiere a los repositorios. Tanto svnadmin como svnlook son considerados como utilidades del lado del servidor— se usan en la máquina donde reside el repositorio para examinar o modificar aspectos del mismo, y son de hecho, tareas imposibles de realizar por red. Un error común hecho por recién llegados a Subversion es tratar de pasar URLs ( incluso las file: locales ) a ambos programas.

Así, después de que hayas ejecutado el comando svnadmin create, tienes un nuevo y brillante repositorio Subversion en su propio directorio. Echemos una ojeada a qué es lo que realmente se crea dentro de ese subdirectorio.

$ ls repos
conf/  dav/  db/  format  hooks/  locks/  README.txt

Con la excepción de los ficheros README.txt y format, el directorio del repositorio es un grupo de subdirectorios. Al igual que en otras áreas del diseño de Subversion, se le tiene mucho respeto a la modularidad, y se prefiere una organización jerárquica antes que un caos que estorbe. He aquí una breve descripción de todos los objetos que puedes ver en tu nuevo directorio de repositorio:

conf

Un directorio que contiene los ficheros de configuración del repositorio.

dav

Un directorio para Apache y mod_dav_svn y su economía privada de datos.

db

El entorno principal de la base de datos Berkeley, lleno de tablas que el almacenamiento de datos para el sistema de ficheros de Subversion ( donde todos tus datos versionados residen.

format

Un fichero cuyo contenido es un simple valor entero que nos dice el número de versión del repositorio

hooks

Un directorio lleno de plantillas de ganchos (y los mismos ganchos), una vez que hayas instalados algunos.

locks

Un directorio para el bloqueo de datos de repositorio de Subversion, usado para los accesos al repositorio.

README.txt

Un fichero que simplemente informa a sus lectores que están mirando un repositorio Subversion.

En general, no deberías con tu repositorio a mano. La herramienta svnadmin debería ser suficiente para cualquier cambio necesarios en tu repositorio, o puedes echar una ojeada a herramientas de terceros ( como la suitede herramientas de la base de datos Berkeley ) para subsecciones relevantes del repositorio. Sin embargo, hay algunas excepciones, y las veremos aquí.

Scripts de enganche

Un gancho es un programa activado por algún evento del repositorio, como la creación de una nueva revisión o la modificación de una propiedad no versionada. A cada gancho se le da suficiente información para que sepa de qué evento se trata, cuál es su objetivo, y el nombre de usuario de la persona que disparó el evento. Dependiendo de la salida del gancho o de estado de su salida, el programa de enganche puede continuar la acción, pararla, o suspenderla de alguna manera.

El subdirectorio hooks se rellena, por defecto, con plantillas para varios ganchos de repositorio.

$ ls repos/hooks/
post-commit.tmpl          pre-revprop-change.tmpl
post-revprop-change.tmpl  start-commit.tmpl
pre-commit.tmpl           

Hay una plantilla por cada gancho que implementa el repositorio Subversion, y examinando los contenidos de dichas plantillas de scripts, puede ver qué disparador ejecuta cada script y qué datos se le pasan. También se encuentran en muchas de estas plantillas ejemplos de cómo debería usar dicho script, conjuntamente con otros programas provistos por Subversion, para realizar tareas comunes y útiles. Para instalar realmente un gancho funcional, sólo necesita colocar algún ejecutable o script en el directorio repos/hooks que pueda ser ejecutado con el nombre del gancho ( como start-commit o post-commit).

En plataformas Unix, esto significa proveer un script o programa (podría ser un shell script, un programa Python, un binario C compilado, o cualquier otra cosa) llamado exactamente igual que el nombre del gancho. Por supuesto, los ficheros de plantillas están presentes para algo más que sólo propósitos informativos—la manera más fácil de instalar un gancho en plataformas Unix es simplemente copiar el fichero de plantilla apropiado en un nuevo fichero sin la extensión .tmpl, personalizando los contenidos del gancho, y asegurándose de que el script sea ejecutable. Windows, sin embargo, usa extensiones de fichero para determinar si un programa es ejecutable o no, así que debería dar poner un programa cuyo nombre sea el nombre del gancho, y cuya extensión sea una de las extensiones especiales reconocidas por Windows como ficheros ejecutables, como .exe o .com para programas, y .bat para ficheros de scripts.

Actualmente hay cinco ganchos implementados por el repositorio Subversion:

start-commit

Se ejecuta antes de la transacción de envío haya sido creada. Se usa normalmente para decidir si el usuario tiene los privilegios suficientes. El repositorio pasa dos argumentos a este programa: la ruta al repositorio, y el nombre de usuario que intenta realizar el envío. Si el programa devuelve algún valor distinto de cero, se paraliza el envío antes de haber creado la transacción.

pre-commit

Se ejecuta cuando se completa la transacción, pero antes de ser enviados los datos al repositorio. Este gancho se usa normalmente como protección contra envíos que no se permiten debido a contenido o ubicación ( por ejemplo, su sitio puede requerir que todos los cambios sobre una rama determinada incluyan un número de ticket del seguimiento de fallos, o que el mensaje de informe de cambios entrante no esté vacío). El repositorio pasa dos argumentos a este programa: la ruta al repositorio, y el nombre de la transacción que va a sufrir el cambio. Si el programa devuelve una salida que no sea cero, el envío se aborta y la transacción se borra.

La distribución Subversion incluye algunos scripts de control de acceso ( ubicados en el directorio tools/hook-scripts del árbol de fuentes de Subversion ) a los que se puede llamar desde pre-commit para implementar un control de de acceso más en detalle. En estos momentos, esta es la única manera por la cual los administradores pueden controlar el acceso más allá de lo que el fichero httpd.conf de Apache ofrece. En una versión futura de Subversion, planeamos implementar listas de control de acceso (ACLs) directamente en el sistema de ficheros.

post-commit

Esto se ejecuta después de que la transacción se haya confirmado, y una nueva revisión haya sido creada. La mayoría de la gente usa este gancho para enviar correos descriptivos acerca de la confirmación o para hacer una copia de seguridad del repositorio. El repositorio pasa dos argumentos a este programa: la ruta al repositorio, y el número de la nueva revisión creada. El código de salida del programa es ignorado.

La distribución de Subversion incluye un script commit-email.pl ( ubicado en el directorio tools/hook-scripts/ del árbol de fuentes de Subversion ) que puede ser usado para enviar correos electrónicos con ( añadiendo o no un fichero de log ) una descripción de la confirmación hecha. Este correo contiene una lista de las rutas que fueron cambiadas, el mensaje de log adjunto a la confirmación, el autor y fecha de la confirmación, así como un informe en formato de GNU diff de los cambios hechos en los ficheros versionados como parte de la confirmación.

Otra herramienta útil de Subversion es el script hot-backup.py ( ubicado en el directorio tools/backup/ del árbol de fuentes de Subversion ). Este script realiza copias de seguridad en caliente de su repositorio Subversion ( una capacidad soportada por el motor de bases de datos Berkeley), y puede ser usada para hacer una foto por cada confirmación de su repositorio para archivar, o con el objetivo de recuperación de emergencia.

pre-revprop-change

Al no ser versionadas las propiedades de revisión de Subversion, hacer modificaciones a una de ellas ( como por ejemplo, el mensaje de informe de cambios svn:log ) sobreescribirá el valor previo de esa propiedad para siempre. Como hay datos aquí que potencialmente se pueden perder, Subversion provee este gancho ( y su contrapartida, post-revprop-change) de tal manera que los administradores de repositorios puedan mantener con métodos externos si así lo desean, registros de los cambios de dichas propiedades. Como precaución contra la pérdida de datos de propiedades no versionadas, no se permite a los clientes Subversion modificarlos del todo remotamente a no ser que este gancho se implemente para su repositorio.

Este gancho se ejecuta justo antes de que una modificación de este tipo se haga en el repositorio. El repositorio pasa cuatro argumentos al gancho: la ruta al repositorio, la revisión en la cual la propiedad que se va a modificar existe, el nombre autenticado de usuario de la persona que va a hacer el cambio, y el nombre mismo de la propiedad.

post-revprop-change

Como se ha mencionado antes, este gancho es la contrapartida del gancho pre-revprop-change. De hecho, por paranoia, este script no se ejecutará a no ser que el gancho pre-revprop-change exista. Cuando ambos están presentes, el gancho post-revprop-change se ejecuta justo después de que una propiedad de revisión haya sido modificad, y se usa típicamente para enviar un correo electrónico con el nuevo valor de la propiedad cambiada. El repositorio pasa cuatro argumentos al gancho: la ruta al repositorio, la revisión en la cual la propiedad existe, el nombre autenticado de usuario de la persona que va a hacer el cambio y el nombre mismo de la propiedad.

La distribución de Subversion incluye el script propchange-email.pl script (ubicado en el directorio tools/hook-scripts/ del árbol de fuentes de Subversion ) que puede ser usado para enviar un correo electrónico con ( y/o añadido a un fichero de log ) los detalles de un cambio en una propiedad de revisión. Este correo electrónico contiene la revisión y nombre de la propiedad modificada, el usuario que hizo el cambio, y el nuevo valor.

Subversión tratará de ejecutar los ganchos como el mismo usuario que posee el proceso que está accediendo al repositorio Subversion. En la mayoría de los casos, el repositorio se accede a través del servidor HTTP Apache y mod_dav_svn, así que este usuario es el mismo que el usuario con el que se ejecuta Apache. Los mismos ganchos necesitarán ser configurados con permisos a nivel de sistema operativo que les permitan ser ejecutados por dicho usuario. Asimismo, esto significa que cualquier fichero o programas ( incluyendo el repositorio mismo ) al que acceda directa o indirectamente el gancho, será a través del mismo usuario. En otras palabras, esté alerta a problemas potenciales relacionados con permisos que impidan al gancho realizar las tareas para las cuales lo haya escrito.

Configuración de la base de datos Berkeley

Un entorno de base de datos Berkeley es una encapsulación de una o más bases de datos, ficheros de registro, ficheros regionales y ficheros de configuración. El entorno de base de datos Berkeley tiene su propio conjunto de valores de configuración por defecto para cosas como el número de bloqueos que se permite eliminar de una sola vez, o el tamaño máximo de los ficheros de registro , etc. El código del sistema de ficheros de Subversion elige además valores por defecto para algunas de las opciones de configuración de la base de datos Berkeley. De todas maneras, algunas veces su repositorio particular, con su colección única de datos y patrones de acceso, podría necesitar un grupo diferente de valores de configuración.

La gente de Sleepycat ( los programadores de la base de datos Berkeley ) entienden que bases de datos diferentes, tienen requerimientos específicos, de tal manera, que nos proveen de un mecanismo para sobreescribir en tiempo de ejecución, muchos de los valores de configuración del entorno Berkeley. Berkeley comprueba la presencia de un fichero denominado DB_CONFIG en cada directorio del entorno, y las opciones que encuentra en dicho fichero para usarlas en ese entorno Berkeley particular.

El fichero de configuración de Berkeley para su repositorio se encuentra en directorio del entorno db, en repos/db/DB_CONFIG. Subversion crea por sí mismo el fichero al mismo tiempo que el resto del repositorio. El fichero inicialmente contiene algunas opciones por defecto, así como enlaces a la documentación en línea de la base de datos Berkeley de tal manera que pueda saber qué significan dichas opciones. Por supuesto, es libre de añadir cualquiera de las opciones soportadas por la base de datos Berkeley a su fichero DB_CONFIG. Tan sólo tenga cuidado porque mientras Subversion no trata de leer o interpretar los contenidos del fichero, y no hace uso de sus opciones de configuración, tendrá que evitar cualquier cambio en la configuración que pueda causar que la base de datos Berkeley se comporte de una manera inesperada para el resto del código de Subversion. Por otra parte, los cambios hechos en DB_CONFIG no tendrán efecto hasta que vuelva a leer en entorno de la base de datos ( usando svnadmin recover).

Mantenimiento del Repositorio

El mantenimiento de un repositorio Subversion puede ser una tarea que asuste, principalmente debido a las complejidades inherentes a sistemas que tienen un motor de base de datos. Para hacer bien este trabajo, basta con conocer las herramientas—qué son y cuándo y cómo usarlas. Esta sección le presentará las herramientas de administración que provee Subversion, y cómo utilizarlas para realizar tareas como migraciones de repositorios, actualizaciones, copias de seguridad y limpiezas.

Una caja de herramientas del Administrador

Subversion provee un conjunto de utilidades para crear inspeccionar, modificar y reparar sus repositorio. Estudiemos más de cerca cada una de estas herramientas. Más tarde, examinaremos brevemente otras incluidas en la distribución de la base de datos Berkeley que añaden funcionalidades específicas al motor de base de datos de las que no disponen las herramientas propias de Subversion.

svnlook

svnlook es una herramienta provista por Subversion para examinar las revisiones y transacciones realizadas en un repositorio. Este programa no intente en ningún momento cambiar el repositorio —es una utilidad de sólo lectura. svnlook es utilizada normalmente por los ganchos del repositorio para informar acerca de los cambios que se van a realizar ( en el caso del gancho pre-commit ) o que se acaban de hacer ( en el caso del gancho post-commit ) en el repositorio. Un administrador del repositorio puede usar esta herramienta para diagnosis.

svnlook tiene una sintaxis muy simple:

$ svnlook help
uso general. svnlook SUBCOMANDO RUTA_REPOS [PARAMS y OPCIONES ...]
Nota: todo subcomando que tome los parámetros '--revision' y '--transaction'
      actuará, si se lo invoca sin una de estas opciones, sobre la versión
      más reciente del repositorio.
Escriba "svn help <subcomando>" para ayuda en un subcomando específico.
…

Casi cada uno de los subcomandos de svnlook puede trabajar sobre una revisión o una transacción, imprimiendo información acerca del propio árbol, o de sus diferencias con respecto a la revisión anterior del repositorio. Usará la opciones --revision y --transaction para especificar qué revisión o transacción examinar respectivamente. Nótese que mientras los números de revisión se ven como números naturales, los nombres de transacción son cadenas alfanuméricas. Recuerde que el sistema de ficheros sólo permite navegar entre transacciones no confirmadas ( transacción que no han tenido como resultado una nueva revisión ). La mayoría de los repositorios no tendrán ese tipo de transacciones, porque las transacciones normalmente son confirmadas ( lo que evita que puedan ser examinadas ) o paradas y borradas.

En ausencia de las opciones --revision y --transaction, svnlook examinará la última revisión ( o HEAD ) del repositorio. Así que las siguientes órdenes hacen exactamente lo mismo cuando 19 es la última revisión del repositorio ubicado en /path/to/repos:

$ svnlook info /path/to/repos
$ svnlook info /path/to/repos --revision 19

La única excepción a estas reglas es el subcomando svnlook youngest, que no recibe opciones, y que simplemente escribe el número de revisión de HEAD.

$ svnlook youngest /path/to/repos
19

La salida de svnlook está diseñada para que sea, a la vez, legible por humanos y por máquinas. Cojamos como ejemplo la salida del subcomando info:

$ svnlook info /path/to/repos
sally
2002-11-04 09:29:13 -0600 (Mon, 04 Nov 2002)
27
Added the usual
Greek tree.

La salida del subcomando info está definida como:

  1. El autor, seguido por un salto de línea.

  2. La fecha, seguida por un salto de línea.

  3. El número de caracteres en el mensaje de registro, seguido por un salto de línea.

  4. El mensaje de registro, seguido por un salto de línea.

Esta salida es legible para humanos, elementos como la fecha, se presentan usando una representación de texto en lugar de algo más oscuro ( como el número de nanosegundos desde que ). Pero esta salida es también legible por la máquina—debido a que el registro del mensaje puede contener muchas líneas y no tener un límite de tamaño, svnlook informa del tamaño del mensaje antes que el mismo mensaje. Esto permite a los y otros alrededor de este comando, tomar decisiones inteligentes acerca del mensaje de registro, como cuánta memoria reservarle, o al menos, cuántos bytes saltarse en el evento para que esta salida no sea el último bit de datos en el flujo.

Otro uso común de svnlook es ver realmente los contenidos de un árbol de revisión o de transacción. El comando svnlook tree presenta los directorios y fichero del árbol solicitado. Si añade la opción --show-ids, también enseñará los identificadores de nodos del sistema de ficheros de la revisión para cada una de las rutas (que, en general, es más útil para los desarrolladores que para los usuarios).

$ svnlook tree /path/to/repos --show-ids
/ <0.0.1>
 A/ <2.0.1>
  B/ <4.0.1>
   lambda <5.0.1>
   E/ <6.0.1>
    alpha <7.0.1>
    beta <8.0.1>
   F/ <9.0.1>
  mu <3.0.1>
  C/ <a.0.1>
  D/ <b.0.1>
   gamma <c.0.1>
   G/ <d.0.1>
    pi <e.0.1>
    rho <f.0.1>
    tau <g.0.1>
   H/ <h.0.1>
    chi <i.0.1>
    omega <k.0.1>
    psi <j.0.1>
 iota <1.0.1>

Una vez que haya visto el esquema de los ficheros y directorios de su árbol, podrá usar comandos como svnlook cat, svnlook propget, y svnlook proplist para profundizar en los detalles dichos elementos.

svnlook puede realizar otros tipos de consultas, presentando los subgrupos de información que mencionamos antes, informando acerca de qué rutas fueron modificadas en una revisión o transacción especificada, mostrando las modificaciones de textos o propiedades realizadas, etc ... A continuación hay una breve descripción de la actual lista de subcomandos aceptados por svnlook, y la salida de los mismos:

author

Dice el autor del árbol.

cat

Vuelca los contenidos de un fichero en el árbol.

changed

Da una lista de todos los ficheros y directorios que han sido modificados en el árbol.

date

Dice la fecha del árbol.

diff

Vuelca de ficheros modificados.

dirs-changed

Lista los directorios del árbol que han sido modificados, o aquellos en los que alguno de sus ficheros han sido modificados.

history

Presenta puntos interesantes en la historia de una ruta versionada ( lugares donde ha habido modificaciones o copias ).

info

Vuelca el autor, la fecha, el número de caracteres del mensaje de registro, y el mensaje de registro.

log

Dice el mensaje de registro del árbol.

propget

Dice el valor de una propiedad de una ruta en el árbol.

proplist

Vuelca los nombres y valores de las propiedades de rutas en el árbol.

tree

Vuelca el listado del árbol, revelando opcionalmente el identificador de la revisión de nodo de sistema de ficheros asociado a cada ruta.

uuid

Dice el identificador único de usuario del árbol.

youngest

Dice el último número de revisión.

svnadmin

El programa svnadmin es el mejor amigo del administrador del repositorio. Además de darle la posibilidad de crear repositorios Subversion, le permite realizar varias operaciones de mantenimiento en ellos. La sintaxis de svnadmin es parecida a la de svnlook:

$ svnadmin help
uso general: svnadmin SUBCOMANDO RUTA_REPOS  [PARAMS y OPCIONES ...]
Escriba "svnadmin help <subcomando>" para ayuda sobre un subcomando.

Subcomandos disponibles:
   create
   deltify
   dump
   help (?, h)
…

Ya hemos mencionado antes el subcomando create de svnadmin ( ver “Creación y Configuración de Repositorios”). La mayoría de los demás subcomandos los veremos más adelante en en este capítulo. De momento, veamos por encima lo que cada uno de ellos nos ofrece.

create

Crear un nuevo repositorio Subversion.

deltify

Recorre un rango de revisiones específico, realizando la deltificación de predecesores en las rutas modificadas de esas revisiones. Si no se especifica ninguna revisión, este comando simplemente deltificará la revisión HEAD.

dump

Vuelca los contenidos del repositorio, agrupados por por un conjunto dado de revisiones, usando un formato portable de volcado.

hotcopy

Hace una copia en caliente de un repositorio. Puede ejecutar esta comando en cualquier momento, y hacer una copia segura del repositorio sin que se vea afectada por el hecho de que otros procesos estén accediendo al repositorio.

list-dblogs

Lista las rutas a los ficheros de registro de la base de datos Berkeley asociados al repositorio. Esta lista incluye todos los ficheros de registros—aquellos que están todavía en uso por Subversion, así como los que no lo están.

list-unused-dblogs

Lista las rutas de los ficheros de registro de la base de datos Berkeley asociados al repositorio, pero que han dejado de usarse. Puede borrar de forma segura estos ficheros del entorno del repositorio y archivarlos para el caso de tener que hacer una recuperación de algún evento catastrófico del repositorio.

load

Carga un grupo de revisiones en un repositorio desde un flujo de datos que utilice el mismo formato portable de información que el generado por el subcomando dump.

lstxns

Lista los nombres de las transacciones Subversion no confirmadas que existen actualmente en el repositorio.

recover

Realiza tareas de recuperación en un repositorio que lo necesite, generalmente tras un error fatal que haya impedido a un proceso cerrar limpiamente su comunicación con el repositorio.

rmtxns

Borra limpiamente del repositorio transacciones Subversion ( convenientemente nutrido por la salida del subcomando lstxns).

setlog

Sustituye el valor actual de la propiedad svn:log ( mensaje de confirmación ) de una transacción en particular en el repositorio con un nuevo valor.

verify

Verificar los contenidos del repositorio. Esto incluye, entre otras cosas, comparaciones de firmas de los datos versionados almacenados en el repositorio.

svndumpfilter

Debido a que Subversion almacena toda la información en un sistema de bases de datos opaco, intentar hacer cambios a mano no es nada recomendable, además de ser bastante complicado. Además, una vez que la información ha sido guardada en su repositorio, Subversion, en general, no tiene ninguna manera cómoda de borrarla. [20] Inevitablemente, habrá veces que querrá manipular el histórico de su repositorio. Podría necesitar eliminar todas la versiones de un fichero que se añadió accidentalmente ( y que no debería estar ahí por alguna razón). O quizás tiene varios proyectos compartiendo el mismo repositorio, y ha decidido separarlos cada uno en su propio repositorio. Para realizar este tipo de tareas, los administradores necesitan una representación de los datos más manejable—el formato de volcado.

El formato de volcado del repositorio Subversion es una representación legible por humanos de los cambios hechos a tus datos versionados a lo largo del tiempo. Utiliza el comando svnadmin dump para generar el volcado, y svnadmin load para poblar un nuevo repositorio con ellos ( ver “Migrando un repositorio”). Lo mejor del hecho de que el formato sea legible es que, si no te asusta, puedes revisarlo y modificarlo manualmente. Por supuesto, lo pero que es si tienes dos años de trabajo en un repositorio encapsulado en lo que se supone que será un fichero de volcado muy grande, llevará mucho, mucho tiempo el revisarlo y modificarlo.

A pesar de que no será la herramienta más usada a disposición del administrador, svndumpfilter tiene un producto muy peculiar con una funcionalidad muy útil—la posibilidad de modificar rápida y fácilmente esos datos de volcado actuando como un filtro basado en las rutas. Simplemente, diga una lista de rutas que quieres mantener, o una lista de rutas que no quieres mantener, luego, dirige tu volcado a través de este filtro. El resultado será un volcado modificado que contendrá sólo las rutas versionadas que ( explícita o implícitamente ) pediste.

La sintaxis de svndumpfilter es así:

$ svndumpfilter help
uso general: svndumpfilter SUBCOMANDO [ARGS & OPCIONES ...]
Escriba "svndumpfilter help <subcommand>" para obtener información sobre un subcomando específico.

Subcomandos disponibles:
   exclude
   include
   help (?, h)

Hay sólo dos subcomandos interesantes. Te permiten elegir entre una inclusión explícita o implícita de rutas en el flujo de información:

exclude

Excluye del flujo de datos del volcado a un grupo de rutas.

include

Permite sólo al grupo de rutas solicitado, pasar al flujo de datos del volcado.

Veamos un ejemplo realista acerca de qué uso le podría dar a este programa. Más tarde hablaremos ( ver “Escogiendo el esquema de repositorio”) del proceso de decisión del modelo de datos para su repositorio—un repositorio por proyecto o combinándolos, organizar todo el material dentro de su repositorio. A veces, después de que las nuevas revisiones comienzan a volar, te vuelves a pensar el modelo, y querrías hacer algunos cambios. Un cambio común es la decisión de mover proyectos que están compartiendo un sólo repositorio en un repositorio separado para cada proyecto.

Nuestro repositorio imaginario contiene tres proyectos: calc, calendar, y spreadsheet. Han estado en una modelo como este:

/
   calc/
      trunk/
      branches/
      tags/
   calendar/
      trunk/
      branches/
      tags/
   spreadsheet/
      trunk/
      branches/
      tags/

Para meter cada uno de estos tres proyectos en su repositorio respectivo, debemos primero hacer un fichero de volcado del repositorio completo:

$ svnadmin dump /path/to/repos > repos-dumpfile
* Dumped revision 0.
* Dumped revision 1.
* Dumped revision 2.
* Dumped revision 3.
…
$

Luego, pasar ese fichero de volcado a través del filtro, incluyendo cada vez, sólo uno de los directorios superiores, dando como resultado tres nuevos ficheros de volcado:

$ svndumpfilter include calc < repos-dumpfile > calc-dumpfile
…
$ svndumpfilter include calendar < repos-dumpfile > cal-dumpfile
…
$ svndumpfilter include spreadsheet < repos-dumpfile > ss-dumpfile
…
$

En este momento, debe tomar una decisión. Cada uno de sus ficheros de volcado creará un repositorio válido, pero mantendrá las rutas exactamente como estaban en el repositorio original. Esto significa que aunque tenga un repositorio exclusivamente para su proyecto calc, dicho repositorio tendrá todavía un directorio superior llamado calc. Si quiere que los directorios trunk, tags, y branches estén en la raíz del repositorio, necesitará editar los ficheros de volcado, cambiando las cabeceras Node-path y Copyfrom-path para conseguir que esa primera ruta calc/ no aparezca; además, deberá borrar la sección del volcado que crea el directorio calc. Será algo como:

Node-path: calc
Node-action: add
Node-kind: dir
Content-length: 0

Todo lo que queda por hacer ahora es crear sus tres nuevos repositorios, y cargar cada fichero de volcado en el repositorio correcto:

$ svnadmin create calc; svnadmin load calc < calc-dumpfile
<<< Started new transaction, based on original revision 1
     * adding path : Makefile ... done.
     * adding path : button.c ... done.
…
$ svnadmin create calendar; svnadmin load calendar < cal-dumpfile
<<< Started new transaction, based on original revision 1
     * adding path : Makefile ... done.
     * adding path : cal.c ... done.
…
$ svnadmin create spreadsheet; svnadmin load spreadsheet < ss-dumpfile
<<< Started new transaction, based on original revision 1
     * adding path : Makefile ... done.
     * adding path : ss.c ... done.
…
$

Ambos subcomandos de svndumpfilter aceptan opciones para decidir cómo tratar las revisiones vacías. Si una revisión dada sólo contenía cambios a rutas que fueron excluidas al filtrarlas, podría ser considerada como no interesante o incluso ignorada. Así que, para darle al usuario el control sobre qué hacer con esas revisiones, svndumpfilter tiene las siguientes opciones:

--drop-empty-revs

No generar ninguna revisión vacía— tan sólo ignorarlas.

--renumber-revs

Si las revisiones vacías son descartadas ( usando la opción --drop-empty-revs), cambiar los números de revisión de las restantes de tal manera que no haya huecos en la secuencia numérica.

--preserve-revprops

Si las revisiones vacías no son descartadas, preservar las propiedades de revisión ( mensaje de registro, autor, fecha, propiedades personalizadas, etc ... ) de dichas revisiones vacías. Por otra parte, las revisiones vacías sólo contendrán la marca de tiempo original, y un mensaje de registro generado indicando que esta revisión fue vaciada por svndumpfilter.

Mientras svndumpfilter puede ser muy útil, y un gran ahorrador de tiempo, desafortunadamente hay un par de problemas. Primero, esta utilidad es . Preste atención a si las rutas en su fichero de volcado se especifican con o sin barras al principio. Necesitará mirar las cabeceras Node-path y Copyfrom-path.

…
Node-path: spreadsheet/Makefile
…

Si las rutas tienen barras al principio, deberá incluir barras al principio en las rutas que quiera pasar a svndumpfilter include y svndumpfilter exclude ( si no las tienen, no debería incluirlas). Además, si su fichero de volcado tiene un uso inconsistente de estas barras por alguna razón, [21] probablemente debería normalizar esas rutas de tal manera que todas ellas tengan, o no tengan, barras al principio.

También las rutas copiadas pueden darle algunos problemas. Subversion soporta operaciones de copia en el repositorio, donde una nueva ruta se crea copiando otra ruta ya existente. Es posible que en algún momento de la vida de su repositorio, vd. haya copiado un fichero o un directorio desde alguna ubicación que svndumpfilter esté excluyendo a alguna otra que se esté incluyendo. Para conseguir que los datos volcados sean autosuficientes, svndumpfilter todavía necesita enseñar la adición de la nueva ruta—incluyendo los contenidos de los ficheros creados por la copia—y no representar dicha adición como una copia de una fuente que no existirá en el flujo filtrado de los datos de volcado. Pero debido a que el formato de volcado de repositorios Subversion sólo presenta lo modificado en cada revisión, los contenidos del origen de la copia podrían no estar disponibles para su lectura. Si sospechase que tiene alguna copia de este tipo en su repositorio, deberá repensar su grupo de rutas incluyentes y/o excluyentes.

svnshell.py

El árbol de código fuente de Subversion también trae una interfaz al repositorio tipo línea de comando. El script en Python svnshell.py (ubicado en tools/examples/ en el árbol de fuentes) usa las interfaces de lenguaje de Subversion (así que debe tenerlas correctamente compiladas e instaladas para que este script funcione) para conectar al repositorio y las librerías de sistema de ficheros.

Una vez iniciado, el programa se comporta de manera similar a una línea de comando, permitiéndole navegar a través de los directorios de su repositorio. Inicialmente se le posiciona en el directorio raíz de la revisión HEAD del repositorio, y se le muestra la línea de comando. Puede usar el comando help en cualquier momento para leer un listado de los comandos disponibles y su propósito.

$ svnshell.py /path/to/repos
<rev: 2 />$  help
Available commands:
  cat FILE     : dump the contents of FILE
  cd DIR       : change the current working directory to DIR
  exit         : exit the shell
  ls [PATH]    : list the contents of the current directory
  lstxns       : list the transactions available for browsing
  setrev REV   : set the current revision to browse
  settxn TXN   : set the current transaction to browse
  youngest     : list the youngest browsable revision number
<rev: 2 />$

Puede navegar por la estructura de directorios de su repositorio del mimo modo que lo haría con la línea de comando de Unix o Windows—usando el comando cd. En todo momento la línea de comando le mostrará qué revisión (prefijada por rev:) o transacción (prefijada por txn:) está examinando, y la ruta dentro de esa revisión o transacción. Puede cambiar la revisión o transacción actual con los comandos setrev y settxn respectivamente. Al igual que en una línea de comando de Unix, puede usar el comando ls para mostrar el contenido del directorio actual, y puede usar el comando cat para mostrar el contenido de un fichero.

Ejemplo 5.1. Usando svnshell para navegar por el repositorio

<rev: 2 />$ ls
   REV   AUTHOR  NODE-REV-ID     SIZE         DATE NAME
----------------------------------------------------------------------------
     1    sally <     2.0.1>          Nov 15 11:50 A/
     2    harry <     1.0.2>       56 Nov 19 08:19 iota
<rev: 2 />$ cd A
<rev: 2 /A>$ ls
   REV   AUTHOR  NODE-REV-ID     SIZE         DATE NAME
----------------------------------------------------------------------------
     1    sally <     4.0.1>          Nov 15 11:50 B/
     1    sally <     a.0.1>          Nov 15 11:50 C/
     1    sally <     b.0.1>          Nov 15 11:50 D/
     1    sally <     3.0.1>       23 Nov 15 11:50 mu
<rev: 2 /A>$ cd D/G 
<rev: 2 /A/D/G>$ ls
   REV   AUTHOR  NODE-REV-ID     SIZE         DATE NAME
----------------------------------------------------------------------------
     1    sally <     e.0.1>       23 Nov 15 11:50 pi
     1    sally <     f.0.1>       24 Nov 15 11:50 rho
     1    sally <     g.0.1>       24 Nov 15 11:50 tau
<rev: 2 /A>$ cd ../..
<rev: 2 />$ cat iota
This is the file 'iota'.
Added this text in revision 2.

<rev: 2 />$ setrev 1; cat iota
This is the file 'iota'.

<rev: 1 />$ exit
$


Como puede ver en el ejemplo anterior, puede indicar múltiples comandos en una única línea separados por puntos y comas. Además, la línea de comando entiende los conceptos de rutas relativas y absolutas, y entenderá los componentes de ruta especiales . y ...

El comando youngest muestra la versión más joven. Lo cual es útil para determinar el rango válido de revisiones que puede usar como argumentos del comando setrev—se le permite navegar por todas las revisiones (le recordamos que son nombradas por números) entre 0 y la más joven inclusive. Determinar la transacción navegable válida no es tan bonito. Use el comando lstxns para obtener un listado de las transacciones que puede consultar. El listado de las transacciones navegables es el mismo listado que el devuelto por svnadmin lstxns y el mismo listado que es válido para ser usado con la opción --transaction de svnlook.

Una vez haya acabado de usar la línea de comando, puede salir de manera limpia usando el comando exit. Alternativamente, puede mandar un carácter de fin de línea—Control-D (aunque algunas distribuciones de Python para Win32 usan en su lugar la convención de Windows de Control-Z).

Utilidades de la base de datos Berkeley

Toda la estructura y datos versionados de su sistema de ficheros viven en un conjunto de tablas de la base de datos Berkeley dentro del subdirectorio db de su repositorio. Este subdirectorio es un directorio de entorno de base de datos Berkeley habitual, y por lo tanto puede ser usado con cualquiera de las herramientas de la base de datos Berkeley (puede ver su documentación en la página web de SleepyCat, http://www.sleepycat.com/).

Para un uso diario de Subversion, estas herramientas son innecesarias. La mayor parte de la funcionalidad típicamente requerida por los repositorios Subversion ha sido duplicada en la herramienta svnadmin. Por ejemplo, svnadmin list-unused-dblogs y svnadmin list-dblogs realizan un conjunto de lo que proporciona el comando db_archive de Berkeley, y svnadmin recover engloba los casos de uso habituales de la utilidad db_recover.

Aunque todavía hay algunas herramientas de la base de datos Berkeley que puede encontrar útiles. Los programas db_dump y db_load escriben y leen, respectivamente, un fichero de formato propio que describe las claves y valores de la base de datos Berkeley. Dado que las bases de datos Berkeley no son portables entre arquitecturas diferentes, este formato es útil para transferir las bases de datos de una máquina a otra, sin importar su arquitectura o sistema operativo. Además, la utilidad db_stat proporciona información útil sobre el estado de su entorno de base de datos Berkeley, incluyendo estadísticas detalladas sobre los bloqueos y subsistemas de almacenamiento.

Limpieza del repositorio

En general su repositorio Subversion requerirá poca atención una vez lo haya configurado a su gusto. No obstante, hay ocasiones en las que podría necesitar la asistencia manual de un administrador. La utilidad svnadmin proporciona algunas funcionalidades provechosas para realizar tareas como

  • modificar mensajes de informes de cambios,

  • eliminar transacciones muertas,

  • recuperar repositorios tocados, y

  • migrar el contenido de un repositorio a otro.

Quizás el subcomando de svnadmin más usado sea setlog. Cuando una transacción es guardada en el repositorio y ascendida a categoría de revisión, el mensaje del informe de cambios asociado con esa nueva revisión (y proporcionado por el usuario) se almacena como una propiedad no versionada adjunta a la propia revisión. En otras palabras, el repositorio sólo recuerda el último valor de la propiedad, y descarta valores anteriores.

A veces el usuario cometerá errores (ortográficos o quizás información equivocada) en sus informes de cambios. Si configura el repositorio (usando los ganchos pre-revprop-change y post-revprop-change; vea “Scripts de enganche”) para permitir modificaciones sobre el mensaje de informe de cambios de revisiones existentes, entonces el usuario puede corregir su informe remotamente usando el comando propset del programa svn (vea Capítulo 9, Referencia completa de Subversion). No obstante, debido al peligro potencial de perder información para siempre, los repositorios Subversion se configuran por defecto para impedir cambios a propiedades no versionadas—excepto para el administrador.

Si un informe de cambios necesita ser cambiado por un administrador, puede ejecutar svnadmin setlog. Éste comando modifica el mensaje de informe de cambios (la propiedad svn:log) de una revisión concreta de un repositorio, leyendo el nuevo valor del fichero proporcionado.

$ echo "Here is the new, correct log message" > newlog.txt
$ svnadmin setlog myrepos newlog.txt -r 388

El comando svnadmin setlog a secas sigue estando limitado por las mismas protecciones que impiden a los usuarios remotos modificar propiedades no versionadas—los ganchos pre- y post-revprop-change siguen ejecutándose, y por lo tanto debe configurar el repositorio para permitir cambios de esta naturaleza. Pero un administrador puede saltarse estas protecciones pasando el parámetro --bypass-hooks al comando svnadmin setlog.

[Aviso]Aviso

Recuerde, no obstante, que al ignorar los ganchos, probablemente esté evitando acciones como notificaciones automáticas por correo informando de cambios a propiedades, sistemas de copias de seguridad que siguen los cambios de propiedades sin versionar, etc, etc. En otras palabras, sea muy cuidadoso con lo que vaya a cambiar, y cómo lo cambia.

Otro uso habitual de svnadmin es comprobar que en el repositorio no hay transacciones pendientes—posiblemente muertas. En el caso de que una transacción falle, ésta normalmente se anula. Es decir, la propia transacción y cualquier dato asociado con ella (y sólo con ella) es eliminada del repositorio. No obstante, ocasionalmente ocurre un fallo de tal modo que la limpieza de la transacción no se llega a realizar nunca. Esto podría pasar por varias razones: quizás la operación del cliente fue terminada de manera poco elegante por el usuario, quizás ocurrió un fallo de red durante la operación, etc. En cualquier caso, estas transacciones muertas sólo molestan en el repositorio y consumen recursos.

Puede usar el comando lstxns de svnadmin's para obtener un listado de los nombres de las transacciones actuales de relevancia.

$ svnadmin lstxns myrepos
19
3a1
a45
$

Cada elemento del listado mostrado puede ser usado con svnlook (y su opción --transaction) para determinar quién creó la transacción, cuándo fue creada, qué tipos de cambios fueron realizados en la transacción—en otras palabras, si la transacción es o nó una candidata segura para ser eliminada. En caso de serlo, el nombre de la transacción puede ser usado con svnadmin rmtxns, que realizará la limpieza de la transacción. ¡De hecho, el subcomando rmtxns puede usar directamente como entrada la salida de lstxns!

$ svnadmin rmtxns myrepos `svnadmin lstxns myrepos`
$

Si usa estos subcomandos de éste modo, debería considerar desactivar temporalmente el acceso al repositorio por parte de sus clientes. Así, nadie podrá comenzar una transacción legítima antes de que comience con la limpieza. Lo que viene a continuación es un poco de shell-scripting que puede generar rápidamente la información sobre cada transacción pendiente de su repositorio:

Ejemplo 5.2. txn-info.sh (Informe de transacciones pendientes)

#!/bin/sh

### Generate informational output for all outstanding transactions in
### a Subversion repository.

SVNADMIN=/usr/local/bin/svnadmin
SVNLOOK=/usr/local/bin/svnlook

REPOS="${1}"
if [ "x$REPOS" = x ] ; then
  echo "usage: $0 REPOS_PATH"
  exit
fi

for TXN in `${SVNADMIN} lstxns ${REPOS}`; do 
  echo "---[ Transaction ${TXN} ]-------------------------------------------"
  ${SVNLOOK} info "${REPOS}" --transaction "${TXN}"
done


Puede ejecutar el script anterior usando /ruta/a/txn-info.sh /ruta/al/repositorio. La salida es básicamente la concatenación de varios bloques de salida de svnlook info (vea “svnlook”), y tendrá el siguiente aspecto:

$ txn-info.sh myrepos
---[ Transaction 19 ]-------------------------------------------
sally
2001-09-04 11:57:19 -0500 (Tue, 04 Sep 2001)
0
---[ Transaction 3a1 ]-------------------------------------------
harry
2001-09-10 16:50:30 -0500 (Mon, 10 Sep 2001)
39
Trying to commit over a faulty network.
---[ Transaction a45 ]-------------------------------------------
sally
2001-09-12 11:09:28 -0500 (Wed, 12 Sep 2001)
0
$

Habitualmente, si descubre una transacción muerta que no tiene adjunto un informe de cambios, se trata del resultado de una operación fallida de actualización (o de estilo similar). Estas operaciones usan transacciones de Subversion internamente para emular el estado de una copia de trabajo local. Dado que éstas transacciones no tienen como propósito ser almacenadas, Subversion no requiere que tengan un mensaje de informe de cambios. Las transacciones que tienen informes de cambios son casi con total seguridad algún tipo de operación de almacenamiento fallida. Además, la fecha de una transacción puede proporcionar información interesante—por ejemplo, ¿qué probabilidades hay de que una operación que comenzase hace nueve meses todavía esté activa?

En resumen, no debe tomar a la ligera las decisión de limpieza de transacciones. Puede emplear varias fuentes de información—incluyendo los registros de acceso y errores de Apache, los registros de transacciones Subversion completadas con éxito, etc— durante la toma de decisiones. Finalmente, un administrador siempre puede comunicarse con el autor de una transacción aparentemente muerta (vía email, por ejemplo) para verificar que la transacción está, de hecho, en un estado zombi.

Gestionando el espacio de almacenamiento

Mientras que el coste del almacenamiento ha caído de manera increíble durante los últimos años, el espacio en disco sigue siendo un motivo de preocupación válido para los administradores que buscan versionar grandes cantidades de datos. Cada byte adicional consumido por el repositorio en vivo es un byte que debe ser almacenado en una copia de seguridad, quizás múltiples veces como parte de una política de rotado periódico de copias de seguridad. Dado que el principal medio de almacenamiento de un repositorio de Subversion es un complejo sistema de bases de datos, es útil saber qué piezas deben permanecer en el servidor activo, cuáles deben ser almacenadas en una copia de seguridad, y cuáles pueden ser eliminadas.

Hasta hace poco, el mayor culpable en cuanto al uso de disco duro por parte de un repositorio de Subversion eran los fichero de registro que usaba la base de datos Berkeley para realizar sus pre-escrituras antes de modificar realmente los ficheros de la base de datos. Éstos ficheros capturan todas las acciones tomadas durante la ruta que modifica el estado de la base de datos— si los ficheros de la base de datos reflejan en cualquier momento un estado determinado, los ficheros de registro contienen todos los cambios entre diferentes estados. Como tales, pueden comenzar a acumularse rápidamente.

Afortunadamente, y comenzando con la versión 4.2 de la base de datos Berkeley, el entorno de la base de datos tiene la capacidad para borrar sus propios ficheros de registro obsoletos sin necesidad de procedimientos externos. Cualquier repositorio creado usando svnadmin, el cual se compila contra la versión 4.2 o superior de la base de datos Berkeley, será configurado automáticamente para borrar sus ficheros de registros. Si no quiere activar esta característica, simplemente pase la opción --bdb-log-keep al comando svnadmin create. Si olvida hacer esto, o cambia de opinión posteriormente, puede editar el fichero DB_CONFIG ubicado en el directorio db de su repositorio, comentar la línea que contiene la directiva set_flags DB_LOG_AUTOREMOVE, y entonces ejecutar svnadmin recover sobre su repositorio para forzar los cambios de configuración. Vea “Configuración de la base de datos Berkeley” para más información sobre la configuración de la base de datos.

Sin mecanismo de algún tipo que elimine los ficheros de registro, éstos se acumularán a medida que use el repositorio. Esto es en cierto sentido una característica del sistema de base de datos—debería ser capaz de recrear su base de datos por completo sin usar nada más que los ficheros de registro, así que éstos pueden ser útiles en recuperaciones de la base de datos tras una catástrofe. Pero habitualmente querrá archivar los ficheros de registro que la base de datos Berkeley ya no usa, y entonces eliminarlos del disco para conservar espacio. Use el comando svnadmin list-unused-dblogs para obtener un listado de los ficheros de registro no usados:

$ svnadmin list-unused-dblogs /path/to/repos
/path/to/repos/log.0000000031
/path/to/repos/log.0000000032
/path/to/repos/log.0000000033

$ svnadmin list-unused-dblogs /path/to/repos | xargs rm
## disk space reclaimed!

Para mantener el tamaño del repositorio tan pequeño como sea posible, Subversion usa la deltificación (o bien, almacenamiento deltificado) dentro del propio repositorio. La deltificación conlleva codificar la representación de un bloque de datos como una colección de diferencias contra otro bloque de datos. Si dos piezas son muy similares, la deltificación proporcionará ganancias de almacenamiento para los bloques deltificados—en lugar de tomar tanto espacio como el tamaño de los datos originales, tomará justo lo necesario para decir, tengo casi el mismo aspecto de este otro bloque de datos de ahí, excepto por las siguientes diferencias. De manera más específica, cada vez que una nueva versión de un fichero es enviada al repositorio, Subversion codifica la versión anterior (en realidad, varias versiones anteriores) como un delta contra la nueva versión. El resultado es que la mayor parte de los datos que suelen ocupar espacio—en concreto, los contenidos de los ficheros versionados—se almacenan con un tamaño mucho menor que la representación original completa (fulltext) de esos datos.

[Nota]Nota

Dado que todos los datos del repositorio Subversion sujetos a la deltificación se almacenan en un único fichero de base de datos Berkeley, reducir el tamaño de los valores almacenados no reducirá necesariamente el tamaño de éste fichero. La base de datos Berkeley no obstante mantendrá un registro interno de áreas internas sin usar de la base de datos, y usará esas áreas primero antes de volver a redimensionar el tamaño del fichero de la base de datos. Así que aunque la deltificación no produce ganancias de espacio inmediatas, puede enlentecer drásticamente el crecimiento futuro de la base de datos.

Restauración del repositorio

Para poder proteger sus datos en el repositorio, el motor de la base de datos usa un mecanismo de bloqueo. Éste se asegura de que no hay porciones de la base de datos modificadas simultáneamente por múltiples clientes de la base de datos, y que cada proceso ve los datos en un estado correcto cuando éstos son leídos. Cuando un proceso necesita cambiar algo en la base de datos, primero comprueba la existencia de un bloqueo en los datos objetivo. Si los datos no están bloqueados, el proceso los bloquea, hace los cambios que desea realizar, y desbloquea los datos. Esto obliga a otros procesos a esperar hasta que se elimine el bloqueo antes de que se les permita continuar accediendo a esa sección de la base de datos.

A lo largo del uso de su repositorio Subversion, los errores fatales (como quedarse sin espacio en disco duro o sin memoria) o las interrupciones pueden evitar que un proceso tenga la oportunidad de eliminar los bloqueos que inició sobre la base de datos. El resultado es que el motor de la base de datos del sistema se wedged. Cuando esto ocurre, cualquier intento de acceso al repositorio se bloqueará de manera indefinida (dado que cada nuevo cliente estará esperando a que los bloqueos desaparezcan—lo cual no va a ocurrir).

Primero, si esto ocurre en su repositorio, no se preocupe. El sistema de ficheros de Subversion tiene la ventaja de usar transacciones, puntos de control y ficheros de registro pre-escritura para asegurarse de que sólo los eventos más catastróficos [22] pueden destruir de manera permanente el entorno de la base de datos. Un administrador de repositorio suficientemente paranoico estará haciendo copias de seguridad del repositorio en otra máquina separada de alguna manera, pero todavía no es el momento de llamarle para que realice la restauración de una cinta.

En segundo lugar, use la siguiente receta para intentar unwedge su repositorio:

  1. Asegúrese de que no hay procesos accediendo (o intentando acceder) al repositorio. Para repositorios en red, esto también significa tener que desconectar el servidor HTTP Apache.

  2. Conviértase en el usuario que posee y gestiona el repositorio. Esto es importante, dado que recuperar el repositorio poseyendo el usuario erróneo puede alterar los permisos de los ficheros del repositorio de tal modo que será inaccesible incluso después de haberlo unwedged.

  3. Ejecute el comando svnadmin recover /ruta/al/repositorio. Debería ver algo como esto:

    Please wait; recovering the repository may take some time...
    
    Recovery completed.
    The latest repos revision is 19.
    

    Este comando puede tardar muchos minutos en completar su tarea.

  4. Rearranque el servidor Subversion.

Este procedimiento corrige casi todos los casos de bloqueos del repositorio. Asegúrese de ejecutar este comando como el usuario a quien le pertenece la base de datos y la gestiona, no basta con ser root. Parte del proceso de recuperación puede conllevar la regeneración de varios ficheros de bases de datos (por ejemplo, regiones de memoria compartidas). Al realizar la recuperación como root, éstos ficheros tendrán sus permisos, lo cual significa que incluso cuando recupere la conexión con el repositorio, los usuarios normales no serán capaces de acceder a él.

Si el procedimiento anterior, por alguna razón, no consigue desbloquear su repositorio, debería hacer dos cosas. En primer lugar, mueva su repositorio estropeado a otro lugar y recupere la última copia de seguridad que tenga del mismo. Entonces, envíe un correo electrónico a la lista de usuarios de Subversion (en ) describiendo su problema con todo detalle. Para los desarrolladores de Subversion mantener la integridad de los datos es una prioridad extremadamente alta.

Migrando un repositorio

Un sistema de ficheros de Subversion almacena sus datos repartidos por varias tablas de bases de datos de una forma generalmente comprensible (y sólo de interés) para los propios desarrolladores de Subversion. No obstante, pueden darse las circunstancias adecuadas que requieran que todos los datos, o un subconjunto de los mismos, sean recopilados en un fichero único, portable, y de formato simple. Subversion proporciona tal mecanismo implementado como la pareja de subcomandos de svnadmin: dump y load.

La razón más común para volcar y recargar un repositorio de Subversion es por cambios en el propio Subversion. A medida que Subversion madura, hay momentos en los que hace falta realizar cambios al esquema del motor de la base de datos que hacen a Subversion incompatible con versiones anteriores de su repositorio. La acción recomendada para actualizarse atravesando una de estas barreras de incompatiblidad es relativamente simple:

  1. Usando su actual versión de svnadmin, vuelque su repositorio a ficheros de volcado.

  2. Actualícese a la nueva versión de Subversion.

  3. Mueva sus viejos repositorios a otra parte, y cree nuevos repositorios vacíos en su lugar usando su nuevo svnadmin.

  4. Otra vez con su svnadmin nuevo, cargue sus ficheros de volcado en sus repositorios recién creados respectivamente.

  5. Finalmente, asegúrese de copiar en los nuevos repositorios cualquier personalización realizada en los antiguos, incluyendo los ficheros DB_CONFIG y los ficheros de ganchos. Le recomendamos que preste atención a las notas de lanzamiento de la nueva versión de Subversion para comprobar si algún cambio desde su última actualización afecta a esos ficheros de ganchos u opciones de configuración.

svnadmin dump generará en su salida un rango de revisiones del repositorio usando el formato propio de Subversion para volcar el sistema de ficheros. El volcado formateado será enviado al flujo estándar de salida, mientras que los mensajes informativos son enviados al flujo estándar de errores. Esto le permite redirigir el flujo de salida estándar a un fichero mientras observa el estado del volcado en su terminal. Por ejemplo:

$ svnlook youngest myrepos
26
$ svnadmin dump myrepos > dumpfile
* Dumped revision 0.
* Dumped revision 1.
* Dumped revision 2.
…
* Dumped revision 25.
* Dumped revision 26.

Al final del proceso tendrá un único fichero (dumpfile según el ejemplo anterior) que contendrá todos los datos almacenados en su repositorio en el rango de revisiones especificado. Tenga en cuenta que svnadmin dump lee los árboles de versiones del repositorio igual que cualquier otro proceso lector (por ejemplo, svn checkout). Así que es seguro ejecutar este comando en cualquier momento.

El otro subcomando de la pareja, svnadmin load, procesa el flujo estándar de entrada como un fichero de volcado de repositorio de Subversion, y reproduce de manera efectiva esas revisiones volcadas en el repositorio destino durante la operación. También proporciona mensajes informativos, esta vez usando el flujo de salida estándar:

$ svnadmin load newrepos < dumpfile
<<< Started new txn, based on original revision 1
     * adding path : A ... done.
     * adding path : A/B ... done.
     …
------- Committed new rev 1 (loaded from original rev 1) >>>

<<< Started new txn, based on original revision 2
     * editing path : A/mu ... done.
     * editing path : A/D/G/rho ... done.

------- Committed new rev 2 (loaded from original rev 2) >>>

…

<<< Started new txn, based on original revision 25
     * editing path : A/D/gamma ... done.

------- Committed new rev 25 (loaded from original rev 25) >>>

<<< Started new txn, based on original revision 26
     * adding path : A/Z/zeta ... done.
     * editing path : A/mu ... done.

------- Committed new rev 26 (loaded from original rev 26) >>>

Dado que svnadmin usa la los flujos estándar de entrada y salida para el volcado de repositorio y el proceso de carga, las personas que se sientan atrevidas pueden probar algo como ésto (quizás usando diferentes versiones de svnadmin en cada lado de la tubería):

$ svnadmin create newrepos
$ svnadmin dump myrepos | svnadmin load newrepos

Hemos mencionado anteriormente que svnadmin dump genera un rango de revisiones. Use la opción --revision para indicar que quiere volcar una revisión concreta, o un rango. Si omite esta opción, todas las revisiones existentes del repositorio serán volcadas.

$ svnadmin dump myrepos --revision 23 > rev-23.dumpfile
$ svnadmin dump myrepos --revision 100:200 > revs-100-200.dumpfile

A medida que Subversion vuelca cada revisión nueva, muestra la información justa para permitir a un cargador futuro recrear esa revisión basándose en la anterior. En otras palabras, para cualquier revisión dada en el fichero de volcado, sólo aparecerán los elementos que cambiaron en ella. La única excepción a esta regla es la primera revisión volcada por el comando svnadmin dump actual.

Por defecto, Subversion no expresará la primera revisión volcada como las meras diferencias aplicables sobre la revisión anterior. Para empezar, ¡es que no hay revisión anterior alguna en el fichero de volcado! Y en segundo lugar, Subversion no puede saber el estado del repositorio en el cual los datos volcados serán cargados (si este hecho llega a producirse). Para asegurarse de que la salida de cada ejecución de svnadmin dump es autosuficiente, la primera revisión volcada es por defecto una representación completa de cada directorio, fichero, y propiedad en esa revisión del repositorio.

No obstante, puede cambiar este comportamiento por defecto. Si añade la opción --incremental cuando vuelque su repositorio, svnadmin comparará la primera versión volcada del repositorio contra la revisión anterior del repositorio, igual que trata cualquier otra revisión volcada. Entonces mostrará la primera revisión exactamente igual que el resto de las revisiones del rango de volcado—mencionando únicamente los cambios ocurridos en esa revisión. El beneficio de esto es que puede crear varios ficheros de volcado menores que pueden ser cargados secuencialmente, en lugar de un único fichero grande, siguiendo estos pasos:

$ svnadmin dump myrepos --revision 0:1000 > dumpfile1
$ svnadmin dump myrepos --revision 1001:2000 --incremental > dumpfile2
$ svnadmin dump myrepos --revision 2001:3000 --incremental > dumpfile3

Estos ficheros de volcado podrían ser cargados en un nuevo repositorio usando la siguiente secuencia de comandos:

$ svnadmin load newrepos < dumpfile1
$ svnadmin load newrepos < dumpfile2

$ svnadmin load newrepos < dumpfile3

Otro truco interesante que puede realizar con la opción --incremental consiste en añadir a un fichero de volcado existente un nuevo rango de revisiones volcadas. Por ejemplo, digamos que tiene un gancho post-commit que simplemente añade el volcado del repositorio de la versión única que despertó la ejecución del gancho. O quizás tenga un script que se ejecute por las noches para añadir los datos del fichero de volcado de todas las revisiones que fueron añadidas al repositorio desde la última vez que fue ejecutado el script. Usados de este modo, los comandos svnadmin dump y load pueden ser valiosos aliados para realizar copias de seguridad de su repositorio a lo largo del tiempo en caso de un fallo de sistema o algún otro evento catastrófico.

El formato de volcado también puede ser usado para fusionar los contenidos de varios repositorios diferentes en uno único. Al usar la opción --parent-dir de svnadmin load, puede especificar un nuevo directorio virtual para el proceso de carga. Lo cual significa que si tiene ficheros de volcados para tres repositorios, digamos calc-dumpfile, cal-dumpfile, y ss-dumpfile, puede crear primero un nuevo repositorio para almacenarlos todos:

$ svnadmin create /path/to/projects
$

Entonces, cree nuevos directorios en el repositorio que encapsularán los contenidos de cada uno de los tres repositorios anteriores:

$ svn mkdir -m "Initial project roots" \
      file:///path/to/projects/calc \
      file:///path/to/projects/calendar \
      file:///path/to/projects/spreadsheet
Committed revision 1.
$ 

Por último, cargue los ficheros de volcado individuales en sus ubicaciones respectivas del nuevo repositorio:

$ svnadmin load /path/to/projects --parent-dir calc < calc-dumpfile
…
$ svnadmin load /path/to/projects --parent-dir calendar < cal-dumpfile
…
$ svnadmin load /path/to/projects --parent-dir spreadsheet < ss-dumpfile
…
$

Mencionaremos un último uso del formato de volcado de repositorio de Subversion—conversión desde un formato de almacenamiento diferente o sistema de control de versiones. Dado que el fichero de volcado es, en su mayor parte, legible por un humano, [23] debería ser relativamente sencillo describir un conjunto genérico de cambios—cada uno de ellos debería ser descrito como una nueva revisión—usando este formato de fichero. De hecho, la utilidad cvs2svn.py (vea “Convirtiendo un repositorio de CVS a Subversion”) usa el formato de volcado para representar el contenido de un repositorio CVS para que sus contenidos pueden ser trasladados a un repositorio Subversion.

Copias de seguridad del repositorio

A pesar de numerosos avances tecnológicos desde el nacimiento de la computadora moderna, hay una cosa que se entiende con cristalina claridad—a veces, las cosas van muy, pero que muy mal. Pérdidas de corriente, fallos de conectividad en la red, RAM corrupta y discos duros estropeados son un mero avance del mal que el Destino puede desatar sobre incluso el administrador más concienzudo. Y por lo tanto, llegamos a un tema muy importante—cómo hacer copias de seguridad de los datos de su repositorio.

En general hay dos tipos de métodos de copia de seguridad disponibles para los administradores de repositorios de Subversion—incrementales y completos. Hemos discutido en una sección anterior de este capítulo cómo usar svnadmin dump --incremental para realizar copias de seguridad incrementales (vea “Migrando un repositorio”). Esencialmente, la idea es realizar en un momento dado copias de seguridad de los cambios realizados desde la última vez que se hizo la copia de seguridad anterior.

Una copia de seguridad completa del repositorio es casi una copia literal del directorio que contiene todo el repositorio (lo cual incluye el entorno de la base de datos Berkeley). Ahora, a no ser que desactive temporalmente todo el acceso a su repositorio, ejecutar un simple copiado recursivo del directorio conlleva el riesgo de generar una copia de seguridad errónea, dado que alguien podría estar modificando la base de datos.

Afortunadamente, los documentos de la base de datos Berkeley de Sleepycat describen un orden concreto en el cual se pueden copiar los ficheros de la base de datos de tal modo que se garantice una copia de seguridad válida. Y aun mejor, usted no necesita implementar ese algoritmo por su cuenta, porque el equipo de desarrolladores de Subversion ya lo ha hecho. El script hot-backup.py que puede encontrar en el directorio tools/backup/ de la distribución de código fuente de Subversion. Usando como parámetros la ruta al repositorio y a la ubicación de la copia de seguridad, hot-backup.py—que realmente no es más que un envoltorio más inteligente sobre el comando svnadmin hotcopy— realizará los pasos necesarios para realizar una copia de seguridad en caliente del repositorio—sin necesitar en absoluto tener que impedir el acceso al mismo—y después borrará los ficheros de registro sin usar de Berkeley de su repositorio activo actual.

Incluso si también dispone de una copia de seguridad incremental, quizás quiera ejecutar también este programa de manera habitual. Por ejemplo, podría considerar añadir hot-backup.py a un programa de planificación (como por ejemplo cron en sistemas Unix). O, si prefiere soluciones de copias de seguridad con alta granularidad, podría hacer que su gancho post-commit llame hot-backup.py (vea “Scripts de enganche”), lo cual provocará una nueva copia de seguridad de su repositorio con cada nueva versión. Simplemente añada el script hooks/post-commit a su directorio activo del repositorio:

(cd /path/to/hook/scripts; ./hot-backup.py ${REPOS} /path/to/backups &)

La copia de seguridad resultante es un repositorio de Subversion completamente funcional, capaz de reemplazas su repositorio activo en caso de que algo vaya muy mal.

Ambos métodos de copias de seguridad son beneficiosos. La copia de seguridad completa es de lejos la más sencilla, la que siempre resultará ser una réplica de su repositorio perfectamente funcional. Le recordamos que esto significa que si algo malo le ocurre a su repositorio activo, puede recuperar su copia de seguridad con un simple copiado recursivo de directorios. Desafortunadamente, si está manteniendo múltiples copias de seguridad de su repositorio, estas copias completas acabarán ocupando la misma cantidad de espacio de disco que su repositorio activo.

Las copias de seguridad incrementales usando el formato de volcado del repositorio son excelentes para tener a mano si el esquema de la base de datos cambia entre sucesivas versiones del propio Subversion. Dado que para actualizar su repositorio al nuevo esquema es necesario que haga un volcado y carga completa del repositorio, es muy conveniente tener la mitad de ese proceso (la parte del volcado) realizada. Desafortunadamente, la creación—y recuperación—de copias de seguridad incrementales tarda mucho más, dado que cada revisión es reproducida en el fichero de volcado o el repositorio.

En ambos escenarios, los administradores del repositorio necesitan estar atentos a cómo las modificaciones de propiedades de revisión no versionadas afectan a sus copias de seguridad. Dado que estos cambios no generan por sí mismos nuevas revisiones, no activarán ganchos post-commit, y quizás ni siquiera los ganchos pre-revprop-change y post-revprop-change. [24] Y dado que puede cambiar las propiedades de revisión sin respetar su orden cronológico—puede cambiar cualquier propiedad de revisión en cualquier momento—una copia de seguridad incremental de las últimas pocas revisiones quizás no capture la modificación de una propiedad de revisión que fue incluida como parte de una copia de seguridad anterior.

Hablando de manera general, sólo los realmente paranoicos necesitarían hacer una copia de seguridad de su repositorio completo, digamos que, cada vez que se realiza un cambio. No obstante, asumiendo que un repositorio determinado tiene funcionando otros mecanismos de redundancia con relativa granularidad (como correos electrónicos por modificación), una copia de seguridad en caliente de la base de datos sería algo que un administrador de repositorio querría incluir como parte de una copia de seguridad de sistema nocturna. Para la mayoría de los repositorios, los correos archivados con cambios son suficiente fuente de redundancia, al menos para los últimos cambios. Pero son sus datos—protéjalos tanto como desee.

A menudo, la mejor estrategia para realizar copias de seguridad es aquella que diversifique. Puede aprovechar combinaciones de copias de seguridad completas e incrementales, y adicionalmente archivos de correo con los cambios. Los desarrolladores de Subversion, por ejemplo, realizan una copia de seguridad del repositorio de código fuente tras cada nueva revisión, y mantienen un archivo de todas las notificaciones por correo electrónico de cambios y modificaciones de propiedades. Su solución podría ser similar, pero debería estar orientada a las necesidades y ese delicado balance entre la conveniencia y la paranoia. Y aunque nada de todo esto le ayude a salvar su hardware del puño de hierro del Destino, [25] ciertamente debería ayudarle a recuperarse tras uno de esos momentos.

Añadiendo proyectos

Una vez haya creado y configurado su repositorio, todo lo que queda es comenzar a usarlo. Si tiene una colección de datos existentes listos para ser puestos bajo control de versiones, es muy probable que desee usar el subcomando import del programa cliente svn. No obstante, antes de hacer esto debería considerar con cuidado los planes a largo plazo de su repositorio. En esta sección le ofreceremos algunos consejos sobre la planificación del esquema de su repositorio, y cómo reorganizar sus datos de acuerdo con éste.

Escogiendo el esquema de repositorio

Aunque Subversion le permite desplazar los ficheros y directorios versionados sin pérdida de información de cualquier tipo, al hacerlo puede entorpecer el flujo de trabajo de aquellos quienes acceden al repositorio con frecuencia y esperan encontrar algunas cosas en ubicaciones determinadas. Intente asomarse un poco al futuro; planifique antes de poner sus datos bajo control de versiones. Al organizar el contenido de sus repositorios de una manera efectiva la primera vez, podrá evitar un montón de futuros dolores de cabeza.

Hay algunas cosas que deberá considerar al configurar repositorios con Subversion. Asumamos que como administrador de repositorio será responsable de dar soporte del sistema de control de versiones a varios proyectos. La primera decisión consiste en decidir si usará un único repositorio para múltiples proyectos, o le dará un repositorio a cada proyecto, o una mezcla de cambos.

Hay múltiples beneficios en el uso de un único repositorio para múltiples proyectos, siendo el más obvio evitar duplicar el trabajo de mantenimiento. Tener un único repositorio significa que sólo hay un conjunto de scripts de enganche, un elemento del que hacer rutinariamente copias de seguridad, un único volcado y recarga si Subversion lanza alguna nueva versión incompatible, y así con todo. Además, puede mover datos entre proyectos muy fácilmente, y sin perder ninguna información histórica de versionado.

Las desventajas de usar un único repositorio son que los diferentes proyectos pueden tener diferentes listas de correo que reciben notificaciones de los cambios realizados o diferentes requisitos de autenticación y autorización. Además, recuerde que Subversion usa números de revisión globales por repositorio. Hay gente a la que no le gusta el hecho de que incluso sin haber realizado cambios recientes en sus proyectos, el número de la revisión más reciente sigue subiendo porque otros proyectos siguen añadiendo revisiones de manera activa.

También puede diseñar una solución híbrida. Por ejemplo, los proyectos pueden ser agrupados en función de las relaciones que tengan entre ellos. Quizás tenga varios repositorios con un puñado de proyectos en cada uno. De ese modo, los proyectos más propensos a compartir sus datos pueden hacerlo de manera sencilla, y a medida que se añaden revisiones al repositorio, al menos los desarrolladores del mismo saben que están relacionadas remotamente con todo aquél que usa ese repositorio.

Tras decidir cómo desea organizar sus proyectos con respecto a los repositorios, probablemente querrá decidir las jerarquías de los directorios almacenados en ellos. Dado que Subversion usa copias de directorio normales para hacer ramas y etiquetas (vea Capítulo 4, Crear ramas y fusionarlas), la comunidad de Subversion recomienda que elija una ubicación en el repositorio para cada raíz de proyecto— el directorio superior que contiene todos los datos relacionados con el mismo—y cree tres nuevos subdirectorios en esa raíz: trunk, que sería el directorio bajo el cual se lleva el desarrollo principal del proyecto; branches, que sería el directorio que alojaría las diferentes ramas con nombre de la línea principal de desarrollo; tags, que es el directorio donde las ramas son creadas, y quizás destruidas, pero nunca modificadas.

Por ejemplo, su repositorio podría tener este aspecto:

/
   calc/
      trunk/
      tags/
      branches/
   calendar/
      trunk/
      tags/
      branches/
   spreadsheet/
      trunk/
      tags/
      branches/
   …

Tenga en cuenta que no importa dónde está la raíz de cada proyecto en su repositorio. Si sólo tiene un proyecto por repositorio, el lugar lógico para poner la raíz de cada proyecto es en la raíz del repositorio respectivo. Si tiene múltiples proyectos, quizás desee organizarlos en grupos dentro del repositorio, quizás ubicando proyectos con fines similares o código compartido en el mismo subdirectorio, o quizás simplemente prefiera agruparlos alfabéticamente. Tal esquema podría tener el siguiente aspecto:

/
   utils/
      calc/
         trunk/
         tags/
         branches/
      calendar/
         trunk/
         tags/
         branches/
      …
   office/
      spreadsheet/
         trunk/
         tags/
         branches/
      …

Despliegue su repositorio de la manera que crea más conveniente. Subversion no espera ni obliga un esquema de directorios concreto—a sus ojos, un directorio es un directorio es un directorio. Por último, debería elegir la organización del repositorio de manera que satisfaga las necesidades de aquellos que trabajen en los proyectos ahí alojados.

Creando el esquema, importando los datos iniciales

Tras decidir cómo organizar los proyectos de su repositorio, probablemente quiera poblarlo con ese esquema y los datos iniciales de los proyectos. Hay varios modos de hacer esto en Subversion. Podría usar el comando svn mkdir (vea Capítulo 9, Referencia completa de Subversion) para crear cada directorio en su esquema esquelético, uno tras otro. Una forma más rápida de realizar esta misma tarea es usar el comando svn import (vea svn import). Al crear el esquema en una ubicación temporal de su disco, puede importarlo por completo en un único cambio:

$ mkdir tmpdir
$ cd tmpdir
$ mkdir projectA
$ mkdir projectA/trunk
$ mkdir projectA/branches
$ mkdir projectA/tags
$ mkdir projectB
$ mkdir projectB/trunk
$ mkdir projectB/branches
$ mkdir projectB/tags
…
$ svn import . file:///path/to/repos --message 'Initial repository layout'
Adding         projectA
Adding         projectA/trunk
Adding         projectA/branches
Adding         projectA/tags
Adding         projectB
Adding         projectB/trunk
Adding         projectB/branches
Adding         projectB/tags
…
Committed revision 1.
$ cd ..
$ rm -rf tmpdir
$

Puede comprobar los resultados de la operación de importado ejecutando el comando svn list:

$ svn list --verbose file:///path/to/repos
      1 harry               May 08 21:48 projectA/
      1 harry               May 08 21:48 projectB/
…
$

Una vez tenga la estructura de su esquema en lugar, puede comenzar a importar los del proyecto en su repositorio, si es que tales datos existen. De nuevo, hay varios métodos para hacer esto. Puede usar el comando svn import. Podría obtener una copia de trabajo local de su reciente repositorio, mover y organizar los datos de su proyecto dentro, y usar los comandos svn add y svn commit. Pero una vez comenzamos a hablar de tales cosas, ya no estamos discutiendo la administración del repositorio. Si no está familiarizado con el programa cliente svn, vea Capítulo 3, Recorrido guiado.

Sumario

A estas alturas debería tener un conocimiento básico sobre cómo crear, configurar, y mantener repositorios de Subversion. Le hemos introducido a varias herramientas que le asistirán con estas tareas. A lo largo del capítulo hemos avisado sobre los posibles problemas, y proporcionado sugerencias para evitarlos.

Todo lo que queda es decidir qué datos excitantes almacenará en su repositorio, y finalmente, cómo hacerlos disponibles por red. El siguiente capítulo está dedicado a todo lo relacionado con la red.




[19] Puede sonar realmente prestigioso y , pero sólo estamos hablando de cualquier personal que esté interesada en el misterioso reino más allá de la copia de trabajo, donde los datos de todos

[20] Esto, de todas maneras, es una característica, y no un error.

[21] Mientras svnadmin dump tiene una política consistente de barras—no incluirlas— otros programas que generan datos de volcado puede no ser tan consistentes.

[22] Ej: disco duro + enorme electroimán = desastre.

[23] El formato de volcado de repositorio de Subversion recuerda al formato RFC-822, el mismo tipo de formato usado para la mayor parte del correo electrónico.

[24] svnadmin setlog puede ser invocado de un modo que evita por completo el mecanismo de ganchos.

[25] Ya sabe—el término genérico para sus erráticos dedos.

Capítulo 6. Configuración del servidor

Un repositorio Subversion puede ser accedido simultáneamente por clientes que son ejecutados en la misma máquina en la que reside el repositorio usando el método file:///. Pero una típica configuración de Subversion implica a un servidor al que acceden clientes desde sus computadoras en el entorno de una oficina— o quizás, alrededor del mundo.

Esta sección describe como publicar su repositorio Subversion para ser accedido por clientes remotos. Cubriremos los tipos de mecanismos de servidor disponibles actualmente , debatiendo la configuración y uso de cada uno de éstos. Después de que haya leído está sección, usted debería estar capacitado para decidir que tipo de configuración de red es la apropiada para sus necesidades, y comprender como realizar la instalación en su computadora.

Introducción

Subversion cuenta con un diseño de la capa de red abstracto. Esto significa que el repositorio puede ser accedido por cualquier clase de proceso de servidor y el API[26] de acceso al repositorio cliente permite a los programadores escribir componentes que se comuniquen a través de su protocolo de red pertinente. En teoría, Subversion puede lucir una infinidad de implementaciones de red. En la práctica, sólo existen dos tipos de servidores al momento de escribirse este documento.

Apache es un servidor web extremadamente popular; utilizando el módulo mod_dav_svn, Apache puede acceder a un repositorio y hacerlo disponible a los clientes a través del protocolo WebDAV/DeltaV, el cuál es una extensión del HTTP. En la otra esquina se encuentra svnserve: un pequeño programa servidor independiente que se comunica a través de un protocolo personalizado con los clientes. La Tabla 6-1 presenta una comparación de ambos tipos de servidores.

Observe que Subversion, siendo un proyecto de código abierto, no apoya oficialmente a ningún servidor como opción primaria u oficial. Ninguna de las implementaciones de red es considerada como un ciudadano de segunda clase; cada servidor posee ventajas y desventajas distintivas. De hecho, es posible ejecutar los diferentes servidores en paralelo, cada uno accediendo a sus repositorios a su modo y sin obstaculizar al otro (ver “Ofrecer múltiples métodos de acceso al repositorio”). A continuación una breve visión general y comparación de los servidores Subversion disponibles—es tiempo de que elija la instalación que sea mejor para usted y sus usuarios.

Tabla 6.1. Comparación de tipos de servidores de red

CaracterísticaApache + mod_dav_svnsvnserve
Opciones de autenticaciónAutorización básica HTTP(S), Certificados X.509, LDAP, NTLM, o cualquier otro mecanismo disponible por Apache httpdCRAM-MD5 o SSH
Opciones de cuenta de usuarioarchivo privado de usuarios 'users'archivo privado de usuarios 'users', o cuentas (SSH) existentes en el sistema
Opciones de autorizaciónacceso general de lectura/escritura, o control de acceso por directoriosacceso general de lectura/escritura
Cifradoa través de SSL (opcional)a través de un túnel SSH (opcional)
Interoperabilidaddisponible parcialmente por otros clientes WebDAVno posee interoperabilidad
Ver a través de la websoporte integrado limitado, o a través de herramientas de terceras partes tales como ViewCVSa través de herramientas de terceras partes tales como ViewCVS
Velocidadalgo lentoalgo rápido
Configuración inicialalgo complejabastante simple


Modelo de red

En esta sección se debate en términos generales como interactúan el cliente y servidor Subversión entre sí, independientemente de la implementación de red que esté utilizando. A su término, usted comprenderá como se comporta un servidor y las diferentes formas en que un cliente puede ser configurado para responder.

Solicitudes y Respuestas

El cliente Subversion insume la mayor parte de su tiempo manejando copias de trabajo. No obstante, cuando necesita información del repositorio efectúa una solicitud a través de la red, y el servidor responde apropiadamente. Los detalles del protocolo de red son ocultados por el usuario; el cliente intenta acceder a una URL, y dependiendo del esquema URL, utiliza un protocolo particular para contactar al servidor (ver URLs del repositorio). Los usuarios pueden ejecutar svn --version para ver que esquemas URL y protocolos que comprende el cliente.

Cuando el proceso del servidor recibe una solicitud del cliente, típicamente demanda que el cliente se identifique. Este solicita la autenticación al cliente , y el cliente responde proporcionando las credenciales al servidor. Una vez completado ésto, el servidor responde con la información que el cliente solicitó originalmente. Observe que este método se diferencia de sistemas como CVS, dónde el cliente por cuenta propia ofrece las credenciales (de acceso al sistema), antes de realizar una solicitud. En Subversion, el servidor exige tomar las credenciales a través de la negociación con el cliente en el momento apropiado, en lugar de que el cliente las impulse. Esto asegura operaciones más elegantes. Por ejemplo, si se configura un servidor para que permita acceso de lectura al repositorio a todo el mundo, nunca demandará a un cliente que se autentique cuando intente realizar un svn checkout.

Si la solicitud de red del cliente escribe datos nuevos en el repositorio (por ejemplo: svn commit), un nuevo árbol de revisión es creado. Si la solicitud del cliente fue autenticada, el nombre del usuario es guardado como valor de la propiedad svn:author en la nueva revisión (ver “Propiedades no versionadas”). Si el cliente no fue autenticado (en otras palabras, si el servidor nunca promulgó la autenticación), la propiedad svn:author de la revisión está en blanco.[27]

Client Credentials Caching

Muchos servidores son configurados para requerir autenticación por cada solicitud que reciban. Esto puede convertirse en una gran molestia para los usuarios, quienes son forzados a escribir sus contraseñas una y otra vez.

Afortunadamente, el cliente Subversion ha remediado ésto: un sistema incorporado para ocultar las credenciales en el disco. Por defecto, en el momento en que el cliente se autentica satisfactoriamente con un servidor, éste guarda las credenciales en el área de configuración de ejecución privada del usuario— en ~/.subversion/auth/ en los sistemas tipo Unix o %APPDATA%/Subversion/auth/ en Windows. (El área de ejecución es cubierto con mayor detalle en “Área de configuración de parámetros de ejecución”.) Con éxito las credenciales son ocultadas en el disco, encerradas en una combinación de hostname, puerto y realm de autenticación.

Cuando el cliente recibe la solicitud para que se autentique, primero busca las credenciales apropiadas en el caché de disco; sino existen, o si las credenciales fallan en la autenticación, el cliente simplemente pregunta al usuario por la información.

Las personas que sufren de paranoia sobre asuntos de seguridad pueden estar pensando, Contraseñas ocultas en el disco? Eso es terrible! Pero por favor conserve la calma. Primero, el área de caché auth/ se encuentra protegida por permisos a fin de que sólo el usuario (dueño) puede leer la información que se encuentra en ella, evitando que otros puedan acceder. Si eso no es lo suficientemente seguro para usted, puede deshabilitar el caché de credenciales. Para lograrlo, sólo basta con un único comando, pasando la opción --no-auth-cache:

$ svn commit -F log_msg.txt --no-auth-cache
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
Password for 'joe':

Adding         newfile
Transmitting file data .
Committed revision 2324.

# password was not cached, so a second commit still prompts us

$ svn rm newfile
$ svn commit -F new_msg.txt
Authentication realm: <svn://host.example.com:3690> example realm
Username:  joe
[...]

O, si usted desea deshabilitar el caché de credenciales en forma permanente, puede editar su archivo de configuración config (ubicado dentro del directorio auth/). Simplemente coloque no a store-auth-creds, y las credenciales no serán guardadas en el caché nunca más.

[auth]
store-auth-creds = no

Algunas veces los usuario desearán eliminar credenciales específicas del caché de disco. Para hacer ésto, necesitará navegar dentro del área auth/ y eliminar el archivo de caché apropiado en forma manual. Las credenciales son almacenadas en el área de caché en archivos individuales; si usted mira dentro de cada archivo, verá claves y valores. La clave svn:realmstreng describe el "realm" servidor particular con el que que el archivo es asociado:

$ ls ~/.subversion/auth/svn.simple/
5671adf2865e267db74f09ba6f872c28        
3893ed123b39500bca8a0b382839198e
5c3c22968347b390f349ff340196ed39

$ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28

K 8
username
V 3
joe
K 8
password
V 4
blah
K 15
svn:realmstring
V 45
<https://svn.domain.com:443> Joe's repository
END

Una vez que haya localizado el archivo de caché apropiado, sencillamente elimínelo.

Una última palabra acerca del comportamiento de autenticación del cliente: es necesaria una pequeña explicación respecto de las opciones --username y --pasword. Muchos sub-comandos del cliente aceptan estas opciones; sin embargo, es importante comprender que la utilización de las mismas no envía automáticamente las credenciales al servidor. Como se dijo anteriormente, el servidor exige tomar las credenciales del cliente cuando lo considera necesario; el cliente no puede impulsarlas a voluntad. Si un usuario y/o contraseña son pasados como opción, ellos serán presentados al servidor sólo si este así lo demanda. [28] Típicamente, estas opciones son utilizadas cuando:

  • el usuario desea autenticarse con un nombre diferente a su usuario de sistema, o

  • un guión desea autenticarse sin utilizar las credenciales del caché.

Aquí hay un resumen final que describe como se comporta un cliente Subversion cuando recibe la demanda de autenticación:

  1. Verifica si el usuario ha especificado algunas credenciales como opción de línea de comando, a través de la opción --username y/o --pasword. Sino, o si esas opciones no pertenecen a una autenticación válida, entonces

  2. Busca en el "realm" del servidor en el área de ejecución auth/, para ver si el usuario ya dispone de credenciales apropiadas en el caché. Sino, o si las credenciales del caché fallan en la autenticación, entonces

  3. Recurre a preguntarle al usuario.

Si el cliente se autentica correctamente a través de alguno de los métodos listados aquí arriba, éste intentará guardar en las credenciales en el caché de disco (a no ser que el usuario haya deshabilitado ese funcionamiento, como hemos mencionado anteriormente.)

svnserve, un servidor personalizado

El programa svnserve es un servidor ligero, capaz de hablar a clientes sobre TCP/IP utilizando un protocolo con estado personalizado. Los clientes contactan un servidor svnserve utilizando URLs que comienzan con el esquema svn:// o svn+ssh://. Esta sección explica las diferentes maneras de ejecutar un svnserve, cómo los clientes se autentican ellos mismos con el servidor, y cómo se configura un control de acceso apropiado a su repositorio.

Invocando el Servidor

Existen unas cuantas maneras diferentes de invocar el programa svnserve. Si se lo invoca sin argumentos, usted no verá nada más que un mensaje de ayuda. No obstante, si planea que inetd dispare el proceso, puede pasar la opción -i (--inetd):

$ svnserve -i
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )

Cuando es invocado con la opción --inetd, svnserve intenta comunicarse con un cliente Subversion a través de stdin y stdout utilizando un protocolo personalizado. Este es un comportamiento estándar de los programas que son ejecutados a través de inetd. El IANA ha reservado el puerto 3690 para el protocolo Subversion, por lo tanto en un sistema tipo Unix usted puede agregar líneas a /etc/services como éstas (si es que no existen todavía):

svn           3690/tcp   # Subversion
svn           3690/udp   # Subversion

Y si su sistema utiliza un demonio inetd tipo Unix clásico, puede agregar esta línea a /etc/inetd.conf:

svn stream tcp nowait svnowner /usr/local/bin/svnserve svnserve -i

Asegúrese que svnowner es un usuario que posee permisos apropiados para acceder a su repositorio. Ahora, cuando una conexión cliente ingrese a su servidor en el puerto 3690, inetd disparará un proceso svnserve para servirlo.

Una segunda opción es ejecutar svnserve un proceso demonio independiente. Para esto, utilice la opción -d:

$ svnserve -d
$               # svnserve is now running, listening on port 3690

Cuando svnserve ejecute en modo demonio, usted puede utilizar las opciones --listen-port= y --listen-host= para personalizar el puerto y nombre exacto en el cual escuchará.

Aún queda una tercera forma de invocar a svnserve, y esa es en modo túnel, con la opción -t. Este modo supone que un programa de servicio remoto tal como RSH o SSH ha autenticado un cliente exitosamente y ahora está invocando un proceso privado svnserve como tal usuario. El programa svnserve se comporta normalmente (comunicándose a través de stdin y stdout), y asume que el tráfico está siendo redirigido automáticamente a través de algún tipo de túnel de vuelta al cliente. Cuando svnserve es invocado por un agente túnel como éste, está seguro que el usuario autenticado posee acceso total de lectura y escritura a los archivos de la base de datos del repositorio. (Ver Servidores y Permisos: Una Palabra de Advertencia.) Esencialmente es lo mismo que un usuario local accediendo al repositorio a través de las URLs file:///.

Una vez el programa svnserve se esté ejecutando, hace que cada repositorio en su sistema se encuentre disponibles a la red. Un cliente necesita especificar una ruta absoluta en la URL del repositorio. Por ejemplo, si un repositorio se encuentra ubicado en /usr/local/repositories/project1, el cliente accederá a través de svn://host.example.com/usr/local/repositories/project1 . Para incrementar la seguridad, usted puede pasar la opción -r a svnserve, que limita a exportar sólo los repositorios debajo de esa ruta:

$ svnserve -d -r /usr/local/repositories
…

En efecto, utilizando la opción -r modifica la ubicación que el programa tratará como raíz del espacio de sistema de archivos remoto. En consecuencia, los clientes utilizarán URLs que tengan la porción que le ha sido removida de la ruta, dejando URLs mucho más cortas (y mucho menos reveladoras):

$ svn checkout svn://host.example.com/project1
…

Autenticación y autorización integradas

Cuando un cliente se conecta a un proceso svnserve, las siguientes cosas suceden:

  • El cliente selecciona un repositorio específico.

  • El servidor procesa el archivo conf/svnserve.conf del repositorio, y comienza a poner en ejecución cualquier política de autenticación y autorización definida en éste.

  • Dependiendo de la situación y las políticas de autorización,

    • se le puede permitir al cliente realizar pedidos de forma anónima, y sin haber recibido la demanda de autenticación, O

    • el cliente puede ser consultado por la autenticación en cualquier momento, O

    • si opera en el modo túnel, el cliente se anuncia a sí mismo que ya ha sido autenticado externamente.

En el momento de escribir estas líneas, el servidor sólo sabe cómo realizar la autenticación por CRAM-MD5 [29]. En esencia, el servidor envía unos pocos datos al cliente. El cliente usa el algoritmo hash MD5 para crear una huella digital de los datos y la palabra clave combinados, y envía la huella digital como respuesta. El servidor realiza el mismo cálculo con la palabra clave almacenada para verificar que el resultado es idéntico. En ningún momento el password real viaja a través de la red.

También es posible, por supuesto, que el cliente se autentifique externamente vía agente de túnel , como por ejemplo SSH. En este caso, el servidor simplemente examina el usuario bajo el cual se está ejecutando, y lo usa durante la autenticación.

Tal y como habrá imaginado, el fichero svnserve.conf es el mecanismo central de un repositorio para controlar las políticas de autenticación y autorización. El fichero sigue el mismo formato que otros ficheros de configuración (vea “Área de configuración de parámetros de ejecución”): los nombres de sección están enmarcados por corchetes ([ y ]), los comentarios comienzan con almohadillas (#), y cada sección contiene variables específicas que pueden ser modificadas (variable = valor). Démonos un paseo por este fichero para aprender a usarlas.

Crear un fichero 'users' y realm

Por ahora, la sección [general] de svnserve.conf tiene todas las variables que usted necesita. Comencemos definiendo un fichero que contenga nombres de usuario y palabras clave, y una realm de autenticación:

[general]
password-db = userfile
realm = example realm

El nombre realm es algo que define usted. Le dice a los clientes a qué tipo de espacio de nombres de autenticación se están conectando; el cliente de Subversion lo muestra durante las preguntas de autenticación, y lo usa como palabra clave (junto con el nombre y puerto del servidor) para cachear las credenciales en disco (vea “Client Credentials Caching”.) La variable password-db apunta a un fichero separado que contiene una lista de nombres de usuario y claves, usando el mismo formato familiar. Por ejemplo:

[users]
harry = foopassword
sally = barpassword

El valor de password-db puede ser una ruta absoluta o relativa al fichero de usuarios. Para muchos administradores, es sencillo guardar el fichero justo en el área conf/ del repositorio, junto a svnserve.conf. Por otra parte, es posible querer compartir el mismo fichero de usuarios entre dos o más repositorios; en este caso, el fichero probablemente debería ubicarse en un lugar más público. Los repositorios que compartan el fichero de usuarios también deberían estar configurados para usar la misma realm, dado que la lista de usuarios define esencialmente un realm de autenticación. Esté donde esté el fichero, asegúrese de ajustar apropiadamente los permisos de lectura y escritura. Si sabe bajo qué usuario(s) se ejecutará svnserve, restrinja el acceso de lectura al fichero correspondientemente.

Activar control de acceso

Hay dos variables más en el fichero svnserve.conf: determinan qué pueden hacer los usuarios autenticados y sin autenticar (anónimos). Las variables anon-access y auth-access pueden tener los valores none, read, o write. Poner el valor a none restringe el acceso de todo tipo; read permite únicamente el acceso de sólo lectura al repositorio, y write permite acceso completo de lectura y escritura. Por ejemplo:

[general]
password-db = userfile
realm = example realm

# anonymous users can only read the repository
anon-access = read

# authenticated users can both read and write
auth-access = write

Los parámetros del ejemplo, son de hecho los valores por defecto de las variables, en caso de que olvide definirlas. Si desea ser aun más conservativo, puede bloquear el acceso anónimo completamente:

[general]
password-db = userfile
realm = example realm

# anonymous users aren't allowed
anon-access = none

# authenticated users can both read and write
auth-access = write

Fíjese que svnserve sólo entiende de control de acceso básico. Un usuario tiene acceso universal de lectura escritura, acceso universal de lectura, o ningún tipo de acceso. No hay control detallado del acceso a rutas específicas dentro del repositorio. Para muchos proyectos y equipos, este nivel de control de acceso es más que suficiente. No obstante, si necesita control de acceso por directorio, tendrá que usar Apache en lugar de svnserve como su proceso servidor.

Autenticación y autorización SSH

La autenticación de serie de svnserve puede ser muy útil, porque evita tener que crear cuentas de sistema reales. Por otro lado, algunos administradores ya tienen entornos de autenticación SSH bien establecidos y funcionando. En estas situaciones, todos los usuarios del proyecto tienen cuentas en el sistema y la habilidad para conectarse con SSH a la máquina servidora.

Es fácil usar SSH junto con svnserve. El cliente simplemente usa el esquema de URL svn+ssh:// para conectar:

$ whoami
harry

$ svn list svn+ssh://host.example.com/repos/project
harry@host.example.com's password:  *****

foo
bar
baz
…

Lo que sucede en esta situación es que el cliente de Subversion invoca el proceso local ssh, conectándolo con host.example.com, autenticándose como el usuario juan, y entonces lanzando un proceso svnserve privado en la máquina remota, que se ejecuta como el usuario juan. El comando svnserve se invoca en modo túnel (-t) y todo el protocolo de red es canalizado a través tunneled de la conexión cifrada por ssh, el agente del túnel. svnserve se da cuenta de que se está ejecutando como el usuario juan, y si el cliente realiza cambios en el repositorio, el nombre de usuario autenticado será usado para atribuir la autoría de la nueva revisión.

Cuando se usa un túnel, la autorización es principalmente controlada por los permisos del sistema operativo a los ficheros de la base de datos del repositorio; es casi igual que si Juan estuviese accediendo al repositorio directamente vía URL file:///. Si necesita que múltiples usuarios del sistema accedan al repositorio directamente, podría ponerlos en un mismo grupo, y ajustar cuidadosamente sus umasks. (Asegúrese de haber leído “Ofrecer múltiples métodos de acceso al repositorio”.) Pero incluso en el caso de usar túneles, el fichero svnserve.conf todavía puede ser usado para bloquear el acceso, simplemente usando auth-access = read o auth-access = none.

Podría pensar que la historia del túnel por SSH acabaría aquí, pero no lo hace. Subversion le permite personalizar el comportamiento de los túneles en su fichero de configuración config (vea “Área de configuración de parámetros de ejecución”.) Por ejemplo, supongamos que desea usar RSH en lugar de SSH. En la sección [tunnels] de su fichero config, defina lo siguiente:

[tunnels]
rsh = rsh

Y ahora, usted puede usar esta nueva definición de túnel usando un esquema URL que concuerde con el nombre de su nueva variable: svn+rsh://host/path. Cuando use el nuevo esquema URL, el cliente Subversion estará ejecutando realmente el comando rsh host svnserve -t tras las cortinas. Si incluye un nombre de usuario en la URL (por ejemplo, svn+rsh://nombreusuario@host/path) el cliente también lo incluirá en su comando (rsh nombreusuario@host svnserve -t.) Pero puede definir nuevos esquemas de túneles mucho más elaborados que eso:

[tunnels]
joessh = $JOESSH /opt/alternate/ssh -p 29934

Este ejemplo muestra varias cosas. Primero, enseña cómo hacer que el cliente de Subversion lance un binario de túnel muy específico (uno ubicado en /opt/alternate/ssh) con opciones específicas. En este caso, acceder a la URL svn+joessh:// invocaría este particular binario de SSH con -p 29934 como parámetro—útil si desea que el programa de túnel se conecte a un puerto no estándar.

Segundo, enseña cómo definir una nueva variable de entorno personalizada que puede reemplazar el nombre de un programa de túnel. Modificar la variable de entorno SVN_SSH es un modo conveniente de reemplazar el agente de túneles SSH por defecto. Pero si necesita tener diferentes reemplazos para servidores diferentes, cada uno de ellos conectando quizás a un puerto diferente o usando un conjunto diferente de opciones, puede usar el mecanismo demostrado en este ejemplo. Ahora, si fuese a crear la variable de entorno JOESSH, su valor reemplazaría el valor completo de la variable del túnel—se ejecutaría $JOESSH en lugar de /opt/alternate/ssh -p 29934.

httpd, el servidor HTTP Apache

El servidor HTTP apache es un servidor de red heavy duty que Subversion puede aprovechar. A través de un módulo propio, httpd permite servir a clientes repositorios Subversion por el protocolo WebDAV/DeltaV, el cual es una extensión sobre HTTP 1.1 (vea http://www.webdav.org/ para más información.) Este protocolo coge el ubicuo protocolo HTTP, núcleo de la World Wide Web, y añade la capacidad de escritura—específicamente el versionado de la misma. El resultado es un sistema robusto, estandarizado, empaquetado convenientemente como parte del software Apache 2.0, soportado por numerosos sistemas operativos y productos de terceros, y no necesita que sus administradores de red abran otro puerto adicional. [30] Si bien el servidor Apache-Subversion tiene más características que svnserve, también es más difícil de configurar. La flexibilidad a menudo se paga en complejidad.

Buena parte del texto siguiente incluye referencias a las directivas de configuración de Apache. Aunque proporcionamos algunos ejemplos que muestran el uso de estas directivas, describirlas con todo detalle está fuera del alcance de este capítulo. El equipo de Apache mantiene una documentación excelente, disponible públicamente en su página web en http://httpd.apache.org. Por ejemplo, una referencia general de las directivas de configuración está ubicada en http://httpd.apache.org/docs-2.0/mod/directives.html.

También, a medida que vaya realizando cambios en su configuración de Apache, es probable que en alguna parte cometerá algún fallo. Si no está familiarizado con el subsistema de archivos de registro de Apache, debería dedicarle un tiempo. En su fichero httpd.conf hay directivas que especifican ubicaciones físicas de los archivos de registro de acceso y error generados por apache (las directivas CustomLog y ErrorLog respectivamente). El módulo mod_dav_svn de Subversion usa también la interfaz de registro de mensajes de error de Apache. Siempre puede explorar el contenido de esos ficheros para encontrar información que podría revelar la fuente de un problema difícilmente discernible de otro modo.

Requisitos previos

Para hacer disponible su repositorio por HTTP, básicamente necesita cuatro componentes disponibles en dos paquetes. Necesita el comando httpd de Apache 2.0, el módulo DAV mod_dav que viene con él, Subversion, y el módulo mod_dav_svn, que proporciona sistemas de ficheros, distribuido con Subversion. Una vez tenga todos estos componentes, el proceso para hacer disponible su repositorio es tan simple como:

  • preparar y ejecutar httpd 2.0 con el módulo mod_dav,

  • instalar el plug-in mod_dav_svn para mod_dav, el cual usa las librerías de Subversion para acceder a un repositorio, y

  • configurar su fichero httpd.conf para exportar (o exponer) el repositorio.

Puede realizar los dos primeros pasos ya sea compilando httpd y Subversion a partir del código fuente, o instalando paquetes binarios precompilados en su sistema. Puede encontrar la información más actualizada sobre cómo compilar Subversion para su uso con el servidor HTTP Apache, al igual que cómo compilar y configurar Apache para este propósito, en el fichero INSTALL en el directorio raíz del código fuente de Subversion.

Configuración básica de Apache

Una vez tenga todos los componentes necesarios instalados en su sistema, todo lo que queda por hacer es configurar Apache mediante su fichero httpd.conf. Ordene a Apache que cargue el módulo mod_dav_svn module usando la directiva LoadModule. La directiva debe preceder cualquier otro elemento de configuración relacionado con Subversion. Si su Apache fue instalado usando el esquema por defecto, su módulo mod_dav_svn debería haber sido instalado en el subdirectorio modules de la instalación (a menudo /usr/local/apache2). La directiva LoadModule tiene una sintaxis sencilla, relacionando el nombre de un módulo con la ubicación de una librería dinámica en disco: disk:

LoadModule dav_svn_module     modules/mod_dav_svn.so

Tenga en cuenta que si mod_dav fue compilado como un objeto compartido (en lugar de haber sido enlazado de manera estática con el binario httpd), necesitará también una línea LoadModule similar para él. Asegúrese de que aparece antes de la línea mod_dav_svn:

LoadModule dav_module         modules/mod_dav.so
LoadModule dav_svn_module     modules/mod_dav_svn.so

En un lugar posterior de su fichero de configuración, necesita indicarle a Apache dónde guardará su repositorio (o repositorios) de Subversion. La directiva Location sigue una notación tipo XML, comenzando con una etiqueta de apertura y terminando con una de cierre, con varias otras directivas de configuración en medio. El propósito de la directiva Location es indicar a Apache que debe realizar algo especial cuando tenga que procesar peticiones dirigidas a una URL determinada o a una hija suya. En el caso de Subversion, quiere que Apache simplemente le pase el control a la capa DAV de todas las URLs que apunten a recursos versionados. Puede ordenar a Apache que delegue el control de todas las URLs cuyas porciones de rutas (aquella parte de la URL que sigue el nombre del servidor y número de puerto opcional) empiecen con /repos/ a un proveedor DAV cuyo repositorio se encuentre en /ruta/absoluta/al/repositorio usando la siguiente sintaxis de httpd.conf:

<Location /repos>
  DAV svn
  SVNPath /ruta/absoluta/al/repositorio
</Location>

Si planea soportar múltiples repositorios de Subversion que residirán en el mismo directorio padre de su disco local, puede usar una directiva alternativa, la directiva SVNParentPath, para indicar ese directorio padre común. Por ejemplo, si sabe que creará múltiples repositorios Subversion en el directorio /usr/local/svn que sería accesible mediante URLs como http://my.server.com/svn/repos1, http://my.server.com/svn/repos2, y así en adelante, podría usar la sintaxis de configuración httpd.conf del siguiente ejemplo:

<Location /svn>
  DAV svn

  # any "/svn/foo" URL will map to a repository /usr/local/svn/foo
  SVNParentPath /usr/local/svn
</Location>

Usando la sintaxis anterior, Apache delegará el control de todas las URLs cuyas porciones comiencen con /svn/ al proveedor DAV de Subversion, quien asumirá que todos los elementos en el directorio especificado por la directiva SVNParentPath son en realidad repositorios de Subversion. Esta es una sintaxis particularmente conveniente, ya que a diferencia de la directiva SVNPath, no necesita reiniciar Apache para crear y hacer disponibles nuevos repositorios.

Asegúrese de que cuando defina su nuevo Location, no se solapa con otras ubicaciones exportadas. Por ejemplo, si su DocumentRoot principal es /www, no exporte ningún repositorio Subversion en <Location /www/repos>. En caso de recibir una petición para la URI /www/repos/foo.c, Apache no sabrá si tiene que buscar el fichero repos/foo.c en DocumentRoot, o si debe delegar en mod_dav_svn para que devuelva foo.c del repositorio Subversion.

En este punto, debería considerar seriamente el tema de los permisos. Si ha estado ejecutando Apache durante algún tiempo como su servidor de web habitual, probablemente ya tenga una colección de contenido—páginas web, scripts y demás. Estos elementos ya han sido configurados con un conjunto de permisos que les permite funcionar con Apache, o de manera más apropiada, le permite a Apache funcionar con esos ficheros. Apache, cuando es usado como servidor de Subversion, también necesitará los permisos adecuados de lectura y escritura a su repositorio Subversion. (Vea Servidores y Permisos: Una Palabra de Advertencia.)

Tendrá que encontrar una configuración de permisos de sistema que satisfaga los requisitos de Subversion sin estropear instalaciones ya existentes de páginas web o scripts. Esto podría significar cambiar los permisos de su repositorio Subversion para que encajen con aquellos usados por otras elementos servidos por Apache, o podría significar usar las directivas User y Group del fichero httpd.conf para especificar que Apache debe ejecutarse con el usuario y grupo a quien pertenece su repositorio Subversion. No hay una única manera correcta de configurar sus permisos, y cada administrador tendrá diferentes razones para hacer las cosas a su manera. Sólo tenga presente que los problemas relacionados con los permisos son quizás el fallo más común cuando se configura un repositorio Subversion para ser usado con Apache.

Opciones de autenticación

En este punto, si ha configurado httpd.conf para que contenga algo como

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn
</Location>

...entonces su repositorio es accesible al mundo de manera anónima. Hasta que configure algunas políticas de autenticación y autorización, los repositorios Subversion que haga disponibles con la directiva Location serán generalmente accesibles por cualquiera. En otras palabras,

  • cualquiera puede usar su cliente de Subversion para obtener una copia local de la URL de un repositorio (o cualquiera de sus subdirectorios),

  • cualquiera puede navegar de manera interactiva las últimas revisiones del repositorio simplemente usando la URL del repositorio con su navegador web, y

  • cualquiera puede enviar cambios al repositorio.

Autenticación HTTP básica

La forma más sencilla de autenticar a un cliente es mediante el mecanismo de autenticación HTTP básico, que simplemente usa un nombre de usuario y clave para verificar que el usuario es quien dice ser. Apache proporciona la utilidad htpasswd para gestionar la lista de nombres de usuarios y claves aceptables, aquellos a quienes desea proporcionar acceso especial a su repositorio Subversion. Permitamos el acceso de escritura a Juan y Carmen. Primero, necesitamos añadirlos al fichero de claves.

$ ### First time: use -c to create the file
$ ### Use -m to use MD5 encryption of the password, which is more secure
$ htpasswd -cm /etc/svn-auth-file harry
New password: ***** 
Re-type new password: *****
Adding password for user harry
$ htpasswd /etc/svn-auth-file -m sally
New password: *******
Re-type new password: *******
Adding password for user sally
$

Ahora, necesitamos añadir algunas directivas httpd.conf más dentro del bloque Location para decirle a Apache qué hacer con su nuevo fichero de claves. La directiva AuthType especifica el tipo del sistema de autenticación que va a usar. En este caso, queremos especificar el sistema de autenticación Basic. AuthName es un nombre arbitrario que usted proporciona para el dominio de autenticación. La mayoría de los navegadores mostrarán este nombre en una ventana de diálogo emergente cuando el navegador le pregunte su nombre y clave. Finalmente, use la directiva AuthUserFile para especificar la ubicación del fichero de claves creado usando htpasswd.

Tras añadir estas tres directivas, su bloque <Location> debería tener un aspecto similar a este:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
</Location>

Este bloque <Location> todavía no está completo, y no hará nada útil. Meramente indica a Apache que cuando requiera autorización, Apache debe obtener el nombre de usuario y clave del cliente Subversion. Lo que falta aquí, no obstante, son las directivas que le dicen a Apache qué tipos de solicitudes por parte de los clientes requieren autorización. Donde se requiera autorización, Apache demandará también autenticación. Lo mas sencillo es proteger todas las peticiones. Añadir Require valid-user indica a Apache que todas las peticiones requieren un usuario autenticado:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
  Require valid-user
</Location>

Asegúrese de leer la siguiente sección (“Opciones de autorización”) para más detalles sobre la directiva Require y otros modos de establecer políticas de autorización.

Una advertencia: en la autenticación básica por HTTP las palabras claves viajan por la red casi como texto simple, y son por lo tanto extremadamente inseguras. Si está preocupado por la existencia de un rastreador de claves, lo mejor sería usar algún tipo de cifrado SSL, para que los clientes se autentiquen vía https:// en lugar de http://; como mínimo podrá configurar Apache para que use use un certificado firmado por sí mismo. [31] Consulte la documentación de Apache (y la de OpenSSL) para saber cómo hacer esto.

Gestión de certificados SSL

Las empresas que necesitan exponer sus repositorios a un acceso externo al firewall de la compañía deberían ser conscientes de que grupos no autorizados podrían estar rastreando su tráfico de red. SSL consigue que ese tipo de atención no deseada desemboque con menor probabilidad en fugas de datos sensitivos.

Si se compila un cliente de Subversion para que use OpenSSL, entonces ganará la habilidad de hablar con un servidor Apache vía URLs https://. La librería Neon usada por el cliente Subversion no solo es capaz de verificar los certificados de servidor, sino que también puede proporcionar certificados de cliente cuando sean solicitados. Una vez el cliente y servidor hayan intercambiado sus certificados SSL y realizado una autenticación mutua con éxito, todas sus comunicaciones siguientes serán cifradas con una clave de sesión.

Está fuera del alcance de este libro describir la generación de certificados de cliente y servidor, y cómo configurar Apache para usarlos. Muchos otros libros, incluyendo la propia documentación de Apache, describen esta tarea. Pero lo que podemos cubrir aquí es cómo gestionar los certificados de cliente y servidor desde un cliente Subversion ordinario.

Cuando un cliente Subversion se comunica con Apache vía https://, puede recibir dos tipos diferentes de información:

  • un certificado de servidor

  • una demanda de un certificado de cliente

Si el cliente recibe un certificado de servidor, necesita verificar que confía en el certificado: ¿es realmente el servidor quien dice ser? La librería OpenSSL realiza esto examinando al firmante del certificado de servidor o la autoridad certificadora (CA). Si OpenSSL no es capaz de confiar automáticamente en la CA, o bien ocurre algún otro problema (como que el certificado expiró o no concuerda con el nombre del servidor), el cliente de línea de comando de Subversion le preguntará si quiere confiar en el certificado del servidor de todas maneras:

$ svn list https://host.example.com/repos/project

Error validating server certific