Está en la página 1de 13

Git...

come si mangia
Distribuido...
Se dice de git que es un sistema de control de versiones distribuido.
Que sea distribuido implica que van a existir al menos tantos repositorios como personas trabajando en el proyecto, a lo que se le pueden sumar
uno o ms repositorios que se usen como intermediarios para compartir cambios entre las distintas personas (en FDV tenemos un servidor de
repositorios git en la gringa para este fin).

DAG lo qu?
Cmo es un repositorio git?
Veamos primero un dibujito:

El modelo que implementa un repositorio git es un grafo dirigido acclico (DAG).


Cada uno de los crculos representa un objeto almacenado en el repositorio.
Los rectngulos son referencias que sirven como punto de entrada a los objetos que viven en el repositorio.
Los distintos colores de los circulos representan cada uno de los 4 tipos de objetos que existen:
blob
representa un archivo en el proyecto.
tree
representa una carpeta en el proyecto, el primero corresponde al directorio raiz del proyecto
commit
la forma de agregar objetos al repositorio es con la opercin commit. Esto agrega un objeto commit que tiene un vrtice hacia un tree

que representa el directorio raz del proyecto. El sub-grafo que comienza en este tree representa el estado de todo el proyecto tras la
ejecucin del commit. En el dibujo se ve que los subgrafos que parten de cada commit comparten un objeto blob, esto significa que el
commit B no modific el archivo que agreg el commit A.
El commit tambin conoce a su padre que es el commit que lo antecede, entonces los cambios que aplica un commit al proyecto son las
diferencias entre los sub-grafos que representan el file system de un commit y el de su padre.
Cuando se ejecuta la operacin merge se qenera un commit que tiene dos padres.
tag
la operacin tag genera un objeto tag en el repositorio y una referencia de tipo tag para accederlo.
Un tag conoce un commit y a partir de este el estado del proyecto para ese commit.
En su cuerpo tambin uncluye un ttulo un comentario y opcionalmente se le puede agregar una firma gpg.
Todos los objetos (crculos en el dibujo) tiene un nombre bastante feo para identificarlos (algo as como
8357799df0b47164c9726be6610ea1b7ed41ff32), este nombre es el resultado de aplicar un funcin de hash (SHA1) al objeto.
Referencias:
Las referencias son los rectngulos en el dibujo, son simplemente puntos de entrada a los objetos del repositorio.
Referencia de tipo tag (amarillo en el dibujo) apunta a un objeto de tipo tag y es inmutable.
Referencia de tipo branch (verde en el dibujo) apunta a un ojeto commit, es mutable y al commit al que apunta es al * ltimo que se
agreg para ese branch.
Referencia de tipo branch remoto (gris en el dibujo) es muy parecida a un branch slo que se usa para seguir el estado de un branch en
otro repositorio (recuerden que esto era distribuido). La del dibujo (origin/master) significa que sigue un branch llamdo master en un
repositorio llamado origin. El nombre del repositorio es un shortcut de la url completa.
HEAD esta referencia la usa git para saber que commit ser el padre del prximo commit, normalemnte apunta indirectamente a un
commit a travez de un branch. Si almomento de ejecutar la opercin commit el HEAD apunta a un branch el branch se actualiza para
apuntar al nuevo commit.

Todo muy lindo... pero cmo lo uso?


Bueno... primero lo tendras que tener instaldo. Eso se explica ac.
Ahora que ya tengo git intalado y configurado podemos ver un ejemplo partiendo de un proyecto nuevo.
En la consola (git bash si ests en windows)

$ mkdir ~/prueba
$ cd ~/prueba
$ git init

Esto inicializ un repositorio vaco, todo el repositorio est en un la carpeta ~/prueba/.git


