¿Qué es el control de versiones?

Durante el desarrollo e incluso durante toda la vida de cualquier programa de software se requiere de escribir o corregir código continuamente, ya sea para publicar nuevas versiones mejoradas o con nuevas funcionalidades o por necesidad de corrección de errores (bug fixes).

Ocurre que en todo tipo de proyectos de desarrollo de software se acaban generando decenas, cientos, miles de ficheros de código y, de una versión a otra se han de modificar unos cuantos de ellos. Sería extremadamente laborioso llevar la cuenta, manualmente, de las líneas de código que se han modificado de una versión a otra. Si además, sobre el mismo código fuente, están trabajando equipos de desarrolladores, imagínate lo complejo que sería coordinar los cambios realizados por dos, tres… decenas de programadores sobre los mismos ficheros. La implementación de nuevas versiones llevaría horas o incluso días dedicados exclusivamente a la revisión y coordinación de cambios en el código.

De ahí que sean necesarios sistemas de gestión de cambios o lo que se ha venido a conocer como Sistemas de Control de Versiones (VCS, en inglés), que básicamente consisten en estrategias (y software) que habilitan para realizar esa gestión de cambios.

¿Qué es eso de Git?

Git es un programa para control de versiones. No es el único ni necesariamente el mejor en todo. Hay docenas de sistemas de gestión de versiones por ahí: algunos son de pago, otros gratuitos, unos afrontan la problemática de editar los mismos fichero por distintas personas de una manera, otros de otra… No nos interesa demasiado entrar en detalle ni compararlos en este post, aunque si te pica la curiosidad siempre puedes echar un vistacillo por aquí.

Yo te voy a introducir a Git por la sencilla razón de que, junto con Mercurial, es el VCS más popular con diferencia (o eso me parece a mí). Además, sabiendo más o menos de qué va Git, la terminología y los conceptos generales (merge, branch, pull, push…), aprender a hacer lo mismo con un otro VCS no puede ser muy complicado.

Git básico, básico

Como decía un compañero mío de universidad: “para aprender a andar primero hay que aprender a gatear”, y como para aprender a gatear primero hay que nacer, qué mejor que empezar por el principio de todo: la instalación de git.

Nacer: instalar Git en tu ordenador

Básicamente si usas Linux lo puedes instalar con tu gestor de paquetes habitual:

sudo apt-get install git-all

O, alternativamente:

sudo yum install git-all

Si usas Mac te lo puedes bajar de aquí. Si usas Windows te puedes bajar el instalador aquí.

Si necesitas más detalles sobre cómo instalarlo, problemas, casos especiales… ya sabes que Google es tu amigo. También puedes preguntar más abajo e intentaré ayudarte.

Si todo ha ido bien cuando escribas en tu terminal el comando git version, debería devolverte la versión del Git que has instalado en tu SO. Por ejemplo, en mi Ubuntu a mí me aparece:

$ git version
git version 2.7.0

Mientras que en la versión de Git para mi Windows me aparece

C:\> git version
git version 2.7.0.windows.1

Aprendiendo a gatear: usar Git en tu entorno local

La gracia de los sistemas de control de versiones (VCS) está, sobretodo, en la gestión de repositorios remotos, a los cuales puede acceder un equipo de desarrolladores (repositorios privados) o incluso cualquier persona que desee colaborar en el desarrollo de un proyecto de código abierto (repositorios públicos). Los repositorios son, sencillamente, los lugares donde se guarda el código de nuestro proyecto, donde se coordinan y se registran los cambios que se van haciendo (qué cambios se han hecho, quién los ha hecho, cuándo…).

Pero qué mejor manera de aprender a trabajar con repositorios remotos que aprendiendo primero a trabajar con las versiones de nuestro propio código en nuestro entorno local.

Por aquello de aprender practicando, vamos a crear un fichero de texto vacío en una nueva carpeta. El fichero de texto vacío representa un programa que iremos modificando para simular lo que serían modificaciones en la aplicación de verdad,

En mi Windows yo he creado una nueva carpeta en c:\myFakeProject\ y he creado dentro un nuevo fichero .txt vacío con el nombre thisIsAnAppYouBetterBelieveIt.txt. Ahora abramos nuestra magnífica aplicación con apariencia de fichero de texto vacío con cualquier editor de texto (notepad, gedit o cualquier editor sencillo y rápido que tengas más a mano).

Escribamos alguna nueva línea de “código”, por ejemplo: “Hola mundo!”. Ahora borra la palabra “mundo” y sustitúyela por “Paco”. Guarda y cierra el fichero.

Imagínate que en vez de un .txt se trata de una aplicación de verdad y que, por error, modificaste la línea hace unas semanas. Te tocaría perder un tiempo precioso buscando la modificación (“Paco” por “mundo”)que introdujiste sin querer.

Bien, vamos a poner solución a este problema con nuestro Git, nuestro flamante nuevo sistema de control de versiones. Comprueba que en tu terminal o línea de comandos estés en el directorio raíz de tu proyecto. En mi caso c:\myFakeProject\ y escribe

