Javier Valencia Javier Valencia
Terminal con los comandos git worktree, git submodule y git lfs

Git avanzado III: entornos y repos grandes

Javier Valencia · · 4 min de lectura · 3 visitas · Desarrollo
git desarrollo terminal herramientas productividad git-avanzado

Tercera y última entrega de la serie Git avanzado — y cierre del recorrido completo que empezó con Git básico y pasó por Git intermedio. Tiempo de lectura estimado: 5 minutos.

Para cerrar la serie, tres comandos que la mayoría de desarrolladores no tocan hasta que los necesitan, pero que cuando llega el momento resuelven problemas que no tienen otra buena solución. git worktree para tener varias ramas checked out simultáneamente, git submodule para incluir otros repos dentro del tuyo, y git sparse-checkout para trabajar con monorepos sin descargar el mundo entero.

git worktree: varias ramas a la vez

El problema clásico: estás trabajando en feature/carrito y entra un bug urgente en producción. Lo normal: git stash, git switch main, arreglas, commiteas, pusheas, vuelves. Si estás compilando algo pesado o tienes un dev server corriendo, cambiar de rama lo rompe todo.

git worktree resuelve esto creando un segundo directorio con un checkout independiente del mismo repo:

git worktree add ../proyecto-hotfix main

Resultado: al lado de tu directorio original aparece proyecto-hotfix/ con el contenido de main. Las dos copias comparten el .git/, así que no ocupa apenas espacio extra. Puedes trabajar en las dos en paralelo, cada una con su terminal, su dev server, su IDE.

Flujo real:

# estoy en ~/proyectos/blog, en feature/admin
git worktree add ../blog-hotfix -b hotfix/login-roto origin/main
cd ../blog-hotfix
# ... arreglo, commit, push ...
cd ../blog
git worktree remove ../blog-hotfix
git branch -d hotfix/login-roto

Comandos básicos:

git worktree list                   # ver todos los worktrees
git worktree add ruta rama          # crear worktree
git worktree add ruta -b nueva      # crear worktree con rama nueva
git worktree remove ruta            # eliminar (aviso si hay cambios)
git worktree prune                  # limpiar referencias a worktrees borrados

Restricciones:

  • No puedes tener dos worktrees con la misma rama checked out. Si feature/X está en un worktree, otro worktree no puede estar también en feature/X.
  • El worktree principal (tu clone original) no se puede mover ni borrar fácilmente. Los secundarios sí.

Cuándo usarlo:

  • Hotfix mientras trabajas en una feature sin tirar de stash.
  • Comparar el comportamiento de dos ramas ejecutándolas al mismo tiempo.
  • Tener un worktree por versión mantenida: ~/blog-main, ~/blog-v1.x, ~/blog-v2.x.
  • Sesiones de review largas: abres el PR en un worktree aparte, pruebas, sin molestar tu trabajo habitual.

Para mí, worktree es de los comandos que, una vez descubiertos, reemplazan al 80% de los usos de stash.

git submodule: repos dentro de repos

Un submódulo es un repositorio git que vive dentro de otro repositorio git, apuntando a un commit específico. El repo "padre" no guarda los ficheros del submódulo, sino una referencia (commit hash) del repo "hijo".

Casos donde se usa:

  • Tu proyecto incluye una librería propia que mantienes en un repo aparte.
  • Tienes un tema o plugin de terceros que quieres fijar a una versión.
  • Dos equipos contribuyen a una dependencia compartida que se integra en varios proyectos.

Añadir un submódulo:

git submodule add https://github.com/usuario/libreria libs/libreria
git commit -m "Añadir submódulo libs/libreria"

Esto crea un fichero .gitmodules en la raíz con la configuración, más un "puntero" en libs/libreria que no es una carpeta normal sino una referencia a un commit.

Al clonar un repo con submódulos:

git clone URL proyecto
cd proyecto
git submodule update --init --recursive

O en un paso:

git clone --recurse-submodules URL proyecto

Actualizar el submódulo a una versión nueva:

cd libs/libreria
git fetch
git checkout v2.1.0
cd ../..
git add libs/libreria
git commit -m "Subir libreria a v2.1.0"

El commit que registras en el repo padre no contiene los ficheros del submódulo: solo el hash del commit de libs/libreria que le corresponde.

Comandos que se usan:

git submodule status                # versiones actuales de cada submódulo
git submodule update                # sincroniza los submódulos con lo comiteado
git submodule foreach 'git pull'    # ejecuta comandos en todos los submódulos

