Está en la página 1de 30

Introducción a

Con explicación de un modelo de ramificación

Autor: Jorge López Fernández


lopez.fernandez.jorge@gmail.com
jorge@egalcom.com

Neda, 9 de noviembre de 2010


RESUMEN

En este documento se recogen los conocimientos básicos para el manejo de un


repositorio git.

En primer lugar se abordan los conceptos técnicos, es decir, los comandos nece-
sarios para la mayorı́a de las operaciones y el funcionamiento y filosofı́a del sistema
de control de versiones git.

En la segunda parte se explican un conjunto de buenas prácticas en lo relativo


al manejo de ramas y flujo de trabajo entre miembros de un equipo, tomando para
ello como base un modelo que ha demostrado su éxito a lo largo de diversos proyectos.
Índice general

Índice general I

Índice de figuras II

1. Tutorial 1
1.1. Descripción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2. Importar un proyecto nuevo . . . . . . . . . . . . . . . . . . . . . . 1
1.3. Realizando cambios . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4. Git controla contenidos, no ficheros . . . . . . . . . . . . . . . . . . 3
1.5. Consultando la evolución del proyecto . . . . . . . . . . . . . . . . . 3
1.6. Gestionando ramas . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.7. Git como herramienta de colaboración . . . . . . . . . . . . . . . . 6
1.8. Explorando el histórico . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.9. Siguientes pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2. Un modelo de ramificación exitoso 13


2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2. ¿Por qué git? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3. Descentralizado pero centralizado . . . . . . . . . . . . . . . . . . . 14
2.4. Las ramas principales . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.5. Soportando ramas . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.5.1. Feature branches . . . . . . . . . . . . . . . . . . . . . . . . 17
2.5.2. Release branches . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5.3. Hotfix branches . . . . . . . . . . . . . . . . . . . . . . . . 21
2.6. Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3. Comenzando a trabajar con Git en Windows 24


3.1. Instalación de la herramienta . . . . . . . . . . . . . . . . . . . . . 24
3.2. Inicialización del repositorio para NetBeans . . . . . . . . . . . . . . 24

Referencias 26

i
Índice de figuras

2.1. Esquema del modelo de ramificación . . . . . . . . . . . . . . . . . 13


2.2. Esquema de coordinación entre repositorios . . . . . . . . . . . . . . 15
2.3. Flujo entre las ramas master y develop . . . . . . . . . . . . . . . 16
2.4. Flujo de las ramas de caracterı́sticas (feature branches) . . . . . . . . 17
2.5. Comparación de “merge” con fast-forward y sin él . . . . . . . . . . 18
2.6. Flujo de las ramas de arreglos rápidos (hotfix branches) . . . . . . . 22

ii
Capı́tulo 1
Tutorial

1.1. Descripción
Git es un software de control de versiones diseñado por Linus Torvalds, pensando
en la eficiencia y la confiabilidad del mantenimiento de versiones de aplicaciones
cuando éstas tienen un gran número archivos de código fuente.
En este tutorial sobre git [1] se explicará cómo importar un proyecto, hacer cambios
en él y compartir esos cambios con otros desarrolladores.
Antes de nada señalaremos que se puede conseguir documentación para cualquier
comando de git en una lı́nea de comando tipo UNIX con un comando del estilo de

$ man git - log

sustituyendo log por el comando de git que nos interese.


Al comenzar es una buena idea introducir los datos personales, nombre y dirección
de e-mail, antes de hacer cualquier operación. La manera más sencilla es:

$ git config -- global user . name " Tu nombre "


$ git config -- global user . email tu@tudominio . ejemplo . com

1.2. Importar un proyecto nuevo


Si asumimos que tenemos un proyecto dentro de un directorio, simplemente nos
situaremos en la lı́nea de comando en dicho directorio y ejecutaremos

$ git init

y git nos responderá

1
1.3. Realizando cambios 2

Initialized empty Git repository in . git /

Ya hemos inicializado el directorio de trabajo, y ahora podremos ver que se ha


creado un directorio nuevo, “.git”.
Lo siguiente será indicarle a git que anote el contenido de todos los ficheros bajo
el directorio actual, con g it add:

$ git add .

La información sobre el estado de los archivos está almacenada en un área de alma-


cenamiento temporal que git llama “ı́ndice”. Podemos almacenar permanentemente
los contenidos del ı́ndice en el repositorio con el comando commit:

$ git commit

Con esto te pedirá un mensaje que describa el commit, y ya habrás almacenado


la primera versión de tu proyecto en git.

1.3. Realizando cambios


Modifica algunos archivos, entonces añade sus contenidos actualizados al ı́ndice:

$ git add file1 file2 file3

Ahora ya estás listo para hacer un commit. Puedes ver los cambios que vas a
confirmar usando git diff con la opción --cached:

$ git diff -- cached

(Sin --cached, git diff te mostrarı́a cualquier cambio que hayas hecho pero
no hayas añadido al ı́ndice.) También puedes obtener un resumen del estado del
repositorio con git status:

$ git status
# On branch master
# Changes to be committed :
# ( use " git reset HEAD < file >..." to unstage )
#
# modified : file1
# modified : file2
1.4. Git controla contenidos, no ficheros 3

# modified : file3
#

Si necesitas hacer algún ajuste adicional, hazlo ahora, y entonces añade cualquier
archivo nuevamente modificado al ı́ndice. Finalmente, confirma tus cambios con:

