cover

Git Subtrees: Gestión de trabajo en paralelo


Escuchar este post

Selecciona una voz y genera audio para escuchar este post

El Problema de las Dependencias Compartidas

En desarrollo profesional, es común encontrarse con esta situación: tienes código que necesitas compartir entre múltiples proyectos. No es una librería pública que publiques en npm o PyPI, sino código interno de tu organización.

Las opciones tradicionales presentan limitaciones:

  • Copy-paste: Duplicación de código, pesadilla de mantenimiento
  • Git submodules: Complejidad operacional, require sincronización manual constante
  • Packages privados: Overhead de versionado y publicación para cambios menores
  • Monorepos: Reestructuración completa de la arquitectura del proyecto

Git Subtrees: Una Alternativa Elegante

Git Subtrees resuelve este problema permitiendo incluir un repositorio completo como subdirectorio de otro, manteniendo:

  • Bidireccionalidad: Lee cambios del repositorio origen y contribuye de vuelta
  • Transparencia: Los archivos están directamente en tu working directory
  • Simplicidad operacional: No requiere inicialización especial para nuevos desarrolladores

Arquitectura del Ejemplo

proyecto-principal/
├── src/
│   ├── app/
│   └── shared-ui/          ← Git Subtree (repo independiente)
│       ├── components/
│       ├── hooks/
│       └── utils/
└── package.json

El directorio shared-ui/ es simultáneamente:

  • Parte integral de proyecto-principal
  • Un repositorio independiente con su propia historia y colaboradores

Operaciones Fundamentales

1. Integración Inicial (subtree add)

git subtree add --prefix=src/shared-ui git@github.com:org/shared-ui.git main --squash

Parámetros críticos:

  • --prefix: Path de destino en el repositorio host
  • --squash: Colapsa el historial del subtree en un merge commit único
  • main: Branch de referencia del repositorio remoto

Resultado: El contenido completo del repositorio remoto se integra como subdirectorio, manteniendo la capacidad de sincronización bidireccional.

2. Sincronización Entrante (subtree pull)

git subtree pull --prefix=src/shared-ui git@github.com:org/shared-ui.git main --squash

Casos de uso:

  • Integrar cambios desarrollados por otros teams
  • Mantener consistencia con la versión canónica
  • Resolver conflicts de sincronización

Comportamiento: Ejecuta un git fetch + git merge especializado que respeta la estructura del subtree.

3. Contribución Upstream (subtree push)

git subtree push --prefix=src/shared-ui git@github.com:org/shared-ui.git main

Proceso interno: Git extrae únicamente los commits que modificaron archivos dentro del prefix y los reaplica al repositorio de destino.

Consideración importante: A diferencia de pull, push no usa --squash para preservar la granularidad de commits en el repositorio upstream.

Workflow de Desarrollo

Configuración Inicial

# Integrar dependencia compartida git subtree add --prefix=src/shared-ui git@github.com:org/shared-ui.git main --squash git push origin main

Ciclo de Desarrollo Iterativo

# 1. Sincronizar con upstream git subtree pull --prefix=src/shared-ui git@github.com:org/shared-ui.git main --squash # 2. Desarrollo local en el subtree vim src/shared-ui/components/Button.tsx git add src/shared-ui/ git commit -m "feat(shared-ui): implement dark mode variant" # 3. Integration en el proyecto host git push origin feature/dark-mode # 4. Contribución upstream (después del code review) git subtree push --prefix=src/shared-ui git@github.com:org/shared-ui.git main

Comparativa Técnica: Subtrees vs Alternativas

Git Subtrees

  • Working Directory: ✅ Archivos nativos directamente en el repo
  • Onboarding: ✅ git clone único incluye todo el código
  • Desarrollo Bidireccional: ✅ Push y pull nativos sin configuración adicional
  • Historia del Proyecto: ❌ Se infla con commits del subtree
  • Granularidad de Updates: ⚠️ Solo a nivel de commit individual

Git Submodules

  • Working Directory: ❌ Referencias indirectas, archivos separados
  • Onboarding: ❌ Requiere git submodule init/update manual
  • Desarrollo Bidireccional: ⚠️ Posible pero workflow complejo
  • Historia del Proyecto: ✅ Mantiene historia limpia y separada
  • Granularidad de Updates: ✅ Control preciso con SHA específicos

