Carla lleva tres días en funcionalidad/borrado-multiple. Es una rama larga: toca app.js, estilos.css y el nuevo diálogo de componentes-ui. Tiene el código a medias, con funciones sin terminar y la aplicación en un estado que ni siquiera arranca.

Y entonces llega el aviso: hay un fallo en producción y hay que publicar una corrección ahora.

Su rutina actual es esta:

git stash -u
git switch main
git switch -c correccion/urgente
# ... arreglar, probar, confirmar, publicar ...
git switch funcionalidad/borrado-multiple
git stash pop
# ... reconstruir mentalmente dónde estaba ...

Diez veces al día. Y cada vuelta tiene su peaje: el stash con ficheros sin seguimiento a veces da conflictos al recuperarlo, hay que reconstruir el contexto mental, el servidor de desarrollo se reinicia, y desde que hay submódulo (lección 06-05) cada cambio de rama arrastra además su actualización.

Su siguiente idea es clonar el repositorio dos veces. Funcionaría, pero como veremos tiene un coste real. Git ofrece algo mejor y bastante desconocido: varios directorios de trabajo compartiendo una única base de datos de objetos. Se llama git worktree, y con esta lección cerramos el módulo.

Contenido

  1. Qué es un worktree
  2. worktree add: el primer directorio adicional
  3. Comparación con clonar dos veces
  4. El juego completo de comandos
  5. La regla clave: una rama, un worktree
  6. Cómo se ve por dentro
  7. Casos de uso reales
  8. Interacción con stash, submódulos y hooks
  9. Cierre del módulo 6

  1. Qué es un worktree

Recordemos la estructura de la lección 01-03. Un repositorio Git tiene dos partes bien diferenciadas:

  • El directorio .git/: los objetos, las referencias, la configuración, el reflog. El repositorio de verdad.
  • El árbol de trabajo (working tree): los ficheros que ves y editas, que son el reflejo de un commit concreto.

Hasta ahora hemos dado por hecho que hay uno de cada. Pero no es obligatorio:

Un worktree es un directorio de trabajo adicional, con su propio HEAD, su propio índice y sus propios ficheros, que comparte la base de datos de objetos y las referencias con el repositorio original.

flowchart TD
    subgraph BD[".git/ — una sola base de datos"]
      O["Objetos: blobs, trees, commits"]
      R["Referencias: ramas, etiquetas, remotos"]
      C["Configuración y reflog"]
    end
    subgraph W1["~/proyectos/gestor-tareas"]
      H1["HEAD → funcionalidad/borrado-multiple"]
      I1["Índice propio"]
      F1["app.js, estilos.css... (a medias)"]
    end
    subgraph W2["~/proyectos/gestor-tareas-urgente"]
      H2["HEAD → correccion/urgente"]
      I2["Índice propio"]
      F2["app.js, estilos.css... (limpio)"]
    end
    W1 --> BD
    W2 --> BD

Lo que se comparte: objetos, ramas, etiquetas, remotos, git fetch, configuración del repositorio, reflog de las referencias.

Lo que es propio de cada worktree: HEAD, el índice, los ficheros del disco, el estado de operaciones en curso (una fusión a medias, un rebase parado) y la pila de stash.

El directorio original se llama worktree principal; los añadidos, worktrees enlazados. Funcionalmente son casi idénticos.

  1. worktree add: el primer directorio adicional

Carla resuelve su problema con un comando:

cd ~/proyectos/gestor-tareas
git worktree add ../gestor-tareas-urgente -b correccion/urgente main
Preparing worktree (new branch 'correccion/urgente')
HEAD is now at c8f2a1e Fusionar funcionalidad/filtro-estado

Lo que acaba de pasar:

  • Se ha creado el directorio ~/proyectos/gestor-tareas-urgente/.
  • Contiene una copia de trabajo completa del proyecto en el estado de main.
  • Se ha creado la rama correccion/urgente a partir de main y está activa allí.
  • El directorio original no se ha tocado en absoluto: sigue en funcionalidad/borrado-multiple, con todo su trabajo a medias intacto.
git worktree list
/home/carla/proyectos/gestor-tareas           c8f2a1e [funcionalidad/borrado-multiple]
/home/carla/proyectos/gestor-tareas-urgente   c8f2a1e [correccion/urgente]

Ahora Carla trabaja en el segundo directorio con total normalidad:

cd ../gestor-tareas-urgente
# ... corregir app.js ...
git commit -am "Corregir el borrado cuando la tarea tiene subtareas"
git push -u origin correccion/urgente

