Tutorial de GIT

Sep 2020

Te mostramos los comandos más útiles que utilizarás día a día en el trabajo de proyectos de desarrollo de software bajo el control de versiones distribuido open source Git. Sincroniza, compara, mezcla y comparte el código fuente de tus proyectos.

Qué es Git

Git es un sistema de control de versiones para proyectos basados en ficheros. Es un sistema distribuido, por lo que cada copia del proyecto contendrá todo el histórico de cambios. Con él, se pueden añadir, borrar o modificar archivos de un proyecto de software y compara o recuperar el estado de los archivos en cualquier momento determinado. Cada paquete de cambios se confirma contra el repositorio de git, dejándolos registrados como una anotación o commit.

Opcionalmente, se utiliza una copia remota del proyecto bajo el control de git en una ubicación de Internet, para que uno o más programadores puedan intercambiar las actualizaciones de los ficheros fuente sin perder el control de las ediciones que se hayan hecho por separado. Hay muchas plataformas, pero una de ellas muy famosa es Github.

Cómo instalar Git

Git es una herramienta por consola de comandos que se integra bastante bien en los principales editores de programación existente. Si no lo tienes aún visita la guía de instalación de git.

Terminología de Git

Git usa unos términos específicos de forma común para referirse a todos los aspectos que le conciernen, así que si vas a trabajar con git conviene que aprendas todos ellos:

Término Significado
repository se refiere a cualquier copia del proyecto bajo el control y seguimiento de git. Git guarda toda su información en un directorio oculto, dentro del mismo proyecto denominado .git.
remote repository hace referencia a un repositorio remoto utilizado como mecanismo de sincronización de uno o más programadores. Todos ellos «suben» sus cambios al remoto para dejarlos disponibles al resto de programadores.
commit Anotación. Corresponde a un paquete de cambios que se confirma en el repositorio, de forma local. Típicamente éste contendrá uno o más cambios sobre uno o más ficheros del proyecto y por tanto del código fuente del programa que se está desarrollando. Se considera la unidad básica dentro de la línea de tiempo en cuanto a los cambios se refiere.
staging Preparación. Es una fase previa en donde se añaden las modificaciones del proyecto a una lista temporal que terminará siendo un commit. Los cambios se podrán descartar o constituirán un commit en el repositorio. Se entiende por cambios las modificaciones, inclusiones de archivos y también las eliminaciones de líneas, palabras o ficheros.
branch Rama. Git proporciona la posibilidad de tener varias líneas de tiempo en la evolución de un proyecto. Estas ramas representan una evolución potencialmente distinta de los ficheros, pero que en un momento determinado pueden fusionarse. Por ejemplo, para tener en el mismo repositorio la versión 1.x y la 2.x del proyecto.
merge Fusionar. Este término se utiliza cuando se desean juntar dos ramas para traspasar las anotaciones (commits) de una rama a otra, muy útil para llevar a la rama «oficial» aquellos cambios «experimentales» en los que se han puesto nuevas funcionalidades o se han arreglado algunos errores.
conflict La fusión no siempre es automática, si git detecta que un mismo fichero ha sido editado en anotaciones diferentes, a la hora de fusionar todos los cambios, puede pasar que entre en un conflicto porque no sepa con cuál de los cambios de los commits diferente deba quedarse. En este caso se produce un conflicto que debe ser solucionado manualmente.

Obviamente hay muchos más conceptos técnicos que pueden listarse, pero estos de aquí son los básicos para poder realizar una operativa básica con git. Veamos cómo se aplican.

Crear un repositorio vacío

Para crear un repositorio git, ubicados en el directorio raíz de nuestro proyecto, escribiremos:

git init

Este comando inicializa los índices y referencias internas del repositorio git dentro del directorio .git. A partir de ese momento podemos realizar las operaciones de añadir ficheros, sincronizarnos con otros repositorios y demás.

Crear copia de un repositorio existente