El directorio raz del proyecto es ~/prueba
Todo el contenido de ~/prueba menos la carpeta .git es lo que en git llaman working tree. Es similar a la working copy de svn.
El repositorio no tiene ningn objeto (crculos del dibujo) pero s, tiene la referencia HEAD que apunta a ref/heads/master esto siginifica que
apunta a un branch llamado master.
Un branch no es otra cosa que una referencia a un commit, pero como todava no existe ningn commit tampoco existen branches.
Vamos a crear nuestro primer commit

$ cd ~/prueba #carpeta raz del proyecto


$ touch unTexto.txt #creo un archivo
$ git add unTexto.txt #agrego el archivo al index de git
$ git commit -m "primer commit" #ejecuto la operacin commit pasndole el comentario "primer
commit"

Basicamente la forma de trabajo es:


Modifico el contenido del working tree (agrego y modifico archivos)
Agrego alguno o todos los cambios al index (git add)
Ejecuto un commit (git commit)
Los cambios que aporta el commit es todo los que est en el index.
Y qu es el index? Es un lugar donde voy acomodando los cambios que van a entrar en el prximo commit. Tambin llamado staging, pero en
casi toda la documentacin de git aparece como index.
como qued el grafo de nuestro repo:

Ahora s existe el branch master que apunta al nico commit que existe (commit A). El sub-grafo que comienza en el tree al que apunta el
commit slo tiene dos nodos un tree y un blob. El tree se corresponde con la carpeta raiz del proyecto y el blob con el archivo unTexto.txt.

Y si compartimos?
Para compartir con otros el proyecto lo que hace falta es que haya ms repositorios que compartan sus objetos (Crculos de los dibujos)
La forma que usamos en fdv es crear un repositorio en el servidor de repos que vive en la gringa para compartir el proyecto con el resto.
La ventaja de hacer esto es que los repos de la gringa estn includos en el script de backup de fdv.
vamos a crear un repo local que simule ser el de la gringa para poder seguir el tutorial. En la vida real este paso no existe.

$ cd ~
$ mkdir pruebaPublico.git
$ cd pruebaPublico.git
$ git init --bare
#el --bare es para inicializar un repo pelado (sin working tree)

volavmos a nuestro repositorio, haciendo de cuenta que lo anterior nunca pas.

$
#
#
$
#
$
#
$
#
$

cd ~/prueba
agregamos un repositorio remoto al que llamaremos publico
si estoy en windows
git remote add publico file:///c/Documents\ and\ Settings/miUsuario/pruebaPublico.git
si estoy en linux
git remote add publico file:///home/miUsuario/pruebaPublico.git
si estoy en mac
git remote add publico file:///Users/miUsuario/pruebaPublico.git
vamos a pasar los objetos de nuestro repositorio al pblico
git push -u publico master

El comando push es para pasar objetos de mi repo local un repo remoto.


El parmetro -u es para decirle a git que de ahora en ms qiero que el branch master de mi repo siga al branch mester del repo pblico. Esto
sirve para pasar menos parmetros en el futuro.
veamos como qued el grafo de nuestro repo:

Lo nico que cambi es que agregamos una referenciar gris (branch remoto) que nos dice que la ltima vez que hablamos con el repositorio al
que llamamos "publico" tena un branch llamado master que apuntaba al commit A
Ahora vamos a simular que somos otro usuario que va a meter mano en el mismo proyecto:

$ cd ~
$ git clone pruebaPublico.git pruebaElOtro
#
#
#
#
#
#

pruebaPublico.git en la vida real se cambiara por la


url al repo pblico normalmente sera algo as:
ssh://gitosis@git.fdvs.com.ar/prueba.git
el segundo parmetro es el nombre de la carpeta donde se va a
clonar el repo si no se pasa nada se usa el mismo nombre que
tiene el original que estamos clonando

En este momento tenemos tres repositorios:


~/prueba con el que empezamos todo
~/pruebaPublico.git el que simula ser el que est en la gringa, o github o donde sea que est
~/pruebaElOtro con el que vamos a hacer de cuenta que somos otro en cualquier otra mquina
Veamos como se ven sus grafos:

En este momento los tres repos tienen los mismos objetos y cada uno tiene un branch llamado master.
El ltimo, como lo creamos a partir de clonar el repositorio pblico ya tiene un repositorio remoto al que llama "origin" del que tiene una referencia
(el branch master del repositorio de origen, en este caso el que est en ~/pruebaPublico.git)
Bueno... ahora vamos a meter algunos cambios al proyecto.

$ cd ~/prueba
$ echo "un texto" > unTexto.txt
$ git commit -a -m "agrego un texto"

El -a es para ahorrarme hacer el git add antes del commit.

Estado de los repos:

Lo que pas es que se agreg a mi repositorio un nuevo commit con su subgrafo que representa el estado del proyecto, un tree (el directorio
raz) y un blob (el archivo unTexto.txto con un contenido distinto al que tena en el primer commit)
Estoy muy conforme con mis cambios as que los voy a pasar al repo pblico

$ cd ~/prueba
$ git push
#como no pas ningn parmetro al push es equivalente a git push publico master (ver lo que deca
de -u)

Veamos el efecto en los grafos:


A partir de aqu voy a simplificar los grafos sacando todos los trees y blobs para que no metan confusin.

El efecto del pus:


para el repo local: cambio la referencia publico/master
para el repo remoto: se agregaron los objetos nuevos y se actualiz la refernecia "master"
Ahora vomos a simular se "El Otro" que est trabajando en el proyecto

$
$
$
$

cd ~/pruebaElOtro
echo "soy el otro" > elOtro.txt
git add .
git commit -m "agrego el Otro texto"

Bueno... esto intenta reflejar los cambios que pruduce "El Otro"
As quedan los grafos:

En este momento "El Otro" se siente muy contento con su trabajo y lo quiere compartir..
Es la hora de hacer un push

$ cd ~/pruebaElOtro
$ git push

Ops! el push fall, me dice que no me conviene hacer un push en esta situcin porque se agragaron objetos en el repo remoto que todava no
existen en le repo local.
vamos a traer los cambios del repo remoto

$ cd ~/pruebaElOtro
$ git fetch

El estado de los grafos:

Ya tenemos en el repositorio local de "El Otro" los cambios que estaban en el repositorio remoto, pero todava no estamos en condiciones de
subir nuestros cambios porque el branch master del repo pblico tom un camino diferente al branch master del repositorio local.
Para salir adelante de esta situacin hay dos alternativas.
La primera es hacer un merge

$ cd ~/pruebeElOtro
$ git merge origin/master

El estado de los grafos:

El merge agreg un nuevo commit (el D) que tiene como padres al D y al B. Ya podemos enviar nuestros cambios al repo pblico.
Existe tambin la operacin pull que es el equivalente de lo que hicimos recin con el fetch ms el merge.

$ cd ~/pruebeElOtro
$ git push

El estado de los grafos:

Algunas consideraciones
Si bien estuvimos usando al repositorio que creamos en la carpeta ~/pruebaPublico.git como intermediario, esto no significa que sea necesario
usarlo as. Cualquier repositorio puede intercambiar objetos con cualquier otro. El ejemplo est hecho para parecerse a como lo usaramos en
fdv.
Cuantos branches existen?
Si bien en todos los repos que usamos hay un branch llamado master, esto no significa que haya un slo branch. En realidad hay tres branches
llamados master (uno en cada repositorio). El nombre master es el nombre que le pone git al branch que se crea con el primer commit por lo que
lo ms comn es usar el branch master como la lnea de de evolucin principal del proyecto, algo parecido al trunk del svn.
Pero que son los branches?
En el dibujo son los rectangulos verdes y son simplemente una referencia a un commit.

Branches y ms branches
Supongamos que ElOtro quiere trabajar en un nuevo feature (featureLoco). Para esto se crea un branch.
Crear un branch por cada feature puede ser una buena idea. Realizar merges entre branches no es distinto al merge que hicimos en el paso
anterior.
Creamos el branch

