Saltar al contenido

Git básico para científicos

Pero, ¿qué es git?

Tienes razón, mejor empezamos por el principio.

Git es un sistema de control distribuido para poder hacer seguimiento de cambios en código fuente (y otras cosas). En palabras más mundanas, es un vigilante de las cosas que vamos haciendo (cambios de código, cambios de nombres de ficheros,…) en un carpeta de ficheros en un ordenador.

Git se ha convertido en un estándar de facto al haber sido adoptado por sitios como github o gitlab.

¿Qué significa que sea distribuido?

Significa que el repositorio completo de código, sus cambios, sus mensajes de nuevo código,…, se pueden encontrar en distintos ordenadores.

Normalmente, hay un repositorio central de código. Este repositorio central de código puede alojarse en sitios como github, gitlab o bitbucket y así puedes tener una interfaz web para recibir informes de errores, poder hacer revisiones de código, poder repartir tareas,…

¿Y para qué necesito yo git si me organizo muy bien?

Fuente: https://giphy.com/gifs/mess-nBjOqZ6h0ili0

El uso de git no es un capricho, es una necesidad, Algunas posibles razones para su uso:

  • A la larga ahorrarás tiempo.
  • Podrás rehacer y deshacer funcionalidad teniendo múltiples versiones de tus experimentos junto con seguimiento de lo que ha ido funcionando y lo que no.
  • Podrás trabajar en funcionalidad específica en paralelo y solo unir esa nueva funcionalidad si merece la pena.
  • Evitarás tener algún esperpento como lo de más abajo, es decir, tendrás una única versión que funciona bajo cierto entorno:
    • fichero_codigo.py
    • fichero_codigo_20120203-py
    • fichero_codigo_20120203_rev1.py
    • fichero_codigo_nuevo.py
  • Puedes trabajar de forma colaborativa con otros compañeros de tu laboratorio, empresa o de cualquier otra parte del mundo.
  • Cuando añades nuevo codigo puedes acompañarlo de mensajes útiles.
  • Otros pueden auditar tu código permitiendo que te puedan informar de errores.
  • Seguramente tengas una copia en caso de que te roben el PC, se te queme, le caiga agua encima,…
  • Puedes integrarlo con muchas otras herramientas para hacer pruebas, comprobar funcionamiento en distintos entornos,…
  • Otros pueden usar tu código encontrando nuevos usos en los que no habías caido o reproduciendo tus resultados (reproducibilidad).
  • Tienes formas de mantener tu código privado en caso de que lo necesites.
  • Tienes control absoluto de lo que quieres integrar y de lo que no en tu proyecto.

¿He logrado convencerte para usar git? Vamos, pues, a instalarlo

Perfecto, vamos a instalarlo primero. Te diré como lo hago yo pero hay otras formas.

Normalmente tengo un entorno conda de herramientas. Puedes crear uno de la siguiente forma (desde la línea de comandos):

conda create -n tools
conda activate tools
conda install -c conda-forge git

Lo bueno de lo anterior es que se hace igual en Windows, Linux y/o Mac.

Y ya estaría instalado y se podría usar desde la línea de comandos con el entorno tools activado.

¿Algunos comandos git indispensables?

Esto no pretende ser un tutorial extenso ni detallado sobre como usar git. Más bien se puede ver como una chuleta de comandos con los cuales resuelves el mayor porcentaje de lo que tienes que hacer con git en tu día a día.

Primero hemos de configurar git para identificarnos y que aparezcamos como los autores cuando estamos interactuando con el repositorio de código.

git config --global user.name "KikoCorreoso"
git config --global user.email KikoCorreoso@example.com

En lo anterior he definido el nombre de usuario y el correo de forma global y será el que se use desde la máquina en la que os encontráis.

Muy bien, ya nos hemos identificado pero queremos empezar a toquetear código. Para ello podemos crear un repositorio nuevo o ‘clonar’ (hacer una especie de copia) uno ya existente. Lo podemos hacer de la siguiente forma:

Para un repositorio nuevo:

mkdir mi_repo
cd mi_repo
git init

