Mostrando entradas con la etiqueta versionado de codigo Android. Mostrar todas las entradas
Mostrando entradas con la etiqueta versionado de codigo Android. Mostrar todas las entradas

jueves, 12 de julio de 2012

Manejo de ramas de desarrollo con git


Git Header
En un post anterior hablamos de que Git es un sistema de control de revisiones distribuido amparado por la GPLv2 que enfatiza sobre todo la velocidad. Fue diseñado y desarrollado inicialmente por Linus Torvalds, creador de Linux como proyecto para mantener repositorios del kernel de Linux.
Cada directorio de trabajo git es un repositorio completamente funcional y con total capacidad independientemente de si existe acceso a la red y de un servidor centralizado, lo cual lo convierte en una poderosa herramienta tanto para el desarrollo de grupos de trabajo como para el desarrollo individual.
Git dispone de muchas características pero hoy voy a escribir sobre el manejo de ramas de desarrollo con git. Si estas familiarizado con Github sabras que la base de dicha red social de desarrollo es el “fork“ de ramas de desarrollo y su posterior “merge“. Git es treméndamente eficiente en ese aspecto.

Git Branching

El manejo de ramas o branching es la parte más atractiva y divertida de trabajar con Git. Cuando inicializamos o clonamos un repositorio, siempre obtenemos una rama principal por defecto llamada master a no ser que especifiquemos lo contrario.

Cambiando de rama

Supongamos que estamos trabajando en un proyecto y queremos agregar una nueva funcionalidad al mismo. La forma adecuada de hacerlo con Git es crear una nueva rama con el nombre de la nueva funcionalidad donde agregaremos nuestros cambios y después cambiaremos a ella (git branch switching).
$ git branch nuevafuncionalidad
$ git checkout nuevafuncionalidad
Existe un atajo para crear una rama que aún no existe usando directamente el comando checkou tque consiste en pasar el parámetro -b en la llamada.
$ git checkout -b nuevafuncionalidad
Una vez creada nuestra nueva rama de desarrollo, podemos comprobar en que rama nos encontramos utilizando el comando git branch
$ git branch
master
* nuevafuncionalidad
En el ejemplo anterior podemos ver como nos encontramos en la rama nuevafuncionalidad. Esto significa que si modificamos, borramos o agregamos archivos, ésta incluirá esos cambios pero no así la rama master. Supongamos que hemos agregado la nueva funcionalidad y hemos modificado un par de archivos. Vamos a hacer un commit.
$ git commit -a -m 'añadida nueva funcionalidad chula chula de la ostia'
Created commit: a6b18c4: añadida nueva funcionalidad chula chula de la ostia
  2 files changed, 6 insertions(+), 0 deletions (-)
Ahora tenemos que hacer un cambio en un archivo de la rama master para arreglar un bug que nos han reportado la gente que está testeando esa rama y debemos corregirlo de manera inmediata para que puedan seguir con su trabajo. Podemos volver a la rama master y agregar ahi los cambios sin preocuparnos de posibles inconsistencias con nuestra nueva rama nuevafuncionalidad
$ git checkout master
$ git commit -a -m 'arreglado el bug #00002345'
Created commit b89ae56: arreglado el bug #00002345
 1 files changed, 1 insertions(+), 1 deletions(-)
Ahora podemos ver las diferencias entre nuestras dos ramas usando el comando diff.
git diff --stat master nuevafuncionalidad
deliverance.py                      |      4 ++--
lib/nuevafuncionalidad.py           |      4 ++++
lib/test/test_nuevafuncionalidad.py |      4 ++++
3 files changed, 7 insertions(+), 2 deletions(-)
Además podemos crear un parche para aplicar uno al otro pero lo que realmente querremos hacer es mezclar ambas ramas, lo que se conoce como un branch merging

Mezclando Ramas

Hemos testeado profundamente nuestra nueva funcionalidad y estamos listos para moverla de la rama nuevafuncionalidad a la rama principal master. Dicha acción requiere que mezclemos el contenido de una de las ramas dentro de la otra, esto es lo que en Github llaman “Solicitud de Pull“ (realmente en Github lo que se hace es un git rebase pero esa funcionalidad la trataremos otro día).
Para realizar la mezcla de la rama nuevafuncionalidad dentro de master vamos a pararnos en la rama master y a mezclar ambas:
$ git checkout master
$ git merge nuevafuncionalidad
Como vemos esto es cualquier cosa menos traumática, realmente es muy sencillo mezclar ramas en Git. Una vez hemos mezclado nuestras ramas, y en caso de que no haya conflictos, podemos eliminar la rama nuevafuncionalidad por que no vamos a necesitarla más.
$ git branch -d nuevafuncionalidad

