Comandos avanzados de Git y su aplicación

Publicado el 13 noviembre 2024 por Daniel Rodríguez @analyticslane

Esta entrada es la segunda de la serie "Introducción a Git y el control de versiones" escrita para explicar que es Git y como se puede utilizar en equipos de trabajo. Entrada en la que se analizaran algunos comandos avanzados de Git. Centrándose en aquellos que permiten trabajar de forma eficaz con repositorios remotos, ramas y la gestión de versiones en equipo.

La serie de publicaciones consta de las siguientes entradas:

  1. Introducción a Git y el control de versiones
  2. Comandos avanzados de Git y su aplicación
  3. Mejores prácticas en Git
  4. Introducción a la metodología GitFlow

La publicación comienza con un análisis de los comandos avanzados de Git necesarios para clonar y trabajar con repositorios remotos, continuando con una revisión de los pasos para la gestión de ramas, para luego revisar herramientas avanzadas como git rebase, git cherry-pick, git stash, y git reflog. Esta entrada permite comprender y utilizar estos comandos para mejorar el control y la organización en los proyectos gestionados con Git.

Clonar y trabajar con repositorios remotos

Uno de los primeros comandos avanzados de Git que es necesario conocer de Git es git clone. Este comando permite copiar un repositorio Git que se encuentra en un servidor remoto (como GitHub, GitLab o Bitbucket) a la máquina local.

Cuando se clona un repositorio, Git descarga todos los archivos, historial de commits y ramas desde el repositorio remoto y los guarda en una carpeta local. Siendo especialmente útil cuando un desarrollador se une a un proyecto ya existente o si se desea colaborar en un repositorio de código abierto.

Sintaxis y uso básico de git clone

La sintaxis básica de git clone es:

git clone <URL del repositorio>

Por ejemplo, si se desea clonar un repositorio de GitHub llamado mi-proyecto con la siguiente URL https://github.com/usuario/mi-proyecto.git, se debe ejecutar el siguiente comando:

git clone https://github.com/usuario/mi-proyecto.git

Esto creará una carpeta llamada mi-proyecto en la ruta donde se ejecute el comando que contendrá todos los archivos del repositorio.

Personalización del proceso de clonado

Si se desea que la carpeta local tenga un nombre diferente, este se conseguir proporcionando un segundo argumento al comando git clone. Por ejemplo:

git clone https://github.com/usuario/mi-proyecto.git nuevo-nombre

Esto creará una carpeta llamada nuevo-nombre en lugar de mi-proyecto.

Trabajar con un repositorio clonado

Una vez clonado el repositorio, ya se puede comenzar a trabajar con él como se haría con cualquier otro proyecto. Todos los cambios que se hagan en la copia local podrán ser confirmados (usando commit) y luego enviados al repositorio remoto mediante git push. Los pasos para ello serían:

  1. Implementar cambios en los archivos.
  2. Agregar los cambios a la staging área con git add.
  3. Confirmar los cambios con git commit.
  4. Envías los cambios al repositorio remoto con git push.

Este es el flujo de trabajo habitual cuando se colabora en proyectos con otros desarrolladores.

Ramas (branches)

Una rama en Git es una línea paralela de desarrollo. Permite crear una copia del estado actual del proyecto y hacer cambios sin afectar la rama principal (generalmente llamada main o master). Esto es especialmente útil cuando se trabaja en una nueva característica o una corrección de errores, ya que, para evitar que la rama principal pueda dejar de funcionar durante los cambios, se puede desarrollar en una rama aislada y luego fusionar esos cambios una vez que estén listos.

Creación de ramas con git branch y git checkout

En el caso de que se esté trabajando en una nueva característica llamada "mejoras en el modelo". Se puede crear una rama llamada model-improvements usando el siguiente comando:

git branch model-improvements

Al ejecutar el comando, Git creará una nueva rama, pero se continúa estando en la rama actual. Para cambiar a la nueva rama se debe usar el comando git checkout como se muestra a continuación:

git checkout model-improvements

Alternativamente, se pueden combinar ambos pasos con el parámetro -b:

git checkout -b model-improvements

Este comando creará y se moverá a la nueva rama en un solo paso.

Visualización de ramas

Se pueden ver todas las ramas disponibles en un repositorio usando el siguiente comando:

git branch

Este comando lista las ramas locales. La rama en la que se estás actualmente estará marcada con un asterisco.

Fusión de ramas con git merge

Una vez terminado el trabajo en una rama, se deberían fusionar los cambios en la rama principal ( main o master). Para hacer esto, primero es necesario comprobar que se está en la rama en la que se desea fusionar los cambios (por ejemplo, main):

git checkout main

Luego, se debe ejecutar el comando de fusión:

git merge model-improvements

Este comando combinará los cambios de la rama footer-improvements en la rama main.

Resolución de conflictos al fusionar ramas

Algunas veces, cuando se intentan fusionar dos ramas, Git no puede hacer la fusión automáticamente porque algunos archivos fueron modificados en ambas ramas de manera incompatible. Esto se conoce como un conflicto.

Cuando ocurre un conflicto, Git marca las áreas del código que están en conflicto para que el programador los resuelva manualmente. En esto casos se tendrá algo como lo siguiente en los archivos afectados:

<<<<<< HEAD
Contenido en la rama actual
=======
Contenido en la rama que se está fusionando
>>>>>> model-improvements