$ cd ~/elOtro
$ git branch featureLoco
$ git checkout featureLoco

qu cambi?:

git branch featureLoco agreg el branch (el rectngulo verde)


git checkout featureLoco hizo que el head apunte al branch featureLoco y actualiz el working tree a la versin que conoce el commit E. En este
caso el working tree no cambia porque el nuevo branch apunta al mismo commit que el branch al que apuntaba el HEAD antes de realizar el
checkout.
"El otro" comienza a trabajar en el feature loco.

$
#
$
#
$
#
$

cd ~/pruebaElOtro
comienzo el desarrollo del feature loco
echo "Esto es una locura" > locura.txt
agrego al index (staging) mi trabajo
git add locura.txt
agrego un commit con mi trabajo (lo que est en staging)
git commit -m "agrego locura"

En este momento "El otro" quiere compartir su trabajo por lo que lo va a pasar al repo pblico

$ cd ~/pruebaElOtro
# hago el push dicindole a git que este branch va a segir a otro con el mismo nombre en el repo
remoto
$ git push -u origin featureLoco

"El otro" nos pidi una mano con el feature loco y nos dijo que ya haba empezado a hacer algo as que vamos a traer los cambios que existen
en el repo pblico, pero ahora ya no como el otro sino como nosotros mismos.

$
$
#
#
$
$

cd ~/prueba
git pull
recuerden que pull es una sucecin de fetch y merge.
fue una forma resumida de hacer
git fetch
git merge publico/master

y que pas?

En este caso el merge no genr ningn commit. por qu?

El merge era entre el commit B (a donde apuntaba el branch master) y el commit E (a donde apuntaba la referencia publico/master. Como el
commit B es un ancestro de E, entonces E incluye los cambios de B por lo que no hace falta ningn commit adicional. Simplemente alcanza con
"adelantar" el branch master a el commit E. A estos commits git los llama fast forward commits (por eso lo de adelantar).
Y que pas con el nuevo branch?
En mi repo local no tengo ningn branch llamado featureLoco. Lo que s tengo es una referencia nueva que me dice que en el repo remoto al
que llamo publico tiene un branch llamado featureLoco y que apunta al commit F (El rectngulo gris)
Bueno pero queremos trabajar en el mismo feature as que deberamos crear un branch local que siga al branch featureLoco del repo publico.
La forma ms simple de hacer esto es:

$ cd ~/prueba
$ git checkout featureLoco

Estamos haciendo un checkout a un branch que no existe, pero git se da cuenta que hay un con el mismo nombre en el repo publico y crea uno
al que le agrega la metadata (esto est en .git/config) de que nuestro branch featureLoco sigue a uno del mismo nombre en el repo llamado
publico.
La forma larga de hacer lo mismo

$ git branch -t featureLoco publico/featureLoco


$ git checkout featureLoco

El checkout implica cambiar el HEAD y el working tree.

Bueno... ya tanto "El otro" como yo estamos trabajando en el feature loco.


Supongamos que pas un poco de tiempo y ambos agregamos algunos commits.

El otro hace un push para subir sus cambios

$ cd ~/pruebaElOtro
$ git push

Ahora nos queda a nosotros resolver el merge entre los cambios de "El Otro" y los nuestros.
Cmo hago? Fcil... hago un pull (que resuma fetch ms merge). Pero si recuerdan antes de hacer el primer merge les dije que exista otra
forma de resolver esta situcin.
La otra forma es una operacin llamada rebase (como alternativa del merge).
Vamos a hacerlo en dos pasos, primero fetch y despus rebase

$ cd ~/prueba
$ git fetch

Con esto trajimos los cambios del repo pblico y actualizamos las referencias remotas

Si hicieramos el merge entre featureLoco y publico/featureLoco se agregara un nuevo commit que tiene como padre a H y J. Pero vamos a ver
que pasa si hacemos un rebase.

$ git rebase publico/featureLoco

El resultado:

También podría gustarte