6 Control de versiones con git y GitHub

6.1 Objetivos

En este capítulo veremos:

  • ¿Por qué git es útil para análisis reproducibles?
  • ¿Cómo usar git para registrar los cambios que se hacen en el tiempo?
  • ¿Cómo usar GitHub para colaborar?
  • ¿Cómo estructurar los “commits” para que los cambios sean claros para otros?
  • ¿Cómo escribir mensajes de “commits” que sean efectivos?

6.2 El problema con los nombres de archivo

El dilema de usar nombres como descriptor de versiones.

Figure 6.1: El dilema de usar nombres como descriptor de versiones.

Cada archivo en un proceso científico sufre de cambios. Los manuscritos son editados. Las figuras son revisadas. Los códigos se corrigen cuando se encuentran problemas. Los archivos de datos se combinan, los errores son corregidos, se dividen y combinan nuevamente. En el curso de un análisis simple, uno puede esperar miles de cambios en los archivos. Y aún así, todo lo que usamos para identificas este sinnúmero de cambios son los simples nombres de archivos (fig. 6.1). Teniendo esto en consideración, es lógico pensar que debe existir una forma mejor… Y si la hay, se conoce como Control de Versiones.

Un sistema de control de versiones ayuda a seguir lso cambios que se realizan a nuestros archivos, sin el desastre que resulta de utilizar sólo el nombre de los archivos. En los sistemas de control de versiones como git, se registra no sólo el nombre del archivo, si no que además su contenido, de esta forma, cuando el contenido cambia, se puede identificar que partes estaban y donde. El registro además contiene la relaciones entre las versiones, de esta forma se tiene un historial de todos los cambios deribados de las cada una de las versiones y es fácil dibujar un gráfico que muestre los cambios que ha sufrido un archivo, con sus versiones previas y aquellas derivadas (Fig. 6.2)

Evolución de las versiones de un archivo.

Figure 6.2: Evolución de las versiones de un archivo.

Los sistemas de control de versiones asignan un identificador a cada versión de cada archivo, manteniendo un registro de como están relacionados entre ellos. Además, estos sistemas permiten generar ramas laterales a una versión, la que puede ser fusionada de regreso a la tronco principal. Es posible además tener múltiples copias en múltiples computadores como respaldos y para trabajar colaborativamente. Finalmente, se pueden incluir etiquetas (tags) a versiones particulares, de esta forma es fácil retornar la versión que tenían los archivos cuando fueron etiquetados. Esto es particulamente útil para identificar una versión exacta de los datos, código y texto, por ejemplo, de un manuscrito que fue enviado para ser publicado, este puede ser el caso de la etiqueda R2 en la figura 6.2.

6.3 Control de versiones y colaboración usando Git and GitHub

Es importante hacer la distinción entre git y GitHub.

  • git: Software para el control de versiones que monitorea los archivos de un directorio (repositorio).
    • git crea el historial de versiones del repositorio.
  • GitHub: Sitio web que permite a los usuarios almacenar sus repositorios git y compartirlos con otros usuarios.
Diferencia entre git y GitHub.

Figure 6.3: Diferencia entre git y GitHub.

6.4 Veamos un repositorio de GitHub

La captura de pantalla en la figura 6.4 muestra una copia de un repositorio almacenado en GitHub, con el listado de archivos y directorios, indicando cuando fueron modificados, incluyendo información acerca de quien hizo los cambios y una pequeña descripción de los cambios realizados.

Captura de pantalla de un repositorio en GitHub.

Figure 6.4: Captura de pantalla de un repositorio en GitHub.

Si nos metemos en los “commits” del repositorio (fig. 6.5), podemos ver la historia de los cambios que se le han realizado. Por ejemplo, se ve que kellijohnson y seananderson hicieron algunos cambios durante junio y julio:

Captura de pantalla de los Commits de un repositorio en GitHub.

Figure 6.5: Captura de pantalla de los Commits de un repositorio en GitHub.

Si entramos ahora a ver los cambios realizados el 13 de julio (fig. 6.6), podemos saber exactamente cuales fueron los cambios realizados a cada archivo:

Captura de pantalla donde se presentan las diferencias entre dos versiones alojadas en un repositorio de GitHub.

Figure 6.6: Captura de pantalla donde se presentan las diferencias entre dos versiones alojadas en un repositorio de GitHub.