$ git commit

Se te pedirá de nuevo un mensaje descriptivo sobre los cambios, y entonces alma-


cenará una nueva versión del proyecto.
Alternativamente, en lugar de git add puedes emplear

$ git commit -a

que automáticamente detectará cualquier archivo modificado (pero no nuevo), lo


añadirá al ı́ndice y lo confirmará en un solo paso.
Una nota sobre los mensajes de los “commits”: aunque no es obligatorio, se reco-
mienda comenzar el mensaje con una lı́nea corta (menos de 50 caracteres) resumiendo
el cambio, seguida de una lı́nea en blanco y luego una descripción más detallada. Las
herramientas que convierten “commits” en mails, por ejemplo, emplean la primera
lı́nea para el asunto del mensaje, y el resto para el cuerpo.

1.4. Git controla contenidos, no ficheros


Muchos sistemas de control de versiones proporcionan un comando add que le
dice al sistema que comience a controlar los cambios en un fichero nuevo. El comando
add en Git hace algo más sencillo y más potente: git add se usa tanto para ficheros
nuevos como para aquellos que han sido modificados, y en ambos casos toma una
copia de los ficheros y envı́a su contenido alı́ndice, listo para ser incluido en el siguiente
“commit”.

1.5. Consultando la evolución del proyecto


En cualquier momento se puede consultar el histórico de los cambios empleando

$ git log

Si también quieres consultar la lista completa de cambios en cada etapa, usa


1.6. Gestionando ramas 4

$ git log -p

En muchas ocasiones un resumen de los cambios es útil para tener una idea sobre
cada etapa:

$ git log -- stat -- summary

1.6. Gestionando ramas


Un único repositorio git puede tener múltiples ramas para desarrollo. Para crear
una nueva rama llamada “experimental”, usa

$ git branch experimental

Si ahora ejecutas

$ git branch

verás una lista de todas las ramas existentes:

experimental
* master

La rama “experimental” es la que acabas de crear, mientras que la rama “master”


es una rama por defecto que se crea automáticamente. El asterisco marca la rama en
la que estás actualmente; ahora ejecuta

$ git checkout experimental

para cambiar a la rama “experimental”. Ahora edita un fichero, confirma el cambio


en un “commit”, y cambia de nuevo a la rama “master”:

( editar fichero )
$ git commit -a
$ git checkout master

Comprueba que el cambio que hiciste ya no es visible, ya que se realizó en la rama


“experimental” y estás de nuevo en la rama “master”.
1.6. Gestionando ramas 5

Puedes hacer un cambio distinto en la rama “master”:

( editar fichero )
$ git commit -a

en este momento las dos ramas son divergentes, con diferentes cambios realizados
en cada una. Para juntar los cambios de la rama “experimental” en “master”, ejecuta

$ git merge experimental

Si no existen conflictos en los cambios, ya está. En caso contrario, se dejarán


marcadores en los ficheros problemáticos indicando el conflicto;

$ git diff

nos mostrará esto. Una vez que hayas editado el fichero para resolver los conflictos,

$ git commit -a

confirmará el resultado de la mezcla. Finalmente,

$ gitk

mostrará una representación gráfica del histórico resultante.


En este punto podrı́as eliminar la rama “experimental” con

$ git branch -d experimental

Este comando asegura que cambios en la rama “experimental” ya están en la


rama actual.
Si desarrollas en una rama alguna idea que luego quieres desechar, simplemente
bórrala con

$ git branch -D estupidez

Las ramas con sencillas y rápidas, por lo que probarlas es una buena práctica.
1.7. Git como herramienta de colaboración 6

1.7. Git como herramienta de colaboración


Supongamos que Alicia ha comenzado un proyecto nuevo con un repositorio git
en /home/alicia/proyecto, y que Roberto, que tiene un directorio home en la misma
máquina, quiere contribuir.
Roberto comienza con:

rober$ git clone / home / alicia / proyecto mirepo

Esto crea un nuevo directorio “mirepo” con una copia del repositorio de Alicia. La
copia es exactamente igual al proyecto original, teniendo su propia copia del histórico
del original.
Roberto entonces hace algunos cambios y los confirma:

( editar ficheros )
rober$ git commit -a
( repetir si es necesario )

Cuando esté listo, le dice a Alicia que traiga sus cambios desde el repositorio en
/home/rober/mirepo. Para ello hace:

alicia$ cd / home / alicia / project


alicia$ git pull / home / rober / mirepo master

Esto mezcla los cambios de la rama “master” de Roberto en la rama actual de


Alicia. Si Alicia tenı́a cambios en los mismos ficheros, entonces probablemente necesite
solucionar manualmente los conflictos.
El comando “pull” realiza dos operaciones: recoge los cambios de una rama remota
y luego los aplica en la rama actual.
Téngase en cuenta que en general Alicia querrı́a confirmar sus cambios locales
antes de realizar el “pull”. Si el trabajo de Roberto tiene conflictos con el de Alicia,
ésta usará su árbol de trabajo y el ı́ndice para resolver los conflictos, y cambios locales
existentes podrı́an interferir con el proceso de resolución del conflicto (git realizarı́a
la operación de recogida de los cambios, pero no los mezcları́a — Alicia tendrá que
de algún modo librarse de sus cambios locales y luego traer los cambios de Roberto
de nuevo cuando esto ocurre).
Alicia puede echar una ojeada a lo que hizo Bobo sin necesidad de mezclar primero,
usando el comando “fetch”; esto permite a Alicia inspeccionar lo que Roberto hizo,
usando un sı́mbolo “FETCH HEAD” para determinar si tiene algo que merezca la
pena actualizar, de este modo:
1.7. Git como herramienta de colaboración 7