Y cuando termina, vuelve:

cd ../gestor-tareas
git status
On branch funcionalidad/borrado-multiple
Changes not staged for commit:
	modified:   app.js
	modified:   estilos.css

Untracked files:
	nuevo-dialogo.js

Exactamente como lo dejó. Ni stash, ni pop, ni reconstruir el contexto: el editor, el servidor de desarrollo y la ventana del navegador siguen abiertos donde estaban.

Las formas de invocar add:

Comando Qué hace
git worktree add <ruta> <rama-existente> Abre esa rama en el nuevo directorio
git worktree add <ruta> Crea una rama con el nombre del directorio
git worktree add -b <rama-nueva> <ruta> [<punto>] Crea la rama y la abre
git worktree add -B <rama> <ruta> [<punto>] Como -b, pero la restablece si ya existe
git worktree add --detach <ruta> <commit> Detached HEAD en ese commit, sin rama
git worktree add --track -b <rama> <ruta> origin/<rama> Crea la rama con seguimiento del remoto

Y una útil cuando la rama viene del remoto:

git fetch
git worktree add ../gestor-tareas-revision origin/funcionalidad/exportar

Git detecta que funcionalidad/exportar existe en origin y crea automáticamente una rama local con seguimiento (el comportamiento de --guess-remote, que se puede fijar con git config worktree.guessRemote true).

  1. Comparación con clonar dos veces

La alternativa evidente era git clone otra vez. La comparación explica por qué worktree es mejor casi siempre:

git worktree add Segundo git clone
Espacio en disco Solo los ficheros del árbol de trabajo Ficheros + toda la base de objetos duplicada
Objetos compartidos : una sola copia No: dos copias independientes
Ramas visibles Las mismas en todos los worktrees Cada clon tiene las suyas
Etiquetas Compartidas Duplicadas y desincronizables
git fetch Uno solo actualiza todos Uno por clon
Remotos y credenciales Compartidos Hay que configurarlos en cada uno
Configuración local Compartida (con matices) Independiente por clon
Un commit en A, ¿visible en B? Inmediatamente Solo tras push + fetch
Stash Independiente por worktree Independiente
Hooks Compartidos (un solo .git/hooks) Uno por clon
Tiempo de creación Segundos (no hay transferencia) Lo que tarde el clon completo
Riesgo de desincronización Ninguno: es un solo repositorio Real

El punto que más se subestima es "un commit en A, ¿visible en B?". Con dos clones, para llevar un commit de uno a otro hay que pasar por el servidor. Con worktrees, en cuanto Carla confirma en un directorio, ese commit ya existe para el otro: puede hacer git cherry-pick, git rebase o git log sobre él inmediatamente, sin red de por medio.

El ahorro de espacio, con cifras: si .git/ ocupa 400 MB (historial largo, algún binario) y el árbol de trabajo 15 MB, un clon extra cuesta 415 MB y un worktree 15 MB. En repositorios grandes la diferencia deja de ser anecdótica.

Antes existía git clone --shared / --reference para compartir objetos entre clones. Funciona, pero es frágil: si el repositorio de referencia se mueve o se limpia, el clon dependiente puede quedar corrupto. git worktree es la solución moderna y segura al mismo problema, y la que debe usarse.

  1. El juego completo de comandos

list

git worktree list
git worktree list --porcelain      # formato estable, para scripts
/home/carla/proyectos/gestor-tareas           c8f2a1e [funcionalidad/borrado-multiple]
/home/carla/proyectos/gestor-tareas-urgente   3d8f1a6 [correccion/urgente]
/home/carla/proyectos/gestor-tareas-v1        b7e2c4a (detached HEAD)
/home/carla/proyectos/gestor-tareas-viejo     a1b2c3d [experimento] prunable

La marca prunable indica que el directorio ya no existe en el disco y su registro se puede limpiar.

remove

La forma correcta de eliminar un worktree:

git worktree remove ../gestor-tareas-urgente

Borra el directorio y su registro. Si hay cambios sin confirmar, se niega —lo cual es una protección, no una molestia:

fatal: '../gestor-tareas-urgente' contains modified or untracked files, use --force to delete it
git worktree remove --force ../gestor-tareas-urgente

Y ojo: remove no borra la rama. Eso es aparte:

git branch -d correccion/urgente

prune

Si borraste el directorio a mano con rm -rf (que funciona, pero deja el registro), prune limpia los restos:

git worktree prune
git worktree prune --dry-run -v      # ver qué haría sin hacerlo