Con lo anterior hemos creado una carpeta llamada “mi_repo” y desde dentro de la misma he inicializado un repositorio indicándole a git que quiero que siga los cambios que se produzcan en esa carpeta.

Si el repositorio ya existiera podríamos clonarlo así:

git clone https://github.com/kikocorreoso/pyboqt.git
cd pyboqt

Con lo anterior hemos clonado un repositorio ya existente que se encuentra en github.com, en la cuenta de kikocorreoso y con nombre pyboqt.

Eso nos descarga una carpeta con el código a la que podemos acceder. Esa carpeta está sometida al seguimiento de cambios por parte de git.

Si una vez que estás dentro de la carpeta del repositorio haces algún cambio y quieres que git sea consciente de ello puedes hacer:

git add nombre_del_fichero
git add * # para añadir todos los ficheros con cambios de una vez

Ahora has añadido nuevos ficheros al índice pero tienes que informar de los cambios y puedes aportar un mensaje descriptivo:

git commit -m "Cambios en la función x para que reciba el parámetro j"

Para ver cómo se encuentra el estátus del repositorio puedes usar:

git status

Y te informará del estado actual de las cosas.

Si ya has añadido cambios en tu repositorio local y los quieres enviar al repositorio remoto (que puede estar en github, por ejemplo) puedes usar:

git push origin master

Es decir, envía los cambios locales (origin) al repositorio remoto (master).

Lo anterior puede que falle si no has informado a git de cual será el remositorio remoto. Lo podrías hacer así:

git remote add origin https://github.com/kikocorreoso/pyboqt.git

Ver más sobre esto aquí.

Para verificar que hemos añadido correctamente el repositorio remoto podemos hacer uso de:

git remote -v

Hasta ahora hemos visto lo más básico. Imagina ahora que quieres añadir una nueva rama de código para comprobar la viabilidad de añadir una nueva funcionalidad o para trabajar en un reformateo de código o para trabajar en un bug,… Podemos hacer:

git branch nombre_de_la_rama

Para cambiar a la nueva rama que hemos creado podemos usar:

git checkout nombre_de_la_rama

Ahora podríamos empezar a trabajar en esta nueva rama sin modificar lo que ya tenemos en la rama original, normalmente llamada master.

Si no nos convence lo que hemos hecho en la nueva rama la podemos eliminar usando:

git branch -d nombre_de_la_rama

Si nos convence pero no hemos terminado aun podemos enviarla al remoto para seguir trabajando en otro momento usando:

git push origin nombre_de_la_rama

Puede que tengas más de una rama. Las puedes ver usando:

git branch

Puedes mandar todas las ramas locales al remoto usando:

git push --all origin

Si has cambiado de opinión con respecto a una rama que ya habías mandado al remoto puedes eliminarla usando:

git push origin --delete nombre_de_la_rama

Muy bien, estamos en otro PC que ya tiene el repositorio y queremos seguir trabajando con nuestro repositorio. Lo podemos actualizar haciendo

git pull

Si creemos que la funcionalidad que estábamos desarrollando en una nueva rama está lista para ser integrada podemos unirla a la rama que tengamos activa, que normalmente será master, usando:

git merge nombre_de_la_rama

Antes de unir nada puedes usar:

git diff

Para ver las diferencias.

En el caso de que ya hayas unido algo y te hayas arrepentido puedes deshacer los cambios usando:

git checkout -- nombre_del_fichero

Puedes acceder a un log de lo que has ido haciendo usando

git log

También puedes guardar ‘versiones’ usando etiquetas que te permiten etiquetar determinados momentos de tu desarrollo. Por ejemplo, si consideras que has llegado a una nueva versión la puedes etiquetar usando:

git tag 1.0.0 id_del_commit

Lo último, id_del_commit, no es estrictamente necesario y usará el último “commit”. Podemos añadir un mensaje también usando:

git tag -a 1.0.0 -m "Final 1.0.0 version"

Esta etiqueta estará en tu repositorio local. Si la quieres mandar también al remoto puedes usar:

git push --tags origin

Para borrar tags en remoto y en local, respectivamente, se puede usar:

git push --delete origin nombre_del_tag # remoto
git tag -d nombre_del_tag # local

