CRLF vs LF
Al trabajar en desarrollo de software, uno de los aspectos más sutiles pero cruciales que debes tener en cuenta es la diferencia entre los finales de línea en archivos de texto entre Windows (CRLF \r\n
) y Linux/MacOS (LF \n
).
Este pequeño detalle puede generar grandes problemas si no se maneja correctamente, especialmente cuando se trabaja en entornos mixtos, conflictos en el control de versiones incompatibilidades en scripts, problemas de compilación o ejecución. Cree este apunte para tener a mano una forma de tratar este tema, incluyendo algún que otro truco.
Introducción
Cuando trabajas en un proyecto multiplataforma (Windows y Mac/Linux) es recomendable estandarizar, definir una política coherente para el manejo de CRLF
frente a LF
, estas son algunas de las soluciones:
- Que Git lo gestione, que él se encargue de revisar los finales de línea antes de los commits.
- Puede hacerse con la opción
core.autocrlf
a nivel globalgit config --global core.autocrlf true
- Recomendado en Windows.git config --global core.autocrlf input
- Recomendado en Linux/MacOS.
- Pero también puede hacerse de forma más granular.
- Usando el archivo
.gitattributes
en la raiz del repositorio.
- Usando el archivo
- Puede hacerse con la opción
- Otra opción es hacerlo a mano, gestionarlo tú mismo:
- Con un editor que soporte elegir (por ejemplo VSCode).
- Haciendo conversiones manuales con
dos2unix
yunix2dos
- Sentido común y mantiendo el control, controlando ediciones, apoyándote en herramientas y scripts para manejar y respetar el formato de finales de línea.
Estas son algunas de las preguntas que me hice:
- Impongo el uso de LF (estilo Unix) en el repositorio?. La decisión es que sí, merece la pena.
- De las varias soluciones, ¿cuál escojo?. Decido usar .gitattributes.
- Si mi repo es antiguo y tiene múltiples archivos existentes con finales de línea inconsistentes ¿Cómo lo manejo?. La decisión es usar una herramienta para convertir la mayoría a
LF
(con excepciones donde dejoCRLF
). - ¿Debería el sistema de control de versiones normalizar automáticamente los finales de línea durante los commits y checkouts?. La decisión es que sí, a través del archivo
.gitattributes
. - ¿Cómo manejo los finales de línea en bibliotecas de terceros o git submodules terceros?. La decisión es dejarlas tal cual, no tocarlas.
- ¿Necesitaré herramientas o scripts para verificar y aplicar la política de finales de línea (por ejemplo, un hook pre-commit)?. No es necesario.
Renormalización
Un ejemplo de cómo haría (en dos fases) para normalizar un repositorio. Durante el proceso, un par de herramientas bastante útiles.
- Comando
git ls-files --eol
que nos da información valiosísima - Programa
git-repo-eol-analyzer
para verificar la normalización antes y después.
Fase 1, agrego el archivo .gitattributes a mi clone
curl -LJs -o .gitattributes https://gist.githubusercontent.com/LuisPalacios/9821bd9c9ff8183cf772fbe11cec55fc/raw/.gitattributes
git commit -a -m 'agregando .gitattributes para unificar finales de línea'
git push
# Pido al equipo que haga pull y pause el trabajo hasta que la fase 2 esté completada.
Fase 2, normalización, encontrar y convertir todos los archivos que me interesen a LF (Unix). El comando find a continuación es un ejemplo, adáptalo a tus necesidades.
# En MacOS
find -E . -iregex '.*\.(c|cpp|h|hpp|md|svg|csx|yml|yaml|gitattributes|gitignore|json)' -exec dos2unix {} \;
# En Linux o WSL2
find . -regextype posix-extended -regex '.*\.(c|cpp|h|hpp|md|svg|csx|yml|yaml|gitattributes|gitignore|json)$' -exec dos2unix {} \;
git commit -a -m 'Ficheros normalizados con dos2unix'
git push
Ahora ya solo queda pedir al equipo que vuelva a hacer un pull. Aunque es mucho mejor pedirles que clonen desde cero el repositorio. Así verán la copia completamente sincronizada.