Git también lo ejecuta solo de vez en cuando, y el plazo de gracia se controla con gc.worktreePruneExpire (por defecto, tres meses).

move

git worktree move ../gestor-tareas-urgente ~/trabajo/urgente

Mueve el directorio y actualiza las rutas internas. Moverlo con mv a secas rompe los enlaces, así que usa siempre este comando.

lock y unlock

git worktree lock ../gestor-tareas-usb --reason "Está en el disco externo de copias"
git worktree unlock ../gestor-tareas-usb

Un worktree bloqueado no se puede prune ni move. Es exactamente para el caso de un worktree en una unidad extraíble o un recurso de red: cuando la unidad no está montada, el directorio "no existe" y prune lo borraría del registro sin más. lock lo impide, y el motivo aparece en git worktree list --porcelain para que se sepa por qué.

repair

git worktree repair

Reconstruye los enlaces internos cuando algo los ha roto: has movido directorios a mano, has restaurado una copia de seguridad, o has renombrado el repositorio principal.

Tabla resumen:

Comando Qué hace
git worktree add <ruta> [<rama>] Crea un worktree nuevo
git worktree list Lista todos, con su rama y su commit
git worktree remove <ruta> Elimina uno (con --force si hay cambios)
git worktree prune Limpia registros de worktrees ya borrados
git worktree move <origen> <destino> Mueve uno actualizando las rutas
git worktree lock/unlock <ruta> Protege uno de prune y move
git worktree repair [<rutas>] Repara los enlaces internos rotos

  1. La regla clave: una rama, un worktree

Esta es la restricción fundamental, y hay que entenderla porque explica la mitad de los mensajes de error que verás:

Una misma rama no puede estar activa en dos worktrees a la vez.

cd ~/proyectos/gestor-tareas-urgente
git switch funcionalidad/borrado-multiple
fatal: 'funcionalidad/borrado-multiple' is already used by worktree at
'/home/carla/proyectos/gestor-tareas'

Y lo mismo al crear:

git worktree add ../otro-mas funcionalidad/borrado-multiple
fatal: 'funcionalidad/borrado-multiple' is already used by worktree at
'/home/carla/proyectos/gestor-tareas'

Por qué existe la restricción. Una rama es un puntero que avanza al confirmar (lección 03-01). Si dos worktrees tuvieran la misma rama activa, un commit en uno movería la rama bajo los pies del otro: el segundo se encontraría con que su HEAD apunta a un commit que él no ha creado, y su árbol de trabajo dejaría de corresponder a nada coherente. Git prohíbe la situación en lugar de dejar que ocurra.

No es una limitación, es una protección. Y tiene tres salidas cuando de verdad necesitas el mismo código dos veces:

# A) En detached HEAD: sin rama que mover, no hay conflicto
git worktree add --detach ../revision-solo-lectura funcionalidad/borrado-multiple

# B) En un commit concreto, que es lo mismo
git worktree add --detach ../version-1.0 v1.0.0

# C) Una rama nueva desde el mismo punto
git worktree add -b experimento/otra-via ../experimento funcionalidad/borrado-multiple

La opción A es la que más se usa: para leer o compilar una rama no hace falta tenerla activa como rama.

Y una consecuencia práctica que conviene saber: git branch -d se niega a borrar una rama que esté activa en otro worktree, y git rebase o git merge tampoco pueden operar sobre ella desde fuera. Para localizar dónde está:

git worktree list | grep nombre-de-la-rama

  1. Cómo se ve por dentro

Retomamos la lección 01-04 y el modelo de datos, porque el mecanismo es elegante y explica todo lo anterior.

En el worktree principal, .git es un directorio:

cd ~/proyectos/gestor-tareas
ls -ld .git
drwxrwxr-x 9 carla carla 4096 ago  1 11:23 .git

En un worktree enlazado, .git es un fichero de texto:

cd ~/proyectos/gestor-tareas-urgente
ls -l .git
cat .git
-rw-rw-r-- 1 carla carla 78 ago  1 11:25 .git
gitdir: /home/carla/proyectos/gestor-tareas/.git/worktrees/gestor-tareas-urgente

Una sola línea que dice: "mi repositorio está allí". Es exactamente el mismo mecanismo que usan los submódulos de la lección 06-05, donde el .git del submódulo es un fichero que apunta a .git/modules/<nombre> del padre.

Y en el repositorio principal:

ls ~/proyectos/gestor-tareas/.git/worktrees/
gestor-tareas-urgente/  gestor-tareas-v1/
ls ~/proyectos/gestor-tareas/.git/worktrees/gestor-tareas-urgente/
HEAD  ORIG_HEAD  commondir  gitdir  index  logs/  ORIG_HEAD
Fichero Contenido
HEAD El HEAD propio de ese worktree
index Su índice propio (la zona de preparación)
gitdir La ruta absoluta del directorio de trabajo al que sirve
commondir Ruta al .git común, donde están los objetos y las refs
logs/HEAD El reflog de ese worktree

Y ahí está la explicación completa del modelo:

  • HEAD e index están por worktree → cada uno puede estar en una rama distinta y tener sus propios cambios preparados.
  • objects/ y refs/ están en el commondir → las ramas, las etiquetas y los objetos son los mismos para todos, y por eso un commit en uno es visible al instante en el otro.

Con esto se entiende también por qué la restricción del apartado 5 es inevitable: hay un solo fichero refs/heads/funcionalidad/borrado-multiple, y no puede ser el HEAD de dos directorios que confirman por separado.

Hay incluso una notación para consultar el HEAD de otro worktree:

git rev-parse main@{worktree-principal}   # sintaxis avanzada, rara vez necesaria
git worktree list --porcelain             # lo habitual

Y una nota sobre configuración: por defecto, .git/config es compartido por todos los worktrees. Si necesitas que uno tenga configuración propia, existe extensions.worktreeConfig:

git config extensions.worktreeConfig true
git config --worktree core.sparseCheckout true

Es un caso avanzado y poco frecuente, pero conviene saber que existe si te encuentras con un config.worktree dentro de .git/worktrees/<nombre>/.

  1. Casos de uso reales

Caso 1: el hotfix sin tocar lo que tienes a medias

El de Carla, y el más común. Un worktree permanente para urgencias:

git worktree add ../gestor-tareas-hotfix main

Cuando llega el aviso, cd ../gestor-tareas-hotfix, git pull, se crea la rama, se arregla y se publica. Sin stash, sin cambiar de rama, sin perder el contexto.

Caso 2: comparar dos versiones en ejecución

Bruno tiene que comprobar si un problema de rendimiento existía en la versión 1.0:

git worktree add --detach ../gestor-tareas-v1 v1.0.0
# Terminal 1
cd ~/proyectos/gestor-tareas && python3 -m http.server 8000

# Terminal 2
cd ~/proyectos/gestor-tareas-v1 && python3 -m http.server 8001

Dos servidores, dos pestañas del navegador, las dos versiones funcionando a la vez. Comparar así es incomparablemente más fiable que ir alternando ramas y recordar cómo era.

Caso 3: compilar una rama mientras trabajas en otra

Una compilación completa de gestor-tareas tarda cuatro minutos. Con un solo directorio, esos cuatro minutos son de espera, porque cualquier edición ensucia el resultado. Con worktrees:

git worktree add ../gestor-tareas-build funcionalidad/borrado-multiple-rc
cd ../gestor-tareas-build && npm run build &
cd ~/proyectos/gestor-tareas    # seguir trabajando mientras compila

Caso 4: revisar la rama de un compañero

Ana tiene que revisar la pull request de Bruno, pero está a mitad de su propio trabajo:

git fetch
git worktree add --detach ../revision origin/funcionalidad/exportar
cd ../revision
# ... ejecutar, leer, probar ...
cd .. && git worktree remove revision

Sin --detach funcionaría también, pero para revisar no hace falta rama local, y así se evita acumular ramas de revisión.

Caso 5: un rebase largo sin bloquear el trabajo

Un git rebase -i con conflictos (lección 05-02) deja el repositorio en un estado intermedio. Si aparece algo urgente, estás atrapado: rebase --abort y a empezar de nuevo. Con un worktree dedicado, el rebase se queda parado en su directorio y tú trabajas en otro. Como el estado de las operaciones en curso es propio de cada worktree, no interfieren.

Caso 6: bisect sin parar

git bisect (lección 06-02) hace decenas de checkout en tu directorio. Si es una bisección larga con compilaciones, puedes lanzarla en un worktree aparte:

git worktree add --detach ../gestor-tareas-bisect main
cd ../gestor-tareas-bisect
git bisect start main v1.0.0
git bisect run /tmp/probar-borrado.sh

Mientras tanto, tu directorio principal sigue en tu rama, intacto. Al terminar, git bisect reset y git worktree remove.

Caso 7: documentación en una rama huérfana

Algunos proyectos publican la documentación en una rama independiente (gh-pages y similares). Con worktrees:

git worktree add ../gestor-tareas-docs gh-pages

Se edita la documentación en su directorio y el código en el suyo, con historiales separados y sin cambiar de rama nunca.

  1. Interacción con stash, submódulos y hooks

Con git stash

La pila de stash es propia de cada worktree. Un git stash list en el directorio principal no muestra lo guardado en otro:

cd ~/proyectos/gestor-tareas
git stash list
stash@{0}: WIP on funcionalidad/borrado-multiple: 8d4f2a7 Delegar los eventos...
cd ../gestor-tareas-urgente
git stash list
(vacío)

Técnicamente, refs/stash se guarda por worktree. Es coherente con el modelo —el stash es trabajo a medias de un árbol concreto— pero sorprende la primera vez.

Y la conclusión práctica de todo esto: los worktrees no sustituyen a stash, sino que reducen mucho la necesidad de usarlo. stash sigue siendo la herramienta correcta para apartar algo durante dos minutos dentro de la misma rama; el worktree lo es para trabajar en dos ramas durante días.

Situación Herramienta
Apartar cambios dos minutos para hacer un pull git stash
Probar algo rápido en la misma rama git stash
Trabajar en dos ramas durante horas o días git worktree
Atender urgencias sin perder el contexto git worktree
Comparar dos versiones en ejecución git worktree
Guardar trabajo antes de cambiar de máquina git stash o una rama WIP

Con submódulos

Los worktrees y los submódulos (lección 06-05) conviven, pero hay que saber dos cosas:

  • Los submódulos no se inicializan solos en un worktree nuevo. Después del add, toca:
git worktree add ../gestor-tareas-urgente -b correccion/urgente main
cd ../gestor-tareas-urgente
git submodule update --init --recursive
  • Los repositorios internos de los submódulos viven en el .git/modules/ común, así que los objetos se comparten igual que los del padre: la inicialización no vuelve a descargar nada de la red, solo hace checkout. Es rápida.

El soporte de worktrees dentro de submódulos ha mejorado mucho en las versiones recientes de Git, pero sigue siendo el terreno donde más rarezas aparecen. Si algo se descoloca, git worktree repair suele arreglarlo.

Con hooks

Los hooks son compartidos: hay un solo .git/hooks/ (o un solo core.hooksPath, lección 06-01) para todos los worktrees. Un pre-commit instalado funciona en todos automáticamente, lo cual es una ventaja.

El matiz: un hook que asuma rutas absolutas o que dé por hecho un directorio concreto puede confundirse. Los hooks se ejecutan con el directorio de trabajo puesto en la raíz del worktree activo, así que usar rutas relativas es lo correcto. Y si un hook necesita distinguir dónde está:

git rev-parse --show-toplevel      # raíz del worktree actual
git rev-parse --git-common-dir     # el .git compartido
git rev-parse --git-dir            # el .git/worktrees/<nombre> de este worktree

Con el rendimiento y el mantenimiento

Un apunte breve, porque el tema completo es de otra lección: los worktrees comparten la base de objetos, así que un git gc afecta a todos y Git tiene cuidado de no eliminar objetos referenciados desde cualquiera de ellos. Un worktree registrado pero cuyo directorio ya no existe puede, en cambio, mantener vivos objetos innecesariamente: por eso conviene ejecutar git worktree prune de vez en cuando.

Todo lo relativo a git gc, git maintenance y el rendimiento en repositorios grandes es el tema de la lección 08-06: Consejos de Rendimiento; los clones parciales, sparse-checkout y las técnicas de escalado, el de la 10-04.

  1. Cierre del módulo 6

Con esta lección cierras el bloque de herramientas. Repasando lo que ha cambiado en la forma de trabajar del equipo de gestor-tareas:

  • Hooks (06-01): comprobaciones automáticas en pre-commit, commit-msg y pre-push, versionadas con core.hooksPath. Ayudan contra el despiste, pero lo obligatorio se comprueba en el servidor.
  • git bisect (06-02): búsqueda binaria que convierte 214 commits en 8 pruebas, automatizable con bisect run y sus códigos de salida.
  • git blame (06-03): la historia de cada línea, con -w, -C e --ignore-rev para atravesar el ruido, y git log -L para ver la evolución completa.
  • git log avanzado y alias (06-04): --graph, --first-parent, --simplify-by-decoration, --left-right, formatos con colores, shortlog, y los alias que convierten todo eso en una palabra.
  • Submódulos (06-05): un puntero a un commit de otro repositorio, con reproducibilidad exacta a cambio de fricción, y comparados con subtree, paquetes y monorepo.
  • git worktree (06-06): varios directorios de trabajo sobre una única base de objetos.