alicia$ git fetch / home / bob / myrepo master


alicia$ git log -p HEAD .. FETCH_HEAD

Esta operación es segura incluso si Alicia tiene cambios locales sin confirmar. La
notación de rango “HEAD..FETCH HEAD” significa “muestra todo lo que es alcan-
zable desde FETCH HEAD pero excluye todo lo que sea alcanzable desde HEAD”.
Alicia ya conoce todo lo que conduce a su estado actual (HEAD), y comprueba lo
que Roberto tiene en su estado (FETCH HEAD) que ella no ha podido ver con este
comando.
Si Alicia quiere visualizar lo que hizo Roberto desde que sus trabajos divergieron
puede hacerlo con el siguiente comando:

$ gitk HEAD .. FETCH_HEAD

Esto emplea la misma notación de rangos con dos puntos que vimos antes en git
log.
Alicia puede querer ver qué hicieron exactamente los dos desde que divergieron.
Puede usar lo notación de tres puntos en lugar de la de dos:

$ gitk HEAD ... FETCH_HEAD

Esto significa “muestra todo lo que es alcanzable desde cualquiera de ellos, pero
exclude lo que sea alcanzable desde ambos”.
Toma nota de que esta notación para el rango se puede emplear tanto para gitk
como para git log.
Tras inspeccionar lo que Roberto hizo, si no hay nada urgente, Alicia decide
continuar trabajando sin aplicar los cambios de Roberto. Si el trabajo de Roberto
tuviera algo urgente para Alicia, ella decidirı́a apartar sus cambios, descargar lo que
realizó Roberto y luego aplicar su trabajo en los ficheros resultantes.
Cuando se trabaja en un grupo pequeño y cercano, no es habitual interactuar con
el repositorio una y otra vez. Definiendo un repositorio remoto se simplifica:

alicia$ git remote add rober / home / rober / mirepo

Con esto, Alicia puede realizar la primera parte de la operación de “pull” por
separado usando el comando git fetch sin tener que mezclar los cambios con su
propia rama, usando:

alicia$ git fetch rober


1.7. Git como herramienta de colaboración 8

Al contrario que la forma más larga, cuando Alicia realiza “fetch” desde Roberto
usando el atajo con un repositorio remoto preparado con git remote, los cambios que
fueron descargados de almacenan en una rama con información remota, en este caso
rober/master. Ası́ que después de esto:

alicia$ git log -p master .. rober / master

enseña una lista de todos los cambios que Roberto ha hecho desde que creó su
rama a partir de la rama “master” de Alicia.
Tras examinar esos cambios, Alicia puede mezclarlos en su rama “master”:

alicia$ git merge rober / master

Este merge puede hacerse también actualizando desde su propia rama de segui-
miento remoto, de este modo:

alicia$ git pull . remotes / rober / master

Nótese que “git pull” siempre mezcla en la rama actual, sin importar ninguna otra
cosa en la lı́nea de comando.
Más tarde, Roberto puede actualizar su repo con los últimos cambios de Alicia
empleando

rober$ git pull

Nótese que no necesita dar la ruta al repositorio de Alicia; cuando Roberto clonó el
repositorio de Alicia, git almacenó la localización de su repositorio en la configuración
del repositorio, y esa localización es la usada para el “pull”:

rober$ git config -- get remote . origin . url


/ home / alicia /

(La configuración completa creada por git clone es visible usando git config
-l, y git-config explica el significado de cada opción.)
Git también mantiene una copia limpia de la rama “master” de Alicia bajo el
nombre “origin/master”:

rober$ git branch -r


origin / master
1.8. Explorando el histórico 9

Si Roberto decide más adelante trabajar desde un host diferente, todavı́a puede
realizar operaciones “clone” y “pull” con ssh:

rober$ git clone alicia . org :/ home / alicia / proyecto mirepo

De manera alternativa, git tiene un protocolo nativo, o puede emplear rsync o


http; mirar git-pull para más detalles.
Git también se puede emplear en un modo similar a CVS, con un repositorio
central al que varios usuarios envı́an sus cambios; mirar git-push y gitcvs-migration.

1.8. Explorando el histórico


El histórico en git está representado como una serie de commits inter-relacionados.
Ya hemos visto que el comando git log puede listar todos esos commits. Nótese que
la primera lı́nea de cada entrada del log también da un nombre para el commit:

$ git logcommit c 8 2 a 2 2 c 3 9 c b c 3 2 5 7 6 f 6 4 f 5 c 6 b 3 f 2 4 b 9 9 e a 8 1 4 9 c 7
Author : Junio C Hamano < junkio@cox . net >
Date : Tue May 16 17:18:22 2006 -0700

merge - base : Clarify the comments on post processing .

Podemos pasar este nombre a git show para ver detalles sobre ese commit.

$git show c 8 2 a 2 2 c 3 9 c b c 3 2 5 7 6 f 6 4 f 5 c 6 b 3 f 2 4 b 9 9 e a 8 1 4 9 c 7

