git worktree

Porque me puede el ansia y quiero trabajar a la vez en varias cosas sin que se pisen entre ellas. Pero claro, copiar el directorio o clonar varias veces el mismo repositorio no parece la mejor opción.

Por eso, aparece al rescate git-worktree.

git worktree no es algo nuevo, lleva en git (de una forma u otra) desde julio del 2015, pero es ahora con la IA cuando ha ganado popularidad y es una herramienta must-have.

Primero ¿qué es y para qué sirve un worktree?

Los worktrees permiten trabajar en varias ramas de un repositorio a la vez, cada una en su propio directorio.

Porque como decía en la entradilla del post, sin esto te quedaba clonar el repositorio varias veces o duplicar la copia en distintos directorios. Ninguna de las dos es una buena idea.

Para verlo funcionar con un ejemplo, preparemos un directorio en el que podamos trabajar:

mkdir learning-worktrees
cd .\learning-worktrees\
mkdir local
cd .\local\
echo Sergio > sergio.txt
echo panicoenlaxbox > panicoenlaxbox.txt
echo "Hello, World!" > hello-world.txt
git init
git add .
git commit -m "Initial commit"

Ten en cuenta que cada worktree crea un directorio nuevo, que puede estar dentro o fuera del worktree principal. Y además, el repositorio principal (en nuestro caso local/) ya es un worktree. Es decir, todo es un worktree, podrá ser main o linked, pero todo es un worktree.

Por eso funciona directamente git worktree list dentro de local/.

Parece que hay varias convenciones para ubicar los linked worktrees:

  • ..\<repository_root>.worktrees\<name> VSCode, fuera
  • <repository_root>\.claude\worktrees\<name> Claude Code, dentro

Para crear un worktree:

git worktree add <path> -b <new-branch> <commit-ish> por ejemplo git worktree add ..\local.worktrees\feature-1 -b feature/1 main

Y ahora (y si has configurado tu prompt) deberías verlo de un vistazo con el icono de worktree.

Eso sí, no intentes activar una rama en más de un worktree, no se puede y Git se quejará de ello como no puede ser de otra forma.

A partir de aquí, puedes trabajar en ambos directorios con normalidad, nada se va a pisar. Sin embargo, si estás haciendo pruebas (y antes de meternos con el merge) la forma de deshacerte de los worktrees es la siguiente:

git worktree remove --force <path> # con --force da igual si hay ficheros sin trackear o cambios sin confirmar
git branch {-d|-D} <branch> # además de borrar el worktree hay que borrar la rama, con -D nos da igual si hay commits sin mergear
# git worktree prune # si eliminaste el directorio del worktree a mano, limpia el vínculo que todavía queda en el main worktree

En cualquier caso, como lo que queremos es trabajar en paralelo sin pisarnos, vamos a ver cómo proceder cuando los cambios estén confirmados.

Estando en learning-worktrees\local:

cd ..\local.worktrees\feature-1\ # ahora estoy en feature/1
echo "Sergio León" > .\sergio.txt # modificación
echo Carmen > carmen.txt # nuevo fichero
git add .
git commit -m "Sergio & Carmen"
cd ..\..\local\ # ahora estoy en main
git merge feature/1
git worktree remove ..\local.worktrees\feature-1\
git branch -d feature/1

Dejando a un lado la línea de comandos, cualquier herramienta moderna entenderá los worktrees.

Vamos a verlo con un worktree con cambios no confirmados:

De nuevo estando en learning-worktrees\local:

git reset --hard HEAD~1 # deshacer el merge anterior y volver al commit inicial
git worktree add ..\local.worktrees\feature-1 -b feature/1 main
cd ..\local.worktrees\feature-1\
echo "Sergio León" > .\sergio.txt
echo Carmen > carmen.txt

Si quieres, también puedes mover un linked worktree con git worktree move <worktree> <new-path>

Ahora abre VSCode y teniendo activado el setting git.detectWorktrees verás algo así:

VSCode

Además de verlo ahí en la pestaña de Source Control, VSCode tiene comandos específicos para trabajar con worktrees:

VSCode commands

Especial atención merecen estos 2:

  • Git: Compare with Workspace
  • Git: Migrate Worktree Changes...

Compare with Workspace permite seleccionar un fichero de un workspace y compararlo con el workspace actual.

Migrate Worktree Changes… te permite seleccionar desde qué worktree traer los cambios para fusionar en el worktree actual (además de deshacer los cambios en el worktree origen)

Migrate Worktree Changes

Fíjate que esto no es un merge al uso, es algo que te da VSCode como un atajo. Básicamente es lo mismo que hacer lo siguiente:

git stash -u
cd ..\..\local\
git stash pop

De hecho, recientemente Visual Studio también ha incluido soporte para worktrees. Está claro que, hoy por hoy, ninguna herramienta puede hacer caso omiso de la tendencia dominante.

Volviendo al origen del post, decíamos que los worktrees están viviendo una segunda juventud por el empuje de la IA. Y es que son una excelente opción para ejecutar múltiples sesiones en paralelo sobre la misma base de código.

Además de movernos a una ruta donde haya un worktree y arrancar desde allí CC (es lo más obvio y funciona), también podemos arrancar CC en un nuevo worktree con claude --worktree <name>

CC nos permite añadir el fichero .worktreeinclude para incluir en el worktree ficheros ignorados (por ejemplo un .env).

También podemos pedirle directamente a CC que haga un worktree. Algo tan simple como “Crea un worktree para trabajar en el bug de la carga de imágenes”… y ya.

Puesto que CC crea los linked worktrees dentro del main worktree, es decir, los crea en la ruta <repository_root>\.claude\worktrees\<name>, es aconsejable añadir .claude/worktrees/ a .gitignore.

También podemos configurar que un subagente se ejecute en un worktree usando isolation: worktree en su frontmatter.

Al hilo de usar worktrees con CC, aparece el concepto de que un worktree puede estar “bloqueado”.

git worktree add ..\local.worktrees\feature-1 -b feature/1 main
git worktree lock ..\local.worktrees\feature-1\ --reason "está en un disco externo"
git worktree list # aparecerá locked y con el motivo
git worktree unlock ..\local.worktrees\feature-1\
# ahora ya podemos borrarlo. No obstante con doble --force podemos saltarnos el bloqueo
git worktree remove -f -f ..\local.worktrees\feature-1\

Un saludo!

git