6 Comandos para Aprender a Usar Git

6 Comandos para Aprender a Usar Git

Capítulo I: ¿Por qué versionar el código? Primeros comandos de Git

Primero, déjenme darles un poco de contexto sobre por qué quiero escribir ésto.

En Abril último, un par de amigos (Adri y Bruno) y yo comenzamos un grupo de Telegram para dar una mano a quienes se están iniciando en el desarrollo de software. En el grupo charlamos sobre la industria en general, los procesos de desarrollo, practicamos programación en distintos lenguajes, compartimos información sobre cursos y esas cosas. Lo vemos como una forma de devolver un poco a la sociedad todo lo bueno que hemos recibido en éstos +15 años "picando código".

Ahora, el formato de chat no resulta muy bueno para documentar o para explicar conceptos más complejos (estamos todos desde los celulares, por ejemplo), así que a también empezamos a hacer reuniones para charlar más "cara a cara" (reuniones virtuales, porque pandemia). La verdad es que también estamos explorando formatos que nos resulten cómodos y sean útiles a la causa.

Una de las tantas cosas que nos preguntan en el grupo es la necesidad de conocer las herramientas que usamos en el día a día que sirven de soporte al desarrollo. Así es como llegamos a éste artículo, el primero (de muchos más, espero!) de una serie introductoria para explicar herramientas y procesos que usamos para desarrollar, mantener y extender! nuestros sistemas sin romper todo (o tratando de romper lo menos posible jejeje).

El (primer) problema

Empecemos con un ejemplo: Jorge estuvo horas implementando unas funciones en Javascript para ordenar las filas de una tabla html, según cierta columna. Funciona. Jorge está contento. Ahora, envalentonado, quiere poder aplicar esa funcionalidad a otras columnas. Pero como las funciones dependen mucho de dicha columna (tipo de datos, ids, etc), entonces empieza a modificar el código que ya tiene andando para hacerlo más genérico. A medio camino se da cuenta que su estrategia para implementarlo tiene un defecto fundamental y no le sirve. Tiene que tirar lo nuevo que hizo y volver a empezar con otra estrategia que ya se le ocurrió. Pero su código ya no funciona, hay cambios por todos lados y no puede volver a ese código que antes andaba, aunque sea en una única columna.

Solución rápida: Jorge usa una carpeta donde se va guardando copias de lo que anda. Esto funciona, si, peeeero hasta cierto punto. Eventualmente termina teniendo que administrar una carpeta con un par de copias (decenas!) y no sabe bien cómo era el orden en que lo desarrolló, ni cual tenía aquella funcionalidad X que ahora quiere usar, ni qué diferencias hay entre unas y otras. El esquema de nombres de las carpetas ("funcionalidad-X-5.6.2.1_final_final") colapsó hace rato.

Con ustedes, el control de versiones

Existen herramientas que nos permiten "sacar una foto" o un "snapshot" del contenido de una carpeta, guardando su estado tal cual estaba en ese momento, para poder volver a él cuando lo necesitemos, o comparar qué diferencias hay entre lo que tengo ahora y una "foto" dada.

Y acá empiezo con algunos conceptos. Cada "foto" o "snapshot" que tomo, es una versión de mi carpeta. Cuando queremos empezar a versionar una carpeta, tenemos que pedirle al controlador de versiones que empiece a "observar" dicha carpeta. Voy a llamar a ésta acción: inicializar el repositorio (de código). La carpeta con todo mi código se convierte en el repositorio (o simplemente "el repo"). Cada nueva versión se agrega como la diferencia con la versión anterior, a ésto se le llama commit. Cuando lo tratamos como un sustantivo, tiene un ID (ej.: "el commit 1234 agrega la funcionalidad X"). Cuando lo tratamos como un verbo, nos referimos a la acción de agregar una nueva versión (ej.: "commiteá tus cambios cuando lo tengas listo").

Sistemas para versionar código hay muchos (cvs, svn, perforce, git, mercurial, etc), pero hoy en particular, quiero hablar de Git.

Git

No voy a repetir sobre Git lo que pueden encontrar en Wikipedia. Los ejemplos que vamos a ver usan la terminal, asumen que ya instalamos Git (les debo el link a las instrucciones) y que nuestro sistema operativo es Linux (con Windows es prácticamente igual).

Inicializar el repo

Abro la terminal, voy hasta la carpeta donde tengo el proyecto y ejecuto git init:

$ cd mi_super_proyecto
$ git init
Initialized empty Git repository in mi_super_proyecto/.git/

Listo, ya está inicializado. La carpeta .git contiene la información de las versiones de mi proyecto. Ahora a meter la primera versión de nuestro súper proyecto!

Estado actual de mi repo

Ejecutando git status, vamos a obtener información del estado del repo:

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    hola_mundo.html

nothing added to commit but untracked files present (use "git add" to track)

Y acá hay dos cosas muy importantes para charlar que hasta ahora les estuvimos guardando (fue por su bien jeje).

1. Branches o ramas

Habíamos dicho que cada nueva versión es un commit con la diferencia respecto de la anterior. Esto arma una secuencia de versiones o commits, uno detrás del otro, que llamamos "branch" (o rama).
En nuestro caso, Git dice: On branch master, estamos en el branch por defecto, llamado "master".
El poder de ésto reside en que, como un árbol, puedo abrir otro branch y meter nuevas versiones ahí, independientemente de lo que tenga master, y puedo moverme entre los branches o ver sus diferencias sólo con un comando. Esto lo vamos a ver más adelante.