Monitorear estas modificaciones, como se relacionan a cada una de las versiones de un software en particular y a los archivos es exactamente para lo que fueron diseñados git y GitHub. Vamos a mostrar como estos sistemas pueden ser realmente efectivos para monitorear las versiones de códigos científicos, figuras y manuscritos y de esta forma tener flujos de trabajo reproducibles.

6.5 El ciclo de vida de Git

Como usuario de git usted tiene que entender algunos conceptos básicos asociados a los sets de cambios con versiones y como estos son almacenados y se mueven a través del repositorio. Cualquier repositorio de git puede ser clonado, de esta forma existe en forma local y remota. Pero cada uno de estos repositorios clonados son una copia simple de todos los archivos y del historial de los cambios que se han realizado, lo que son almacenados en un forma repositio git particular. Para nuestros propósitos, tenemos que considerar un repositorio de git simplemente como un directorio que contiene adicionalmente un set de metadatos relacionado las versiones.

En un directorio local donde git fue habilitado, el directorio contiene la versión actual de todos los archivos del repositorio. Estos archivos de trabajo están unidos a un directorio escondido que contiene el ‘Repositorio local’, el que a su vez contiene todos los otros cambios que se han realizado a los archivos y los metadatos sobre las versiones.

De esta forma, cuando se está usando git para trabajar con archivos, se pueden usar los comandos de git para indicar específicamente que cambios a los archivos de trabajo deben ser definidos para las versiones (usando el comando git add) y cuando grabar estos cambios como una versión en el repositorio local (usando el comando git commit).

Los demás conceptos se relacionan a la sincronización de los cambios en el repositorio local a un repositorio remoto. El comando git push se usar para enviar los cambios realizados en forma local a un repositorio remoto (posiblemente en GitHub) y el comando git pull es usado para traer los cambios del repositorio remoto y unirlos al repositorio local.

Diagrama de flujo del ciclo de vida de git.

Figure 6.7: Diagrama de flujo del ciclo de vida de git.

  • git clone: Copia todo el repositorio remoto a uno local.
  • git add (stage): Notifica a git de monitorear un set particular de cambios.
  • git commit: Almacena los cambios realizados como una versión.
  • git pull: Combina los cambios de un repositorio remoto a nuestro repositorio local.
  • git push: Copia los cambios de nuestro repositorio local al repositorio remoto.
  • git status: Determina el estado de los archivos en el repositorio local.
  • git log: Imprime el historial de cambios en el repositorio.

Estos siete comandos son la mayoría de los comando que va a necesitar para utilizar git en forma exitosa. Pero todo esto es demasiado abstracto, mejor exploremos estos conceptos utilizando ejemplos reales.

6.6 Cree un repositorio remoto en GitHub

Empecemos creando un repositorio en GitHub, luego editaremos algunos archivos.

  • Ingrese al sitio GitHub.
  • Clic en el botón de repositorio nuevo (New repository).
  • Nómbrelo como sasap-test.
  • Cree un archivo README.md.
  • Defina la licencia a Apache 2.0.
Captura de pantalla de la creación de un repositorio nuevo en GitHub.

Figure 6.8: Captura de pantalla de la creación de un repositorio nuevo en GitHub.

¡Usted acaba de crear su primer repositorio! Este repositorio fue creado con un par de archivos que GitHub hace por usted, son los archivos README.md, LICENSE y .gitignore.

Captura de pantalla con el repositorio recién creado llamado sasap-test.

Figure 6.9: Captura de pantalla con el repositorio recién creado llamado sasap-test.

Para hacer cambios menores a archivos de texto se puede utilizar la interfase web de GitHub. Navegue al archivo README.md en el listado de archivos y habilite la edición haciendo clic en el icono del lápiz. Este es un archivo normal con el formado Markdown, ahora se puede editar, agregando o removiendo texto. Cuando haya terminado, incluya un mensaje de commit y luego haga clic en el botón Commit changes.

Captura de pantalla con la interfase web de GitHub para la edición de documentos de texto.

Figure 6.10: Captura de pantalla con la interfase web de GitHub para la edición de documentos de texto.

Captura de pantalla con la interfase web de GitHub para la confirmar de los cambios realizados al documento de texto (commit changes).

Figure 6.11: Captura de pantalla con la interfase web de GitHub para la confirmar de los cambios realizados al documento de texto (commit changes).

Felicitaciones, ahora usted acaba de confirmar (commit) los cambios, ahora es el autor de la primera versión de este archivo. Si navega de regreso a la página del repositorio GitHub, puede ver la lista de los cambios confirmados (commits) ahí, así como la visualización del documento generado a partir del archivo README.md.

