miércoles, 14 de septiembre de 2011

Branch remotos en git, dos pasos. ¿Y despues? (version Tortoise)

Anteriormente, en gitevangelism, hice un post que explica como crear un branch remoto en dos pasos, aca un resumen:

Crear un branch de manera local



Publicar el branch


Despues, hay que trabajar con el branch, hay cosas que no resultaran tan triviales al principio

Trabajar en el branch, en los branches

Si se ejecutaron los comandos para creacion de branch explicados, habra quedado activo el nuevo_branch, sino se esta seguro se puede revisar el menu contextual de tortoise git, asi:

Si el branch en el que queremos trabajar no es el activo, entonces utilizamos la opción de switch/checkout (accesible desde el menu contextual de tortoise git)


A partir de entonces, todos los commits seran dirigidos al branch activo, actualizando el branch para que apunte a cada commit que se hace.

Si por alguna razon, es necesario dejar el trabajo que se esta realizando en este branch para trabajar en otro (por ejemplo, en master), se utiliza el comando checkout para reactivar master


Si hay cambios no commiteados, seguramente el comando se negara a cambiar el branch activo, si ese fuera el caso hay que commitearlos o stashearlos (stash es un commit temporal que se usa para guardar el indice y los cambios no commiteados y que despues se puede recuperar):

Para volver, otra vez hay que utilizar otra vez la opcion switch/checkout y si se tuvo que salvar los cambios mediante stash, hay que recuperarlo haciendo

Por ultimo, se debe hacer un push, este sube cada branch trackeado a su correspondiente branch remoto (en este caso subiria el master local al master remoto y el nuevo_branch local al nuevo_branch remoto)

Colaboracion

Si se necesita que otra persona contribuya al branch, desde otra maquina, tiene que trackearlo, primero haciendo un pull o fetch, y despues con la opcion switch/checkout también:


NOTA: el checkout a remotes/origin/demo_branch solo es necesario la primera vez que se trabaja con el branch para crear el branch local que trackea, el resto de las veces un checkout al branch local alcanza

Despues, trabajar de esa manera es lo mismo que trabajar al master, con la diferencia (claro esta) que es el nuevo_branch el que recibe los cambios que se pushean

Integrar los cambios

Esta es la manera mas practica de integrar, que utiliza el 3-way-merge (otro dia hago un articulo explicando eso), no hay mas que hacer que lo siguiente:

Activar el branch destino del merge, es decir al que se va a integrar


Utilizar la opcion merge del tortoise

Elegir el branch que mergear y las opciones (es conveniente por ahora dejar las default, que son todas desmarcadas)





Branch remotos en git, dos pasos (la version Tortoise)

En dos pasos se puede crear un branch remoto

PASO 1: Crear branch de manera local (esto no es necesario si ya existe el branch local)


NOTA: en switch to, se puede seleccionar cualquier otro branch (en este caso el master), cualquier tag o cualquier commit arbitrario, en ese caso el branch se creara a partir de ese commit.

PASO 2: Publicar el branch


También se puede trabajar en el branch local antes de publicarlo.

miércoles, 7 de septiembre de 2011

Beyond git svn clone: Svn Survival Kit Part I

Si alguna vez hay que trabajar con un repositorio svn...

Comandos basicos


Clonar un repositorio svn (equivalente a git clone)
git svn clone [url]

Bajar últimos cambios del repositorio svn (equivalente a git fetch)
git svn fetch

Bajar ultimos cambios e integrar con cambios propios (equivalente a git pull --rebase)
git svn rebase

Commitear cambios (equivalente a git push)
git svn dcommit

Mantener linealidad

Mientras git es un versionador diseñado para representar cambios no-lineales, la estructura de svn representa cambios lineales, esto en terminos de usar git con svn significa que siempre sin excepción los cambios que se hacen al master local deben ser lineales, es decir que no se permiten merge commits, en el workflow normal de git-git es frecuente que se generen merge commits cuando se hace pull de cambios que divergen, pero esa operacion se reemplaza por git svn rebase en el workflow de git-svn, la cual mantiene la linealidad re-aplicando los commits divergentes locales en la rama remota.