Pero hay otros modos de referenciar commits. Puedes emplear cualquier trozo
inicial del nombre que sea suficientemente largo como para identificar de manera
inequı́voca al commit:

$ git show c82a22c39c # los primeros caracteres del nombre


suelen ser suficiente
$ git show HEAD # el atajo de la rama actual
$ git show experimental # el atajo de la rama ‘‘ experimental ’ ’

Todos los commits tienen habitualmente un commit “padre” que apunta al estado
previo del proyecto:

$ git show HEAD ^ # para mirar el padre de HEAD


$ git show HEAD ^^ # para mirar el abuelo de HEAD
$ git show HEAD ~4 # para mirar el tatarabuelo de HEAD
1.8. Explorando el histórico 10

Nótese que los commits de mezcla (operaciones “merge”) pueden tener más de
un padre:

$ git show HEAD ^1 # muestra el primer padre de HEAD ( igual que


HEAD ^)
$ git show HEAD ^2 # muestra el segundo padre de HEAD

También puedes dar tus propios nombres a los commits; tras ejecutar

$ git tag v2 .5 1 b2e1d63ff

puedes referenciar a 1b2e1d63ff con el nombre “v2.5”. Si tienes intención de


compartir este nombre con otra gente (e.g. para identificar una versión de release),
deberı́as crear un objeto “tag”, y quizás firmarlo; mirar git-tag para más detalles.
Cualquier comando de git necesita conocer un commit acepta cualquiera de estos
nombres. Por ejemplo:

$ git diff v2 .5 HEAD # comparar el HEAD actual con v2 .5


$ git branch stable v2 .5 # comenzar una nueva rama ‘‘ stable ’ ’
a partir de v2 .5
$ git reset -- hard HEAD ^ # resetear tu rama actual y
directorio de trabajo a HEAD ^

Ten cuidado con este último comando: además de perder cualquier cambio en
el directorio de trabajo, eliminará los commits más recientes de esta rama. Si esta
rama es la única que contiene esos commits, se perderán. Tampoco debemos usar git
reset en una rama visible públicamente de la que otros desarrolladores actualizan, ya
que obligará a realizar operaciones “merge” innecesarias para limpiar los históricos.
Si necesitas deshacer cambios que has confirmado, emplea git revert en su lugar.
El comando git grep puede buscar cadenas en cualquier versión de tu proyecto,
ası́ que

$ git grep ‘‘ hello ’ ’ v2 .5

busca todas las apariciones de “hello” en v2.5.


Si no se le indica un nombre de commit, git grep buscará en cualquiera de los
ficheros que administra en el directorio actual. Por lo que

$ git grep ‘‘ hello ’ ’


1.8. Explorando el histórico 11

es una manera rápida de buscar simplemente en todos los ficheros administrados


por git.
Muchos comandos de git aceptan conjuntos de commits, que se pueden especificar
de numerosas maneras. Aquı́ hay algunos ejemplos con git log :

$ git log v2 .5.. v2 .6 # commits entre v2 .5 y v2 .6


$ git log v2 .5.. # commits desde v2 .5
$ git log -- since = " 2 weeks ago " # commits de las últimas 2
semanas
$ git log v2 .5.. Makefile # commits desde v2 .5 que
afectaron a Makefile

También se le puede proporcionar a git log un “rango” de commits donde el


primero no es necesariamente ancestro del segundo; por ejemplo, si los extremos de
las ramas “stable” y “master” divergieron de un commit común hace tiempo, entonces

$git log stable .. master

listará los commits realizados en la rama “master” pero no en “stable”, mientras


que

$git log master .. stable

realizará a la inversa.
El comando git log tiene un contra: debe presentar los commits en una lista.
Cuando un histórico tiene lı́neas de desarrollo que divergieron y se juntaron de nuevo,
el orden en el que git log presenta esos commits no tiene sentido.
La mayorı́a de los proyectos con múltiples colaboradores (como el kernel de Linux
o el propio git) tienen operaciones “merge” frecuentes, y gitk realiza un mejor trabajo
para visualizar su histórico. Por ejemplo,

$ gitk -- since = ‘ ‘2 weeks ago ’ ’ drivers /

permite navegar por cualquiera de los commits de las últimas 2 semanas que
modificaron cualquier fichero bajo el directorio “driver” (Nota: la fuente de gitk se
puede ajustar presionando la tecla Ctrl y “-” o “+”.)
Finalmente, la mayorı́a de los comandos que toman nombres de ficheros opcional-
mente permitirán preceder cualquier nombre de fichero con un commit, para especi-
ficar una versión concreta del fichero:

$ git diff v2 .5: Makefile HEAD : Makefile . in


1.9. Siguientes pasos 12

También se puede usar git show para ver dicho fichero:

$ git show v2 .5: Makefile

1.9. Siguientes pasos


Este tutorial deberı́a ser suficiente para llevar un control de versiones distribuido
básico sobre tus proyectos. Pero, para comprender realmente la potencia y profundidad
de git debes entender las dos ideas básicas en que se basa:

La base de datos de objetos es un sistema elegante empleado para almacenar


el histórico de tu proyecto–ficheros, directorios y commits.

El fichero de ı́ndice es una caché del estado del árbol de directorio, usado para
crear commits, confirmar cambios en directorios de trabajo y almacenar los
diversos árboles vinculados en una operación “merge”.