Captura de pantalla con la interfase web los cambios corfimados (commits) y de la visualización de la página README.md.

Figure 6.12: Captura de pantalla con la interfase web los cambios corfimados (commits) y de la visualización de la página README.md.

Expliquemos algunas cosas sobre esta ventana. Representa una vista del repositorio que acaba de crear, hasta ahora mostrando todos sus archivos. Para cada archivo, muestra la fecha de la última modificación y el mensaje asociado al commit que se utilizó para describir los cambios realizados. Esta es la razón por qué es tan importante escribir buenos mensajes, que contengan información relevante cuando se hace el commit. Además, el título azul sobre el listado de archivos muestra el commit más reciente, con su mensaje asociado y su identificados SHA. Ese identificador SHA es la clave para el set de versiones. Si hace clic en el identificador SHA (810f314), va a mostrar los cambios que se hicieron en ese commit en particular.

En la siguiente sección vamos a usar el URL de GitHub del repositorio que acaba de crear y lo usaremos plara clonarlo (clone) a un repositorio en nuestra máquina local y así editar los archivos con RStudio. Para eso, primero tenemos que copiar el URL de GitHub, que representa la dirección del repositorio:

Captura de pantalla para clonar un repositorio GitHub.

Figure 6.13: Captura de pantalla para clonar un repositorio GitHub.

6.7 Trabajando localmente con Git en RStudio

RStudio incluye soporte para Git como sistema de control de versiones, pero esto sólo ocurre si estamos trabajando en un Proyecto de RStudio (RStudio project folder). En esta sección vamos a clonar el repositorio que se creó en GitHub y lo vamos a dejar un repositorio local de un proyecto de RStudio.

Esto es lo que vamos a hacer:

  1. Crear un proyecto nuevo.
  2. Inspeccionar el panel Git y el historial de versiones.
  3. Confirmar una modificación (commit) al archivo README.md.
  4. Commit las modificaciones que hicieron en RStudio.
  5. Inspeccionar el historial de versiones.
  6. Crear y commit un archivo Rmd.
  7. Enviar (Push) estos cambios a GitHub.
  8. Ver el historial de cambios en GitHub.

6.7.1 Crear nuevo Proyecto (Create a New Project)

Comience creando un New Project… en RStudio, seleccione la opción Version Control y pegue el URL de GitHub que copió en el espacio para repositorio remoto (Repository URL). Si bien usted puede darle el nombre que quiera al repositorio local, se usa típicamente el mismo nombre que el que se tiene en GitHub, de esta forma se mantiene un grado de correspondencia. Usted puede elegir cualquier directorio para su copia local, en mi caso use el directorio development (fig. 6.14).

Captura de pantalla para la creasción de un proyecto de RStudio clonando un repositorio remoto.

Figure 6.14: Captura de pantalla para la creasción de un proyecto de RStudio clonando un repositorio remoto.

Un vez que haga clic en Create Project (crear proyecto), una nueva página de RStudio se abrirá con todos los archivos copiados localmente desde el repositorio remoto. Dependiendo de como esté configurada la versión de RStudio, la posición y tamaño de los paneles puede cambiar, pero generalmente todos van a estar presentes, incluyendo los paneles Git y el listado de archivos (Files) que fueron creados en el repositorio remoto.

Captura de pantalla de la interfase del projecto de RStudio con el clon local de repositorio.

Figure 6.15: Captura de pantalla de la interfase del projecto de RStudio con el clon local de repositorio.

En la figura 6.15 puede ver que apareció un archivo llamado sasap-test.Rproj y que están los otros 3 archivos que se crearon con el repositorio remoto de GitHub (.gitignore, LICENSE y README.md).

En el panel Git en RStudio se pueden ver 2 archivos. Este es el panel de estatus donde se se muestran todos los archivos del repositorio en los cuales se han realizado modificaciones. En este caso, el archivo .gitignore se muestra con una M que significa Modificado y sasap-test.Rproj con un ? ? para indicar que este archivo no está siendo monitoreado. Esto significa que git no tiene registro de ninguna version para este archivo y que no sabe nada acerca de el. A medida que usted vaya tomando decisiones sobre el control de versiones en RStudio, estos iconos van a ir cambiando para reflejar el estatus de la versión actual de cada uno de los archivos.

6.7.2 Inspeccionar el historial (history)

