05.07.08
Git: SCM distribuido
Hace unos días comentaba que SVN no es la mejor opción como sistema de VCS/SCM, que existen alternativas mejores. Hoy quiero hablar un poco sobre Git, el SCM que usan para el kernel de Linux y que está adquiriendo mucha importancia en la comunidad de software libre, con muchos proyectos como Ruby on Rails pasándose a GIT desde SVN o similares. Esto será largo
Historia
Git fue creado por Linus Torvalds debido la pérdida de la licencia de BitKeeper para la gestión del kernel de Linux. Por decirlo suavemente Linus odia CVS y por extensión SVN, y buscaba un sistema que cumpliera 4 requisitos:
- No se pareciera a CVS
- Fuera distribuido
- Seguro frente corrupción, accidental o intencionada
- Gran rendimiento en las operaciones
El resultado fue Git, un sistema que creó él mismo en apenas 3 meses. Sus principales características son:
- Distribuido, cada usuario tiene en su ordenador una copia de todo el historial del repositorio
- Accesible mediante http, ssh o el protocolo git entre otros
- Facilita los procesos de “branching” y “merging” para desarrollos no lineales en proyectos
- Algoritmos de merging (especialmente 3-way merging) muy buenos
- Altamente eficiente y escalable
Los principales defectos de Git, actualmente, son la falta de soporte en algunos IDE y que no hay una versión eficiente para Windows, se necesita usar Cygwin lo que penaliza mucho el rendimiento. También se menciona que las operaciones interrumpidas en Git dejan “basura” en la base de datos, que no afecta al rendimiento o estabilidad del sistema pero ocupa espacio. No es un gran problema ya que se puede limpiar mediante un comando.
Características propias
La filosofía de Git difiere bastante de la de SVN, el SCM más popular. La diferencia principal es que se trata de un sistema distribuido, en el que la carpeta raíz de un proyecto contiene un subdirectorio (.git) con todo el contenido del repositorio.
La segunda gran diferencia es que Git no almacena directorios, sólo ficheros. Eso implica que se pierden las rutas? No, internamente Git asocia a cada fichero su ruta, con lo que al obtener
los ficheros de un repositorio el propio Git generará los directorios
necesarios. Simplemente es una medida para facilitar el trabajo permitiendo al usuario crear y eliminar directorios sin problemas, al contrario de lo que pasa con SVN. Una consecuencia importante de este sistema es que si tenemos en nuestro código directorios vacíos se “perderán”, ya que ningún fichero los incluye en su ruta.
Respecto a las versiones, en lugar de usar números secuenciales como SVN, Git genera claves hash SHA-1 para cada revisión. Esto es necesario al ser un sistema distribuido ya que cada usuario podría tener 2 revisiones número 15 en su ordenador, completamente diferentes. De esta forma se evitan los conflictos y se garantiza la unicidad de las claves en el proyecto, ya que cada una contiene 160-bits.
El último punto se trata de los “tags” y “branches”. En SVN, podemos crear un “tag” o un “branch” mediante una copia (svn copy) de “trunk”. Este sistema tienes varios inconvenientes, siendo el más importante que un “tag” es mutable, podemos realizar commits en él, algo que contradice el concepto de “tag”. En Git son estructuras dentro del repositorio, cada una con su propia función.
Cómo funciona
La primera en la frente: Git no es un SCM. No, no os estoy mintiendo. Entonces, a que viene todo el artículo y las comparaciones con SVN? Bueno, de hecho Git es un conjunto de aplicaciones de bajo nivel, parecido a un sistema de ficheros, que se usan para crear un SCM. Si miráis los scripts de Git veréis que la mayoría consisten en una serie de comandos encadenados por pipes de Unix. Es un detalle menor para lo que interesa a un usuario, pero es importante ya que da idea de lo que tenemos detrás: algo tan complejo de construir y simple de usar como un sistema de ficheros.
Internamente Git almacena una base de datos de objetos y un caché de directorios. La base de datos puede contener Blobs (contenido de ficheros), Arboles (colecciones de blobs) y Commits (puntos en la historia del proyecto). Los Blobs corresponden a todas las versiones de todos los ficheros del proyecto, pero no están asociados al nombre, tan solo son los contenidos. Esta separación ente nombre y contenido permite renombrar ficheros en un proyecto sin perder su historial de cambios. Los Arboles son los que mantienen la asociación entre ficheros y contenidos, junto a las rutas y permisos de estos, creando la estructura del proyecto. Por último los Commits contienen un árbol, un mensaje de commit y punteros a diferentes Commit “padres”, de forma que en cada uno tenemos asociada toda la estructura, el estado del proyecto en ese momento, a diferencia de otros sistemas que solo almacenan las diferencias entre commits.
El hecho de tener varios punteros a los padres es causa de ser un sistema distribuido: un commit puede crearse debido al merge de dos commits de dos usuarios diferentes, con lo que su historial incluye a dos padres. La estructura usa activamente las claves SHA-1 para todos sus componentes: blobs, árboles y commits, facilitando la gestión.
Explicada la estructura interna, como funciona el repositorio? La revisión “HEAD”, la principal, consiste en un puntero al Commit más reciente. Cuando creamos un “tag” o un “branch”, lo que hacemos es crear un puntero a un commit. Dado que los Commit no son modificables ya que al modificar los datos se crea un nuevo Commit, garantizamos que el “tag” será siempre el mismo, sin cambios. Si trabajamos con un “branch”, al hacer cambios se crearan nuevos commit que generar una nueva jerarquía de “padres-hijos” cuya raíz es el “commit” desde el que generamos el “branch”.
Y como funciona al trabajar en equipo? Al trabajar varios desarrolladores en paralelo es como si hiciéramos un “branch”. Cada programador seguirá con su código, generando “tags”, “branch” y “commits” según necesite. Cuando los programadores sincronicen sus repositorios tendremos un repositorio con dos (o más) “branch”, siendo el “HEAD” el Commit más reciente, independientemente de a que programador pertenezca. Aquí tenemos dos opciones: podemos mantener los branch de los desarrolladores ya que quizás no han terminado su tarea y aún no están listos para un merge. O podemos decidir unirlos a la rama principal. Si hacemos esto, generaremos un nuevo Commit (el nuevo HEAD) cuyos padres serán los Commits de los branch que hemos fusionado (parece un lío, lo se).
Existen herramientas que permiten ver gráficamente el proceso. Un ejemplo más gráfico lo tenemos aquí:

En este ejemplo “i” sería el código inicial. En ese punto generamos dos “branch” (2 programadores, por ejemplo), que desarrollan sus versiones (g,h,f,e). En cierto momento hacemos un merge entre “e” y “f”, generando el commit “c”. Paralelamente “e” genera el commit “d” (nuevos cambios), con el que hacemos otro merge (commit “b”). A partir de este punto sólo seguimos con una rama, aunque podríamos seguir codificando con las dos o generar más branches.
Como se puede observar, en Git el funcionamiento de “branch” es algo natural, a diferencia de, por ejemplo, SVN, lo que facilita hacer pruebas con el código sabiendo que será muy fácil unirlas a la rama principal de desarrollo.
Instalación
En la web de Git tenéis enlaces a varios binarios precompilados: rpm, deb, binarios de Mac y Windows. Instalarlo en Debian/Ubuntu es tan fácil como:
sudo apt-get install git-core
Apt es adorable, verdad? Esta instrucción nos deja el sistema listo para usar Git. Dado que es un SCM descentralizado no necesitamos instalar ningún servidor Apache como sería el caso de SVN, al menos para un uso básico.
Uso
Git sigue una filosofía muy distinta a SVN, pero esto no debería ser un impedimento para adaptarse a su uso, ya que en el fondo un SCM es un SCM, y se rigen por un comportamiento básico de “update-modify-commit”. A continuación incluyo una lista de las instrucciones más comunes con su equivalente en SVN:
- git clone <repositorio> equivalente a svn checktou <repositorio>
- git init (desde el directorio que queremos situar bajo scm) equivale a svnadmin create <repositorio>
- git add <fichero> equivale a svn add <fichero>
- git rm <fichero> equivale a svn rm <fichero>
- git checkout <ruta> equivale a svn revert <ruta>
- git pull equivale a svn update
- git commit -a equivale a svn commit
- git diff equivale a svn diff | less
- git status equivale a svn status
- git tag/git branch equivalen a svn copy <origen> <destino>
Como podéis ver la sintaxis es muy parecida. Hay algunas excepciones como el “git checkout” y están las particularidades de Git: el no gestionar directorios con lo que “git add” sólo funciona con ficheros y su sistema de branch/tag, muy diferente al de SVN. El resto no debería suponer un problema una vez entendéis la filosofía del repositorio. Para familiarizaros con su uso os recomiendo esta web, en la que encontraréis un curso de adaptación para usuarios de SVN.
Soporte
Existe un módulo (no oficial) de soporte de Git para Netbeans aquí.
Los que prefiráis Eclipse podéis encontrar un addon aquí.
Enlaces
Podéis encontrar más información en:
Hasta aquí la “breve” introducción a Git. Ahora sólo os falta instalarlo y disfrutar. Otro día hablamos de Mercurial ![]()
GIT: una seria alternativa a Subversion dicho,
Mayo 21, 2008 en 4:45 pm
[...] GIT: una seria alternativa a Subversionpvillega.wordpress.com/2008/05/07/git-scm-distribuido/ por patilleru hace pocos segundos [...]
Git: resources « So be IT dicho,
Junio 15, 2008 en 8:32 pm
[...] he llegado a una web con varios enlaces y anotaciones sobre Git. Un complemento a mi anterior post sobre esta maravilla de [...]