La segunda parte de este tutorial explica la base de datos de objetos, el fichero


de ı́ndice y algunos otros conceptos que te permitirán sacar todo el partido a git. Se
puede encontrar en gittutorial-2.
Algunos comandos interesantes relacionados con git que te pueden resultar útiles
en este momento son:

git-format-patch, git-am: convierten series de commits de git en parches que


se pueden enviar por mail, y viceversa, útil para proyectos como el Kernel de
Linux que se apoyan fuertemente en parches enviados de ese modo.

git-bisect: cuando hay una regresión en tu proyecto, un modo de seguir el bug


es buscar a través del histórico el commit responsable. “Git bisect” te puede
ayudar a realizar una búsqueda binaria por ese commit. Este tipo de búsqueda
es una buena opción incluso en el caso de complejos históricos no lineales con
numerosas ramas mezcladas.

gitworkflows: da una visión general sobre flujos de trabajo recomendados.

Trabajar dı́a a dı́a con 20 comandos de git

gitcvs-migration: git para usuarios de CVS.


Capı́tulo 2
Un modelo de ramificación
exitoso

2.1. Introducción
En este apartado se recoge el modelo de trabajo propuesto por Vincent Driessen
y explicado en su blog [2].

Figura 2.1: esquema del modelo de ramificación de Vincent Driessen

13
2.2. ¿Por qué git? 14

Aplicado tanto en sus proyectos privados como públicos, ha demostrado ser un


modelo exitoso del empleo de git como herramienta de control de versiones para todo
el código. A partir de este punto todo el texto viene de boca de Vincent Driessen.

2.2. ¿Por qué git?


Para una discusión más profunda sobre los pros y contras de Git comparado con
sistemas centralizados de control de versiones de código, podemos mirar por la web.
Hay numerosas discusiones sobre el tema. Como desarrollador, prefiero Git sobre todas
las demás herramientas existentes. Git realmente ha cambiado el modo en que los
desarrolladores piensan sobre ramificar y mezclar código. Desde el clásico mundo de
CVS/SVN del que vengo, mezclar y ramificar siempre se han considerado peligrosos
(“¡Cuidado con los conflictos, te destrozarán!”) y algo que sólo se deberı́a hacer muy
de vez en cuando.
Pero con Git estas acciones son extremadamente sencillas y rápidas, y están consi-
deradas parte del trabajo diario, de verdad. Por ejemplo, en los libros de CVS/SVN, la
ramificación y la mezcla se explican en los últimos capı́tulos (para usuarios avanzados),
mientras que todos los libros de Git lo cubren entre los conceptos básicos.
Como consecuencia de su simplicidad y naturaleza repetitiva, no se debe seguir
teniendo miedo de ramificar o mezclar. Las herramientas de control de versiones deben
supuestamente asistir en las tareas de ramificado y mezcla más que cualquier otra
cosa.
Ya hemos hablado suficiente sobre las herramientas, entremos en el modelo de
desarrollo. El modelo que vamos a detallar es esencialmente un conjunto de procedi-
mientos que todo miembro del equipo debe seguir con el fin de conseguir un proceso
de desarrollo de software bien administrado.

2.3. Descentralizado pero centralizado


El modelo de repositorio que ha demostrado funcionar bien con este modelo de
ramificación y que empleamos es el de un repositorio central “verdadero”. Nótese que
este repositorio sólo es considerado el central (ya que Git es DVCS, por lo que no
existe un repositorio central desde un punto de vista técnico). Si nos referimos a este
repositorio como origin, ya que este nombre es familiar para todos los usuarios de
Git.
Todos los desarrolladores descargan y suben las modificaciones a origin. Pero
aparte de las relaciones centralizadas de “push”-“pull”, cada desarrollador podrá des-
cargar cambios de otras personas para formar sub-equipos. Por ejemplo, esto puede
ser útil para trabajar junto a otros desarrolladores en una caracterı́stica nueva, antes
de enviar los cambios en progreso a origin prematuramente. En la figura 2.2 en la
página siguiente, hay subequipos de Alice y Bob, Alice y David, y Clair y David.
2.4. Las ramas principales 15

Figura 2.2: esquema de coordinación entre repositorios

Técnicamente, esto simplemente significa que Alice ha definido un Git remoto,


llamado bob, apuntando al repositorio de Bob, y viceversa.

2.4. Las ramas principales


En su esencia, este modelo está enormemente inspirado en otros modelos existen-
tes. El repositorio central almacena dos ramas principales con una lı́nea de vida en
principio infinita:

master

develop

La rama master en origin será familiar para todo usuario de Git. De forma para-
lela a la rama master, existe otra llamada develop. Consideramos origin/master
como la rama principal donde el código fuente de HEAD siempre está en un estado
de versión de producción estable.
Consideramos origin/develop la rama principal donde el código fuente de
HEAD siempre refleja el estado de los últimos cambios realizados pendientes de aplicar
a la siguiente release. Algunos llamarı́an a esto “rama de integración”.
Cuando el código dentro de la rama develop alcanza un punto estable y está listo
para ser liberado, todos los cambios deberı́an ser aplicados en la rama master y
marcados con un tag con el número de versión. Más adelante discutiremos en detalle
este proceso.
2.5. Soportando ramas 16