Honestamente: submódulos son potentes pero pesados. Atasques típicos: alguien hace commit sin actualizar los submódulos, clones que se olvidan del --recurse-submodules, conflictos raros entre el puntero y la rama del submódulo. Antes de adoptarlos, considera alternativas: paquetes en un registry privado, monorepo con todo junto, Go workspaces o equivalentes del lenguaje. Submódulos son la solución cuando no hay otra mejor, no el primer recurso.

git sparse-checkout: descargar solo lo que te interesa

En un monorepo gigante (cientos de miles de ficheros, varios gigas) no necesitas tenerlo todo checked out para trabajar en la parte de frontend/. sparse-checkout le dice a git qué partes del working tree quieres que existan en disco. El .git completo sigue ahí, pero el working tree solo tiene lo que pides.

Habilitarlo en un repo existente:

git sparse-checkout init --cone
git sparse-checkout set frontend/app frontend/shared tools/build

A partir de ese momento, tu working tree solo contiene esas rutas. El resto del repo está en .git/ pero no aparece como ficheros.

Con --cone (recomendado) funciona en modo "directorios": le das rutas completas y git incluye todo lo que cuelga de ellas. Sin --cone es modo "patterns" (tipo gitignore al revés), más flexible pero mucho más propenso a sorpresas.

Comandos:

git sparse-checkout init --cone         # activar
git sparse-checkout set ruta1 ruta2     # establecer rutas visibles
git sparse-checkout add otra-ruta       # añadir rutas
git sparse-checkout list                # ver rutas actuales
git sparse-checkout disable             # todo el repo de vuelta

Combinado con --filter=blob:none al clonar, la reducción es enorme:

git clone --filter=blob:none --sparse URL proyecto
cd proyecto
git sparse-checkout set frontend/app

Esto clona la estructura del repo pero descarga solo los blobs necesarios para las rutas que activas con sparse-checkout. Un monorepo de 10GB puede quedarse en 300MB en disco.

Cuándo usarlo:

  • Monorepos corporativos donde solo trabajas con una parte pequeña.
  • Repos académicos o de recursos que pesan mucho y solo necesitas una carpeta.
  • Setups de CI donde el build necesita solo un subdirectorio.

Cuándo no:

  • Repos pequeños o medianos: la complejidad extra no compensa.
  • Si vas a tocar ficheros de distintas áreas continuamente: vas a estar añadiendo rutas todo el rato.

Combinando los tres

Un setup realista para un monorepo grande, trabajando en frontend:

# Clone mínimo con solo frontend
git clone --filter=blob:none --sparse [email protected]:empresa/mono
cd mono
git sparse-checkout init --cone
git sparse-checkout set frontend/app frontend/shared

# Un worktree aparte para atender un hotfix en backend
git sparse-checkout add backend/api    # traemos el área
git worktree add ../mono-hotfix -b hotfix/api-timeout origin/main
cd ../mono-hotfix
# ... trabajo ...

# Librería común vivida como submódulo
git submodule update --init libs/ui-kit

Un setup así, hace cinco años, era prácticamente inviable. Hoy git lo soporta nativamente.

Errores típicos con estas herramientas

Submódulos olvidados al actualizar. Tras un git pull que incluye cambios de submódulo, acuérdate de git submodule update --init --recursive. Hay un alias para hacer pull "completo":

git config --global submodule.recurse true

Worktrees huérfanos. Si borras a mano un worktree secundario sin git worktree remove, queda registrado. git worktree prune lo limpia.

Sparse-checkout sin --cone. Patrones tipo gitignore invertidos son difíciles de depurar. Quédate con --cone salvo que sepas exactamente qué haces.

Fin de la serie

Con esto cerramos los 27 comandos (nueve más básicos, nueve intermedios, nueve avanzados) que cubren, en mi experiencia, el 95% de lo que uno necesita en un trabajo normal con git. Lo que queda (hooks, plumbing commands, filter-repo, git-lfs, bundles, notes) es territorio muy específico que vale la pena cuando lo necesitas y no antes.

Si quieres repasar: Git básico son los cimientos, Git intermedio es el día a día con equipo, y Git avanzado es la caja de herramientas para cuando las cosas se ponen interesantes. Y si todavía no lo has leído, el post sobre git aliases es lo que te convierte todo esto en muscle memory.