Tratando de arreglar los finales de línea con git filter-branch, pero no tuve suerte

270

Me ha picado el problema de final de línea de Windows / Linux con git. Parece, a través de GitHub, MSysGit y otras fuentes, que la mejor solución es tener sus repositorios locales configurados para usar terminaciones de línea estilo Linux, pero configurados core.autocrlfen true. Desafortunadamente, no hice esto lo suficientemente temprano, así que ahora cada vez que hago cambios, las terminaciones de línea se descifran.

Pensé que había encontrado una respuesta aquí, pero no puedo hacer que funcione para mí. Mi conocimiento de la línea de comandos de Linux es limitado en el mejor de los casos, por lo que ni siquiera estoy seguro de lo que hace la línea "xargs fromdos" en su script. Sigo recibiendo mensajes sobre la ausencia de dicho archivo o directorio, y cuando logro señalarlo a un directorio existente, me dice que no tengo permisos.

He intentado esto con MSysGit en Windows y a través del terminal Mac OS X.

Brian Donahue
fuente
No puedo votar este hilo ni siquiera lo suficiente. +1 ++ por proporcionar la mejor respuesta al respecto.
sjas
De acuerdo con Charles. Sin embargo, en mi caso (usando Mac OS X 10.8)> git config core.autocrlf falso funcionó, no> git config core.autocrlf input
user1045085

Respuestas:

187

La documentación de git para gitattributes ahora documenta otro enfoque para "arreglar" o normalizar todas las terminaciones de línea en su proyecto. Aquí está la esencia de esto:

$ echo "* text=auto" >.gitattributes
$ git add --renormalize .
$ git status        # Show files that will be normalized
$ git commit -m "Introduce end-of-line normalization"

Si algún archivo que no debe normalizarse aparece en estado git, desarme su atributo de texto antes de ejecutar git add -u.

manual.pdf -text

Por el contrario, los archivos de texto que git no detecta pueden tener la normalización habilitada manualmente.

weirdchars.txt text

Esto aprovecha un nuevo --renormalizeindicador agregado en git v2.16.0, lanzado en enero de 2018. Para versiones anteriores de git, hay algunos pasos más:

$ echo "* text=auto" >>.gitattributes
$ rm .git/index     # Remove the index to force git to
$ git reset         # re-scan the working directory
$ git status        # Show files that will be normalized
$ git add -u
$ git add .gitattributes
$ git commit -m "Introduce end-of-line normalization"
Russ Egan
fuente
1
¿Podría decirme cuál es el propósito de esto git reset, por favor?
crdx
1
fuerza a git a reconstruir el índice, durante el cual escanea cada archivo para adivinar si es binario. El rm elimina el índice anterior, restablecer crea el nuevo índice.
Russ Egan
16
Gracias, esto funcionó para mí. Un comando útil después de ejecutar git statuses ejecutar git diff --ignore-space-at-eolsolo para asegurarse de que los únicos cambios que está confirmando son los finales de línea.
zelanix
1
Nota: La única diferencia "real" entre esta y la solución "antigua" es en presencia de .gitattributes (con el contenido apropiado). Sin esto, git resetno detectará modificaciones y, por lo tanto, es inútil.
Rob
3
Las instrucciones de la gitattributes página se han actualizado para tomar ventaja de la --renormalizebandera añadido en v2.16.0 git que fue lanzado en enero de 2018. El --renormalizeindicador consolida el proceso de re-procesamiento de los finales de línea para cada archivo de orugas en un solo comando: git add --renormalize ..
Mike Hill
389

La forma más fácil de solucionar esto es hacer una confirmación que corrija todas las terminaciones de línea. Suponiendo que no tiene ningún archivo modificado, puede hacerlo de la siguiente manera.

# From the root of your repository remove everything from the index
git rm --cached -r .

# Change the autocrlf setting of the repository (you may want 
#  to use true on windows):
git config core.autocrlf input