Figura 2.3: flujo entre las ramas master y develop

Por lo tanto, cada vez que se aplican cambios en master, es una nueva versión
de producción por definición. Siendo muy estrictos en este aspecto, teóricamente
podrı́amos usar un script para automáticamente compilar y desplegar nuestro software
en nuestros servidores cada vez que hay un commit en master.

2.5. Soportando ramas


Además de las ramas master y develop, nuestro modelo de desarrollo usa una
variedad de ramas auxiliares para ayudar al desarrollo paralelo entre miembros de los
equipos, facilitar el seguimiento de las caracterı́sticas, preparar las liberación de nuevas
versiones y asistir en la solución rápida de problemas de producción. Al contrario que
las ramas principales, éstas siempre tienen una vida limitada, ya que en algún momento
se eliminarán.
Los tipos de ramas que podemos emplear son:

Ramas de caracterı́sticas del software (feature branches).

Ramas para releases (release branches).

Ramas para arreglos rápidos (hotfix branches).

Cada una de estas ramas tendrá un propósito especı́fico y estará sometida a reglas
estrictas como de qué ramas pueden nacir y con cuáles se pueden mezclar. Hablaremos
de ellas en un minuto.
2.5. Soportando ramas 17

De ningún modo estas ramas son especiales desde un punto de vista técnico. Las
ramas las categorizamos según cómo las empleamos. Todas son por supuesto simples
ramas de Git.

2.5.1. Feature branches


 Pueden nacer de: develop
 Deben mezclarse con: develop
 Convención de nombrado: cualquier cosa excepto master, develop, release-*
o hotfix-*
Las ramas de caracterı́sticas del software (algunas veces llamadas ramas de tópi-
cos) se usan para desarrollar nuevas caracterı́sticas para futuras releases. Cuando
comenzamos el desarrollo de una caracterı́stica, la release en la que planeamos incor-
porarla puede ser desconocida en ese momento. La esencia de esta rama es existir
mientras la caracterı́stica está en desarrollo, para en algún momento volver a mez-
clarse con develop (para añadir definitivamente la caracterı́stica a la release) o ser
descartada (en caso de ser un experimento fallido).
Estas ramas suelen existir sólo en los repositorios de los desarrolladores, no en
origin.

Figura 2.4: flujo de las ramas de caracterı́sticas (feature branches)

Creando una feature branch


Cuando comenzamos a trabajar en una caracterı́stica nueva, ramificamos desde la
rama develop.
2.5. Soportando ramas 18

$ git checkout -b myfeature develop


Switched to a new branch ‘‘ myfeature ’ ’

Incorporando una caracterı́stica terminada en develop


Las caracterı́sticas terminadas pueden ser mezcladas en la rama develop para
añadirlas definitivamente a la siguiente versión de release:

$ git checkout develop


Switched to branch ’ develop ’
$ git merge --no - ff myfeature
Updating ea1b82a ..05 e9557
( Summary of changes )
$ git branch -d myfeature
Deleted branch myfeature ( was 05 e9557 ) .
$ git push origin develop

El flag --no-ff hace que el “merge” siempre cree un nuevo objeto commit, incluso
si la operación fue realizada con “fast-forward”. Esto evita la pérdida de información
sobre la existencia histórica de la rama y agrupa todos los commits que conforman la
caracterı́stica nueva. Compara:

Figura 2.5: comparación de “merge” con fast-forward y sin él


2.5. Soportando ramas 19

En el último caso es imposible saber del histórico de Git cuáles de los objetos
commit juntos conforman la caracterı́stica - habrı́a que leer manualmente el log.
Revertir una caracterı́stica completa (i.e. un grupo de commits), es un quebradero de
cabeza en este caso, mientras que es mucho más fácil si se utilizó el flag --no-ff.
Sı́, creará unos pocos más objetos commit (vacı́os), pero la ganancia supera con
creces a las pérdidas.
Desafortunadamente, aún no conozco ningún modo de hacer que --no-ff sea el
comportamiento por defecto de git merge, pero deberı́a ser ası́.

2.5.2. Release branches


 Pueden nacer de: develop

 Deben mezclarse con: develop y master

 Convención de nombrado: release-*

Las ramas de releases soportan la preparación de una nueva release. Permiten


retoques de última hora, además de servir para la solución de pequeños bugs y la
preparación de los meta-datos (número de versión, fechas, etc.). Haciendo todo esto
en la rama de release, la rama develop está libre para recibir nuevas caracterı́sticas
para la siguiente release.
El momento clave para crear una nueva rama de release desde develop es cuando
develop (casi) refleja el estado deseado para la nueva release. Al menos todas las
nuevas caracterı́sticas que están enfocadas a release objetivo deben haber sido aplica-
das sobre develop en este momento. Todas las caracterı́sticas para futuras releases
deberán esperar a que esta rama release haya sido creada.
Es justo después del comienzo de la rama release cuando a la release se le asigna
un número nunca antes. Hasta ese momento, la rama develop reflejó los cambios
para la siguiente release, pero no está claro si la release será la 0.3 o la 1.0 hasta que
comienza la rama. Esa decisión se toma al principio de la rama release y se lleva a
cabo según las reglas de versionado del proyecto.

Creando una rama release