A continuación vamos a hacer clic en el botón History (historial, es el reloj que aparece en la primera fila al interior del panel ed Git), esto despliega una ventana con el registro de los cambios que se han realizado y, en este caso, deben ser idénticos a lo que usted vio en GitHub. Al hacer clic en cada fila del historial, podrá ir viendo exactamente que fue agregado y cambiado en cada uno de los commits en este repositorio.

Historial de los cambios realizados en el repositorio local.

Figure 6.16: Historial de los cambios realizados en el repositorio local.

6.7.3 Confirme cambios haciendo clic en commit al archivo README.md (Commit a README.md change)

Ahora hagamos algunos cambios al archivo README.md en RStudio. Agregue una sección nueva con un block de markdown como este:



## Git desde RStudio

Desde la interfase de RStudio, es posible hacer las mismas acciones con el sistema de versiones que se hicieron en  GitHub
y mucho más. Además en RStudio se tienen las ventajas propias de programar en un IDE con completación de comandos y otras
características que hacen nuestro trabajo más fácil.

- Agregar archivos al control de versiones
- Ccommit cambios
- Push commits a GitHub

Una vez que los haya guardado, podrá ver en forma inmediata el archivo README.md en el panel Git (fig. 6.17), marcado con una M de modificación. Ahora usted puede seleccionar este archivo en el panel de Git y hacer clic en Diff para ver los cambios (comparando las diferencias) que guardó (note que estos cambios no se han confirmado (commit) aun a su repositorio local).

Panel de estado de los cambios en los archivos.

Figure 6.17: Panel de estado de los cambios en los archivos.

En la figura 6.18 se muestra como se ven los cambios comparados con el archivo original. La líneas nuevas se destacan en color verde y en rojo las que fueron eliminadas.

Ventana que presenta las diferencias entre la versión almacenada en el repositorio y los últimos cambios realizados.

Figure 6.18: Ventana que presenta las diferencias entre la versión almacenada en el repositorio y los últimos cambios realizados.

6.7.4 Commit los cambios hechos en Rstudio

Para confirmar los cambios que se acaban de hacer en el archivo README.md, selecciones la caja de selección Staged a lado del nombre del archivo, este le dice a Git cuales son los cambios que quiere sean incluidos en el commit, escriba un mensaje describiendo que cambios se hicieron y por qué y finalmente haga clic en boton Commit (fig. 6.19).

Captura de pantalla con un mensaje describiendo la confirmación (commit) que se realizará.

Figure 6.19: Captura de pantalla con un mensaje describiendo la confirmación (commit) que se realizará.

Note que algunos de los cambios en el repositorio, .gitignore y sasap-test.Rproj, aun no se han confirmado y no serán parte del commit. En otras palabras, aun existen cambios pendientes para ser registrados en el repositorio. Usted vará una notificación que indica (en inglés):

Your branch is ahead of ‘origin/master’ by 1 commit.

Lo que se traduce a: _Su rama esta más avanzadada que el ‘maestro/origen’ por una confirmación

Esto significa que hemos commit 1 cambio en el repositorio local, pero que este no se está en el repositorio de origen (origin), no se ha hecho push, donde origen es el nombre que se usa típicamente para el repositorio en GitHub. Entonces, confirmemos los cambios pendientes, para esto seleccione la caja staged y luego escriba el mensaje describiendo el commit (fig. 6.20).

Captura de pantalla con los archivos a confirmar sus cambios y el texto descriptivo de la confirmación.

Figure 6.20: Captura de pantalla con los archivos a confirmar sus cambios y el texto descriptivo de la confirmación.

Cuando haya terminado no habrán mas cambios pendientes en el panel Git y el repositorio estará completamente limpio.

6.7.5 Inspeccionando el historial

Fíjese que ahora el mensaje dice:

Your branch is ahead of ‘origin/master’ by 2 commits.

_Su rama esta más avanzadada que el ‘maestro/origen’ por 2 confirmación

Estas 2 confirmaciones son las dos que acabamos de hacer y no se han empujado (push) aun a GitHub. Haciendo clic en el botón History (historial), podemos ver que hay un total de 4 commit en el repositorio local, mientras hay sólo 2 en GitHub (fig. 6.21).

Captura de pantalla con 4 commits.

Figure 6.21: Captura de pantalla con 4 commits.

6.7.6 Enviar (Push) cambios a GitHub

