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

¿Qué es y cómo se usa la función clientLoader de Remix?

Checa este otro Post

meta cover

¿En qué consiste React 19?

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