Logo CRLF

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 global
      • git 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.
  • Otra opción es hacerlo a mano, gestionarlo tú mismo:
    • Con un editor que soporte elegir (por ejemplo VSCode).
    • Haciendo conversiones manuales con dos2unix y unix2dos
    • 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 , 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 dejo CRLF).
  • ¿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 , 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.