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
- Qué es un worktree
worktree add: el primer directorio adicional- Comparación con clonar dos veces
- El juego completo de comandos
- La regla clave: una rama, un worktree
- Cómo se ve por dentro
- Casos de uso reales
- Interacción con
stash, submódulos y hooks - Cierre del módulo 6
- 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.
worktree add: el primer directorio adicional
worktree add: el primer directorio adicionalCarla resuelve su problema con un comando:
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/urgentea partir demainy está activa allí. - El directorio original no se ha tocado en absoluto: sigue en
funcionalidad/borrado-multiple, con todo su trabajo a medias intacto.
/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/urgenteY cuando termina, vuelve:
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 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).
- 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 | Sí: 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/--referencepara 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 worktreees la solución moderna y segura al mismo problema, y la que debe usarse.
- El juego completo de comandos
list
/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:
Borra el directorio y su registro. Si hay cambios sin confirmar, se niega —lo cual es una protección, no una molestia:
Y ojo: remove no borra la rama. Eso es aparte:
prune
Si borraste el directorio a mano con rm -rf (que funciona, pero deja el registro), prune limpia los restos:
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
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-usbUn 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
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 |
- 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.
fatal: 'funcionalidad/borrado-multiple' is already used by worktree at '/home/carla/proyectos/gestor-tareas'
Y lo mismo al crear:
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-multipleLa 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á:
- 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:
En un worktree enlazado, .git es un fichero de texto:
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:
| 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:
HEADeindexestán por worktree → cada uno puede estar en una rama distinta y tener sus propios cambios preparados.objects/yrefs/están en elcommondir→ 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 habitualY 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:
Es un caso avanzado y poco frecuente, pero conviene saber que existe si te encuentras con un config.worktree dentro de .git/worktrees/<nombre>/.
- 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:
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:
# Terminal 1
cd ~/proyectos/gestor-tareas && python3 -m http.server 8000
# Terminal 2
cd ~/proyectos/gestor-tareas-v1 && python3 -m http.server 8001Dos 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 compilaCaso 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 revisionSin --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.shMientras 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:
Se edita la documentación en su directorio y el código en el suyo, con historiales separados y sin cambiar de rama nunca.
- Interacción con
stash, submódulos y hooks
stash, submódulos y hooksCon 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:
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 hacecheckout. 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 worktreeCon 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 maintenancey el rendimiento en repositorios grandes es el tema de la lección 08-06: Consejos de Rendimiento; los clones parciales,sparse-checkouty las técnicas de escalado, el de la 10-04.
- 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-msgypre-push, versionadas concore.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 conbisect runy sus códigos de salida.git blame(06-03): la historia de cada línea, con-w,-Ce--ignore-revpara atravesar el ruido, ygit log -Lpara ver la evolución completa.git logavanzado 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 enmainvarias 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
- Crea un repositorio con
mainy una ramafuncionalidad/largacon cambios sin confirmar (modificados y sin seguimiento). - Sin hacer
stash, crea un worktree en../proyecto-hotfixcon una rama nuevacorreccion/urgentedesdemain. - Confirma una corrección en el worktree nuevo.
- Vuelve al directorio original y comprueba que tus cambios sin confirmar siguen exactamente igual.
- Comprueba desde el directorio original que el commit del hotfix ya existe (
git log correccion/urgente), sin haber hecho ningúnpushnifetch. - Elimina el worktree y la rama.
Ejercicio 2: la restricción de una rama por worktree
- Con el repositorio anterior, intenta crear un worktree con una rama que ya esté activa en otro. Anota el mensaje de error.
- Consigue el mismo código en un segundo directorio usando
--detach. - Comprueba con
git worktree listque uno aparece con rama y el otro como(detached HEAD). - Intenta borrar con
git branch -duna rama activa en otro worktree y observa qué pasa.
Ejercicio 3: la anatomía por dentro
- En un worktree enlazado, comprueba que
.gites un fichero y muestra su contenido. - Localiza el directorio correspondiente en
.git/worktrees/del principal y lista su contenido. - Compara el
HEADde ambos worktrees. - Haz un
git stashen uno y comprueba quegit stash listen el otro está vacío. - Borra un worktree con
rm -rf, comprueba que sigue apareciendo engit worktree list(marcado comoprunable) y límpialo conprune.
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 --shortPreparing 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 --oneline7d3a8f4 Corregir el arranque cuando falta el contenedor 4f8a2e6 Añadir el esqueleto de la aplicacion
Intactos. Ni stash ni pop.
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.
La rama sigue viva: remove no la borra.
Solución 2:
Preparing worktree (detached HEAD 9c2f7e4) HEAD is now at 9c2f7e4 Añadir el esqueleto de la aplicacion
Mismo commit, mismo contenido, sin conflicto: en ../otro no hay ninguna rama que pueda moverse.
Git protege la rama activa en cualquier worktree, no solo en el actual.
Solución 3:
cat /tmp/practica-worktree/.git/worktrees/otro/HEAD
cat /tmp/practica-worktree/.git/HEAD
cat /tmp/practica-worktree/.git/worktrees/otro/commondirEl 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.
El registro sigue ahí, marcado como prunable.
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
HEADe í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
fetchsirve 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;-bcrea rama nueva y--detachabre un commit sin rama.- El juego completo es
add,list,remove,prune,move,lock/unlockyrepair. Usaremoveen lugar derm -rfymoveen lugar demv;lockprotege 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
.gitdel worktree enlazado es un fichero con una líneagitdir:que apunta a.git/worktrees/<nombre>, donde viven suHEAD, suindexy su reflog; los objetos y las refs están en elcommondircompartido. 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.stashpara minutos dentro de una rama;worktreepara 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
- ¿Qué es Git?
- Instalando Git
- Terminología Básica de Git
- El Modelo de Datos de Git
- Configurando Git
- Configuración Inicial
Módulo 2: Operaciones Básicas de Git
- Creando un Repositorio
- Clonando un Repositorio
- Flujo de Trabajo Básico de Git
- Preparando y Confirmando Cambios
- Inspeccionando Cambios con git diff
- Visualizando el Historial de Confirmaciones
Módulo 3: Ramas y Fusión
- Entendiendo las Ramas
- Creando y Cambiando Ramas
- Fusionando Ramas
- Estrategias de Fusión
- Resolviendo Conflictos de Fusión
- Gestión de Ramas
Módulo 4: Trabajando con Repositorios Remotos
- Entendiendo los Repositorios Remotos
- Agregando un Repositorio Remoto
- Autenticación con Repositorios Remotos
- Obteniendo y Extrayendo Cambios
- Enviando Cambios
- Rastreando Ramas
Módulo 5: Operaciones Avanzadas de Git
- Rebase
- Rebase Interactivo
- Cherry-Picking de Confirmaciones
- Guardando Cambios Temporales
- Etiquetando Confirmaciones
- Revirtiendo Confirmaciones
Módulo 6: Herramientas y Técnicas de Git
- Usando Git Hooks
- Git Bisect
- Git Blame
- Git Log y Alias
- Submódulos de Git
- Múltiples Copias de Trabajo con git worktree
Módulo 7: Estrategias de Colaboración y Flujo de Trabajo
- Forks y Pull Requests
- Revisiones de Código con Git
- Flujo de Trabajo Git Flow
- GitHub Flow
- Trunk Based Development
- Integración Continua con Git
Módulo 8: Mejores Prácticas y Consejos de Git
- Escribiendo Buenos Mensajes de Confirmación
- Manteniendo un Historial Limpio
- Ignorando Archivos con .gitignore
- Atributos de Fichero con .gitattributes
- Mejores Prácticas de Seguridad
- Consejos de Rendimiento
Módulo 9: Solución de Problemas y Depuración
- Problemas Comunes de Git
- Deshaciendo Cambios
- Resolviendo Divergencias con el Remoto
- Recuperando Confirmaciones Perdidas
- Tratando con Repositorios Corruptos
- Técnicas Avanzadas de Depuración