Eso quiere decir que si uno commitea B despues de A en la rama local, y otro commitea C despues de A, cuando el primero hace git svn rebase quedara lineal, asi:

A -C - B'

Mientras que en git queda la ramificacion de C y B y el merge commit que une ambas lineas divergentes.

Local Branches: De manera local se puede diverger

No hay ningun problema en hacer branches locales, el unico impedimento es que esos branches no se pueden publicar al servidor svn de una manera practica (existe una forma, pero es tan engorrosa que es mejor no utilizarla en un principio).
Lo que es muy importante tener en cuenta, es que si se necesita integrar esos branches a la rama principal con el objetivo de commitearlo a svn hay que hacerlo preservando la linealidad de la rama principal, esto se puede hacer con un squash merge (es la opcion q mas me gusta) o con rebase, rebase es mas complicado pero mantiene todo el historial del branch y yo en lo personal no lo considero conveniente por razones que voy a explicar mas adelante.

Local Merges al trunk: Mantener linealidad --squash

Suponiendo que hay que mergear un branch al master, hay que hacerlo de esta manera:
git merge --squash nombre_del_branch
Ese comando pone el resultado del merge en el indice y la working copy , posteriormente hayque commitear con el mensaje que se considere apropiado y por supuesto ese commit no rompera la linealidad. Esto mezcla todos los cambios del branch en un solo commit

Local Merges al trunk: Mantener linealidad con rebase

Rebasear el branch al master y despues mergearlo (debe ser fast-forward)

git checkout nombre_del_branch
git rebase master
git checkout master
git merge --ff-only nombre_del_branch

El primer comando cambia el branch actual a nombre_del_branch, el segundo lo rebasea a master (es decir, mueve todos los commits divergentes para que esten linealmente a continuacion de master), vuelve a master otra vez y mergea el branch rebaseado, la opcion --ff-only es para indicar que solo haga el merge si es fast-forward

Remote branches: Mejor NO

Da problemas especialmente cuando se elimina un branch y tiende a ser complejo, podria servir como una herramienta para manipular branches de svn de manera mas facil pero generalmente trae complicaciones si uno no esta ducho con la combinacion git-svn

Hacer commit frecuente pero dcommit no tan frecuentes ni tan pesados

Normalmente en git no es un problema hacer push de varios commits, pero efectuar un dcommit que envie muchos commits a svn no conviene. La razon es que git svn dcommit convierte cada commit local de git en un commit de svn y lo envia, este proceso es muy costoso en terminos de network en comparacion del git push de siempre (esto sin contar posibles problemas de race condition mas probables en dcommits que tarden mucho).
Por eso lo mejor siempre es tratar de enviar de a uno o como maximo dos o tres commits a la vez (por eso me parece mejor usar el squash merge para los branches), si se avanzo demasiados commits en el repo local lo mejor sera fusionar esos commits en unos pocos o incluso uno solo si tiene sentido (se puede guardar el historial completo en un tag o branch local)

Debo los links


Git Agile: Push automatico

Parte de la adaptación de git a agile es habilitar la posibilidad de que los push sean automaticos, como en el modo autosync de fossil
La forma mas simple de realizar esto seria editar .git/hooks/post-commit (deje los comentarios del script a propósito)
#!/bin/sh
#
# An example hook script that is called after a successful
# commit is made.
#
# To enable this hook, rename this file to "post-commit".

echo Doing automatic push... edit .git/hooks/post_commit to disable it
git push

Por supuesto que después de hacer eso se encontraran varias razones para invocar push manualmente, como por ejemplo crear un nuevo branch remoto. No obstante un git push después de cada commit cubre la mayoría de los casos.