Las ramas release se crean a partir de la rama develop. Por ejemplo, supongamos
que la versión actual es la 1.1.5 y que próximamente se liberará otra. La rama develop
está lista para la siguiente release, y hemos decidido que será la versión 1.2 (en lugar
de la 1.1.6 o 2.0). Ası́ que ramificamos y le damos a la rama un nombre reflejando
su versión:

$ git checkout -b release -1.2 develop


Switched to a new branch " release -1.2 "
$ ./ bump - version . sh 1.2
2.5. Soportando ramas 20

Files modified successfully , version bumped to 1.2.


$ git commit -a -m " Bumped version number to 1.2 "
[ release -1.2 74 d9424 ] Bumped version number to 1.2
1 files changed , 1 insertions (+) , 1 deletions ( -)

Después de crear la rama y cambiarnos a ella, le asignamos el número de versión.


Aquı́, bump-version.sh es un script imaginario que realiza cambios en algunos fi-
cheros de la copia de trabajo para reflejar la nueva versión. (Esto puede ser cambiado
a mano, obviamente - lo importante es que algunos ficheros cambian) Entonces se
realiza el commit de la versión con el número asignado.
Esta nueva rama podrá existir por un tiempo, hasta que la release haya sido libera-
da definitivamente. Durante ese tiempo, arreglos a bugs serán aplicados en esta rama
(en lugar de en develop). Añadir caracterı́sticas nuevas importantes está totalmente
prohibido. Deben ser aplicadas en develop, y por tanto, esperar a la siguiente release.

Acabando con una rama release


Cuando una rama release está lista para convertirse en una release real, debemos
seguir algunos pasos. En primer lugar, la rama release debe ser aplicada sobre master
(ya que todos los commits en master son una nueva release, por definición). A
continuación, a ese commit en master se le debe poner un tag para referenciarlo en
el futuro en el histórico de versiones. Finalmente, los cambios realizados en la rama
de release deben ser mezclados de nuevo en develop, para que futuras releases sigan
conteniendo los cambios.
Los dos primeros pasos en Git:

$ git checkout master


Switched to branch ’ master ’
$ git merge --no - ff release -1.2
Merge made by recursive .
( Summary of changes )
$ git tag -a 1.2

La release ya ha sido realizada, y se le ha puesto un tag para futuras referencias.


Quizás quieras usar los flags -s o -u <clave> para firmar el tag criptográfica-
mente.
Pero para mantener los cambios realizados en la rama de release, tenemos que
mezclarlos de vuelta en develop. En Git:

$ git checkout develop


Switched to branch ’ develop ’
$ git merge --no - ff release -1.2
Merge made by recursive .
( Summary of changes )
2.5. Soportando ramas 21

Este paso puede llevar fácilmente a un conflicto, y en tal caso deberı́amos solu-
cionarlo y hacer commit.
Ahora ya hemos acabado realmente y la rama de release debe ser eliminar, ya que
no la necesitamos más:

$ git branch -d release -1.2


Deleted branch release -1.2 ( was ff452fe ) .

2.5.3. Hotfix branches


 Pueden nacer de: master

 Deben mezclarse con: develop y master

 Convención de nombrado: hotfix-*

Las ramas para arreglos rápidos se parecen mucho a las ramas de release en que
también están pensadas para preparar una nueva versión de release, pero en este
caso no planeada. Nacen de la necesidad de actuar inmediatamente ante un estado
no deseado en la versión actual en producción. Cuando un bug crı́tico de la versión
actual debe ser solucionado inmediatamente, una rama de arreglo rápido puede nacer
del tag correspondiente de la rama master que marca la versión en producción.
La esencia es que el trabajo de miembros de equipo (en la rama develop) puede
continuar, mientras otra persona está preparando una solución rápida.

Creando una rama de arreglo rápido


Las ramas para arreglos rápidos se crean a partir de la rama master. Por ejemplo,
digamos que la versión 1.2 es la release actual y que está causando problemas debido
a un bug grave. Pero los cambios en develop son todavı́a inestables. Entonces lo
aconsejable es crear una rama para arreglar dicho bug:

$ git checkout -b hotfix -1.2.1 master


Switched to a new branch " hotfix -1.2.1 "
$ ./ bump - version . sh 1.2.1
Files modified successfully , version bumped to 1.2.1.
$ git commit -a -m " Bumped version number to 1.2.1 "
[ hotfix -1.2.1 41 e61bb ] Bumped version number to 1.2.1
1 files changed , 1 insertions (+) , 1 deletions ( -)
2.5. Soportando ramas 22

Figura 2.6: flujo de las ramas de arreglos rápidos (hotfix branches)

No nos debemos olvidar de cambiar el número de la versión en el momento en


que ramifiquemos.
Entonces, arreglamos el bug y hacemos uno o más commits con su solución.

$ git commit -m " Fixed severe production problem "


[ hotfix -1.2.1 abbe5d6 ] Fixed severe production problem
5 files changed , 32 insertions (+) , 17 deletions ( -)

Acabando con una rama de arreglo rápido


Cuando se termina, la solución al bug debe ser mezclada en la rama master, pero
también necesita mezclarse de vuelta en develop, para asegurarnos de que el bug no
ocurrirá de nuevo en la siguiente release. Es un proceso análogo al realizado cuando
las ramas de release se terminan.
Primero, actualizamos master y ponemos el tag a la release.

$ git checkout master


