-
1. Inicio - Sobre el Control de Versiones
-
2. Fundamentos de Git
-
3. Ramificaciones en Git
-
4. Git en el Servidor
- 4.1 Los Protocolos
- 4.2 Configurando Git en un servidor
- 4.3 Generando tu clave pública SSH
- 4.4 Configurando el servidor
- 4.5 El demonio Git
- 4.6 HTTP Inteligente
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Git en un alojamiento externo
- 4.10 Resumen
-
5. Git en entornos distribuidos
-
6. GitHub
-
7. Herramientas de Git
- 7.1 Revisión por selección
- 7.2 Organización interactiva
- 7.3 Guardado rápido y Limpieza
- 7.4 Firmando tu trabajo
- 7.5 Buscando
- 7.6 Reescribiendo la Historia
- 7.7 Reiniciar Desmitificado
- 7.8 Fusión Avanzada
- 7.9 Rerere
- 7.10 Haciendo debug con Git
- 7.11 Submódulos
- 7.12 Agrupaciones
- 7.13 Replace
- 7.14 Almacenamiento de credenciales
- 7.15 Resumen
-
8. Personalización de Git
-
9. Git y Otros Sistemas
- 9.1 Git como Cliente
- 9.2 Migración a Git
- 9.3 Resumen
-
10. Los entresijos internos de Git
-
A1. Apéndice A: Git en otros entornos
- A1.1 Interfaces gráficas
- A1.2 Git en Visual Studio
- A1.3 Git en Eclipse
- A1.4 Git con Bash
- A1.5 Git en Zsh
- A1.6 Git en Powershell
- A1.7 Resumen
-
A2. Apéndice B: Integrando Git en tus Aplicaciones
- A2.1 Git mediante Línea de Comandos
- A2.2 Libgit2
- A2.3 JGit
-
A3. Apéndice C: Comandos de Git
- A3.1 Configuración
- A3.2 Obtener y Crear Proyectos
- A3.3 Seguimiento Básico
- A3.4 Ramificar y Fusionar
- A3.5 Compartir y Actualizar Proyectos
- A3.6 Inspección y Comparación
- A3.7 Depuración
- A3.8 Parcheo
- A3.9 Correo Electrónico
- A3.10 Sistemas Externos
- A3.11 Administración
- A3.12 Comandos de Fontanería
3.1 Ramificaciones en Git - ¿Qué es una rama?
Cualquier sistema de control de versiones moderno tiene algún mecanismo para soportar el uso de ramas. Cuando hablamos de ramificaciones, significa que tú has tomado la rama principal de desarrollo (master) y a partir de ahí has continuado trabajando sin seguir la rama principal de desarrollo. En muchos sistemas de control de versiones este proceso es costoso, pues a menudo requiere crear una nueva copia del código, lo cual puede tomar mucho tiempo cuando se trata de proyectos grandes.
Algunas personas resaltan que uno de los puntos más fuertes de Git es su sistema de ramificaciones y lo cierto es que esto le hace resaltar sobre los otros sistemas de control de versiones. ¿Por qué esto es tan importante? La forma en la que Git maneja las ramificaciones es increíblemente rápida, haciendo así de las operaciones de ramificación algo casi instantáneo, al igual que el avance o el retroceso entre distintas ramas, lo cual también es tremendamente rápido. A diferencia de otros sistemas de control de versiones, Git promueve un ciclo de desarrollo donde las ramas se crean y se unen ramas entre sí, incluso varias veces en el mismo día. Entender y manejar esta opción te proporciona una poderosa y exclusiva herramienta que puede, literalmente, cambiar la forma en la que desarrollas.
¿Qué es una rama?
Para entender realmente cómo ramifica Git, previamente hemos de examinar la forma en que almacena sus datos.
Recordando lo citado en [ch01-introduction], 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]), 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.
Nota
|
La rama “master” en Git, no es una rama especial.
Es como cualquier otra rama.
La única razón por la cual aparece en casi todos los repositorios es porque es la que crea por defecto el comando |
Crear una Rama Nueva
¿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 testing
Esto 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 project
Puedes ver que las ramas “master” y “testing” están junto a la confirmación f30ab
.
Cambiar de Rama
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 testing
Esto 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 master
Este 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.
Nota
|
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 project
Debido 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í.