git init

Este comando creará una nueva carpeta oculta /.git en la raíz de tu proyecto, donde Git guarda toda la información local del estado pasado y actual del código. De hecho, acabas de estrenar un sistema de control de versiones para tu nuevo proyecto. ¡Felicidades!

Siempre que queramos saber en qué estado se encuentra nuestro espacio de trabajo (que por defecto incluye el subdirectorio donde hemos inicializado git y todos sus subdirectorios), debemos escribir:

git status

Hazlo y te aparecerán cuatro o cinco líneas de información, entre las cuales encontrarás referencias a los siguientes asuntos importantes (que en principio te sonarán a checo antiguo):

  • La rama o branch sobre la que estás trabajando, por ejemplo “On branch master
  • El commit o confirmación en cabeza (current HEAD commit). Por ejemplo: “Initial commit” (aún no has hecho ningún commit)
  • Una lista de ficheros con cambios respecto al último commit (como no has hecho ningún cambio desde el git init, no debería aparecer nada de esto ahora).
  • Una lista de ficheros que están untracked (ficheros que Git no está controlando). Estos suelen ser ficheros que acabas de crear, como el fichero .txt de nuestro ejemplo. Si te fijas, el nombre de tu .txt de ejemplo debería aparecerte en pantalla después de “Untracked files:”.

Bien, vayamos por partes. ¿Qué es eso de la branch? Una branch o rama es una ramificación en el seguimiento de las modificaciones del código. Suponte que tú y otro desarrollador tenéis que trabajar sobre la misma aplicación y que no queréis tener que estar constantemente actualizando vuestra entorno de trabajo local con los cambios que realiza cada uno, a diario, sobre la misma rama. Una manera de trabajar en estos casos podría ser, por ejemplo, crear una rama nueva para cada desarrollador, que más tarde, al cabo de unos días, se fusionarán en una sola rama, es decir, se integrarán los dos cambios hechos por separado en una sola rama. A esto de fusionar ramas se le conoce como merge. Sobre branching y merging hablaremos en el próximo post, por ahora basta con que te suene.

¿Qué es eso del commit? Un commit o confirmación es la forma de decir con Git “confirmo los cambios que he hecho hasta ahora porque he terminado con este asunto y el asunto está resuelto”. Suponte que estás corrigiendo un error y modificas cinco ficheros de tu proyecto. Cuando acabas de modificarlo, testeas la aplicación y todo funciona como tú esperas: puedes dar por terminado el arreglo. Es muy buena idea, entonces, hacer un commit y ponerle un titulillo hermosamente explicativo como por ejemplo “fixed bug #6576”.

Podemos hacer lo mismo si acabamos de crear un proyecto o acabamos de crear nuevos ficheros dentro del proyecto. ¡De hecho, este es el caso en el que estamos ahora!. Si intentas hacer un commit ahora, sin embargo, te aparecerá un mensajito preocupante, pruébalo:

git commit -m "My first commit"
...
nothing added to commit but untracked files present

Nada se ha añadido en nuestro commit. La razón es porque Git tiene un sistema de dos pasos para realizar el commit: el primer paso se llama stage o puesta en escena y el segundo, es el commit o confirmación, propiamente. Es decir, primero debemos selecionar cuáles de los cambios realizados queremos poner en escena y preparar para un mismo commit, y luego los hemos de comitear, lo que equivale a reunir todos los cambios bajo una misma confirmación, a razón de que, por ejemplo, son cambios que solucionan un problema en común.

Vamos pues a stagear los cambios realizados y luego realizar el commit. El staging se hace con el commando git add. Por ejemplo, nosotros podemos escribir:

git add thisIsAnAppYouBetterBelieveIt.txt

Ahora haz un git status. Deberías ver una diferencia importante respecto al principio. Ya no aparecen ficheros untracked en tu espacio de trabajo, en su lugar aparece una nueva línea como ésta tras Changes to be committed:

new file:   thisIsAnAppYouBetterBelieveIt.txt

Lo que significa que nuestro fichero está listo para ser confirmado, es decir, para ser incluido en el próximo commit que hagamos. Vamos allá:

git commit -m "My first commit"

¡Felicidades por tu primer commit!. Ahora volvamos a hacer un git status. Git nos dirá esta vez que nuestro espacio de trabajo está libre de nuevas modificaciones desde el último commit (claro, lo acabamos de hacer).

Bien, modifiquemos ahora nuestro querido fichero .txt. Ábrelo y cambia, por ejemplo, “Paco” por “Pedro”. Ahora guarda y sal de tu editor de texto. Volvamos a la línea de comandos. Haz un git status.

Ahora debería informarte Git de que has modificado un fichero respecto al commit que tienes en cabeza (en nuestro caso, respeto al último commit realizado). Si quieres comprobar la modificación realizada escribe:

git diff

Y debería aparecerte algo como esto