Resolviendo conflictos

¿Qué pasa si editamos el mismo archivo en ambas ramas y sobre las mismas líneas?. Pues que se generará un conflicto que tendremos que resolver a mano por que Git aunque es útil y potente, no tiene la capacidad de decidir que versión del código en conflicto es la correcta.
Imagina que hemos modificado el archivo deliverance.py en ambas ramas en las mismas líneas. Git generará un conflicto a la hora de la mezcla y siguiendo su política de no intrusión nos solicitará resolver el conflicto de manera manual.
$ git merge otrarama
Auto-merged deliverance.py
CONFLICT (content): Merge conflict in deliverance.py
Automatic merge failed; fix conflcits and then commit the result.
Como se puede apreciar, Git nos ofrece toda la información relevante para solucionar el conflicto y realizar un nuevo commit. El archivo problemático en este caso es deliverance.py.
    def send_delivery_request(request):
        self._server.request = request
< <<<<<< HEAD:deliverance.py
        self._rtime = 6000
===
        self._rtime = get_time_to_live(request)
>>>>>>> otrarama:deliverance.py
        # Send the request to the service
        self._server.send(self._rtime)
Podemos ver como hay un conflicto entre una y otra versión, mientras que en la rama principal se utiliza un valor hardcoded como tiempo máximo de respuesta, en el código de la rama otrarama se utiliza una función que devuelve un valor en base al request. Debemos de solucionar el problema y realizar un nuevo commit.
    def send_delivery_request(request):
        self._server.request = request
        self._rtime = get_time_to_live(request)
        # Send the request to the service
        self._server.send(self._rtime)
$ git add deliverance.py
$ git commit -m 'solucionando conflictos'
Created commit 17c688a: solucionando conflicto

Deshacer un Merge

En ocasiones veo muertos… no en serio. En ocasiones, querremos deshacer un merge por que la habremos cagado nosotros o otra gente de nuestro equipo de mala manera y encontraremos muchos conflictos a la hora de intentar seguir mezclando ramas o de trabajar normalmente. En esas ocasiones querremos deshacer de forma total el entuerto.
Git viene al rescate con el comando reset. Para hacer un reset de nuestro directorio de trabajo y volver atrás hasta antes de intentar mezclar las ramas, solamente debemos de ejecutar a nuestro buen amigo git reset.
$ git reset --hard HEAD
El parámetro --hard se asegura de que tanto nuestro índice de archivos como el directorio de trabajo cambien para que coincidan con lo que era antes del merge. Por defecto solo se restablece el índice, dejando los archivos parcialmente mezclados en el directorio de trabajo.
Si por casualidad has hecho un cambio y después de hacer un commit has decidido que era un error por que tus tests unitarios han empezado a romperse en mil pedazos (por ejemplo), aun puedes ir atrás y deshacer ese commit usando también reset.
$ git reset --head ORIG_HEAD
Esto es solo útil si lo que quieren es deshacer solo el último cambio. Si lo que quieres es deshacer un commit anterior a ese, entonces debes utilizar la herramienta revert que es un poco peligrosa y se aleja bastante del objetivo de este post.

Conclusión

Git nos ofrece una forma sencilla de trabajar con ramas de desarrollo en nuestros proyectos. Y esto es solo la punta del Iceberg, Git nos ofrece otras funcionalidades como el stashing o el rebase de las que hablaremos en próximos artículos. Hasta entonces, ¡saludos y buen code merging!

miércoles, 11 de julio de 2012

Git para dummies (pero dummies, dummies eh?)


Ese post ni es, ni lo pretende, ser un tutorial de Git. Es simplemente las impresiones de alguien (yo) que ayer empezó a usar, por primera vez, Git. Seguro que hay gente que lee ese blog y que sabe mucho, pero mucho más de Git que yo… Así que sus comentarios y correciones serán bienvenidas! :)
Esos días (ayer, vamos :p) he empezado a usar Git. Para quien no lo conozca Git es un sistema de control de código fuente distribuído. A diferencia de TFS, VSS o SVN que son centralizados, en Git cada desarrollador tiene su propia copia entera del repositorio en local. Los cambios son propagados entre repositorios locales y pueden (o no) sincronizarse con un repositorio central.
La diferencia fundamental con TFS ó VSS es que en esos está claro quien es el repositorio (el servidor). Eso no existe en Git. En Git cada usuario es el repositorioy se sincronizan cambios entre repositorios (usuarios). Opcionalmente puede usarse un repositorio central pero parece que no es obligatorio.
Los cambios en Git se propagan a través de esas operaciones:
  1. commit: Envia los datos del working directory al repositorio local
  2. push: Sincroniza los cambios del repositorio local a otro repositorio remoto.
  3. fetch: Sincroniza los cambios de un repositorio remoto al repositorio local.
  4. pull: Sincroniza los cambios de un repositorio remoto al working directory.
  5. checkout: Actualiza el workind directory con los datos del repositorio local.