2. Agregando archivos a una versión

Ahora viene lo lindo, con ésto empezamos a versionar! Supongamos que tengo una página html, un 'hola mundo', que quiero guardar como mi primera versión. Ese archivo es hola_mundo.html, y git me lo lista bajo Untracked files. Para agregarlo lo hacemos en dos pasos. Primero, metemos nuestros cambios en "stage".

$ git add hola_mundo.html
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
    new file:   hola_mundo.html

Esto significa que seleccionamos éstos cambios para el commit, pero bien podrían haber otros archivos que no queremos agregar ahora. Vean cómo ahora el hola_mundo.html está en estado "to be commited". Si queremos sacarlo de stage para seguir modificándolo, podemos usar git rm como se muestra ahí arriba.

El siguiente paso es el commit propiamente dicho:

$ git commit -m "primera version"
[master (root-commit) bdb2e66] primera version
 1 file changed, 7 insertions(+)
 create mode 100644 hola_mundo.html
$ git status
On branch master
nothing to commit, working tree clean

Listo! Commit adentro. Git devuelve un mensaje con el id (bdb2e66), el branch (master) y un resumen de qué se commiteó (1 archivo, 7 líneas agregadas, porque mi archivo tiene 7 líneas nomás, etc).

Viendo los cambios

Si quiero ver la lista de commits que tengo en ése branch, puedo ejecutar:

$ git log
commit bdb2e66.... (HEAD -> master)
Author: Germán <german@email.com>
Date: Sat Jul 10 12:34:56 2021

    primera version

Ahora si quiero agregar dos versiones más, sigo los mismos pasos y al ejecutar git log nos muestra la lista de commits con sus IDs, autores, fechas y mensajes:

$ git log
commit 7995236.... (HEAD -> master)
Author: Germán <german@email.com>
Date: Mon Jul 12 12:34:56 2021

    agrega texto con nueva version y otro parrafo

commit 056a33e....
Author: Germán <german@email.com>
Date: Sun Jul 11 12:34:56 2021

    agrega nuevo texto con el numero de version

commit bdb2e66....
Author: Germán <german@email.com>
Date: Sat Jul 10 12:34:56 2021

    primera version

Los mensajes sirven para describir qué cambios metimos en ese commit, así nos ayuda a entender lo que hicimos antes.

Si en cambio, quiero ver las diferencias, uso git diff.

$ git diff
diff --git a/hola_mundo.html b/hola_mundo.html
index 36bd395..92b0892 100644
--- a/hola_mundo.html
+++ b/hola_mundo.html
@@ -6,4 +6,7 @@
     <p>esta es la versión 3!</p>
     <p>en breve más cambios!!</p>
   </body>
+  <script>
+    // acá van las funciones de Jorge para ordenar las filas de una tabla
+  </script>
 </html>

También puedo incluir el ID del commit para comparar lo que tengo en ese momento en el repo (commiteado o no) contra una versión específica:

$ git diff 056a33e....
diff --git a/hola_mundo.html b/hola_mundo.html
index af7068d..92b0892 100644
--- a/hola_mundo.html
+++ b/hola_mundo.html
@@ -3,6 +3,10 @@
   </head>
   <body>
     <p>hola mundo!</p>
-    <p>esta es la versión 2!</p>
+    <p>esta es la versión 3!</p>
+    <p>en breve más cambios!!</p>
   </body>
+  <script>
+    // acá van las funciones de Jorge para ordenar las filas de una tabla
+  </script>
 </html>

Aquellas líneas que comienzan con un + son líneas nuevas en mi última versión, y aquellas con un - son las que borré, todo respecto a la versión con la que estoy comparando.

Volvamos al problema

Con todo esto, veamos si podemos solucionar el problema de Jorge. Una vez que tenemos nuestras funciones andando para una columna, commiteamos y guardamos la versión en el repo. Ahora podemos empezar a meter cambios por todos lados para adaptar las funciones para múltiples columnas. Cuando llegamos al punto muerto donde llegó Jorge, revisamos el estado del repo:

$ 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:   hola_mundo.html

no changes added to commit (use "git add" and/or "git commit -a")

Tenemos la versión anterior segura en el repo, y los nuevos cambios todavía no commiteados. Podemos ver exactamente qué cambios metimos usando git diff como hicimos antes:

$ git diff
diff --git a/hola_mundo.html b/hola_mundo.html
index 36bd395..92b0892 100644
--- a/hola_mundo.html
+++ b/hola_mundo.html
@@ -6,4 +6,7 @@
     <p>esta es la versión 3!</p>
     <p>en breve más cambios!!</p>
   </body>
+  <script>
+    // acá van las funciones de Jorge para ordenar las filas de una tabla
+  </script>
 </html>

El comando diff nos muestra las diferencias, contra lo último que hay en el repo.
Podemos descartar esos cambios ejecutando git restore:

$ git restore hola_mundo.html
$ git status
On branch master
nothing to commit, working tree clean

Listo, recuperamos el archivo como estaba antes de romper todo. Podemos volver a intentarlo sin romper lo que ya teníamos andando.

Lo que viene

Ya tenemos una idea mínima de cómo trabajar con Git. En el próximo artículo, vamos a trabajar con otro problema, branches, merges y rollbacks.