`git` muestra los files modificados después de la clonación, sin ninguna otra acción

git clone git@github.com:erocarrera/pydot ( 35a8d858b ) en Debian con la git config core.autocrlf input muestra:

 modified: test/graphs/b545.dot modified: test/graphs/b993.dot modified: test/graphs/cairo.dot 

Estos files tienen terminaciones de línea CRLF, por ejemplo:

 $ file test/graphs/cairo.dot test/graphs/cairo.dot: UTF-8 Unicode text, with CRLF line terminators 

El file .gitattributes contiene :

 *.py eol=lf *.dot eol=lf *.txt eol=lf *.md eol=lf *.yml eol=lf *.png binary *.ps binary 

Cambiar core.autocrlf no tiene ningún efecto en el estado de estos files. La eliminación de .gitattributes tampoco tiene ningún efecto. Cambiar estos files con dos2unix no cambia su estado (como se esperaba), y de vuelta con unix2dos no se ve diferencia con una copy anterior. Los permissions de file no cambian con ls -lsa . Además, los files tienen terminaciones de línea uniformes por lo que puedo decir con vi -b (por lo tanto, no debería ser el caso de que unix2dos o dos2unix conviertan de terminaciones de línea mixtas a uniformes, lo que podría haber explicado este extraño comportamiento). Estoy usando git versión 2.11.0.

¿Qué piensa git que ha cambiado?

Algo relevante:

  1. El estado de Git muestra los files como cambiados aunque los contenidos sean los mismos
  2. Archivos que se muestran modificados directamente después de clon git
  3. Clonando un git repo, y ya tiene un directory de trabajo sucio … Whaaaaa?

No encontré una respuesta que explique este comportamiento durante mi búsqueda en varias discusiones. Este problema surgió de pydot # 163 .

Con más detalle:

 git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: test/graphs/b545.dot modified: test/graphs/b993.dot modified: test/graphs/cairo.dot no changes added to commit (use "git add" and/or "git commit -a") git diff test/graphs/b993.dot warning: CRLF will be replaced by LF in test/graphs/b993.dot. The file will have its original line endings in your working directory. diff --git a/test/graphs/b993.dot b/test/graphs/b993.dot index e87e112..8aa0872 100644 --- a/test/graphs/b993.dot +++ b/test/graphs/b993.dot @@ -1,10 +1,10 @@ -diGraph G{ -graph [charset="utf8"] -1[label="Umlaut"]; -2[label="ü"]; -3[label="ä"]; -4[label="ö"]; -1->2; -1->3; -1->4; -} +diGraph G{ +graph [charset="utf8"] +1[label="Umlaut"]; +2[label="ü"]; +3[label="ä"]; +4[label="ö"]; +1->2; +1->3; +1->4; +} 

ACTUALIZAR:

Por curiosidad, cometí uno de estos files, arrojé git log -1 -p > diff y vi -b diff muestra que git normalizó

  1 commit 2021d6adc1bc8978fa08d729b3f4d565f9b89651 2 Author: 3 Date: 4 5 DRAFT: experiment to see what changed 6 7 diff --git a/test/graphs/b545.dot b/test/graphs/b545.dot 8 index ebd3e8f..2c33f91 100644 9 --- a/test/graphs/b545.dot 10 +++ b/test/graphs/b545.dot 11 @@ -1,9 +1,9 @@ 12 -digraph g {^M 13 -^M 14 -"N11" ^M 15 - [^M 16 - shape = record^M 17 - label = "<p0>WFSt|1571 as Ref: 1338 D"^M 18 -]^M 19 -N11ne -> N11:p0^M 20 -}^M 21 +digraph g { 22 + 23 +"N11" 24 + [ 25 + shape = record 26 + label = "<p0>WFSt|1571 as Ref: 1338 D" 27 +] 28 +N11ne -> N11:p0 29 +} 

Otras observaciones raras: git checkout cualquiera de estos files después de la clonación no tiene ningún efecto. Después de la confirmación anterior, el file b545.dot continúa teniendo terminaciones de línea CLRF en el directory de trabajo. La aplicación de dos2unix seguido de unix2dos no hizo que git pensara que había cambiado (mientras que antes de la confirmación sí, probablemente porque el file comprometido tenía terminaciones de línea CLRF).

Esto sucede precisamente porque esos files están comprometidos con terminaciones CRLF, sin embargo, el file .gitattributes dice que los comprometerá con terminaciones solo LF.