Crear un repositorio vacío puede ser útil a veces, pero normalmente continuaremos el trabajo de uno ya existente. Para ello se necesita un repositorio remoto, que esté accesible y del cual podamos bajarnos una copia completa. Por ejemplo si nos ofrecen un remoto en la ubicación https://host/path/project.git, deberemos usar esta url para hacer una copia:

git clone https://host/path/project.git

Git mantendrá la referencia del remoto del cual se realizó la copia para poder «subir» los cambios que hagamos en local o para descargar los cambios que hagan otros programadores del equipo. Al repositorio remoto le añadirá el nombre de origin. Pueden añadirse tantos remotos como se deseen, y subir o bajar los cambios del remoto que se indique por su nombre. Si no se indica nombre se utiliza el repositorio origin por defecto.

Sincronizar con un repositorio remoto

En el siguiente paso, veremos como realizar alguna anotación. Por un momento imagínese que ya hemos hecho alguna en local y que por el otro lado algún compañero ha realizado otros cambios que ha subido al remoto. En este supuesto si quisiéramos subir nuestros cambios, primero git nos obliga a que nos bajemos los cambios del remoto antes de subir los nuestros. Así que, cuando intentemos sincronizarnos con un remoto primero deberemos ejecutar:

git fetch

Se descarga una fotografía del estado del servidor, en este punto, nuestro repositorio local se da cuenta de que va un cambio por detrás del remoto. El siguiente paso, traspasar los cambios al directorio de trabajo en donde se encuentra nuestro proyecto localmente:

git pull

Este último comando, coge los cambios del remoto que aun no tenemos localmente y los aplica a nuestros ficheros. Si no hay conflicto, el traspaso de los cambios será transparente y simplemente percibiremos que los ficheros han sido modificados. Pero si se produce algún conflicto, el fichero quedará modificado localmente esperando que hagamos una revisión manual sobre este y que terminemos haciendo una anotación que confirme la fusión.

Siguiendo el supuesto en el que ya habíamos hecho una anotación sobre el repositorio, si ha habido un conflicto deberemos subir dos commits al remoto, uno por la modificación que habíamos realizado y otro para resolver el conflicto. De no haber ninguna colisión, sólo subiríamos el commit hecho de forma local.

Suponiendo que ya hemos solucionado los posibles conflictos, para subir todas las anotaciones pendientes al remoto deberemos realizar la operación de push

git push

Esta última operación, subirá todas las anotaciones al servidor, dejándolas disponibles al resto del equipo.

Cómo añadir remotos a git

Si hemos partido de un repositorio vació creado con [git init] no habrá ningún repositorio remoto configurado al que subir los cambios. Puede añadirse uno fácilmente ejecutando la siguiente instrucción:

git remote add origin https://host/path/project.git

Nótese que al remoto se le ha dado el nombre de [origin] por ser el primer y único remoto asignado al repositorio. Los comandos de fetch, push permiten indicar el remoto sobre el que trabajar.

Cómo añadir archivos a git

Bien, ya tenemos un repositorio local y sabemos cómo sincronizarlo, pero cómo registramos los cambios sobre git. Para ello simplemente debemos asegurarnos de si la modificación que hemos realizado es sobre un fichero que está bajo el control de git, o si es sobre un fichero nuevo, del cual git aun no conoce nada. Si empezamos con un repositorio vacío, no habrá ningún fichero bajo el dominio de git, así que el primer paso es añadir el o los archivos al git.

Imaginemos que sobre el repositorio vacío creamos un fichero denominadado index.js, para añadirlo a git debemos invocar la siguiente opción:

git add index.js

[git add] acepta comodines para no tener que ir añadiendo de uno en uno. Incluso los editores avanzados de programación realizan este paso automáticamente por nosotros.

En el momento de hacer add el fichero index.js pasa a estar preparado (staged) y listo para viajar en una anotación hacia el histórico de cambios. Si el fichero ya estuviera bajo el control de git no es necesario invocar la adición.