# Re-add all the deleted files to the index
# (You should get lots of messages like:
#   warning: CRLF will be replaced by LF in <file>.)
git diff --cached --name-only -z | xargs -0 git add

# Commit
git commit -m "Fixed crlf issue"

# If you're doing this on a Unix/Mac OSX clone then optionally remove
# the working tree and re-check everything out with the correct line endings.
git ls-files -z | xargs -0 rm
git checkout .
CB Bailey
fuente
77
PD: recomendé su solución a los chicos de github.com y actualizaron su guía de ayuda para usar su solución (anteriormente solo recomendó un clon nuevo y un restablecimiento completo, que no parecía obtener todos los archivos) . Help.github. com / lidiar con lineendings
Brian Donahue
31
Gracias ... esta es una gran solución. Lo encontré en GitHub.
PHLAK el
44
También puede consultar config.safecrlf para asegurarse de que no está cambiando crlfs en archivos que no son de texto (como los binarios). Compruébelo en los documentos kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88
44
@ vrish88: Sin embargo, si se encuentra en esta situación, es probable que esté sufriendo terminaciones con líneas mixtas y core.safecrlf en realidad puede evitar que haga lo que necesita hacer. Probablemente sea más fácil no usar safecrlf. git no suele equivocarse en la detección de archivos binarios y, si lo hace, puede marcarlo manualmente como binario con un atributo .gitattribute y recuperar la versión correcta de la confirmación anterior.
CB Bailey
26
La solución más nueva recomendada en la respuesta de Russ Egan a continuación es más simple y no implica cosas aterradoras como eliminar todo su código fuente , por lo que realmente recomendaría que la gente lo use, ¡aunque esta vieja solución tiene 10 veces más votos!
Porculus
11

Mi procedimiento para tratar con los finales de línea es el siguiente (batalla probada en muchos repositorios):

Al crear un nuevo repositorio:

  • poner .gitattributesen el primer commit junto con otros archivos típicos como .gitignoreyREADME.md

Cuando se trata de un repositorio existente:

  • Crear / modificar en .gitattributesconsecuencia
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyes omitir ganchos de precompromiso)
    • Tengo que hacerlo con la frecuencia suficiente como para definirlo como un alias alias fixCRLF="..."
  • repite el comando anterior
    • sí, es vudú, pero generalmente tengo que ejecutar el comando dos veces, la primera vez que normaliza algunos archivos, la segunda vez incluso más archivos. En general, probablemente sea mejor repetir hasta que no se cree una nueva confirmación :)
  • ir y venir entre la rama antigua (justo antes de la normalización) y la nueva varias veces. Después de cambiar la rama, ¡a veces git encontrará incluso más archivos que deben renormalizarse!

En .gitattributesDeclaro todos los archivos de texto explícitamente como LF EOL ya que generalmente las herramientas de Windows son compatibles con LF, mientras que las herramientas que no son de Windows no son compatibles con CRLF (incluso muchas herramientas de línea de comandos de nodejs asumen LF y, por lo tanto, pueden cambiar la EOL en sus archivos).

Contenido de .gitattributes

Mi .gitattributesgeneralmente se ve así:

*.html eol=lf
*.js   eol=lf
*.json eol=lf
*.less eol=lf
*.md   eol=lf
*.svg  eol=lf
*.xml  eol=lf

Para averiguar qué extensiones distintas son rastreadas por git en el repositorio actual, mira aquí

Problemas después de la normalización

Una vez hecho esto, hay una advertencia más común.

Digamos masterque ya está actualizado y normalizado, y luego realiza el pago outdated-branch. Muy a menudo, justo después de revisar esa rama, git marca muchos archivos como modificados.

La solución es hacer un commit falso ( git add -A . && git commit -m 'fake commit') y luego git rebase master. Después del rebase, la comisión falsa debería desaparecer.

