/ edsa / posts

Agradecimientos

Me gustaría agradecer a Victoria Caballero Caballero, Silvia Domínguez Sánchez, Ginés García Mateos, Eduardo Martínez Gracia, José Tomás Palma Méndez, Alberto Ros Bardisa, Isabel María Sánchez Jiménez y Diego Sevilla Ruiz por su lectura del artículo y sus sugerencias.

Introducción

Un sistema de control de versiones es una tecnología que permite mantener un historial de todos los cambios de un proyecto, ahorrarnos tener que hacer copias de seguridad manualmente, gestionar múltiples versiones de un proyecto (por ejemplo, para un artículo que va a publicarse en varias revistas) y colaborar al mismo tiempo con otras personas.

Git es el sistema de control de versiones más popular. Está pensado para capturar los cambios en archivos de texto plano (como los archivos de código LaTex [1] ) y para organizar proyectos. El concepto proyecto puede ser muy amplio. En este caso, me referiré con proyecto a un libro, un artículo, unos apuntes de clase o un conjunto de varios de los anteriores, como un listado de exámenes, apuntes y hojas de ejercicios para una asignatura. Es decir, me centraré en proyectos cuyo resultado son materiales de clase.

En este artículo me gustaría introducir Git y explicar por qué creo que puede ser una herramienta útil para ser utilizada por profesores y alumnos en el entorno universitario. El objetivo es abordar qué beneficios aporta, cómo utilizar esta tecnología y en qué se basa. Además, me gustaría cubrir otros temas que, aunque no están relacionados con el control de versiones, son de interés, como quién es el dueño del contenido si otras personas pueden acceder a (o incluso modificar) los archivos y quién decide cómo progresa el proyecto. Para eso, entre otras cosas, comentaré qué licencias son las más usadas en proyectos de este tipo.

Motivación

Utilizando el método tradicional, sin ningún sistema de control de versiones, la gestión de los proyectos es tarea exclusiva del autor y se produce de una forma desconocida para el resto de personas, que normalmente solo tienen acceso al resultado (por ejemplo, un archivo pdf) que distribuye el autor. El motivo por el que quiero promover el uso de Git es porque permite:

  • Mantener una o varias versiones actualizadas de los contenidos.

  • Habilitar la reutilización de contenidos por parte de los docentes.

  • Permitir la colaboración de personas ajenas al proyecto.

  • Organizar los contenidos de forma accesible.

  • Mantener una forma ordenada de reportar erratas y sugerencias de mejora.

En mi opinión, creo que no es posible realizar estas tareas cómodamente con el sistema tradicional.

Por ejemplo, si como alumno encuentro erratas en los apuntes, para notificarlas al profesor tengo que anotar la página y la línea donde se producen y escribirle un correo electrónico al profesor. Después el profesor tiene que localizar en el código fuente esas erratas, corregirlas y volver a subir la versión de los apuntes. Para el profesor supone el trabajo de estar buscando las erratas, el cual ya habían hecho los alumnos previamente. Para los alumnos, supone que no tienes una versión actualizada del pdf hasta que el profesor tenga tiempo de corregir las erratas. En la práctica, como no puedo ver la correción de forma instantánea, es posible que no me moleste en hacer un archivo de erratas.

Como los alumnos también saben utilizar LaTex, lo normal sería que tuviesen el código, pudiesen cambiar el archivo y ver al momento la versión correcta y proponer una nueva versión del código al profesor. Entonces el profesor podría verificar cuáles de las propuestas de cambios son realmente erratas, modificar si le interesa el código del alumno y automáticamente tener una nueva versión del proyecto para el resto de alumnos.

Por otro lado, si como alumno quisiese modificar los apuntes del profesor; por ejemplo para añadir las soluciones de los ejercicios que vaya haciendo, o para añadir notas de clase, etc.; no puedo hacerlo sin el código original. La solución obvia, que sería que el profesor compartiese el código LaTex, es insatisfactoria. Para empezar exige que el profesor confíe en el alumno, pues le puede preocupar que el alumno modifique sus apuntes y haga un mal uso de ellos. Pero aún más importante, si el profesor después actualiza los apuntes, el alumno pasa a tener anotaciones sobre una versión anticuada. Y, además, poder modificar los apuntes sin un sistema que me permita ver las diferentes versiones que hacen mis compañeros, en qué se diferencian sus versiones y las mías, colaborar entre nosotros bajo una licencia que permita que el profesor pueda utilizar las contribuciones de los alumnos para los cursos siguientes sin trabas legales (lo cual puede servir para que los alumnos vayan completando con soluciones escritas los libros de texto, por ejemplo) hace más difícil colaborar. Por ejemplo, es habitual tener recopilaciones de soluciones en formatos inmodificables, como pdf, que rápidamente quedan desactualizadas.