Git puede y va a hacer la conversión de solo CRLF contra LF en dos lugares:

  • Durante la extracción del índice al tree de trabajo. Siempre se supone que un file almacenado en una confirmación o en el índice está en un estado "limpio", pero al extraer ese file del índice, al tree de trabajo, Git debe aplicar las conversiones dirigidas por .gitattributes en forma de "cambie LF-only to CRLF", por ejemplo, y también en la forma de lo que Git llama filters de borrones .

  • Durante la copy de un file del tree de trabajo volver al índice. Un file almacenado en el tree de trabajo está en estado "manchado", por lo que en este punto, Git debe aplicar conversiones de "limpieza": por ejemplo, cambie CR-LF a LF solamente y aplique filters limpios .

Tenga en count que hay dos puntos en los que pueden ocurrir estas conversiones. Esto no significa que ocurrirán en ambos puntos, solo que estos son los dos lugares posibles. Como las notas de documentation de .gitattributes , las conversiones reales son:

  • eol=lf : none en index -> work-tree; CR-LF a LF-only en work-tree -> index
  • eol=crlf : solo LF a CR-LF en índice -> tree de trabajo; ninguno en work-tree -> index

Ahora, un file que está realmente en el repository, almacenado en una confirmación, es puramente de solo lectura. Nunca puede cambiar dentro de ese compromiso. Más precisamente, la confirmación identifica (mediante ID de hash) un tree que identifica (mediante ID de hash) un blob que tiene cualquier contenido que tenga. Estas ID de hash son en sí mismas sums de verificación criptográficas de los contenidos de los objects, por lo que son, por supuesto, de solo lectura: si tratamos de cambiar los contenidos, lo que obtenemos es un nuevo object diferente con una nueva ID de hash diferente.

Debido a que el process de git checkout realidad funciona al hacer frente a los identificadores hash sin procesar del tree o treees de compromiso al índice, las versiones de los files almacenados en el índice son necesariamente idénticas a las almacenadas en el compromiso.

Por lo tanto, si de alguna manera, independientemente de cómo , los files comprometidos están en una forma que no .gitattributes con lo que .gitattributes ordera a Git, los files se volverán "sucios" en el tree de trabajo, independientemente del hecho de que no lo haya hecho cualquier cosa para ellos! Si tuviera que git add los tres files en cuestión, los copyría del tree de trabajo al índice y, por lo tanto, eliminaría los retornos de carro de sus terminaciones de línea. Por lo tanto, están, en términos de git status , modificados pero aún no configurados para commit.

Excluir los retornos de carro en las versiones de tree de trabajo los deja en el mismo estado: se modifican con respecto a lo que hay en el índice, porque git add dejará sin cambios sus terminaciones de línea solo LF, produciendo files nuevos y diferentes que están en el índice.

Una pregunta más interesante es: ¿cómo entraron en la (s) comisión (es) en el estado equivocado? Esto no es algo que podamos responder: solo aquellos que hicieron esos compromisos pueden producir esa respuesta. Solo podemos especular. Una forma de lograr esto es agregar y confirmar los files sin un .gitattributes en vigencia, luego establecer los .gitattributes en vigencia sin git add nuevamente los files. De esta forma, las terminaciones CR-LF entran en el índice de alguien y, por lo tanto, entran en las confirmaciones de ese usuario, aunque el file .gitattributes ahora dice (pero no dijo antes) que cualquier nueva git add debería quitar los retornos de carro.

Cambiar core.autocrlf no tiene ningún efecto en el estado de estos files

Debería, pero solo después de la clonación nuevamente:

 git config --global core.autocrlf false git clone git@github.com:erocarrera/pydot pydot2 cd pydot2 git status 

Eso desactivaría core.autocrlf globalmente, pero esto es solo para probar aquí.

Gracias a @torek por la explicación (que está de acuerdo con mi conjetura ).

En resumen, la configuration de git asimétrica lleva a commit(checkout(Index)) no es la asignación de identidad. Con CRLF en el índice, esta configuration particular comprobó CRLF, pero después de las transformaciones de input en vigor ( eol=lf ), git confirmaría LF en lugar de CRLF.

La causa de esta confusión fue comparar:

  • file que veo en el directory de trabajo, con el
  • file comprometido

Esto no muestra si el file ha cambiado. Lo que uno debería comparar es lo que se comprometerá git después de aplicar las transformaciones de input con lo que ya está comprometido. Claramente, si esos dos elementos difieren, entonces el file ha cambiado.

Siguiendo este razonamiento, uno podría declarar el repository "inestable", en el sentido de que se considera modificado en ausencia de interacción con el mundo. Esto permite evitar este estado cambiando los files comprometidos a LF, o cambiando los .gitattributes (prefiero .gitattributes LF).

En esta situación, git confirmaría LF para LF y CRLF en el directory de trabajo, por lo que dos2unix y unix2dos tendrían ningún efecto en el resultado de la confirmación, por lo tanto, tampoco en el estado del file.