Cómo realizar una anotación

En el momento que se tiene uno o más ficheros preparados (staged), ya es posible confirmar los cambios en el repositorio local mediante una anotación o commit. Todas las anotaciones en git deberán in acompañadas de un comentario:

git commit -m "inclusión del fichero index.js"

Esta sentencia registra la evolución del proyecto, y si se trabaja con un remoto podría realizarse el push. La operación de push podría fallar si alguien hubiera subido algún cambio entre medias, por lo que estaríamos forzados a realizar primero un fetch y pull.

Cómo descartar cambios de git

Si hemos hechos cambios locales que aun no han sido anotados, podemos descartarlos dejando el proyecto en el estado de la última anotación. Para ello simplemente debemos ejecutar:

git stash

Esta operación descarta todos los cambios locales de forma permantente.

Cómo ver el estado del repositorio

Cuando se han hecho varias modificaciones a lo largo de los ficheros fuente el proyecto es fácil perder la pista que de líneas se han editado. Para ver todos los ficheros modificados del proyecto escribiremos:

git status

Esta operativa nos da una visión general de los recursos cambiados, pero si deseamos ver el detalle de uno en concreto le pediremos a git que nos muestre las diferencias:

git diff index.js

Git mostrará un editor de comparación basado en texto algo feote. De nuevo y afortunadamente, los entornos integrados de programación aportan una interfaz visual mucho más atractiva.

Cómo ver el histórico de cambios con git

Para mostrar el histórico de cambios sobre todo el proyecto usaremos la opción log:

git log

Cada commit está identificado con un hash [c561ae54d2ccb9de92d1512a74ce2ea5b91acf88], mediante este identificador podemos ver qué cambios se aplicaron en dicha anotación:

git show c561ae54d2ccb9de92d1512a74ce2ea5b91acf88

Así no será posible que se nos escape ni una sola coma que pueda producir que nuestro proyecto no se ejecute correctamente.

Cómo recuperar el proyecto en una anotación de git

Si deseamos recuperar el proyecto tal y como estaba tras una anotación determinada podemos ejecutar la operación checkout:

git checkout c561ae54d2ccb9de92d1512a74ce2ea5b91acf88

Para volver al presente, podemos realizar la siguiente operación que salta en el tiempo hasta la última anotación disponible:

git switch -

Cómo ignorar archivos con git

Hay ficheros que no deben estar en el repositorio de control de código, por ejemplo aquellos que corresponden con archivos de construcción o autogenerados en el proceso de compilación, como los .class, .exe, .dll, etc.. Estos se pueden construir cuando sea necesario así que es inútil que se guarden en el repositorio, salvo que haya un motivo de fuerza mayor.

Para poder indicar a git que se deben descartar algunos ficheros se debe añadir uno (que sí está bajo el control de git) denominado .gitignore. Este es un fichero de texto plano que se aloja en la raíz del proyecto, en donde se puede indicar ficheros en concreto, patrones o directorios que fuerzan a git a ignorarlos. Por ejemplo:

\*.log
target/
hs_error.log

Como se ha dicho, este fichero sí conviene que esté bajo el control de git para que todos los miembros del equipo de desarrolladores ignoren los mismos ficheros, así que no debemos olvidarnos de añadirlo a la próxima anotación con:

git add .gitignore

Cómo crear una rama con git

Como se ha dicho, las ramas representan fotografías distintas del proyecto que pueden evolucionar de forma independiente. Aunque en la mayoría de los casos termina habiendo un traspaso de anotaciones de una rama a otra. Los casos más habituales del uso de ramas es la aplicación de gitflow.