Puedes borrar cosas para que dejen de estar controladas por git usando, por ejemplo:

git rm nombre_del_fichero

git pull puede ser un comando destructivo puesto que hace dos cosas en una, git fetch y git merge, es decir, descarga datos del repositorio remoto y los intenta unir. Si al hacer la unión aparecen problemas pueden complicarse las cosas.

Es por ello que si no sabes muy bien lo que estás haciendo quizá sea más seguro usar esos dos comandos por separado.

git fetch

descarga la información del repositorio remoto pero no hace nada con ello. Solo la tenemos ahí para inspeccionarla. Normalmente es buena idea hacer eso antes de usar git merge.

Si se producen problemas con un git merge puedes leer aquí.

Muy bien, muchas cosas, ¿practicamos?

Vamos a hacer uso de algunos de los comandos anteriores con un repositorio nuevo. El repositorio, en muchas ocasiones lo creamos primero en el sitio donde centralizaremos el desarrollo. No voy a explicar aquí como crear un repositorio nuevo en Github, GitLab, BitBucket u otros servicios:

El que yo he creado para este ejemplo se llama pybogit y está en github. Visualmente lo podéis ver como:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Para traernos el repositorio a nuestro PC local uso (siempre considero que estás trabajando desde la línea de comandos y con el entorno tools activado, mira más arriba, que es donde hemos instalado git):

git clone https://github.com/kikocorreoso/pybogit.git

Visualmente lo podéis ver como:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Ahora que ya lo tenemos en nuestro PC nos movemos a esa carpeta:

cd pybogit

Y ahí podemos crear una carpeta llamada src y dentro de esa carpeta podemos crear un fichero que se llame mi_modulo.py:

mkdir src
touch src/mi_modulo.py

Lo anterior habrá creado una carpeta y un fichero dentro de ella. Lo podéis hacer de otras formas. En windows, por ejemplo, podéis usar el notepad. Si ahora le preguntamos a git por el estátus del repositorio nos dirá lo siguiente:

git status
En la rama master
Tu rama está actualizada con 'origin/master'.
Archivos sin seguimiento:
   (usa "git add …" para incluirlo a lo que se será confirmado)
     src/
no hay nada agregado al commit pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)

El anterior mensaje me dice que me encuentro en la rama master (luego vemos esto) y que tengo cosas en la carpeta que no están bajo el seguimiento de git. Para decirle a git que tenga en cuenta el nuevo fichero puedo hacer, por ejemplo:

git add src/mi_modulo.py

Si volvemos a mirar el estátus nos dirá:

git status
Tu rama está actualizada con 'origin/master'.
Cambios a ser confirmados:
  (usa "git restore --staged …" para sacar del área de stage)
    nuevo archivo:  src/mi_modulo.py

Me indica que he añadido un nuevo fichero al repositorio pero los cambios todavía no han sido confirmados a git. Para confirmarlos hacemos un commit:

git commit -m "commit con mensaje super util"

En la consola podré leer un mensaje parecido a:

[master d296fbe] commit con mensaje super util
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 src/mi_modulo.py

Si ahora miramos de nuevo el estátus:

git status

Vemos que todos los cambios han sido integrados:

En la rama master
Tu rama está adelantada a 'origin/master' por 1 commit.
  (usa "git push" para publicar tus commits locales)
nada para hacer commit, el árbol de trabajo está limpio

Pero me dice que están en mi rama local y que la remota está un ‘cambio’ (commit) por detrás. La situación actual vista gráficamente sería algo así:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

En este momento podríamos mandar los cambios al repositorio remoto (siempre es buena idea hacer esto). Lo podemos hacer de la siguiente forma:

git push origin master

Visto visualmente sería algo así:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Y ahora estariamos en esta situación:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Es decir, tenemos la misma información en local y en remoto.

Vamos a hacer un nuevo cambio en el fichero que hemos estado usando. Meto el siguiente código:

def greet():
    return "Hello"

Guardo el fichero y si miro el estátus veré que git me dice que:

En la rama master
Tu rama está actualizada con 'origin/master'.
Cambios no rastreados para el commit:
  (usa "git add …" para actualizar lo que será confirmado)
  (usa "git restore …" para descartar los cambios en el directorio de trabajo)
    modificado:     src/mi_modulo.py
