Para entender realmente cómo ramifica Git, previamente hemos de examinar la forma en que almacena sus datos.
Recordando lo citado en ch01-introduction.asc, Git no los almacena de forma incremental (guardando solo diferencias), sino que los almacena como una serie de instantáneas (copias puntuales de los archivos completos, tal y como se encuentran en ese momento).
En cada confirmación de cambios (commit), Git almacena una instantánea de tu trabajo preparado. Dicha instantánea contiene además unos metadatos con el autor y el mensaje explicativo, y uno o varios apuntadores a las confirmaciones (commit) que sean padres directos de esta (un padre en los casos de confirmación normal, y múltiples padres en los casos de estar confirmando una fusión (merge) de dos o más ramas).
Para ilustrar esto, vamos a suponer, por ejemplo, que tienes una carpeta con tres archivos, que preparas (stage) todos ellos y los confirmas (commit). Al preparar los archivos, Git realiza una suma de control de cada uno de ellos (un resumen SHA-1, tal y como se mencionaba en ch01-introduction.asc), almacena una copia de cada uno en el repositorio (estas copias se denominan "blobs"), y guarda cada suma de control en el área de preparación (staging area):
$ git add README test.rb LICENSE
$ git commit -m 'initial commit of my project'Cuando creas una confirmación con el comando git commit, Git realiza sumas de control de cada subdirectorio (en el ejemplo, solamente tenemos el directorio principal del proyecto), y las guarda como objetos árbol en el repositorio Git.
Después, Git crea un objeto de confirmación con los metadatos pertinentes y un apuntador al objeto árbol raíz del proyecto.
En este momento, el repositorio de Git contendrá cinco objetos: un "blob" para cada uno de los tres archivos, un árbol con la lista de contenidos del directorio (más sus respectivas relaciones con los "blobs"), y una confirmación de cambios (commit) apuntando a la raíz de ese árbol y conteniendo el resto de metadatos pertinentes.
Si haces más cambios y vuelves a confirmar, la siguiente confirmación guardará un apuntador a su confirmación precedente.
Una rama Git es simplemente un apuntador móvil apuntando a una de esas confirmaciones.
La rama por defecto de Git es la rama master.
Con la primera confirmación de cambios que realicemos, se creará esta rama principal master apuntando a dicha confirmación.
En cada confirmación de cambios que realicemos, la rama irá avanzando automáticamente.
|
Note
|
La rama |
¿Qué sucede cuando creas una nueva rama?
Bueno…, simplemente se crea un nuevo apuntador para que lo puedas mover libremente.
Por ejemplo, supongamos que quieres crear una rama nueva denominada "testing".
Para ello, usarás el comando git branch:
$ git branch testingEsto creará un nuevo apuntador apuntando a la misma confirmación donde estés actualmente.
Y, ¿cómo sabe Git en qué rama estás en este momento?
Pues…, mediante un apuntador especial denominado HEAD.
Aunque es preciso comentar que este HEAD es totalmente distinto al concepto de HEAD en otros sistemas de control de cambios como Subversion o CVS.
En Git, es simplemente el apuntador a la rama local en la que tú estés en ese momento, en este caso la rama master; pues el comando git branch solamente crea una nueva rama, pero no salta a dicha rama.
Esto puedes verlo fácilmente al ejecutar el comando git log para que te muestre a dónde apunta cada rama. Esta opción se llama --decorate.
$ git log --oneline --decorate
f30ab (HEAD, master, testing) add feature #32 - ability to add new
34ac2 fixed bug #1328 - stack overflow under certain conditions
98ca9 initial commit of my projectPuedes ver que las ramas master'' y testing'' están junto a la confirmación f30ab.
Para saltar de una rama a otra, tienes que utilizar el comando git checkout.
Hagamos una prueba, saltando a la rama testing recién creada:
$ git checkout testingEsto mueve el apuntador HEAD a la rama testing.
¿Cuál es el significado de todo esto? Bueno…, lo veremos tras realizar otra confirmación de cambios:
$ vim test.rb
$ git commit -a -m 'made a change'Observamos algo interesante: la rama testing avanza, mientras que la rama master permanece en la confirmación donde estaba cuando lanzaste el comando git checkout para saltar.
Volvamos ahora a la rama master:
$ git checkout masterEste comando realiza dos acciones:
Mueve el apuntador HEAD de nuevo a la rama master, y revierte los archivos de tu directorio de trabajo; dejándolos tal y como estaban en la última instantánea confirmada en dicha rama master.
Esto supone que los cambios que hagas desde este momento en adelante, divergirán de la antigua versión del proyecto.
Básicamente, lo que se está haciendo es rebobinar el trabajo que habías hecho temporalmente en la rama testing; de tal forma que puedas avanzar en otra dirección diferente.
|
Note
|
Saltar entre ramas cambia archivos en tu directorio de trabajo
Es importante destacar que cuando saltas a una rama en Git, los archivos de tu directorio de trabajo cambian. Si saltas a una rama antigua, tu directorio de trabajo retrocederá para verse como lo hacía la última vez que confirmaste un cambio en dicha rama. Si Git no puede hacer el cambio limpiamente, no te dejará saltar. |
Haz algunos cambios más y confírmalos:
$ vim test.rb
$ git commit -a -m 'made other changes'Ahora el historial de tu proyecto diverge (ver Los registros de las ramas divergen).
Has creado una rama y saltado a ella, has trabajado sobre ella; has vuelto a la rama original, y has trabajado también sobre ella.
Los cambios realizados en ambas sesiones de trabajo están aislados en ramas independientes: puedes saltar libremente de una a otra según estimes oportuno.
Y todo ello simplemente con tres comandos: git branch, git checkout y git commit.
También puedes ver esto fácilmente utilizando el comando git log.
Si ejecutas git log --oneline --decorate --graph --all te mostrará el historial de tus confirmaciones, indicando dónde están los apuntadores de tus ramas y como ha divergido tu historial.
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my projectDebido a que una rama Git es realmente un simple archivo que contiene los 40 caracteres de una suma de control SHA-1, (representando la confirmación de cambios a la que apunta), no cuesta nada el crear y destruir ramas en Git. Crear una nueva rama es tan rápido y simple como escribir 41 bytes en un archivo, (40 caracteres y un retorno de carro).
Esto contrasta fuertemente con los métodos de ramificación usados por otros sistemas de control de versiones, en los que crear una rama nueva supone el copiar todos los archivos del proyecto a un directorio adicional nuevo. Esto puede llevar segundos o incluso minutos, dependiendo del tamaño del proyecto; mientras que en Git el proceso es siempre instantáneo. Y además, debido a que se almacenan también los nodos padre para cada confirmación, el encontrar las bases adecuadas para realizar una fusión entre ramas es un proceso automático y generalmente sencillo de realizar. Animando así a los desarrolladores a utilizar ramificaciones frecuentemente.
Vamos a ver el por qué merece la pena hacerlo así.