diff --git a/thisIsAnAppYouBetterBelieveIt.txt b/thisIsAnAppYouBetterBelieveIt.txt
index 5d8a8b4..4955b9f 100644
--- a/thisIsAnAppYouBetterBelieveIt.txt
+++ b/thisIsAnAppYouBetterBelieveIt.txt
@@ -1 +1 @@
-Hola Paco
\ No newline at end of file
+Hola Pedro
\ No newline at end of file

Para salir de la pantalla de visualización de cambios pulsa la tecla “q”.

Fíjate que git nos informa de los cambios que se han realizado, incluyendo las líneas del fichero donde se han realizado. En este caso nos informa de las diferencias que existen entre el estado actual de nuestro código (que aún no están staged ni committed) y los cambios entre la cabecera de la rama actual (en nuestro caso, respecto al último commit que hicimos).

Al igual que cuando creamos un fichero, cuando modificamos uno existente debemos hacer un staging de los cambios previamente al commit. Por suerte, no tienes que memorizar ningún nuevo comando porque todo tipo de staging se hace, al igual que antes, con git add, ¡también en el caso de eliminar ficheros o cambiarles el nombre!:

git add thisIsAnAppYouBetterBelieveIt.txt

A continuación escribe:

git commit -m "I changed the name for the lulz"

Perfecto, en tu segundo commit has confirmado el cambio del nombre en esa línea de código.

¿Y si creo 200 ficheros, elimino 100 y modifico 500? ¿Tengo que hacer el staging uno a uno? Y la respuesta es que no. Aquí te dejo unos comandos muy útiles que te servirán para hacer el staging de cambios de múltiples ficheros al mismo tiempo:

git add .     # Staging de todos los ficheros modificados o recién creados (sin los eliminados)
git add -A    # Staging de todos los ficheros modificados, eliminados o recién creados
git add -u    # Staging de todos los ficheros modificados o eliminados (sin los nuevos)

No te olvides de hacer el commit después de hacer el staging si estás contento con los cambios realizados. Puedes pensar en el staging como un paso previo en el que valorar cuáles de los cambios merecen formar parte de un mismo commit o confirmación. De hecho, en un entorno profesional deberías tomarte bastante en serio lo de realizar un commit o, dicho de otro modo, no hagas un commit (y aún menos lo subdas al repositorio remoto) sin estar 100% seguro de que los cambios que has realizado son (prácticamente) los definitivos para resolver el asunto que te traes entre manos.

De todos modos no sufras demasiado si cometes un error al estilo “se me olvidó modificar este fichero”. Siempre puedes hacer nuevos commits corrigiendo errores o, incluso, enmendar el último commit realizado escribiendo:

git add fichero_que_se_me_olvidó_modificar
git commit --ammend

Esto sustituirá el último commit realizado por un commit idéntico pero que incluirá el fichero que olvidaste modificar/crear/eliminar.

Otro comando que te puede ser útil es el opuesto al git add, es decir, el que utilizamos para hacer lo opuesto al stage de un fichero. Este comando podría serte útil si deseas hacer un commit sólo con algunos ficheros modificados y te has equivocado y los has stageado todos:

git reset HEAD nombre_de_fichero_a_unstagear   # Lo contrario de: git add nombre_de_fichero 

Y para el caso de querer hacer un unstage de múltiples ficheros o, mejor dicho, de TODOS los ficheros en estado staged:

git reset   # Lo contrario de: git add -A 

En fin, espero que esta introducción te haya servido para empezar a moverte por el mundo de los VCS y, en particular, con Git. Aunque aún no hemos tratado el asunto de los branch y los repositorios remotos piensa que ya estás en el camino de poder hacer un buen control de cambios en el código de tu entorno local.

Consejillos

Y ahora un par de consejos para facilitarte la vida:

  • Siempre que comiences un nuevo proyecto, por pequeño y personal que sea, haz el seguimiento de cambios con tu VCS favorito. No subestimes la utilidad de saber los cambios realizados haces meses en un proyecto que tenías abandonado y que vuelves a retomar.
  • En tus proyectos de desarrollo utiliza un IDE compatible con el VCS que utilices. Hay IDEs muy buenos que te permiten automatizar la ejecución de comandos git (como los que acabamos de aprender) o ejecutarlos con un par de clicks.
  • Puedes utilizar alguna interfaz gráfica para git que, como mínimo, te ayudará a visualizar los cambios realizados en los distintos ficheros antes o después de hacer el staging o el commit. Hay montones y muy buenas GUI para todos los SO: GitHub Desktop, SourceTree, git-cola… De nuevo, Google es tu amigo.

Próximamente…

En el próximo post sobre Git hablaré de cómo manejar las branches y cómo intercambiar datos con los repositorios remotos: la auténtica belleza de todo este asuntillo.

Recuerda que puedes preguntarme sobre cualquier cosa que no te haya quedado clara, sobre errores que te van surgiendo o indicarme correcciones aquí abajo, en el área de comentarios.