Y lejos de intentar dar más poder de control sobre los apuntes a los alumnos, lo que intento es hacer ver que los profesores tienen las mismas necesidades, esencialmente. En primer lugar, sería ideal que los profesores pudiesen copiar apuntes de otras universidades: hojas de ejercicios, anexos o el preámbulo de un documento. Y además de copiar, poder modificar partes para adecuarlas a sus clases. Pero incluso dentro de la propia universidad, tener repositorios de código y apuntes es una forma sencilla de mantener la organización de las asignaturas. De esta manera, se vuelve muy fácil para el profesor ver qué se da en otras asignaturas y cómo se da, copiar alguna parte de los apuntes de un compañero para un capítulo de repaso, o colaborar con otros investigadores para escribir un artículo utilizando la misma tecnología. Y por otro lado, es siempre el autor del proyecto (el docente si se trata de contenido de clase) el que decide cuál es el uso que el resto de usuarios puede hacer de su contenido.

En cierta medida, lo que quiero conseguir es abrir la veda para que el material de clase se adhiera más o menos a la cultura open source y vaya mejorando como resultado de la acción de la comunidad. Así como facilitar que, si los autores originales lo permiten, surjan trabajos derivados de esos contenidos.

Conceptos básicos de Git. La historia del proyecto

Para comenzar a utilizar Git debemos instalar la herramienta en nuestro sistema operativo. En sí, lo que instalamos es un programa que se ejecuta desde la terminal. No obstante, las personas que no estén acostumbradas a usar la terminal y no sientan que este es el momento de aprender a hacerlo, pueden instalar una interfaz gráfica. También existen plugins que integran Git en la mayoría de editores de texto, de forma que aumentan el menú de opciones con botones que ejecutan esos comandos. Por ejemplo, Overleaf, posiblemente el editor online de LaTex más famoso, permite sincronizar proyectos con repositorios Git. En esta sección vamos a explicar cómo funciona Git, y para ello nos vamos a basar en los nombres de los comandos que se ejecutan. Las herramientas visuales utilizan los mismos nombres para las mismas operaciones.

Instalación

Puedes instalar Git para ir utilizándolo a lo largo del artículo. Si no quieres escribir comandos en una terminal, considera leer primero la sección sobre Interfaces gráficas e integración con herramientas e instalar la que prefieras.

Notación

Para los comandos seguiremos una notación donde las palabras entre llaves son los parámetros del comando (como los argumentos de una función). Los parámetros que van seguidos de puntos supensivos pueden incluirse más de una vez, lo que implica que la acción se aplica a todos ellos. Por ejemplo, si hablamos de utilizar el comando git add <file>…​, <file> es el nombre de uno o varios archivos.

Es interesante leer los comandos aunque no vayamos a utilizarlos porque representan acciones en los programas gráficos de Git. Por ejemplo, en algunas interfaces gráficas podemos pulsar un botón que dijese "add" y después nos saldría una ventana para seleccionar archivos, dando lugar al mismo resultado que el comando. En otras, primero se seleccionan los archivos y después se pulsa en un botón que dice "add". Conceptualmente, es la idea de lo que representa la acción lo que importa.

Git trabaja sobre un directorio. Cuando queremos llevar control de algunos archivos, podemos crear un repositorio Git sobre una carpeta con el comando git init. A partir de ese momento, se creará una carpeta oculta que se llama .git dentro de la carpeta inicial. Los comandos que efectuamos cambian el directorio .git, porque en esta carpeta se guarda la información del proyecto, como las versiones anteriores. No obstante, los usuarios no tenemos ni que acceder a esta carpeta ni modificarla de ninguna manera (es una carpeta oculta para evitar esto, precisamente). Para una persona que va haciendo cambios y simplemente quiere mantener copias de seguridad sin tener que hacerlo manualmente, podríamos decir que en la carpeta del proyecto siempre tendrá los archivos actualizados a la versión más reciente y los puede modificar como siempre. Cuando quiera guardar una versión, ejecutará el comando adecuado. Por lo demás, salvo que elimine algo de su proyecto sin querer y quiera volver a una versión anterior, no va a notar ninguna diferencia. Por este motivo, podemos decir que Git es un sistema invisible. Simplemente navegamos por nuestros directorios y modificamos nuestros archivos como siempre. Como los archivos se pueden encontrar en varias máquinas al mismo tiempo (en las de los autores, en un servidor online propiedad de la universidad, etc.), y en diferentes estados (unos pueden tener versiones más nuevas que otros), llamaremos a cada directorio versionado por Git un repositorio.

Definición: Repositorio

Se llama repositorio (de Git) a una carpeta gestionada por Git. Se caracterizan por contener una subcarpeta con nombre .git que contiene toda la información de versiones.

Cuando se usa un repositorio cuya información es accesible desde internet, como un repositorio en plataformas como GitHub o GitLab, se suele utilizar el nombre repositorio remoto (accesible por internet no implica público). De forma análoga, se suele utilizar el nombre repositorio local para un repositorio que se encuentre en un ordenador personal. La distinción es necesaria porque puede haber varios repositorios para el mismo proyecto, uno en el ordenador de cada contribuyente y otra versión en GitLab, por ejemplo.