jakub.g
fuente
1
Pensé que me estaba volviendo loco, hasta que leí tu publicación, porque también tuve que ejecutar la secuencia de comandos especificada varias veces. ¡Vudú! ;)
Sean Fausett
Con la versión git 2.7.0.windows.1, utilicé lo siguiente: git rm --cached -r . && git reset --hard && git add . && git commit -m "Normalize EOL" -n
Sean Fausett
4
git status --short|grep "^ *M"|awk '{print $2}'|xargs fromdos

Explicación:

  • git status --short

    Esto muestra cada línea de la que git es y no es consciente. Los archivos que no están bajo el control de git están marcados al comienzo de la línea con un '?'. Los archivos que se modifican están marcados con una M.

  • grep "^ *M"

    Esto filtra solo aquellos archivos que han sido modificados.

  • awk '{print $2}'

    Esto muestra solo el nombre del archivo sin ningún marcador.

  • xargs fromdos

    Esto toma los nombres de archivo del comando anterior y los ejecuta a través de la utilidad 'fromdos' para convertir los finales de línea.

Lloyd Moore
fuente
Esto es asombroso Gracias. Para cualquiera que busque una solución que use Homebrew en dos2unixlugar de fromdos.
Almir Sarajčić
4

Así es como solucioné todas las terminaciones de línea en todo el historial usando git filter-branch. El ^Mcarácter necesita ser ingresado usando CTRL-V+ CTRL-M. Solía dos2unixconvertir los archivos ya que esto omite automáticamente los archivos binarios.

$ git filter-branch --tree-filter 'grep -IUrl "^M" | xargs -I {} dos2unix "{}"'
pfrenssen
fuente
3

El "| xargs fromdos" lee de la entrada estándar (los archivos findencuentran) y lo usa como argumentos para el comando fromdos, que convierte los finales de línea. (¿Fromdos es estándar en esos entornos? Estoy acostumbrado a dos2unix). Tenga en cuenta que puede evitar el uso de xargs (especialmente útil si tiene suficientes archivos para que la lista de argumentos sea demasiado larga para xargs):

find <path, tests...> -exec fromdos '{}' \;

o

find <path, tests...> | while read file; do fromdos $file; done

No estoy totalmente seguro de sus mensajes de error. Probé con éxito este método. ¿Qué programa está produciendo cada uno? ¿Para qué archivos / directorios no tiene permisos? Sin embargo, aquí hay una puñalada para adivinar cuál podría ser:

Una manera fácil de obtener un error de "archivo no encontrado" para el script es usando una ruta relativa: use una absoluta. Del mismo modo, podría obtener un error de permisos si no ha ejecutado su script (chmod + x).

¡Agregue comentarios y trataré de ayudarlo a resolverlo!

Cascabel
fuente
Vi otro ejemplo con dos2unix y pensé que de alguna manera estaba copiando archivos en una carpeta llamada así, pero ahora lo entiendo. Wow, parece obvio ahora. ¡Gracias por tu ayuda!
Brian Donahue
1

está bien ... bajo cygwin no tenemos fromdos fácilmente disponibles, y ese subpaso awk explota en tu cara si tienes espacios en las rutas a los archivos modificados (que teníamos), así que tuve que hacer eso de manera algo diferente:

git status --short | grep "^ *M" | sed 's/^ *M//' | xargs -n 1 dos2unix

felicitaciones a @lloyd por la mayor parte de esta solución

Anton K
fuente
-2

Siga estos pasos si ninguna de las otras respuestas funciona para usted:

  1. Si estás en Windows, hazlo git config --global core.autocrlf true; si estás en Unix, hazgit config core.autocrlf input
  2. correr git rm --cached -r .
  3. Eliminar el archivo .gitattributes
  4. correr git add -A
  5. correr git reset --hard

Entonces su local debe estar limpio ahora.

zs2020
fuente
44
De Verdad? ¿Eliminar el .gitattributesarchivo es la solución al problema de terminación de línea?
Aleksandr M
Sí, por favor aborde el comentario de @AleksandrM
Mr_and_Mrs_D