El programador deberá editar el archivo para resolver el conflicto y luego confirmar los cambios:

git add archivo-afectado
git commit

Git marca el conflicto como resuelto una vez que se confirmen los cambios.

Rebasing y cherry-picking

Además de git merge existen otras opciones para combinar los cambios de una rama a otra.

¿Qué es git rebase?

El comando git rebase es una alternativa a git merge que reorganiza la secuencia de commits. Cuando se usa rebase, Git toma los cambios de una rama y los "vuelve a aplicar" en la rama actual, como si se estuvieran aplicando esos cambios nuevamente, sobre el estado más reciente de la rama base.

Ejemplo de git rebase

En el caso de que se tenga dos ramas: main y feature. Si el objetivo es mover los commits de feature a main, se puede hacer de la siguiente manera:

git checkout feature
git rebase main

Esto significa que los commits en la rama feature serán reaplicados sobre los commits de main, y la historia del proyecto parecerá más lineal, sin tener la fusión típica que genera un merge.

Rebase interactivo

El rebase interactivo ( git rebase -i) es una herramienta que te permite modificar el historial de commits antes de aplicarlos. Pudiendo combinar, reordenar, o eliminar commits. Esto es útil para limpiar el historial antes de fusionar una rama.

Para iniciar un rebase interactivo se debe usar el comando:

git rebase -i <commit-hash>

El commit-hash es el identificador del commit anterior al primero que se desea modificar. Git abrirá un editor donde se podrá decidir qué hacer con cada uno de los commits.

¿Qué es git cherry-pick?

A veces, no es necesario fusionar una rama completa, sino solo un commit específico de otra rama. Aquí es donde entra en juego el comando git cherry-pick.

Este comando toma un commit de otra rama y lo aplica a la rama actual, como si se hubiera hecho ese commit directamente en la rama donde se aplica.

Ejemplo de git cherry-pick

Si se tiene un commit en la rama feature que se desea aplicar en main. En primer lugar, se debe obtener el hash del commit con git log, una vez hecho esto se puede mover a la rama de destino y aplicar el git cherry-pick:

git checkout main
git cherry-pick <commit-hash>

Este comando aplicará solo el commit seleccionado de feature, al que corresponde el hash <commit-hash>, a la rama main.

Git Stash

git stash es una herramienta útil cuando se está en medio de cambios no confirmados, pero es necesario cambiar de contexto sin perder el trabajo. En lugar de confirmar los cambios a medio hacer, se pueden "guardar" esos cambios temporalmente y volver a ellos más tarde.

Cómo usar git stash

Supongamos que se está trabajando en la rama main y se han realizado cambios en varios archivos, pero no se desea confirmarlos todavía. Para guardar esos cambios sin perderlos se debe ejecutar:

git stash

Esto moverá los cambios actuales a un "stash" temporal y restaurará el directorio de trabajo al último commit. Ahora se puede cambiar de rama o realizar otras tareas sin preocuparte por perder los cambios.

Restaurar los cambios guardados

Cuando se esté listo para continuar trabajando en esos cambios, se pueden restaurar mediante el comando:

git stash apply

Esto volverá a aplicar los cambios que se guardaron anteriormente. Si deseas eliminar el stash después de aplicarlo, se puede usar:

git stash drop

Casos de uso de git stash

El comando git stash es especialmente útil en varias situaciones, por ejemplo:

  • Interrupciones inesperadas: Si se está trabajando en una nueva característica, pero se presenta un problema urgente que es necesario resolver en otra rama, se puede hacer stash de los cambios, cambiar a la rama necesaria, resolver el problema y luego restaurar el trabajo.
  • Experimentos temporales: Si se desea probar algo sin hacer commits, se puede usar git stash para limpiar el directorio de trabajo y volver a un estado limpio de forma rápida.

Reflog y recuperación de commits perdidos

git reflog es una herramienta que permite ver el historial de todas las referencias de Git en un repositorio, incluyendo las operaciones que normalmente no se registran en git log. Esto es útil cuando es necesario recuperar commits que podrían haberse perdido por un reset o un rebase.

Ejemplo de uso de git reflog

Si accidentalmente se ha aplicado un git reset --hard y se han perdido algunos commits, es posible usar git reflog para ver el historial de todas las operaciones recientes:

git reflog

Esto mostrará una lista de las acciones recientes en el repositorio, incluyendo los commits que se han perdido. Posteriormente, se puede recuperar un commit específico haciendo:

git checkout <commit-hash>

De esta manera, se puede restaurar el commit perdido y evitar tener que volver a realizar el trabajo.

Conclusiones

Los comandos avanzados de Git que se han visto en esta entrada como git rebase, git cherry-pick, git stash, y git reflog permiten gestionar de manera eficiente el historial de cambios y abordar situaciones complejas durante el proceso de desarrollo. Haciendo que Git sea una herramienta más flexible y adaptable. Cada uno de los comandos vistos tiene su funcionalidad durante el flujo de trabajo de un desarrollador, ayudando a mantener el código organizado y mejorando la colaboración dentro de los equipos. Con estos conocimientos avanzados, se está más preparado para enfrentarse a cualquier desafío que se presente en los flujos de trabajo con Git.

En la siguiente entrada de esta serie se explicarán las mejores prácticas para trabajar con Git.

Nota: La imagen de este artículo fue generada utilizando un modelo de inteligencia artificial.