Para empezar, es bueno considerar a Git como un sistema de versiones lineal. Es decir, algo como la Figura 1.

git-history
Figura 1. Modelo de cambios lineal o no lineal
Nota

En la imagen, cada versión no es necesariamente una versión final. Cada versión es como una copia de seguridad del proyecto a lo largo del desarollo. Las flechas indican en qué versión(es) anterior(es) se basa cada una y forman ramas del proyecto.

En Git cada nodo de la figura recibiría el nombre de commit. Un commit es un estado del proyecto que yo he decidido guardar. Por ejemplo, si tengo unos apuntes y decido añadir un capítulo, puedo guardar los cambios que haga en un nuevo commit.

Definición: Commit

Un commit es un conjunto de cambios sobre los archivos de un repositorio, un punto unívocamente determinado en la historia del proyecto, que identifica cuál era el contenido de los archivos en ese momento.

En inglés, commit es una de tantas palabras que puede utilizarse como sustantivo y verbo. Esta definición corresponde al uso del sustantivo. Cuando se usa como verbo, significa crear un nuevo nodo en la historia del proyecto (un commit). En español a esta acción la podemos llamar hacer un commit.

A grandes rasgos, Git funciona de la siguiente manera. En todo momento, estoy situado en un punto de la historia del proyecto. Habitualmente, el último commit. Lo normal es trabajar con los archivos y modificarlos. En ese momento, los archivos del proyecto ya no coinciden con los del commit. Para guardar una nueva versión, un nuevo commit, debemos primero ejecutar el comando git add <file>…​, con el que elegimos los archivos para los que queremos guardar los cambios. La versión actual de estos archivos es copiada a una zona intermedia, llamada stage area. El comando git commit toma los cambios que hay registrados en la stage area y crea un nuevo commit con estos cambios. Tras esta operación, la stage area queda vacía.

Hasta aquí hemos visto los conceptos que permiten tener una historia de un proyecto. Quiero terminar la sección comentando que, en realidad, la historia de un repositorio no es como una lista (no es lineal), conceptualmente hablando. Es posible crear varias ramas de proyecto que se bifurcan y se vuelven a unir, obteniendo en el mismo repositorio varias versiones del mismo proyecto. Por eso, el conjunto de commits se parece más a una estructura de árbol con ramas que se unen, un grafo acíclico dirigido.

Además de permitirnos guardar cambios, lo que hace a Git potente es un conjunto de algoritmos que gestionan esa historia. Por ejemplo, el algoritmo de unión de ramas normalmente es capaz de unir automáticamente dos versiones de un fichero conservando los cambios de ambas, y cuando no lo sabe hacer, deja una marca con ambas versiones para que nosotros elijamos qué hacer. Esto sirve para que varias personas puedan trabajar sobre un mismo proyecto. También dispone de algoritmos que nos permiten ver las diferencias entre dos versiones de un archivo, todos los cambios que incluye un commit o todos los cambios desde la última vez que descargamos el proyecto.

diff
Figura 2. Ejemplo de git diff visualizado en GitKraken

Vídeo sobre preguntas frecuentes

Preguntas Frecuentes

Aplicación a proyectos escritos. Workflow

Obtener un repositorio

