Haces un merge o un checkout con Git para Windows, vuelves a la terminal de WSL2 y git status lista cientos de archivos como modificados, aunque no editaste ninguno:
$ git status --short
M src/app.ts
M src/lib/formato.ts
M README.md
...
Si el repositorio usa git worktree y alguno se creó desde Windows, en WSL aparece además este error:
fatal: not a git repository: //wsl.localhost/Ubuntu/home/usuario/mi-app/.git/worktrees/rama-x
Y al intentar filtrar el ruido con git status --ignore-cr-at-eol, Git responde:
error: unknown option `ignore-cr-at-eol'
usage: git status [<options>] [--] [<pathspec>...]
Los tres síntomas tienen una causa común: dos instalaciones de Git (la de Windows y la de WSL) trabajan sobre el mismo árbol de archivos. Este artículo explica cómo confirmarlo, cómo limpiar el estado sin perder cambios reales y cómo evitar que se repita.
Por qué pasa
Git para Windows y Git dentro de WSL son instalaciones independientes, cada una con sus propios archivos de configuración. Es habitual que la de Windows tenga core.autocrlf=true (el libro Pro Git recomienda ese valor en Windows) y que la de WSL no tenga ninguna conversión configurada. Compruébalo ejecutando el mismo comando en ambos lados:
git config --show-origin --get-all core.autocrlf
En Windows puede devolver algo como file:C:/Program Files/Git/etc/gitconfig true; en WSL, ninguna línea.
Según la documentación de Git, core.autocrlf=true equivale a poner text=auto en todos los archivos y core.eol=crlf: al hacer checkout o merge, Git para Windows escribe los archivos con CRLF y los convierte a LF al guardarlos en el índice, que sigue teniendo LF. Git en WSL, sin conversión, compara los bytes del disco (CRLF) con los del índice (LF) y concluye que el archivo cambió. Solo se ven afectados los archivos que esa operación reescribió: el número (100, 400...) depende del merge o del checkout, no del tamaño del repositorio.
Para verlo, git ls-files --eol muestra el fin de línea del índice (i/) y del directorio de trabajo (w/). Los archivos fantasma aparecen como i/lf w/crlf.
La opción --ignore-cr-at-eol pertenece a git diff (ignora el retorno de carro al final de la línea al comparar), no a git status; por eso este último responde unknown option.
El error de los worktrees tiene un origen parecido. Un worktree enlazado tiene un archivo .git que apunta a un directorio privado dentro del .git del repositorio principal (.git/worktrees/<nombre>), y ese directorio guarda a su vez la ruta del worktree. Si el worktree lo creó Git para Windows, esas rutas se escribieron tal como Windows ve el repositorio (una ruta UNC como //wsl.localhost/Ubuntu/... o una ruta de unidad como C:/...), y Git en Linux no sabe resolverlas.
Solución
-
Confirma que son cambios fantasma y separa los reales. Desde WSL, en la raíz del repositorio:
git diff --ignore-cr-at-eol --stat git diff --ignore-cr-at-eol --name-only git ls-files --eol | grep 'w/crlf'Si el primer comando no imprime nada, todas las diferencias son de fin de línea. Si imprime archivos, esos sí tienen cambios reales: el segundo comando los lista y conviene conservarlos. El tercero muestra los archivos con CRLF en disco.
-
Restaura desde WSL los archivos afectados. Desde una terminal de WSL, o desde PowerShell anteponiendo
wsl.exe:wsl.exe -d Ubuntu -e bash -lc "cd ~/mi-app && git restore ."git restore .(equivalente agit checkout -- .en versiones antiguas) descarta todos los cambios sin confirmar. Si el paso anterior mostró cambios reales, exclúyelos con un pathspec:git restore -- . ':(exclude)src/cambio-real.ts'Los archivos se reescriben con LF, que es lo que tiene el índice.
-
Fija el fin de línea con
.gitattributes. En la raíz del repositorio:* text=auto eol=lf *.bat text eol=crlf *.cmd text eol=crlfSegún la documentación de Git,
core.autocrlfsolo se consulta cuando el atributotextno está definido; con este archivo, las dos instalaciones de Git se comportan igual. La combinacióntext=auto eol=lfrequiere Git 2.10 o posterior. Las dos últimas líneas conservan CRLF en los scripts de Windows, que suelen necesitarlo. Después renormaliza y revisa antes de confirmar:git add .gitattributes git add --renormalize . git status git commit -m "chore: normalize line endings to LF"Si el repositorio tenía archivos guardados con CRLF en el índice (
git ls-files --eol | grep 'i/crlf'), aparecerán engit statuscomo cambios preparados: es la conversión única a LF y es lo esperado. -
Repara los worktrees creados desde Windows. En WSL, desde el repositorio principal, indica la ruta Linux real del worktree:
git worktree list git worktree repair ~/mi-app-rama-xLa documentación de
git worktreedescriberepairjusto para este caso: restablece la conexión entre el repositorio principal y los worktrees cuando sus archivos administrativos quedaron desactualizados. Si prefieres hacerlo a mano, el archivo.gitdel worktree debe contenergitdir: /home/usuario/mi-app/.git/worktrees/rama-x, y el archivogitdirdentro de.git/worktrees/rama-x/debe contener la ruta Linux de ese.git. Como última opción, usagit worktree pruney vuelve a crear el worktree desde WSL congit worktree add; el contenido sin confirmar del directorio viejo no se mueve solo, así que cópialo antes. -
Haz las operaciones que escriben desde un único Git. Ejecuta
checkout,merge,pull,rebase,stashyworktree adddentro de WSL, aunque edites los archivos con herramientas de Windows:wsl.exe -d Ubuntu -e bash -lc "cd ~/mi-app && git pull --ff-only"Tampoco ejecutes sobre todo el repositorio formateadores ni conversiones de fin de línea del editor que escriban CRLF.
Cómo verificar que quedó resuelto
git status --short
git diff --ignore-cr-at-eol --stat
git ls-files --eol | grep -c 'w/crlf'
git config --show-origin --get-all core.autocrlf
Los dos primeros no deben imprimir nada. El tercero debe dar 0, salvo por los .bat y .cmd si los tienes, que conservan CRLF a propósito. Después haz un git checkout o un git pull desde WSL y repite el git status: debe seguir limpio. Por último, abre el repositorio con tus herramientas de Windows (editor, cliente gráfico) y confirma que tampoco muestran cambios.
Variantes y errores relacionados
El repositorio está en /mnt/c/...
Funciona igual, pero la documentación de Microsoft sobre sistemas de archivos en WSL recomienda no trabajar con los mismos archivos desde ambos sistemas operativos salvo que haya una razón, y guardarlos en el sistema de archivos donde corren las herramientas. Si trabajas en WSL, un repositorio en ~/mi-app deja a Git para Windows fuera de la ecuación.
No puedes confirmar un .gitattributes
Alinea las configuraciones. La documentación de solución de problemas de VS Code propone, en Windows, git config --global core.autocrlf input (convierte a LF al confirmar y no escribe CRLF) o false (sin conversión), y avisa de que puede hacer falta volver a clonar para que el cambio surta efecto.
El diff muestra old mode y new mode
Ya no son fines de línea sino cambios en el bit ejecutable. La documentación de core.fileMode menciona que un repositorio visitado desde otro entorno puede perder ese bit y que en ese caso conviene ponerlo en false.
Worktrees que se abren desde ambos lados
git worktree admite enlaces con rutas relativas (--relative-paths o worktree.useRelativePaths), pensados para repositorios que se mueven entre entornos. La propia documentación avisa de que los Git antiguos no los entienden. Pruébalo primero en un repositorio de ensayo.
Resumen
- Cientos de archivos "modificados" sin cambios reales suelen ser CRLF escrito por Git para Windows (
core.autocrlf=true) y leído por un Git en WSL sin conversión. --ignore-cr-at-eoles una opción degit diff: úsala con--stato--name-onlypara separar lo real de lo fantasma.- Restaura desde WSL con
git restore ., excluyendo con un pathspec los archivos que sí cambiaste. - Evita la recaída con
* text=auto eol=lfen.gitattributesy ejecutando las operaciones que escriben desde un solo Git. - Los worktrees creados desde Windows guardan rutas que Linux no resuelve: arréglalos con
git worktree repair <ruta-linux>.
Comentarios
Publicar un comentario