Gitflow es una manera de organizar las ramas para tener control del proyecto en todas sus fase. De manera muy resumida diremos que gitflow dice que debe haber la rama con nombre master que represente la versión de la aplicación en producción. Y por otro lado la rama develop que incluye anotaciones adicionales a máster y que corresponden con los evolutivos de la nueva versión a liberar de la aplicación. Una vez que develop se considera estable, se traspasan las anotaciones de develop hacia master justo en el momento de publicar y distribuir la nueva versión de la aplicación. Gitflow recomienda el uso de más ramas, pero con estas es suficiente para escenificar el uso de git.

De manera automática, la rama master es la rama inicial, y es donde se viajan los commits que se hacen si no se indica ninguna otra rama. Pero podemos crear una nueva rama a partir de un commit para hacer evolucionar el proyecto con cambios que son experimentales y puede que se descarten o que finalmente se apliquen a la versión final del software. Sea cual sea el caso, podemos crear una rama con:

git switch -c develop

El argumento -c indica que si la rama no existe la cree. Ahora pueden hacerse las anotaciones que se consideren oportunas. Si se trabaja con un remoto, al hacer push de esta rama automáticamente se creará una igual con el mismo nombre en la ubicación remota. Si deseamos volver a la rama original pulsaremos:

git switch master

Esta nueva rama mantiene el mismo histórico que la rama que le da origen, pero los cambios que se hagan sobre esta no estarán en su predecesora, no al menos hasta que no hagamos una fusión de ramas.

Cómo fusionar ramas con git

Para fusionar dos ramas, debemos estar posicionados en la rama destino, con switch. La operación lo que realiza es la recuperación de las anotaciones de la rama indicada sobre la actual. Así que si deseamos traer los cambios de develop a master primero nos posicionamos en master y luego realizamos la fusión con merge

git swith master
git merge develop

Esta operación puede dar lugar a conflictos, si en master hubiera alguna modificación que no se hubiera realizado sobre develop. Si es este el caso, deberemos realizar un commit manual con la resolución del o de los conflictos. Recuérdese que si se trabaja en equipo con un remoto debe hacerse el push para ofrecer los cambios al resto.

Cómo borrar ramas con git

Si se ha terminado de usar una rama local y se desea borrar, bastará con decirle a git nuestra intención de la siguiente forma:

git branch -d develop

Pero es posible, que deseemos borrar la rama en el repositorio remoto en vez de en local, si es así la sentencia cambia ligeramente.

git push origin --delete develop

Debe tener presente que si el resto del equipo se bajaron los cambios de la rama borrada, aunque haya borrado la rama en el remoto de referencia, ésta seguirá existiendo en las otras copias repartidas en el equipo.

Conclusión

Git es una herramienta muy potente para controlar todos los cambios en los archivos que componen un proyecto, además representa un buen mecanismo para las copias de seguridad y utilidad de sincronización entre computadoras.

En las anteriores líneas hemos visto los fundamentos básicos de git y cómo utilizarlo desde la consola de comandos o terminal. Pero afortunadamente los entornos integrados de programación suelen incorporar las operaciones que hemos visto de forma nativa mediante acciones contextuales o accesos de la barra de herramientas que simplifican y agilizan el uso de git.

Te recomendamos que eches un vistazo a alguno de los IDEs que puedes usar totalmente gratis ya que son Open Source y multi plataforma.

Déjanos tus comentarios abajo o escríbenos para compartir tu experiencia o para mejorar este contenido. Saludos!

Tutorial de GIT

¿Con ganas de seguir leyendo?

Nuestra guía de Java

Cerca de 450 páginas en un libro de tapa blanda que podrás utilizar para aprender a programar en Java desde cero sin conocimientos previos. Explicamos como usar las herramientas más usadas en el mundo empresarial, todas ellas son totalmente gratis y Open Source.

Aprende conceptos como TDD para desarrollar software con garantías. Conecta tus apps con JPA en bases de datos SQL. Integra tus proyectos con Maven y mantenlos bajo control con Git. Mantente al día con la programación funcional de Java 8+.

Nuestra guía de Java
Libro Javañol