La siguiente imagen lo clarifica bastante:Comandos de transporte de Git
Usar Git desde Visual Studio
Para usar Git desde VS2010 he encontrado las Git Extensions que instalan, no sólo un plugin para VS2010, sinó también el propio Git y clientes adicionales que pueden necesitarse (SSH o PuTTY si queremos conexiones seguras con nuestros repositorios).
Una vez instaladas nos aparecerá un menú nuevo llamado “Git” en el VS2010.
Crear un repositorio local y rellenarlo
Para empezar a trabajar con Git, lo primero es crear un repositorio. Recorda que los repositorios son locales, por lo que un repositorio es simplemente un directorio de nuestro disco. En mi caso, tenía ya una solución de VS2010 en local que es la que quería empezar a compartir con Git. Por ello lo que hice fue crear un nuevo repositorio local. Hay otra opción, que es crear un repositorio local a partir de los datos de un repositorio remoto (git-clone) pero todavía no lo he hecho. Si todo va como está planeado, el viernes tocará hacerlo, y ya escribiré al respecto!
Usando las Git Extensions crearnos nuestro propio repositorio es tan sencillo como usar la opción “Initialize new repository” y nos saldrá una ventana como la siguiente:
image
La opción normal es “Personal repository”. Un Central repository es un repositorio sin Working directory, que sólo sirve para sincronizar datos.
Una vez entrada la carpeta (en este caso D:\Gittest) esa pasa a ser el directorio de trabajo (working directory) para ese repositorio. Si abrimos la carpeta con el explorador de windows veremos que hay una carpeta en su interior llamada .git: ese es el repositorio local.
Nota: Dado que el directorio donde inicializamos el repositorio local pasa a ser el directorio de trabajo, lo suyo es inicializar el repositorio local en el mismo directorio donde tenemos la solución de VS. Es decir, si la solución de VS la tenemos en el directorio C:\projects\source\myproject, ese sería el directorio que usariamos (recorda que el repositorio se crea en una subcarpeta llamada .git, por lo que no modificarà ni borrará nada de la carpeta que le digáis).
En mi caso en D:\Gittest ya tenía una solución, por lo que el árbol de directorios me ha quedado:
image
Ahora vamos a hacer commit de la solución al repositorio local. Antes que nada, pero debemos tener presente de que no queremos que todos los archivos que cuelgan del working directory (D:\gittest) se guarden en el repositorio local. Git no entiende de tipos de archivo, no sabe que es código fuente y que no. Existe un fichero en el working directory llamado .gitignore que sirve para indicar que ficheros no deben guardarse en el repositorio local al hacer un commit.
Por suerte editar este fichero con las Git Exensions, es trivial. Nos vamos al menú Git y seleccionamos “Edit .gitignore”. Nos aparecerá una ventana parecida a:
image
A la izquierda el contenido del .gitignore (en este caso vacío, normal ya que ni existe el archivo). A la derecha un ejemplo de .gitignore adaptado a VS. Si pulsais el botón “add default” copiará las entradas de la derecha a la izquierda:
image
Miremos que el .gitignore se ha confiugrado para evitar archivos como *.exe, *.pdb, etc, pero también directorios como TestResult* (que usa VS para guardar los resultados de las pruebas unitarias) o _Resharper* (que usa resharper para guardar sus configuraciones). Nosotros podríamos añadir más entradas y finalmente darle a “Save”. Eso nos creará el archivo .gitignore en nuestro working directory (D:\gittest).
Ahora estamos listos para hacer el commit y rellenar por primera vez el repositorio local. Para ello, de nuevo nos vamos al menú Git y seleccionamos “Commit”. Nos aparecerá una ventana parecida a:
image
Se divide en cuatro secciones:
  1. Izquierda superior: listado de operaciones pendientes de realizar (cambios entre el working directory y el repositorio local)
  2. Izquierda inferior: listado de operaciones que se van a realizar (es decir cuales de los cambios del working directory queremos propagar al repositorio local).
  3. Derecha superior: preview del archivo seleccionado en la parte (1).
  4. Derecha inferior: comandos de Git.
