Cercando di correggere le terminazioni di linea con git filter-branch, ma senza fortuna


270

Sono stato morso dal problema di fine linea di Windows / Linux con git. Sembra, tramite GitHub, MSysGit e altre fonti, che la soluzione migliore sia avere i repository locali impostati per utilizzare i finali di linea in stile Linux, ma impostati core.autocrlfsu true. Sfortunatamente, non l'ho fatto abbastanza presto, quindi ora ogni volta che tiro le modifiche le estremità della linea sono interrotte.

Pensavo di aver trovato una risposta qui, ma non riesco a farlo funzionare per me. La mia conoscenza della riga di comando di Linux è al massimo limitata, quindi non sono nemmeno sicuro di cosa faccia la riga "xargs fromdos" nel suo script. Continuo a ricevere messaggi su tali file o directory esistenti e quando riesco a indirizzarli a una directory esistente, mi dice che non ho autorizzazioni.

Ho provato questo con MSysGit su Windows e tramite il terminale Mac OS X.


Non posso votare questo thread nemmeno abbastanza. +1 ++ per fornire la migliore risposta in materia.
sabato

D'accordo con Charles. Tuttavia, nel mio caso (usando Mac OS X 10.8)> git config core.autocrlf false ha funzionato, non> input git config core.autocrlf
user1045085

Risposte:


187

La documentazione git per gitattributes ora documenta un altro approccio per "riparare" o normalizzare tutte le terminazioni di linea nel progetto. Ecco l'essenza:

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

Se tutti i file che non devono essere normalizzati vengono visualizzati nello stato git, disattiva il loro attributo di testo prima di eseguire git add -u.

manual.pdf -text

Al contrario, i file di testo che Git non rileva possono avere la normalizzazione abilitata manualmente.

weirdchars.txt text

Ciò sfrutta un nuovo --renormalizeflag aggiunto in git v2.16.0, rilasciato a gennaio 2018. Per le versioni precedenti di git, ci sono alcuni passaggi in più:

$ 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"

1
Potresti dirmi qual è lo scopo di git reset, per favore?
crdx,

1
forza git a ricostruire l'indice, durante il quale scansiona ogni file per indovinare se è binario. L'rm elimina il vecchio indice, reset crea il nuovo indice.
Russ Egan,

16
Grazie, ha funzionato per me. Un comando utile dopo l'esecuzione git statusè quello di eseguire git diff --ignore-space-at-eolsolo per essere sicuri che le uniche modifiche che stai commettendo siano le terminazioni di linea.
zelanix,

1
Nota: l'unica "vera" differenza tra questa e la "vecchia" soluzione è in presenza di .gitattributes (con il contenuto appropriato). Senza questo, git resetnon rileverà alcuna modifica ed è quindi inutile.
Rob,

3
Le istruzioni sulla gitattributes pagina sono stati aggiornati per sfruttare la --renormalizebandierina aggiunto v2.16.0 git che è stato rilasciato nel mese di gennaio 2018. La --renormalizebandiera consolida il processo di ri-elaborazione fine riga per ogni file cingolato in un unico comando: git add --renormalize ..
Mike Hill,

389

Il modo più semplice per risolvere questo problema è effettuare un commit che ripari tutti i finali di riga. Supponendo che non si disponga di file modificati, è possibile procedere come segue.

# 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 .

7
PS Ho consigliato la tua correzione ai ragazzi di github.com e hanno aggiornato la loro guida di aiuto per usare la tua soluzione (in precedenza aveva appena raccomandato un nuovo clone e un hard reset, che non sembrava avere tutti i file.) Help.github. com / dealing-with-lineendings
Brian Donahue,

31
Grazie ... questa è un'ottima soluzione. L'ho trovato su GitHub.
PHLAK,

4
Potresti anche voler dare un'occhiata a config.safecrlf per assicurarti di non cambiare crlfs in file non di testo (come binario). Dai un'occhiata al documento kernel.org/pub/software/scm/git/docs/git-config.html .
vrish88,

4
@ vrish88: se ti trovi in ​​questa situazione, è probabile che tu soffra di terminazioni allineate miste e core.safecrlf potrebbe effettivamente impedirti di fare ciò che devi fare. Probabilmente è più facile non usare safecrlf. git spesso non sbaglia il rilevamento dei file binari e, in tal caso, puoi contrassegnarlo manualmente come binario con un .gitattribute e recuperare la versione corretta dal commit precedente.
CB Bailey,

26
La nuova soluzione raccomandata nella risposta di Russ Egan di seguito è più semplice e non comporta cose spaventose come l' eliminazione di tutto il codice sorgente , quindi consiglierei davvero alle persone di usarlo, anche se questa vecchia soluzione ha un numero di voti 10 volte maggiore!
Porculus,

11

La mia procedura per gestire i finali di linea è la seguente (test di battaglia su molti repository):

Quando si crea un nuovo repository:

  • inserisci .gitattributesil primo commit insieme ad altri file tipici come .gitignoreeREADME.md