Las dos formas más habituales de obtener un repositorio son creando un nuevo repositorio o clonando uno existente.

  • El comando git init crea un repositorio vacío en el directorio en el que se ejecute.

    Se crea una carpeta .git y podemos seguir los pasos para hacer el primer commit.

  • El comando git clone <repository>, donde <repository> es una dirección web para un repositorio, clona un repositorio en una carpeta nueva.

    Por ejemplo, si quisiésemos clonar el repositorio calculus-homework que Eric Nguyen tiene publicado en GitLab (https://gitlab.com/airicbear/calculus-homework), podríamos ejecutar el comando git clone https://gitlab.com/airicbear/calculus-homework. El resultado es que se crea una carpeta llamada calculus-homework con los contenidos del repositorio (incluida la carpeta .git).

    [useredsa@Device002 tmp]$ git clone https://gitlab.com/airicbear/calculus-homework
    Cloning into 'calculus-homework'...
    warning: redirecting to https://gitlab.com/airicbear/calculus-homework.git/
    remote: Enumerating objects: 198, done.
    remote: Total 198 (delta 0), reused 0 (delta 0), pack-reused 198
    Receiving objects: 100% (198/198), 2.65 MiB | 8.58 MiB/s, done.
    Resolving deltas: 100% (82/82), done.
    
    [useredsa@Device002 tmp]$ # El siguiente comando muestra los contenidos de un directorio.
    
    [useredsa@Device002 tmp]$ ls -a calculus-homework/
    .   2018-12  2019-02  2019-04  .git        LICENSE
    ..  2019-01  2019-03  2019-05  .gitignore  README.md
  • Cuando lo que queremos es crear un repositorio remoto en un servidor web, como Github o GitLab, podemos hacerlo a través de sus páginas webs iniciando sesión en nuestra cuenta y pulsando en esa opción.

Modificar archivos y hacer un commit

Tras modificar el estado de un repositorio, el comando git add <file>…​ registra una instantánea del archivo en una zona temporal llamada staging area o index. Si en vez de con un archivo ejecutamos el mismo comando con un directorio, se aplicará la acción a todos los archivos contenidos en el directorio. git add funciona tanto para archivos nuevos (que no existían en un commit anterior) como para archivos modificados.

Para guardar las instantáneas de la staging area como un nuevo commit, debemos ejecutar el comando git commit. Tras hacerlo, se abrirá un editor de texto para que escribamos una descripción para el commit. No obstante, también podemos incluir el mensaje en el propio comando escribiendo git commit -m <mensaje>. (Si el mensaje contiene espacios, que es lo habitual si son varias palabras, hay que incluirlo entre comillas.) Si no sabemos usar un editor de texto de terminal, recomiendo usar la segunda forma o utilizar una interfaz gráfica, donde tendremos un campo de texto donde incluir el mensaje.

Además, en cualquier momento podemos ejecutar los siguientes comandos para conocer el estado actual del repositorio:

git status

Muestra un resumen del estado del repositorio. Lista qué archivos se diferencian entre el último commit y la staging area, y entre la staging area y el directorio actual. Los primeros son los archivos de los que se guardaría el registro de cambios al ejecutar git commit. Los segundos son los archivos cuyos cambios quedarían registrados en la staging area al ejecutar git add.

Es un comando muy útil para conocer el estado del repositorio en cualquier momento.

git diff

Muestra la diferencia entre los archivos del directorio actual y los del último commit.

git diff --cached

Muesta la diferencia entre los archivos de la staging area y los del último commit.

Bajar y subir cambios a un repositorio remoto

Si hemos obtenido nuestro repositorio clonándolo de un repositorio online, entonces Git registra automáticamente que nuestro repositorio fue clonado de otro repositorio remoto.

Para trabajar con otras personas en el mismo proyecto o distribuir nuestro trabajo, necesitaremos poder subir y descargar cambios al repositorio online. No obstante, al colaborar de forma asíncrona, puede suceder que antes de subir nuestros cambios al repositorio online, otra persona ya haya subido los suyos. En ese caso, hay que mezclar ambos cambios.

Definición: merge y conflict

Una operación merge incorpora los cambios de un commit de otra rama del proyecto en la rama actual como si los cambios hechos sobre la otra rama desde el punto en el que divergieron se repitiesen a partir de este momento. El resultado de la operación es un nuevo commit sobre la rama actual.

Los algoritmos que utiliza Git internamente son capaces de detectar cuándo los cambios se pueden mezclar de manera automática. Por ejemplo, si cada rama del proyecto ha modificado archivos distintos, entonces conservaremos la versión más nueva posible de cada archivo. Pero si existen dos versiones modificadas del mismo archivo, es posible que los cambios puedan incorporarse de forma automática o que no. Cuando pasa lo segundo, se dice que se ha producido un conflicto, y los usuarios debemos decidir cómo mezclar ambas versiones para terminar la operación merge.

Para actualizar nuestro repositorio con los cambios del repositorio remoto, debemos ejecutar el comando git pull. Un pull primero descarga el historial de cambios del repositorio remoto (lo que se conoce como fetch), y depués realiza un merge sobre nuestra rama (Figura 3). En la siguiente sección veremos cómo solucionar los conflictos si surgen en este paso.

Para subir nuestros cambios al respositorio remoto, debemos ejecutar el comando git push. Por defecto, no se puede hacer un push que registre cambios desde una versión previa a la última guardada en el repositorio. En esos casos, primero debemos realizar un pull; solucionar los conflictos, si surgen; y después realizar un push.

git-pull
Figura 3. Una representación de git pull

Otros comandos para trabajar con repositorios remotos son

git remote

Permite manejar el conjunto de repositorios remotos asociados al nuestro. No es un comando que sea habitual usar, pero puede ser útil para añadir un repositorio remoto.

git fetch

Descarga los cambios de un repositorio remoto sin realizar un merge. Un pull equivale a un fetch seguido del merge que haría el comando git merge FETCH_HEAD.

Solucionar un conflicto tras la modificación del mismo archivo

Ahora veremos un caso práctico en el que se genera un conflicto al intentar hacer un pull. Para hacerlo, primero voy a crear dos repositorios que darán lugar a un conflicto al intentar hacer un merge, y servirá como una práctica de los comandos para los lectores que quieran repetirlo.

Instrucciones para generar un conflicto
  1. Elegimos una carpeta de nuestro ordenador donde crear dos repositorios (se pueden borrar al final de esta sección). En mi caso, será la carpeta /tmp.

  2. Estando en esa carpeta en la terminal, creamos una carpeta llamada rep para nuestro repositorio y cambiamos de directorio hacia esa carpeta.

    [useredsa@Device002 tmp]$ mkdir rep
    [useredsa@Device002 tmp]$ cd rep
  3. Convertimos la carpeta actual (/tmp/rep en mi caso) en un repositorio.

    [useredsa@Device002 rep]$ git init
    Initialized empty Git repository in /tmp/rep/.git/
  4. Vamos a crear un archivo llamado ejemplo.txt dentro de nuestro repositorio y escribimos el siguiente texto dentro.

    * Este es un archivo de ejemplo.
    *
    * Estas tres primeras líneas estaban escritas inicialmente.
  5. Si ejecutamos el comando git status ahora (para conocer el estado de nuestro repositorio), deberíamos obtener una salida parecida a la siguiente.

    [useredsa@Device002 rep]$ git status
    On branch master
    
    No commits yet
    
    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    	ejemplo.txt
    
    nothing added to commit but untracked files present (use "git add" to track)
  6. Vamos a realizar nuestro primer commit. Primero añadimos el fichero ejemplo.txt a la staging area y después realizamos el primer commit con los contenidos de la staging area y un mensaje cualquiera (en mi caso, el mensaje del commit es ejemplo creado).

    [useredsa@Device002 rep]$ git add ejemplo.txt
    [useredsa@Device002 rep]$ git commit -m "ejemplo creado"
    [master (root-commit) 268a68c] ejemplo creado
     1 file changed, 3 insertions(+)
     create mode 100644 ejemplo.txt
  7. A continuación vamos a copiar el repositorio y modificarlo. De esta manera tendremos un repositorio remoto que ha sido modificado con respecto al original. Primero volvemos al directorio padre (/tmp en mi caso) y después copiamos la carpeta rep a una carpeta llamada repremote. Por último, entramos a esa carpeta para empezar a modificar el repositorio remoto.

    [useredsa@Device002 rep]$ cd ..
    [useredsa@Device002 tmp]$ cp -r rep/ repremote
    [useredsa@Device002 tmp]$ cd repremote/
  8. Modificamos el archivo ejemplo.txt (recordemos que debemos estar dentro de la carpeta remoterep) con el siguiente texto.

    * Este es un archivo de ejemplo.
    * En el repositorio remoto, añaden este texto y cambian la siguiente línea.
    * Estas tres MODIFICACIONban escritas inicialmente.
  9. Y con el archivo modificado, repetimos los pasos anteriores que vimos antes para crear un nuevo commit.

    [useredsa@Device002 repremote]$ git status
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git restore <file>..." to discard changes in working directory)
    	modified:   ejemplo.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    [useredsa@Device002 repremote]$ git add ejemplo.txt
    [useredsa@Device002 repremote]$ git commit -m "commit del repositorio remoto"
    [master 8d13047] commit del repositorio remoto
     1 file changed, 2 insertions(+), 2 deletions(-)
  10. Ahora volvemos al repositorio inicial, que no tiene los cambios que acabamos de hacer.

    [useredsa@Device002 repremote]$ cd ../rep
  11. Y modificamos el ejemplo.txt de forma diferente. Escribimos el siguiente texto.

    * Este es un archivo de ejemplo.
    *
    * Estas tres primeras líneas estaban escritas MODIFICACION!
    * En mi repositorio añado estas dos líneas y
    * modifico también la tercera.
  12. Ahora creamos el segundo commit de este repositorio.

    [useredsa@Device002 rep]$ git add ejemplo.txt
    [useredsa@Device002 rep]$ git commit -m "commit de mi repositorio"
    [master daf4df6] commit de mi repositorio
     1 file changed, 3 insertions(+), 1 deletion(-)

Tras seguir estos pasos, debemos tener dos repositorios que comparten la historia inicial (un commit) y que divergen a partir de ahí (hacen cambios distintos sobre el mismo contenido en el segundo commit). Si intentásemos hacer un pull ahora, se produciría un conflicto que tendríamos que solucionar manualmente.

Al realizar un pull con los repositorios creados siguiendo las Instrucciones para generar un conflicto, el comando produce la siguiente salida.

[useredsa@Device002 rep]$ git pull /tmp/repremote/
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 347 bytes | 347.00 KiB/s, done.
From /tmp/repremote
 * branch            HEAD       -> FETCH_HEAD
Auto-merging ejemplo.txt
CONFLICT (content): Merge conflict in ejemplo.txt
Automatic merge failed; fix conflicts and then commit the result.

Si nos fijamos, en las dos últimas líneas nos dice en qué archivo está el conflicto y que el merge ha fallado. Como tenemos que solucionar el conflicto manualmente, abrimos el fichero ejemplo.txt con un editor de texto. A continuación mostramos lo que debe aparecer en nuestro editor: una versión del archivo que muestra las diferencias entre nuestra versión y la que hemos intentado unir.

* Este es un archivo de ejemplo.
<<<<<<< HEAD
*
* Estas tres primeras líneas estaban escritas MODIFICACION!
* En mi repositorio añado estas dos líneas y
* modifico también la tercera.
=======
* En el repositorio remoto, añaden este texto y cambian la siguiente línea.
* Estas tres MODIFICACIONban escritas inicialmente.
>>>>>>> 8d1304758d41a9d25bf87146bd21aa8b96a56f29

La primera línea se mantiene igual porque era la misma en ambos archivos, mientras que para las siguientes, nos muestra las dos versiones separadas por una línea de símbolos igual (=). Ahora podemos modificar el archivo como queramos para integrar las dos versiones. Yo lo dejaré con el siguiente texto.

* Este es un archivo de ejemplo.
* En el repositorio remoto, añaden este texto y cambian la siguiente línea.
* Estas tres MODIFICACIONban escritas MODIFICACION!
* En mi repositorio añado estas dos líneas y
* modifico también la tercera.

Si ahora hacemos un commit, habremos completado la operación de merge que dio lugar al conflicto.

[useredsa@Device002 rep]$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   ejemplo.txt

no changes added to commit (use "git add" and/or "git commit -a")
[useredsa@Device002 rep]$ git add ejemplo.txt
[useredsa@Device002 rep]$ git commit -m "merge completo!"
[master e83f32c] merge completo!
merge-after-conflict
Figura 4. Visualización de la historia del proyecto en GitKraken tras solucionar el conflicto

Ignorar ficheros. El fichero .gitignore

Además de la carpeta .git, pueden existir otros archivos especiales en un repositorio. Un fichero llamado .gitignore es opcional y puede incluirse en un repositorio para indicar los archivos que Git no debe monitorear. Por ejemplo, en un repositorio que contiene archivos LaTex, no tiene sentido guardar los que son resultado de la compilación (.aux, .log, .out, .toc, etc.). Si cada vez que vamos a realizar un commit añadimos uno por uno los archivos, entonces no vamos a registrarlos, pero es habitual que nos acostumbremos a añadir todos los archivos contenidos bajo una carpeta; que siendo habitualmente la del proyecto, significa todos los archivos en el directorio del proyecto. Por eso, el archivo .gitignore es útil para poder ignorar ciertos archivos.

Cada línea del archivo .gitignore indica un patrón para ignorar archivos. Un patrón es una expresión que puede corresponder al nombre de un archivo. Por ejemplo, el patrón *.out representa a cualquier fichero que termine con .out.

  • Una línea en blanco no se corresponde con nada, así que se pueden usar líneas en blanco como separadores dentro del archivo.

  • Una línea que empiece con una almohadilla (#) se interpreta como un comentario y no se tiene en cuenta.

  • La barra (/) se utiliza como el separador en las rutas. Las rutas son relativas al directorio del repositorio.

  • El asterisco (*) y la interrogación (?) tienen un significado especial. El asterisco corresponde una cadena de caracteres cualquiera (incluso la cadena vacía). La interrogación corresponde a un caracter cualquiera. Por ejemplo, el patrón a*z se corresponde con cualquier nombre que empiece en a y acabe en z, y el patrón ???.tex se corresponde con cualquier nombre que sean 3 caracteres cualesquiera seguidos de .tex.

En https://github.com/github/gitignore podemos encontrar una colección de ficheros .gitignore en función de para qué utilicemos nuestro repositorio. Para repositorios con código TeX, podemos usar https://github.com/github/gitignore/blob/master/TeX.gitignore. Cuando creamos un repositorio en GitHub nos permite elegir uno de estos archivos e incluirlo automáticamente.

Resumen. Tabla de operaciones

Operación

Comando

Descripción

Initialize

git init [<directory name>]

Crea un repositorio vacío. Básicamente, un directorio .git en la carpeta actual.

Clone

git clone <url>

Clona un repositorio en una carpeta nueva.

Add to index

git add <path>…​

Actualiza la staging area o index con el contenido actual del directorio.

Create a new commit (or simply, commit)

git commit [-m <message>]

Registra cambios en la historia del repositorio. Crea un nuevo commit que contiene los contenidos del index.

Fetch

git fetch

Descarga la historia de cambios de otro repositorio

Merge

git merge <branch/commit>

Unifica dos ramas de cambios, con o sin resolución manual de conflictos.

Pull

git pull

Una operación fetch seguida de una operación merge.

Push

git push

Incorpora los cambios a un repositorio remoto.

Interfaces gráficas e integración con herramientas

Aunque la terminal es una herramienta muy versátil, la mayor parte de la gente no se siente cómoda utilizándola. Por eso, para algunos es mejor integrar Git en su trabajo habitual utilizando una interfaz gráfica. Las interfaces gráficas utilizan la misma terminología que los comandos de terminal.

Lo importante es conocer que existen dos alternativas. La primera es instalar un programa que permita abrir repositorios locales. Hay bastantes alternativas para elegir. La segunda es instalar un plugin para el editor de archivos que utilicemos habitualmente. Un plugin es una extensión del programa que normalmente se instala desde dentro del propio programa. En general, la mayoría de editores y entornos de desarrollo tienen un plugin para incorporar botones a la barra de herramientas que permitan añadir archivos a la staging area, realizar un commit y bajar y subir los cambios a un repositorio remoto. Los más completos permiten además ver dos versiones del mismo archivo lado a lado para comprobar los cambios o una única versión con las diferencias (ver Figura 2).

Para ordenadores antiguos, lo mejor es utilizar la terminal, que será lo que menos consuma. Pero si queremos una interfaz, es posible que los recursos consumidos por un plugin en nuestro editor sean menores que lanzar otra aplicación gráfica. En ese caso, debemos buscar en nuestra aplicación el menú de plugins o buscar en internet.

Para encontrar una interfaz gráfica para nuestro sistema operativo, podemos buscar en internet git gui clients for <os>. O bien podemos consultar la página oficial de Git, que contiene una lista de clientes para cada sistema. En este artículo incluiré capturas de GitKraken a modo de ejemplo porque se puede instalar en Linux, Windows y MacOs y es una aplicación gratuita siempre que se utilice con fines no lucrativos.

Lista de interfaces gráficas

La página oficial de Git cuenta con una lista de interfaces gráficas que podemos filtrar según nuestro sistema operativo.

Podemos encontrar otra lista en la wiki de Git

Un ejemplo de interfaz gráfica: GitKraken

Podemos descargar GitKraken de su página web. Tras iniciar la aplicación, encontramos una pantalla que nos permite abrir un repositorio (seleccionando una carpeta de nuestro ordenador), clonar un repositorio o inicializar un nuevo repositorio.

start
Figura 5. Pantalla de inicio de Gitkraken

Una vez seleccionado un repositorio, llegamos a una pantalla que muestra la historia del repositorio.

overview
Figura 6. Visión global de un proyecto en GitKraken

En la barra de iconos superior podemos ver varios botones. Vemos aparecer los términos pull y push de los que hablábamos antes. También vemos dos términos nuevos, stash y pop, de los que no hemos hablado porque no son esenciales; y dos menús, boards y timelines, que no forman parte del sistema Git. El estado actual del repositorio se representa al final de la historia y no se marca con un círculo, indicando que no corresponde a ningún commit (vemos que aparecen las siglas WIP, Work in Progress, en vez del mensaje del commit). Si pulsamos sobre este supuesto commit, nos aparece un menú a la derecha que nos permite mover ficheros a la staging area y realizar un commit.

staging
Figura 7. Visión del estado del directorio actual en GitKraken

Derechos de Propiedad. Tipos de Licencias

Uno de los principales motivos por el que los docentes no distribuyen su trabajo es que temen que su trabajo sea copiado sin que se les atribuya la autoría o que otras personas lo modifiquen, lo compartan u obtengan beneficio económico. Para poder compartir el trabajo con otros docentes o permitir que otras personas colaboren, necesitan una licencia que cumpla con los estándares legales y les garantice sus derechos.

Implicaciones legales de la ausencia de licencia
Cuando realizas un trabajo creativo; como un libro, unos apuntes o código software; el trabajo está protegido por las leyes de copyright por defecto. Si el trabajo no tiene licencia, esto significa que nadie más puede copiarlo, distribuirlo o modificarlo sin arriesgarse a tener que retirar el contenido, multas o litigios. Si en el trabajo contribuyen otras personas (cada cual sería propietaria del copyright), "nadie" también te incluye a ti.
— Un extracto de https://choosealicense.com/no-permission/
Traducido del inglés al español. El trabajo original cuenta con licencia Creative Commons Attribution 3.0 Unported License

Dadas las Implicaciones legales de la ausencia de licencia, es necesario pensar en licenciar nuestro trabajo incluso aunque solo le saquemos partido nosotros mismos, si alguien más ha colaborado en él.

Una licencia no es más que un documento legal en el que el(los) propietario(s) del trabajo determina(n) bajo qué condiciones el trabajo se puede usar, redistribuir y modificar. Y decidir los términos para tu proyecto es tan sencillo como escribirlos e incluirlos como un archivo más (un ejemplo de qué se puede llegar a escribir). No obstante, para evitar ambigüedades, puede ser conveniente incluir cuáles son las garantías que ofrece su producto (en caso de que, por ejemplo, el usuario no quede satisfecho), qué responsabilidades se asumen y bajo qué leyes nacionales o internacionales se ampara.

Aunque posiblemente las licencias más famosas que permiten distribuir libremente nuestro trabajo son las de Creative Commons, está recomendado no usarlas para software, dado que no incluyen condiciones que se apliquen al código fuente. Por eso, de cara a publicar documentos escritos en LaTex, sería mejor utilizar una licencia que tenga en cuenta que el producto final se basa en código fuente. Sin embargo, si nuestro objetivo es impedir el uso con fines comerciales (lo cual no se llama open source) podemos optar por una licencia Creative Commons Attribution-NonCommercial 4.0, porque es una licencia muy habitual.

Hay un tipo especial de licencias open source que son más restrictivas. Una licencia Copy Left es una licencia open source que exige que los trabajos derivados mantengan las mismas libertades, normalmente exigiendo que mantengan la misma licencia. Esto significa que si por ejemplo optas por una licencia que permita derivados, los trabajos derivados deben también permitirlos. Las licencias que no son copy left se llaman permisivas. Las licencias permisivas más usadas son The MIT License (MIT) y The Apache License 2.0. Las licencias copy left más utilizadas son The GNU General Public License 3.0 (GPLv3) y The Mozilla Public License 2.0 (MPL-2.0).

Archivo CONTRIBUTING. Sobre las contribuciones a nuestro proyecto

Si nuestro objetivo es permitir la colaboración de otras personas en el proyecto, lo normal es añadir otro archivo más aparte de la licencia que incluya cómo se puede contribuir. Los repositorios suelen incluir un fichero de texto llamado CONTRIBUTING. A veces, en este archivo también se recuerda que contribuir al proyecto implica aceptar la licencia, y se exige que para contribuir se lleven a cabo ciertos pasos que muestren que se acepta conscientemente. Por ejemplo, el proyecto kakoune, contiene el siguiente archivo de contribución.

Archivo CONTRIBUTING del repositorio de kakoune
The preferred way to contribute would be through GitHub pull requests,
as an alternative patches can be discussed on the IRC channel.

When contributing your first changes, please include an empty commit for
copyright waiver using the following message (replace 'John Doe' with
your name or nickname):

  John Doe Copyright Waiver

  I dedicate any and all copyright interest in this software to the
  public domain.  I make this dedication for the benefit of the public at
  large and to the detriment of my heirs and successors.  I intend this
  dedication to be an overt act of relinquishment in perpetuity of all
  present and future rights to this software under copyright law.

The command to create an empty commit from the command-line is:

  git commit --allow-empty

Cómo empezar

Si a pesar de leer el artículo no tienes claro cómo empezar a utilizar Git, puedes probar a seguir los siguientes pasos para aprender :

  1. Instala Git o una interfaz gráfica (elige la que quieras).

  2. Sigue los pasos para generar un conflicto para familiarizarte con la herramienta.

  3. Elige algún repositorio online en GitHub o GitLab. Prueba a clonarlo y modificarlo.

  4. Llegado el punto, querrás mantener repositorios online. Crea una cuenta en el servidor que vayas a utilizar. Puedes hacer un fork (una copia personal en tu cuenta, normalmente aparece un botón para hacer un fork en la página del repositorio) del proyecto que hubieses elegido antes. Prueba a clonar tu fork y a subir y bajar los cambios.

  5. Aplícalo a tu trabajo actual.

Videotutorial de demostración

El vídeo siguiente muestra el uso de algunas de las cosas aprendidas en el artículo. Verlo no es necesario para empezar a utilizar Git, pero puede servir para ver otro ejemplo guiado, esta vez usando GitLab para almacenar un repositorio.

Enlaces de interés

Si has leído el artículo y quieres aprender a utilizar Git, te pueden interesar los siguientes recursos.

  • Para consultar cualquier cosa relacionada con Git, lo ideal es recurrir al manual. El manual está accesible desde la terminal con el comando man <manual-page> o git help <manual-page>. Por ejemplo: man git add, man git-add o git help add (todos muestran la misma página que explica el comando add), man gitignore, etc. Pero como siempre, si preferimos no utilizar la terminal, podemos escribir los mismos términos en internet, porque multitud de páginas web ofrecen versiones online de las páginas.

    En particular, recomiendo leer las páginas gittutorial, para repasar los conceptos más simples, de los que ya he hablado; giteveryday, para leer sobre los comandos habituales (aunque hay más comandos de los que necesitará alguien que no sea programador); y gitworkflows, para indagar un poco sobre cuál es la forma habitual de trabajar con Git.

  • Para ver una colección de tutoriales sobre Git, recomiendo los tutoriales de Atlassian.

  • Para ver un ejemplo de repositorio sencillo (aunque es extenso), podéis mirar el repositorio de apuntes de Juan Marín, otro estudiante de la Universidad de Murcia. En él, Juan va colgando sus propias notas de clase de las asignaturas que cursa bajo una licencia open source. Podemos encontrar otro ejemplo en un libro de análisis complejo escrito por un estudiante de la Universidad Autónoma de México.

  • Para ver una guía avanzada (y muy buena) sobre cómo utilizar LaTex con Git, https://www.desy.de/~bargheer/gitintro/git.html.

  • Para ver una colección de licencias open source, podemos entrar en https://choosealicense.com/licenses/.


1. LaTex es un lenguaje para la creación de documentos escritos que presenten una alta calidad tipográfica. Es ampliamente utilizado para la generación de documentos que contengan expresiones matemáticas.