Package Registry (npm/PyPI)

  • Working Directory: ✅ Archivos nativos después de instalación
  • Onboarding: ✅ npm install o equivalente resuelve todo
  • Desarrollo Bidireccional: ❌ Requiere ciclo completo de publishing
  • Historia del Proyecto: ✅ Historia limpia, sin dependencias
  • Granularidad de Updates: ✅ Semantic versioning con control granular

Configuración Avanzada

Automatización con Git Aliases

# ~/.gitconfig [alias] st-add = "!f() { git subtree add --prefix=\"$1\" \"$2\" main --squash; }; f" st-sync = "!f() { git subtree pull --prefix=\"$1\" \"$2\" main --squash; }; f" st-push = "!f() { git subtree push --prefix=\"$1\" \"$2\" main; }; f" # Uso: # git st-add src/shared-ui git@github.com:org/shared-ui.git # git st-sync src/shared-ui git@github.com:org/shared-ui.git # git st-push src/shared-ui git@github.com:org/shared-ui.git

CI/CD Integration

# .github/workflows/subtree-sync.yml name: Sync Subtrees on: schedule: - cron: '0 9 * * MON' # Weekly sync workflow_dispatch: jobs: sync: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: token: ${{ secrets.SUBTREE_TOKEN }} - name: Sync shared-ui run: | git subtree pull --prefix=src/shared-ui \ git@github.com:org/shared-ui.git main --squash || true - name: Create PR if changes run: | if [[ -n $(git status --porcelain) ]]; then gh pr create --title "chore: sync subtree dependencies" \ --body "Automated subtree synchronization" fi

Caso de Estudio: Design System Compartido

Contexto Empresarial

Tu organización mantiene un design system (@company/design-tokens) usado por múltiples productos:

  • Web App (React/TypeScript)
  • Mobile App (React Native)
  • Marketing Site (Next.js)

Implementación con Subtrees

1. Setup Inicial en cada Proyecto

# Web App git subtree add --prefix=src/design-system git@github.com:company/design-tokens.git main --squash # Mobile App git subtree add --prefix=shared/design-system git@github.com:company/design-tokens.git main --squash # Marketing Site git subtree add --prefix=design/tokens git@github.com:company/design-tokens.git main --squash

2. Desarrollo Cross-Project

# Designer modifica tokens en Web App vim src/design-system/tokens/colors.json git commit -m "feat(tokens): add dark mode palette" # Contribuir cambios al design system central git subtree push --prefix=src/design-system git@github.com:company/design-tokens.git main # Otros proyectos sincronizan los cambios git subtree pull --prefix=shared/design-system git@github.com:company/design-tokens.git main --squash

Criterios de Adopción

✅ Casos de Uso Óptimos

  • Internal libraries: Código propietario compartido entre proyectos
  • Cross-team collaboration: Múltiples equipos contribuyen al mismo código base
  • Rapid iteration: Cambios frecuentes que requieren feedback inmediato
  • Monorepo alternative: Compartir código sin reestructurar completamente

❌ Anti-patterns

  • Third-party dependencies: Usa package managers (npm, pip, cargo)
  • Large binary assets: Git no está optimizado para archivos grandes
  • Complex branching: Subtrees funcionan mejor con workflows lineales
  • High-security environments: La bidireccionalidad puede crear vectores de ataque

Reference Card

# Core Operations git subtree add --prefix=PATH REMOTE BRANCH --squash # Initial integration git subtree pull --prefix=PATH REMOTE BRANCH --squash # Sync downstream git subtree push --prefix=PATH REMOTE BRANCH # Contribute upstream # Management git log --grep="git-subtree-dir: PATH" # Subtree history git subtree merge --prefix=PATH --squash COMMIT # Manual merge

Nota técnica: Los subtrees usan .git/subtree-cache para optimizar operaciones repetidas. No requiere configuración manual pero puede consumir espacio de disco en proyectos grandes.

Abrazo. Bliss. 🤓

meta cover

Aprende en 5 minutos qué es HTML y cuando utilizar cada una de sus etiquetas

Checa este otro Post

meta cover

Cómo eliminar archivos sin usar de mi proyecto JS

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