Y una idea que atraviesa todo el módulo: el historial de Git no es solo un registro de lo que pasó, es una base de datos consultable. Quién escribió cada línea, en qué commit cambió un comportamiento, qué se ha integrado en main este mes, qué versión exacta de la biblioteca usaba la versión 1.0. Todas esas preguntas tienen respuesta exacta, y ahora sabes cómo pedirla.

Lo que viene

El equipo domina la herramienta. Ana, Bruno y Carla saben construir el historial, manipularlo con criterio, consultarlo a fondo y automatizar comprobaciones sobre él.

Lo que no han acordado todavía es cómo trabajar juntos. Y esas preguntas ya no son técnicas:

  • Cuando Bruno termina una funcionalidad, ¿cómo la propone? ¿Empuja directamente a main? ¿Abre una pull request? ¿Y si no tiene permiso de escritura en el repositorio?
  • Cuando Ana revisa el trabajo de Carla, ¿qué mira, cómo comenta y cuándo aprueba? ¿Qué hace un revisor que no sea repetir lo que ya dice el linter?
  • ¿Qué ramas existen y para qué sirve cada una? ¿Hay una rama develop? ¿Ramas de versión? ¿O todo el mundo integra en main varias veces al día?
  • ¿Cuándo se publica una versión? ¿Qué hay que haber pasado antes de que un cambio llegue a producción?

No hay una respuesta única: hay flujos de trabajo distintos, cada uno con su lógica, sus ventajas y su tipo de equipo. Un proyecto de código abierto con cientos de colaboradores externos no puede funcionar como un equipo de tres personas que despliega cinco veces al día.

En el módulo 7: Estrategias de Colaboración y Flujo de Trabajo veremos los forks y las pull requests como mecanismo para proponer cambios, las revisiones de código y cómo se hacen bien, y los tres grandes modelos de ramificación —Git Flow, GitHub Flow y Trunk Based Development— comparados con criterio para saber cuál encaja en cada situación. Y cerraremos con la integración continua: las comprobaciones automáticas que, esta vez sí, nadie puede saltarse con un --no-verify.

Empezamos por cómo se propone un cambio, en la lección 07-01: Forks y Pull Requests.

Errores Comunes y Consejos

Error 1: intentar abrir la misma rama en dos worktrees. Git lo impide y te dice dónde está activa. Usa --detach si solo quieres leer o compilar.

Error 2: borrar el directorio con rm -rf. Funciona, pero deja el registro sucio. Usa git worktree remove, o git worktree prune después.

Error 3: mover el directorio con mv. Rompe los enlaces internos. Usa git worktree move, o git worktree repair si ya lo hiciste.

Error 4: olvidar que remove no borra la rama. Quitar el worktree deja la rama viva; bórrala aparte con git branch -d.

Error 5: esperar que los submódulos se inicialicen solos. Hay que ejecutar git submodule update --init --recursive en cada worktree nuevo.

Error 6: buscar un stash en el worktree equivocado. La pila es propia de cada uno. Si no aparece, mira en el otro directorio.

Error 7: crear worktrees dentro del propio repositorio. git worktree add ./temporal funciona, pero el directorio aparece como contenido sin seguimiento del repositorio principal. Créalos fuera, como hermanos del directorio original.

Error 8: acumular worktrees olvidados. Ocupan disco y mantienen ramas ocupadas. git worktree list de vez en cuando, y remove de los que sobran.

Consejo 1: adopta una convención de nombres. gestor-tareas, gestor-tareas-hotfix, gestor-tareas-v1. Con ../<proyecto>-<propósito> nunca te pierdes.

Consejo 2: ten un worktree permanente para urgencias. Siempre en main, siempre limpio. Es el que más veces te salvará el día.

Consejo 3: crea un alias. Con lo aprendido en la lección 06-04:

git config --global alias.wt "worktree list"
git config --global alias.nuevo '!f() { git worktree add "../$(basename "$PWD")-$1" -b "$1"; }; f'

git nuevo correccion-urgente crea ../gestor-tareas-correccion-urgente con esa rama.

Consejo 4: --detach para todo lo que sea solo lectura. Revisar, compilar, comparar o bisecar no necesita rama local, y así no te chocas con la regla de una rama por worktree.

Consejo 5: un git fetch basta para todos. Es un solo repositorio: no repitas el fetch en cada directorio.