Switched to branch ’ master ’
$ git merge --no - ff hotfix -1.2.1
Merge made by recursive .
( Summary of changes )
$ git tag -a 1.2.1
2.6. Resumen 23

De nuevo, quizás quieras usar los flags -s o -u <clave> para firmar el tag crip-
tográficamente.
Luego incluimos el arreglo también en develop:

$ git checkout develop


Switched to branch ’ develop ’
$ git merge --no - ff hotfix -1.2.1
Merge made by recursive .
( Summary of changes )

La excepción a esta regla es que, cuando actualmente existe una rama de release,
la solución al bug será mezclada en esa rama de release en lugar de en develop.
Aplicar la solución en la rama de release conduce a que en algún momento sea
también aplicado a la rama develop, cuando la rama de release se termine. (Si el
trabajo en develop requiere la solución al bug inmediatamente y no podemos esperar
a que la rama de release se termine, también podemos aplicar la solución en develop
sin problemas.)
Finalmente, eliminamos la rama temporal:

$ git branch -d hotfix -1.2.1


Deleted branch hotfix -1.2.1 ( was abbe5d6 ) .

2.6. Resumen
Aunque realmente no hay nada nuevo en este modelo de ramificación, la imagen
general en la que se basa ha demostrado ser tremendamente útil en nuestros proyectos.
Conforma un modelo mental muy elegante que es fácil de comprender y permite
a miembros de equipos compartir una única visión del proceso de ramificación y
liberación del software.
Capı́tulo 3
Comenzando a trabajar con
Git en Windows

En este apartado abordaremos el proceso de creación del repositorio para su poste-


rior funcionamiento en NetBeans. Dado que el repositorio no contiene un proyecto de
NetBeans completo1 son necesarios algunos pasos adicionales para lograr su correcto
funcionamiento.

3.1. Instalación de la herramienta


Descargaremos el entorno Git para Windows de la página del proyecto sysgit:

1. Nos descargamos la versión más reciente del entorno (los archivos con formato
Git-x.x.x.x) de su web oficial.

2. Durante la instalación se aconseja instalar las dos entradas del menú de contexto
en lugar del git-cheetah.

Podemos comprobar la correcta instalación del plugin si en el navegador de Win-


dows al hacer click derechon sobre una carpeta existen las opciones Git GUI Here y Git
Bash Here, a las cuales también se puede acceder por el menú de inicio a través de Git.

3.2. Inicialización del repositorio para NetBeans


A la hora de inicializar el repositorio para trabajar en NetBeans, la forma más
sencilla es disponiendo de una copia local de DeporXest como proyecto de NetBeans.
En tal caso se hará lo siguiente:
1
al no ser un repositorio exclusivo para desarrollo no es aconsejable incluir archivos de-
pendientes del IDE, ya que ensuciarı́an el repositorio con archivos innecesarios para muchos
usuarios que no empleen ese entorno.

24
3.2. Inicialización del repositorio para NetBeans 25

1. Iniciaremos la interfaz gráfica de Git (es indiferente si con el click derecho sobre
una carpeta cualquiera o por el menú de inicio), y seleccionamos Clone Existing
Repository.

2. Introducimos la URL del repositorio,


ssh://<user>@deporxest.git.sourceforge.net/gitroot/deporxest/deporxest,
sustituyendo ‘<user>’por el nombre de usuario en SourceForge.

3. Elegimos el directorio donde guardarlo (indiferente, ya que será temporal).

4. Antes de descargar nos pedirá la clave ssh de nuestra cuenta en SourceForge.

5. Copiamos todo el contenido de la carpeta que acaba de crear (incluyendo la


carpeta oculta .git) dentro de la carpeta del proyecto DeporXest de NetBeans
que ya tenı́amos anteriormente, sobreescribiendo todo.

Una vez completado, podemos comprobar que es correcto si al hacer click dere-
cho sobre la carpeta del proyecto y pulsar en Git GUI Here nos abre directamente el
repositorio y no la ventana inicial para crear, clonar y abrir repositorios.

En caso de no disponer del proyecto completo en local, serı́a necesario tomar


los siguientes pasos tras descargar el contenido del repositorio de manera similar a
la anterior, necesitando a pesar de todo una carpeta de un proyecto DeporXest de
NetBeans:

 Creamos un proyecto con el nombre que deseemos, y copiamos dentro los con-
tenidos descargados del repositorio.

 Copiaremos la carpeta nbproject de un proyecto NetBeans de DeporXest den-


tro del proyecto recientemente creado.

 Muy probablemente debamos copiar también las librerı́as que necesita el pro-
yecto, manteniendo la misma ruta que en la configuración original del proyecto
(podemos comprobar la ruta con Click derecho sobre el proyecto -> Properties
-> Libraries).

Una vez que la configuración de las librerı́as está lista, de nuevo podemos com-
probar que todo es correcto si Git GUI nos abre el repositorio en lugar de la pantalla
inicial.
Referencias

[1] Tutorial oficial de Git: http://www.kernel.org/pub/software/


scm/git/docs/gittutorial.html

[2] Blog de Vincent Driessen: http://nvie.com

[3] Tutorial de Git por Ben Lynn: http://www-cs-students.stanford.


edu/~blynn/gitmagic/intl/es/index.html

[4] Página del proyecto Sysgit en Google Code: http://code.google.


com/p/msysgit/

26

También podría gustarte