En este caso vamos a propagar todos los cambios, para ello pulsamos el botón “Staged Files” y en el menú desplegable marcamos “Stage all”. Con eso todos los ficheros de la izquierda superior pasarán a la izquiera inferior. Ahora vamos a realizar el commit (si quisieramos podríamos hacer también un push a un repositorio remoto pero todavía no hemos configurado ninguno).  Así que entramos un mensaje de commit en la parte derecha inferior (p.ej. commit inicial) y le damos al botón “Commit”. Git Extensions nos mostrará al final un diálogo con el resumen de lo hecho:
image
Añadir cambios
Bien, una vez hemos hecho el commit podemos seguir trabajando con VS2010 sin necesidad de hacer nada especial. Olvidate de conceptos como “proteger” o “desproteger” de TFS o VSS. Simplemente trabaja modifica el contenido del directorio de trabajo.
Cuando decidas propagar los cambios del directorio de trabajo al repositorio local, repetís la operación de antes: Git –> Commit.
Repositorio remoto
Bien, ha llegado la hora de configurar un repositorio remoto. Para ello, lo primero que necesitamos es tener acceso a un repositorio remoto. Hay varios proveedores de repositorios Git en internet, entre los que destaca GitHub. GitHub pero está limitado a proyectos open source. Si neceistas un hosting de Git para proyectos no open source hay varios de pago (con algunas versiones gratuitas). En esta consulta de StackOverflow hablan de ello: Best git repository hosting for commercial project.
Nota: A título informativo diré que yo me dí de alta en el plan gratuito de Assembla, que te da 2GB de espacio Git.
La configuración de un repositorio remoto dependerá de donde lo tengamos, pero básicamente son dos pasos muy simples:
  1. Generar el par clave pública-privada para ssh o PuTTY y subir la clave pública al repositorio (si queremos acceso seguro).
  2. Ponerle a Git Extensions la URL del repositorio remoto.
Vamos a ver una demostración. Para independizarnos de cualquier proveedor, vamos a crear otro repositorio de Git en nuestra máquina (podríamos hacerlo en una carpeta compartida p.ej.) y lo usaremos como repositorio remoto.
Para esto de nuevo nos vamos al menú Git y le damos a “Initialize new repository” y ahora marcamos la opción de Central Repository. Y ponemos cualquier path nuevo que creemos (en mi caso he creado una carpeta D:\Remote):
image
Si ahora vamos a D:\Remote veremos que allí tenemos el repositorio. En este caso el repositorio no está en un subdirectorio .git, porque hemos elegido la opción de Central repository que no tiene directorio de trabajo.
Bien, ahora vamos a hacer un push de nuestro repositorio local al repositorio remoto. Para ello, primero, debemos dar de alta este repositorio remoto en las Git Extensions. Para ello nos vamos al menú Git y seleccionamos la opción de “Manage Remotes”. Nos aparecerá una ventana y ponemos los datos:
image
El nombre es un nombre identificativo, mientras que la URL en este caso es la carpeta donde está el repositorio. Finalmente le damos a “Save” para guardarlo.
Ahora ya podemos hacer un push para pasar los datos del repositorio local al remoto. Para ello, de nuevo nos vamos al menú Git y marcamos la opción Push. En la ventana que nos aparece marcamos la opción “Remote” y de la combo seleccionamos el repositorio remoto que dimos antes de alta:
image
Luego pulsamos el botón Push. Como antes nos mostrará un diálogo cuando haya finalizado:
image
Navegar por repositorios
Podemos navegar por un repositorio, usando la opción “Browse” del menú Git. Seleccionamos el repositorio y podemos navegar, ver los commits/push que se han realizado y ver los archivos y cambios contenidos en cada commit/push. De todos modos sólo he visto como hacer esto en repositorios (locales o remotos) que estén en disco. No sé como navegar por mi repositorio en Assembla p.ej.
image
Y aquí lo dejamos por el momento… A medida que trabaje y que vaya aprendiendo como funciona Git iré poniendo más información al respecto!
Espero que este post haya sido útil y que en todo caso haya servido para que veas que es Git y las diferencias con otros sistemas de control de código fuente como TFS.
Un saludo!