Consejo 6: comprueba tu versión de Git. worktree existe desde la 2.5, pero move, remove y repair llegaron después (2.17 y 2.30). En versiones antiguas, algunas operaciones son manuales.

Ejercicios

Ejercicio 1: el flujo del hotfix

  1. Crea un repositorio con main y una rama funcionalidad/larga con cambios sin confirmar (modificados y sin seguimiento).
  2. Sin hacer stash, crea un worktree en ../proyecto-hotfix con una rama nueva correccion/urgente desde main.
  3. Confirma una corrección en el worktree nuevo.
  4. Vuelve al directorio original y comprueba que tus cambios sin confirmar siguen exactamente igual.
  5. Comprueba desde el directorio original que el commit del hotfix ya existe (git log correccion/urgente), sin haber hecho ningún push ni fetch.
  6. Elimina el worktree y la rama.

Ejercicio 2: la restricción de una rama por worktree

  1. Con el repositorio anterior, intenta crear un worktree con una rama que ya esté activa en otro. Anota el mensaje de error.
  2. Consigue el mismo código en un segundo directorio usando --detach.
  3. Comprueba con git worktree list que uno aparece con rama y el otro como (detached HEAD).
  4. Intenta borrar con git branch -d una rama activa en otro worktree y observa qué pasa.

Ejercicio 3: la anatomía por dentro

  1. En un worktree enlazado, comprueba que .git es un fichero y muestra su contenido.
  2. Localiza el directorio correspondiente en .git/worktrees/ del principal y lista su contenido.
  3. Compara el HEAD de ambos worktrees.
  4. Haz un git stash en uno y comprueba que git stash list en el otro está vacío.
  5. Borra un worktree con rm -rf, comprueba que sigue apareciendo en git worktree list (marcado como prunable) y límpialo con prune.

Soluciones

Solución 1:

mkdir /tmp/practica-worktree && cd /tmp/practica-worktree
git init -q -b main
echo "<html><body></body></html>" > index.html
echo "console.info('arranque');" > app.js
git add . && git commit -q -m "Añadir el esqueleto de la aplicacion"

git switch -qc funcionalidad/larga
echo "// trabajo a medias, no compila" >> app.js
echo "borrador sin terminar" > notas.txt

git status --short
 M app.js
?? notas.txt
git worktree add ../proyecto-hotfix -b correccion/urgente main
Preparing worktree (new branch 'correccion/urgente')
HEAD is now at 4f8a2e6 Añadir el esqueleto de la aplicacion
cd ../proyecto-hotfix
git status --short          # limpio
echo "console.info('correccion aplicada');" >> app.js
git commit -qam "Corregir el arranque cuando falta el contenedor"
git log --oneline
7d3a8f4 Corregir el arranque cuando falta el contenedor
4f8a2e6 Añadir el esqueleto de la aplicacion
cd /tmp/practica-worktree
git status --short
cat notas.txt
 M app.js
?? notas.txt
borrador sin terminar

Intactos. Ni stash ni pop.

git log --oneline correccion/urgente
7d3a8f4 Corregir el arranque cuando falta el contenedor
4f8a2e6 Añadir el esqueleto de la aplicacion

El commit hecho en el otro directorio es visible inmediatamente: es la misma base de objetos. Con dos clones habría hecho falta push + fetch.

git worktree remove ../proyecto-hotfix
git worktree list
git branch
/tmp/practica-worktree  9c2f7e4 [funcionalidad/larga]
  correccion/urgente
* funcionalidad/larga
  main

La rama sigue viva: remove no la borra.

git branch -D correccion/urgente

Solución 2:

git worktree add ../otro funcionalidad/larga
fatal: 'funcionalidad/larga' is already used by worktree at '/tmp/practica-worktree'
git worktree add --detach ../otro funcionalidad/larga
Preparing worktree (detached HEAD 9c2f7e4)
HEAD is now at 9c2f7e4 Añadir el esqueleto de la aplicacion
git worktree list
/tmp/practica-worktree  9c2f7e4 [funcionalidad/larga]
/tmp/otro               9c2f7e4 (detached HEAD)

Mismo commit, mismo contenido, sin conflicto: en ../otro no hay ninguna rama que pueda moverse.

cd /tmp/otro
git branch -d funcionalidad/larga
error: Cannot delete branch 'funcionalidad/larga' checked out at '/tmp/practica-worktree'

Git protege la rama activa en cualquier worktree, no solo en el actual.

Solución 3:

cd /tmp/otro
ls -l .git
cat .git
-rw-rw-r-- 1 carla carla 42 ago  1 12:10 .git
gitdir: /tmp/practica-worktree/.git/worktrees/otro
ls /tmp/practica-worktree/.git/worktrees/otro/
HEAD  commondir  gitdir  index  logs  ORIG_HEAD
cat /tmp/practica-worktree/.git/worktrees/otro/HEAD
cat /tmp/practica-worktree/.git/HEAD
cat /tmp/practica-worktree/.git/worktrees/otro/commondir
9c2f7e4a8b3d5f1e6c9a2b7d4f8c1e5a3b6d9f2c
ref: refs/heads/funcionalidad/larga
../..

El worktree enlazado tiene un hash directo (detached), el principal una referencia simbólica a su rama, y commondir apunta al .git compartido donde están los objetos y las refs.

# 4. El stash es por worktree
cd /tmp/practica-worktree
git stash -u
git stash list
stash@{0}: WIP on funcionalidad/larga: 9c2f7e4 Añadir el esqueleto de la aplicacion
cd /tmp/otro
git stash list
(vacío)
cd /tmp/practica-worktree && git stash pop     # recuperar el trabajo
# 5. Borrado a mano y prune
rm -rf /tmp/otro
git worktree list
/tmp/practica-worktree  9c2f7e4 [funcionalidad/larga]
/tmp/otro               9c2f7e4 (detached HEAD) prunable

El registro sigue ahí, marcado como prunable.

git worktree prune --dry-run -v
git worktree prune
git worktree list
Removing worktrees/otro: gitdir file points to non-existent location
/tmp/practica-worktree  9c2f7e4 [funcionalidad/larga]

Limpio. Con git worktree remove en lugar de rm -rf, este último paso no habría hecho falta.

Conclusión

git worktree resuelve un problema cotidiano con una idea simple y bien construida. Lo esencial:

  • Un worktree es un directorio de trabajo adicional con su propio HEAD e índice, que comparte objetos y referencias con el repositorio original.
  • Frente a clonar dos veces: no duplica la base de objetos, comparte ramas, etiquetas, remotos y hooks, un solo fetch sirve para todos, y un commit hecho en uno es visible al instante en el otro, sin pasar por el servidor.
  • git worktree add <ruta> [<rama>] lo crea en segundos; -b crea rama nueva y --detach abre un commit sin rama.
  • El juego completo es add, list, remove, prune, move, lock/unlock y repair. Usa remove en lugar de rm -rf y move en lugar de mv; lock protege los worktrees en unidades extraíbles.
  • Una rama no puede estar activa en dos worktrees a la vez. No es una limitación caprichosa: evita que un commit mueva la rama bajo los pies de otro directorio. La salida para lectura y compilación es --detach.
  • Por dentro, el .git del worktree enlazado es un fichero con una línea gitdir: que apunta a .git/worktrees/<nombre>, donde viven su HEAD, su index y su reflog; los objetos y las refs están en el commondir compartido. Es el mismo mecanismo que usan los submódulos.
  • Casos de uso que se ganan el sitio: hotfix sin perder el contexto, comparar dos versiones en ejecución, compilar una rama mientras trabajas en otra, revisar la rama de un compañero, y aislar un rebase largo o una bisección.
  • El stash es propio de cada worktree; los hooks son compartidos; los submódulos hay que inicializarlos en cada worktree nuevo.
  • Y la relación con stash: no lo sustituye, pero reduce mucho su uso. stash para minutos dentro de una rama; worktree para días entre varias.

Con esto se cierra el módulo 6 y el bloque técnico del curso. El equipo de gestor-tareas ya sabe construir, manipular, consultar y automatizar su historial. Lo que necesita ahora es ponerse de acuerdo: cómo se propone un cambio, cómo se revisa y qué flujo de ramas seguir. Eso es el módulo 7, y empieza en la lección 07-01: Forks y Pull Requests.

Dominando Git: De Principiante a Avanzado

Módulo 1: Introducción a Git

Módulo 2: Operaciones Básicas de Git

Módulo 3: Ramas y Fusión

Módulo 4: Trabajando con Repositorios Remotos

Módulo 5: Operaciones Avanzadas de Git

Módulo 6: Herramientas y Técnicas de Git

Módulo 7: Estrategias de Colaboración y Flujo de Trabajo

Módulo 8: Mejores Prácticas y Consejos de Git

Módulo 9: Solución de Problemas y Depuración

Módulo 10: Git en el Mundo Real

© Copyright 2026. Todos los derechos reservados