Ahora que se han hecho todos los cambios deseados en el repositorio local, usted puede empujar (push) los cambios a GitHub usando el botón Push. Se abrirá una ventana donde se pregunta por su usuario y password de GitHub, luego los cambios serán enviados. Esto dejará su repositorio local en un estado totalmente limpio y sincronizado con el repositorio remoto. Terminado esto, en el historial (en GitHub) se muestran todos los commits, incluyendo los 2 que fueron hechos en GitHub y los 2 que se hicieron en forma local con RStudio.

Captura de pantalla con los 4 modificaciones confirmadas.

Figure 6.22: Captura de pantalla con los 4 modificaciones confirmadas.

Ahora puede ver que las etiquetas del repositorio local (HEAD) y del remoto (origin/HEAD) están apuntando a la misma versión en el historial. De esta forma, si miramos el historial de los commits en GitHub serán iguales a los que tenemos en forma local.

Captura de pantalla con el historial de cambios en github.

Figure 6.23: Captura de pantalla con el historial de cambios en github.

6.8 Sobre (buenos) mensajes de confirmación (commit)

Es claro que una buena documentación es crítica para hacer del historial de versiones significativo y útil. Es tentador saltarse la escritura del mensaje asociado al commit o escribir algo por defecto como ‘Actualización!’. Sin embargo es importante escribir mensajes que sean para entender en el futuro que se hizo y por qué. Ademas, los mensajes que se usan en commits son en general más fáciles de entender si usan una convención de verbos activos. Por ejemplo, se puede ver que los mensajes de commit en las capturas de pantallas comienzan siempre con un verbo en pasado (en inglés!) y que explican que fue lo que se cambió.

Si bien muchos de los cambios que aquí se hicieron son simples y se explican por si mismos, para cambios más complejos, es mejor entregar un mensaje completo y auto-contenido. La convención, sin embargo, es tratar de usar mensajes cortos, con una sentencia breve, seguido de una explicación más detallada y racional para el cambio. Esto permite que el nivel de detalles sea legible en el registro de las versiones (version log). No puedo contar el número de veces que he visto los registro d e commits de hace 2, 3 o 10 años y agradecido el nivel de diligencia de los colaboradores que se tomaron el tiempo de describir el trabajo que se realizó.

6.9 Flujos de trabajo colaborativos y libres de conflictos (relacionados con Git)

Hasta ahora nos hemos enfocado al uso de Git y GitHub para el uso personal, como ya se demostró esto es extremadamente útil. Sin embargo donde git y GitHua brilla es cuando se comparte un repositorio GitHub con colaboradores, de esta forma se pede trabajar en un código, análisis y modelos en forma colaborativa. Cuando se trabaja de esta manera con otros investigadores, es muy importante poner atención al estado del repositorio remoto para evitar potenciales conflictos al combinar el trabajo. Un merge conflict ocurre cuando dos colaboradores hacen 2 commits en forma separada donde se ha(n) cambiado(s) la(s) misma(s) línea(s) de código de un archivo. Cuando esto ocurre, git no puede combinar los cambios en forma automática y arroja un error preguntando como resolver el conflicto. Esto no es grave, no es necesario tenerle miedo a los merge conflicts ya que son muy fáciles resolver y aquí hay algunas guías geniales. de como hacerlo (por ahora sólo en inglés).

Dicho esto, es siempre mejor evadir este tipo de conflictos, lo que se pueden minimizar siguiendo las siguientes sugerencias:

  • Asegúrese de traer (pull down) todos los cambios antes de confirmarlos (commit).
    • Asegurese que tiene los cambios más recientes.
    • Pero puede ser que necesite arreglar su código si ocurren conflictos.
  • Coordínese con con sus colaboradores con quien va a trabajar.
    • Usted tiene que comunicarse para colaborar.

6.10 Actividad

Use RStudio para agregar un nuevo archivo al repositorio sasap-test, desarrolle una estructura básica y guárdela.

A continuación stage y commit el archivo en forma local y luego push it a GitHub.

6.11 Tópicos avanzados

Hay mucho que no hemos visto en este tutorial. Existen tutoriales muy completos que cubren tópicos mas avanzados. Algunos de estos temas son:

  • Usando git en la linea de comándos.
  • Resolviendo conflictos.
  • Branching y merging.
  • Pull requests versus contribuciones directas por colaboradores.
  • Usando .gitignore para proteger datos sensitivos.
  • GitHub Issues y por que son útiles.
  • y más, mucho más.

  • Try Git es un tutorial interactivo muy bueno y completo.
  • Software Carpentry Version Control with Git
  • Codecademy Learn Git (some paid)