Quando si ha a che fare con un repository esistente:

  • Crea / modifica di .gitattributesconseguenza
  • git commit -a -m "Modified gitattributes"
  • git rm --cached -r . && git reset --hard && git commit -a -m 'Normalize CRLF' -n"
    • -n( --no-verifyè quello di saltare gli hook pre-commit)
    • Devo farlo abbastanza spesso da definirlo un alias alias fixCRLF="..."
  • ripetere il comando precedente
    • sì, è voodoo, ma generalmente devo eseguire il comando due volte, la prima volta che normalizza alcuni file, la seconda volta ancora più file. Generalmente è probabilmente meglio ripetere fino a quando non viene creato alcun nuovo commit :)
  • andare avanti e indietro tra il vecchio (poco prima della normalizzazione) e il nuovo ramo alcune volte. Dopo aver cambiato il ramo, a volte git troverà ancora più file che devono essere rinormalizzati!

In .gitattributesDichiaro tutti i file di testo in modo esplicito come avere LF EOL poiché generalmente utensili di Windows è compatibile con LF, mentre non-Windows utensili non è compatibile con CRLF (anche molti nodejs comando strumenti da riga assumono LF e, quindi, possono cambiare l'EOL nei file).

Contenuti di .gitattributes

Di .gitattributessolito il mio aspetto è:

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

Per capire quali estensioni distinte sono tracciate da git nel repository corrente, guarda qui

Problemi dopo la normalizzazione

Una volta fatto questo, c'è comunque un avvertimento più comune.

Dì che il tuo masterè già aggiornato e normalizzato, quindi fai il checkout outdated-branch. Abbastanza spesso subito dopo aver verificato quel ramo, git contrassegna molti file come modificati.

La soluzione è fare un commit falso ( git add -A . && git commit -m 'fake commit') e poi git rebase master. Dopo il rebase, il falso commit dovrebbe andare via.


1
Pensavo di impazzire, fino a quando non ho letto il tuo post, perché ho dovuto eseguire più volte la sequenza di comandi specificata. Voodoo! ;)
Sean Fausett,

Con la versione git 2.7.0.windows.1, ho usato il seguente: 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

Spiegazione:

  • git status --short

    Questo mostra ogni linea che Git è e non è a conoscenza. I file che non sono controllati da git sono contrassegnati all'inizio della riga con un '?'. I file modificati sono contrassegnati con una M.

  • grep "^ *M"

    Questo filtra solo quei file che sono stati modificati.

  • awk '{print $2}'

    Questo mostra solo il nome file senza marcatori.

  • xargs fromdos

    Questo prende i nomi di file dal comando precedente e li esegue attraverso l'utilità 'fromdos' per convertire le terminazioni di riga.


Questo e spettacolare. Grazie. Per chiunque cerchi una soluzione usando Homebrew, usa dos2unixinvece di fromdos.
Almir Sarajčić,


3

"| Xargs fromdos" legge dallo standard input (il file findtrova) e lo usa come argomento per il comando fromdos, che converte le terminazioni di riga. (Fromdos è standard in quegli ambienti? Sono abituato a dos2unix). Nota che puoi evitare di usare xargs (particolarmente utile se hai abbastanza file per cui l'elenco degli argomenti è troppo lungo per xargs):

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

o

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

Non sono del tutto sicuro dei tuoi messaggi di errore. Ho testato con successo questo metodo. Quale programma sta producendo ciascuno? Per quali file / directory non hai i permessi? Tuttavia, ecco una pugnalata a indovinare quale potrebbe essere:

Un modo semplice per ottenere un errore "file non trovato" per lo script è utilizzare un percorso relativo: utilizzare uno assoluto. Allo stesso modo potresti ottenere un errore di autorizzazione se non hai reso eseguibile il tuo script (chmod + x).

Aggiungi commenti e proverò ad aiutarti a risolverlo!


Ho visto un altro esempio con dos2unix e ho pensato che in qualche modo questo stesse copiando i file in una cartella chiamata così, ma ora ho capito. Wow, sembra ovvio ora. Grazie per l'aiuto!
Brian Donahue,

1

okay ... sotto cygwin non abbiamo facilmente disponibili fromdos, e quel sottosop awk ti esplode in faccia se hai spazi nei percorsi di file modificati (che avevamo), quindi ho dovuto farlo in modo leggermente diverso:

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

complimenti a @lloyd per la maggior parte di questa soluzione


-2

Segui questi passaggi se nessuna delle altre risposte funziona per te:

  1. Se sei su Windows, fallo git config --global core.autocrlf true; se sei su Unix, fallogit config core.autocrlf input
  2. Correre git rm --cached -r .
  3. Elimina il file .gitattributes
  4. Correre git add -A
  5. Correre git reset --hard

Quindi il tuo locale dovrebbe essere pulito ora.


4
Veramente? Eliminazione.gitattributes file è la soluzione al problema dei finali di linea?
Aleksandr M

Sì, rispondi al commento di @AleksandrM
Mr_and_Mrs_D
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.