Ir al contenido principal

Control de versiones con Git

Introducción

En el desarrollo de software, es común que nuestros proyectos evolucionen constantemente: añadimos nuevas funcionalidades, corregimos errores o experimentamos con distintas soluciones. Sin un sistema para gestionar estos cambios, podríamos perder versiones importantes o tener dificultades para colaborar con otros programadores. Git es la herramienta de control de versiones más utilizada en el mundo, que nos permite llevar un registro detallado de las modificaciones de nuestro código, trabajar en equipo eficientemente y mantener diferentes versiones de un mismo proyecto. En este artículo, aprenderemos a utilizar Git con nuestros proyectos Python, una habilidad fundamental para cualquier desarrollador.

Instalación y configuración de Git

Lo primero que necesitamos hacer es instalar Git en nuestro sistema:

Para Windows:

  1. Descarga el instalador desde git-scm.com
  2. Ejecuta el instalador y sigue las instrucciones (puedes mantener las opciones por defecto)

Para macOS:

# Si tienes Homebrew instalado
brew install git

# Alternativamente, puedes descargarlo desde git-scm.com

Para Linux (Ubuntu/Debian):

sudo apt update
sudo apt install git

Configuración inicial

Una vez instalado, debemos configurar nuestra identidad:

# Configurar nombre de usuario
git config --global user.name "Tu Nombre"

# Configurar correo electrónico (preferiblemente el mismo de GitHub/GitLab)
git config --global user.email "tu.email@ejemplo.com"

# Verificar la configuración
git config --list

Creación de un repositorio Git

Vamos a crear un repositorio para un proyecto Python:

# Crear una carpeta para nuestro proyecto
mkdir mi_proyecto_python
cd mi_proyecto_python

# Inicializar un repositorio Git
git init

# Verificar el estado
git status

Cuando ejecutamos git init, se crea un directorio oculto llamado .git que contiene toda la información necesaria para el control de versiones.

Añadir archivos al repositorio

Creemos algunos archivos Python básicos:

# Crear un archivo principal
echo "def saludar():
    print('¡Hola, mundo!')

if __name__ == '__main__':
    saludar()" > main.py

# Crear un archivo de funciones
echo "def sumar(a, b):
    return a + b

def restar(a, b):
    return a - b" > operaciones.py

# Crear un archivo README.md
echo "# Mi Proyecto Python

Este es un proyecto simple para aprender a usar Git con Python." > README.md

Ahora vamos a añadir estos archivos al repositorio:

# Ver qué archivos no están incluidos en el repositorio
git status

# Añadir todos los archivos al área de preparación (staging)
git add .

# Alternativamente, podemos añadir archivos específicos
# git add main.py operaciones.py README.md

# Comprobar el estado después de añadir los archivos
git status

Crear commits

Un commit es una "instantánea" del estado actual de nuestro proyecto:

# Crear nuestro primer commit
git commit -m "Primer commit: estructura básica del proyecto"

# Ver el historial de commits
git log

Cada commit tiene:

  • Un identificador único (hash)
  • Un autor y una fecha
  • Un mensaje descriptivo
  • Los cambios realizados desde el commit anterior

Modificar archivos y registrar cambios

Vamos a modificar uno de nuestros archivos:

# Modificar operaciones.py añadiendo más funciones
echo "def multiplicar(a, b):
    return a * b

def dividir(a, b):
    if b == 0:
        raise ValueError('No se puede dividir por cero')
    return a / b" >> operaciones.py

# Ver los cambios específicos
git diff operaciones.py

# Añadir el archivo modificado
git add operaciones.py

# Crear un nuevo commit
git commit -m "Añadidas funciones de multiplicación y división"

Trabajando con ramas

Las ramas nos permiten trabajar en distintas versiones del proyecto simultáneamente:

# Ver la rama actual
git branch

# Crear una nueva rama
git branch nueva_caracteristica

# Cambiar a la nueva rama
git checkout nueva_caracteristica

# Alternativa: crear y cambiar en un solo comando
# git checkout -b nueva_caracteristica

Ahora podemos hacer cambios en esta rama sin afectar a la rama principal (main o master):

# Crear un nuevo archivo en la rama nueva_caracteristica
echo "def es_par(numero):
    return numero % 2 == 0

def es_impar(numero):
    return numero % 2 != 0" > validaciones.py

# Añadir y hacer commit del nuevo archivo
git add validaciones.py
git commit -m "Añadidas funciones de validación de paridad"

# Volver a la rama principal
git checkout main

# Notar que el archivo validaciones.py no existe en esta rama
# ls

Fusionar ramas

Cuando estamos satisfechos con los cambios en una rama, podemos fusionarla con la rama principal:

# Asegurarnos que estamos en la rama principal
git checkout main

# Fusionar la rama nueva_caracteristica
git merge nueva_caracteristica

# Ahora el archivo validaciones.py está disponible en la rama principal
# ls

Gestión de conflictos

A veces, Git no puede fusionar automáticamente los cambios debido a conflictos:

# Modifiquemos el mismo archivo en dos ramas diferentes
git checkout -b rama_conflicto
echo "# Archivo de operaciones matemáticas básicas\n$(cat operaciones.py)" > operaciones.py
git add operaciones.py
git commit -m "Añadido comentario en operaciones.py"

git checkout main
echo "# Este archivo contiene operaciones aritméticas\n$(cat operaciones.py)" > operaciones.py
git add operaciones.py
git commit -m "Añadido otro comentario en operaciones.py"