sin cambios agregados al commit (usa "git add" y/o "git commit -a")

Añadimos y ‘commiteamos’ (sí, la jerga es la que es) y lo mandamos al remoto:

git add *
git commit -m "added function greet"
git push origin master

La situación actual sería:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Se me ocurre ahora añadir una función experimental. Voy a crear una rama nueva para meter esta función experimental. La rama master podéis verla como el tronco principal del árbol. Siempre partimos con una sola rama principal o master. Pero al árbol le podemos añadir ramas y vamos a aprovechar este momento para hacer eso mismo. Para crear la rama y cambiarnos a ella en un único comando podemos hacer:

git checkout -b nueva_rama

Lo anterior es un atajo para los dos siguiente comandos:

git branch nueva_rama
git checkout nueva_rama

Ahora, git considera que estamos trabajando en una nueva rama. Lo podemos ver preguntando nuevamente el estátus (git status):

En la rama nueva_rama
nada para hacer commit, el árbol de trabajo está limpio

y los cambios que hagamos no irán al tronco (la rama master). Hagamos algún cambio añadiendo nuestra funcionalidad experimental. Modificamos el fichero que hemos creado anteriormente añadiendo el siguiente código:

def saludo():
    return "Hola"

Añadimos los nuevos cambios a la rama en la que nos encontramos usando:

git add *
git commit -m "added experiment to branch"

La situación actual sería algo así:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Podemos mandar esa nueva rama al repositorio remoto usando (cuidado, enviaría TODAS las ramas al remoto, en este caso solo tengo una por lo que me vale):

git push --all origin

Y me sale un mensaje parecido a:

Username for 'https://github.com': kikocorreoso
Password for 'https://kikocorreoso@github.com': 
Enumerando objetos: 7, listo.
Contando objetos: 100% (7/7), listo.
Compresión delta usando hasta 4 hilos
Comprimiendo objetos: 100% (3/3), listo.
Escribiendo objetos: 100% (4/4), 355 bytes | 355.00 KiB/s, listo.
Total 4 (delta 1), reusado 0 (delta 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote: 
remote: Create a pull request for 'nueva_rama' on GitHub by visiting:
remote:      https://github.com/kikocorreoso/pybogit/pull/new/nueva_rama
remote: 
To https://github.com/kikocorreoso/pybogit.git
[new branch]      nueva_rama -> nueva_rama 

Ahora que la rama local está también en el remoto tendríamos algo así:

Para hacer esta imagen se han usado iconos de FontAwesome sin modificar. Licencia.

Y podríamos pedir a nuestros colaboradores que revisasen esa nueva rama, si creen que es útil, si mejor eliminar ese código por la razón que sea,…

Hemos decidido eliminarla. La elimino del local y del remoto. Primero salgo de la rama:

git checkout master

Podemos ver las ramas que tenemos y en la que nos encontramos usando:

git branch

Que nos mostrará algo como:

* master
  nueva_rama 

Con el asterisco indicando que estamos ahora en la rama master. Voy a coger la sierra y a cortar la rama nueva_rama:

git branch -D nueva_rama

La opción -D es importante que esté en mayúscula. Probadlo primero en minúscula. Cojo la sierra y me la llevo hasta el servidor y corto la rama en el remoto:

git push origin --delete nueva_rama

Ahora podéis mirar las ramas que os quedan y deberíais ver solo la rama master.

Y podríamos seguir probando cosas pero lo voy a dejar aquí para no aburrirte. Espero que si no sabías nada de git este artículo te haya abierto un poco el apetito y te ayude a guiarte mínimamente. Si tienes dudas usa los comentarios.

Tienes este artículo convertido en chuleta gracias a Geocurios (@borlafgis). La puedes descargar de aquí.

Para los que prefieren aprender de forma más visual

Si te gusta más que te cuenten las cosas en lugar de leer añado la siguiente charla de Sofía Denner en la PyConAr2018:

Y aquí tienes una herramienta para aprender de forma visual el uso de diferentes comandos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

− two = three

Pybonacci