# Intentar fusionar la rama_conflicto (causará un conflicto)
git merge rama_conflicto

Git indicará que hay un conflicto. Si abrimos el archivo operaciones.py, veremos algo así:

<<<<<<< HEAD
# Este archivo contiene operaciones aritméticas
=======
# Archivo de operaciones matemáticas básicas
>>>>>>> rama_conflicto
def sumar(a, b):
    return a + b
...

Para resolverlo:

  1. Editamos el archivo para mantener el contenido que deseamos
  2. Eliminamos los marcadores de conflicto (<<<<<<<, =======, >>>>>>>)
  3. Guardamos el archivo
  4. Añadimos el archivo resuelto y creamos un commit
# Después de editar y resolver el conflicto
git add operaciones.py
git commit -m "Resuelto conflicto en operaciones.py"

Trabajo remoto con GitHub/GitLab

Para colaborar con otros desarrolladores, necesitamos un repositorio remoto:

Crear un repositorio en GitHub:

  1. Regístrate en GitHub si aún no tienes cuenta
  2. Haz clic en "New repository"
  3. Nombra tu repositorio (ej. "mi_proyecto_python")
  4. Deja las demás opciones por defecto y haz clic en "Create repository"

Vincular y sincronizar con el repositorio remoto:

# Vincular nuestro repositorio local con el remoto
git remote add origin https://github.com/tuusuario/mi_proyecto_python.git

# Subir nuestro código al repositorio remoto
git push -u origin main

# En futuras ocasiones, bastará con usar:
# git push

Clonar un repositorio existente:

# Clonar un repositorio desde GitHub
git clone https://github.com/usuario/repositorio.git

# Entrar al directorio del repositorio clonado
cd repositorio

Obtener cambios del repositorio remoto:

# Actualizar nuestro repositorio local con los cambios del remoto
git pull

Ignorar archivos

No todos los archivos deben incluirse en el control de versiones (ej. archivos generados, configuraciones locales, etc.):

# Crear un archivo .gitignore
echo "# Archivos y directorios a ignorar

# Archivos de entornos virtuales
venv/
env/
.env/

# Archivos compilados de Python
__pycache__/
*.py[cod]
*$py.class

# Distribución / empaquetado
dist/
build/
*.egg-info/

# Archivos de configuración local
config.ini
secrets.json

# Archivos de caché
.pytest_cache/
.coverage
htmlcov/" > .gitignore

# Añadir y hacer commit del archivo .gitignore
git add .gitignore
git commit -m "Añadido archivo .gitignore"

Buenas prácticas con Git

  1. Commits atómicos: Cada commit debe representar un cambio lógico y completo
  2. Mensajes descriptivos: Los mensajes deben explicar claramente qué cambios se realizaron
  3. Pull antes de push: Siempre actualiza tu repositorio local antes de subir cambios
  4. Ramas temáticas: Usa ramas para desarrollar características específicas
  5. Eliminar ramas fusionadas: Mantén limpio tu repositorio eliminando ramas que ya no necesitas
  6. Revisar antes de commit: Usa git diff y git status para verificar los cambios
  7. No incluir archivos generados: Usa correctamente el .gitignore

Flujos de trabajo comunes

Flujo de trabajo básico (para proyectos personales):

  1. Crear/clonar repositorio
  2. Realizar cambios
  3. Añadir archivos (git add)
  4. Hacer commit (git commit)
  5. Sincronizar con el remoto (git pull y git push)

Flujo de trabajo con ramas (Git Flow, para proyectos en equipo):

  1. Rama main o master: código en producción
  2. Rama develop: integración continua
  3. Ramas feature/...: para nuevas características
  4. Ramas hotfix/...: para correcciones urgentes
  5. Ramas release/...: preparación para lanzamientos

Integración con Python

Gestión de dependencias:

Para proyectos Python, es buena práctica incluir un archivo requirements.txt:

# Con el entorno virtual activado
pip freeze > requirements.txt

# Añadir al repositorio
git add requirements.txt
git commit -m "Añadido archivo de requisitos"

Estructura recomendada para proyectos Python con Git:

mi_proyecto/
├── .git/               # Directorio Git (creado por git init)
├── .gitignore          # Archivos a ignorar
├── README.md           # Documentación básica
├── requirements.txt    # Dependencias
├── setup.py            # Para distribuir el paquete
├── mi_paquete/         # Código fuente
│   ├── __init__.py
│   ├── modulo1.py
│   └── modulo2.py
└── tests/              # Pruebas unitarias
    ├── __init__.py
    ├── test_modulo1.py
    └── test_modulo2.py

Herramientas gráficas para Git

Si prefieres una interfaz visual, existen varias herramientas:

Resumen

Git es una herramienta fundamental para cualquier desarrollador Python, permitiéndonos mantener un historial detallado de los cambios en nuestro código, trabajar en equipo de manera eficiente y gestionar diferentes versiones de nuestros proyectos. Hemos aprendido a inicializar un repositorio, realizar commits, trabajar con ramas, resolver conflictos y sincronizar con repositorios remotos. Estas habilidades son cruciales no solo para el desarrollo individual, sino para la colaboración en proyectos de código abierto y equipos de desarrollo. En el próximo artículo, exploraremos cómo implementar pruebas unitarias con unittest, lo que nos permitirá verificar que nuestro código funciona correctamente a medida que evoluciona con Git.