Fai git rimuovere automaticamente gli spazi bianchi finali prima di eseguire il commit

Sto usando git con il mio team e vorrei rimuovere i cambiamenti di spazi bianchi da differenze, registri, fusioni, ecc. Suppongo che il modo più semplice per farlo sarebbe che Git rimuova automaticamente gli spazi bianchi finali (e altri errori di spazio bianco) ) da tutti i commit man mano che vengono applicati.

Ho provato ad aggiungere il seguente file ~/.gitconfig ma non esegue nulla quando ~/.gitconfig commit. Forse è progettato per qualcosa di diverso. Qual è la soluzione?

 [core] whitespace = trailing-space,space-before-tab [apply] whitespace = fix 

Sto usando il ruby nel caso qualcuno abbia qualche idea specifica di ruby. La formattazione automatica del codice prima del commit sarebbe il passo successivo, ma questo è un problema difficile e non causa realmente un grosso problema.

Queste impostazioni ( core.whitespace e apply.whitespace ) non sono lì per rimuovere gli spazi bianchi finali, ma per:

  • core.whitespace : core.whitespace errori
  • apply.whitespace : e li apply.whitespace , ma solo durante la patch, non “sempre automaticamente”

Credo che il git hook pre-commit del git hook pre-commit possa fare un lavoro migliore (includendo la rimozione degli spazi bianchi finali)


Si noti che in qualsiasi momento è ansible scegliere di non eseguire il hook pre-commit :

  • temporaneamente: git commit --no-verify .
  • permanentemente: cd .git/hooks/ ; chmod -x pre-commit cd .git/hooks/ ; chmod -x pre-commit

Attenzione: per impostazione predefinita, uno script di pre-commit (come questo ), non ha una funzione “rimuovi finale”, ma una funzione di “avviso” come:

 if (/\s$/) { bad_line("trailing whitespace", $_); } 

Potresti comunque creare un hook di pre-commit migliore , specialmente se consideri che:

Il commit in Git con solo alcune modifiche aggiunte all’area di staging produce ancora una revisione “atomica” che potrebbe non essere mai esistita come copia di lavoro e potrebbe non funzionare .


Ad esempio, oldman propone in un’altra risposta un hook pre-commit che rileva e rimuove gli spazi bianchi.
Dato che quell’amo ottiene il nome del file di ogni file, raccomanderei di fare attenzione a certi tipi di file: non vuoi rimuovere lo spazio .md finale nei file .md ( .md )!

Puoi ingannare Git nel fissare gli spazi bianchi per te, ingannando Git nel trattare le tue modifiche come una patch. In contrasto con le soluzioni “pre-commit hook”, queste soluzioni aggiungono comandi di correzione dello spazio bianco a Git.

Sì, questi sono hack.


Soluzioni robuste

I seguenti alias Git sono presi dal mio ~/.gitconfig .

Con “robusto” intendo che questi alias corrono senza errori, facendo la cosa giusta, indipendentemente dal fatto che l’albero o l’indice siano sporchi. Tuttavia, non funzionano se è già in corso un re git rebase -i interattivo git rebase -i ; vedi il mio ~/.gitconfig per ulteriori controlli se ti interessa questo caso d’angolo, dove dovrebbe funzionare il trucco git add -e descritto alla fine.

Se vuoi eseguirli direttamente nella shell, senza creare un alias Git, copia e incolla tutto tra le virgolette (assumendo che la tua shell sia Bash).

Correggi l’indice ma non l’albero

Le seguenti correzioni Alias ​​Git corregge tutti gli errori di spaziatura nell’indice, se ce ne sono, ma non tocca l’albero:

 # Logic: # # The 'git stash save' fails if the tree is clean (instead of # creating an empty stash :P). So, we only 'stash' and 'pop' if # the tree is dirty. # # The 'git rebase --whitespace=fix HEAD~' throws away the commit # if it's empty, and adding '--keep-empty' prevents the whitespace # from being fixed. So, we first check that the index is dirty. # # Also: # - '(! git diff-index --quiet --cached HEAD)' is true (zero) if # the index is dirty # - '(! git diff-files --quiet .)' is true if the tree is dirty # # The 'rebase --whitespace=fix' trick is from here: # https://stackoverflow.com/a/19156679/470844 fixws = !"\ if (! git diff-files --quiet .) && \ (! git diff-index --quiet --cached HEAD) ; then \ git commit -m FIXWS_SAVE_INDEX && \ git stash save FIXWS_SAVE_TREE && \ git rebase --whitespace=fix HEAD~ && \ git stash pop && \ git reset --soft HEAD~ ; \ elif (! git diff-index --quiet --cached HEAD) ; then \ git commit -m FIXWS_SAVE_INDEX && \ git rebase --whitespace=fix HEAD~ && \ git reset --soft HEAD~ ; \ fi" 

L’idea è di eseguire git fixws prima di git commit se si hanno errori di spazio nell’indice.

Correggi l’indice e l’albero

Il seguente alias Git fixws-global-tree-and-index corregge tutti gli errori di spazi bianchi nell’indice e l’albero, se presenti:

 # The different cases are: # - dirty tree and dirty index # - dirty tree and clean index # - clean tree and dirty index # # We have to consider separate cases because the 'git rebase # --whitespace=fix' is not compatible with empty commits (adding # '--keep-empty' makes Git not fix the whitespace :P). fixws-global-tree-and-index = !"\ if (! git diff-files --quiet .) && \ (! git diff-index --quiet --cached HEAD) ; then \ git commit -m FIXWS_SAVE_INDEX && \ git add -u :/ && \ git commit -m FIXWS_SAVE_TREE && \ git rebase --whitespace=fix HEAD~2 && \ git reset HEAD~ && \ git reset --soft HEAD~ ; \ elif (! git diff-files --quiet .) ; then \ git add -u :/ && \ git commit -m FIXWS_SAVE_TREE && \ git rebase --whitespace=fix HEAD~ && \ git reset HEAD~ ; \ elif (! git diff-index --quiet --cached HEAD) ; then \ git commit -m FIXWS_SAVE_INDEX && \ git rebase --whitespace=fix HEAD~ && \ git reset --soft HEAD~ ; \ fi" 

Per correggere anche gli spazi vuoti nei file non decodificati, fallo

 git add --intent-to-add  && git fixws-global-tree-and-index 

Soluzioni semplici ma non robuste

Queste versioni sono più facili da copiare e incollare, ma non fanno la cosa giusta se le loro condizioni laterali non sono soddisfatte.

Correggi l’albero secondario radicato nella directory corrente (ma ripristina l’indice se non è vuoto)

Usando git add -e per “modificare” le patch con l’editor di identity framework::

 (export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .) && git checkout . && git reset 

Correggere e conservare l’indice (ma fallisce se l’albero è sporco o l’indice è vuoto)

 git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset --soft HEAD~ 

Correggi l’albero e l’indice (ma ripristina l’indice se non è vuoto)

 git add -u :/ && git commit -m TEMP && git rebase --whitespace=fix HEAD~ && git reset HEAD~ 

Spiegazione export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue . trucco

Prima che git rebase --whitespace=fix conoscenza di git rebase --whitespace=fix trucco da questa risposta stavo usando il trucco più complicato di git add ovunque.

Se lo avessimo fatto manualmente:

  1. Imposta apply.whitespace per fix (devi farlo una sola volta):

     git config apply.whitespace fix 

    Questo dice a Git di correggere gli spazi bianchi nelle patch .

  2. Convinci Git a trattare le tue modifiche come una patch :

     git add -up . 

    Premi a + Invio per selezionare tutte le modifiche per ogni file. Riceverai un avviso su Git che corregge gli errori di spazio bianco.
    ( git -c color.ui=auto diff a questo punto rivela che le tue modifiche non indicizzate sono esattamente gli errori di spazio bianco).

  3. Rimuovi gli errori di spazio bianco dalla tua copia di lavoro:

     git checkout . 
  4. Ripristina le modifiche (se non sei pronto a eseguirle):

     git reset 

GIT_EDITOR=: significa usare : come editor, e come comando : è l’id quadro.

Ho trovato un hook pre-commit git che rimuove gli spazi bianchi finali .

 #!/bin/sh if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # Find files with trailing whitespace for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -r 's/:[0-9]+:.*//' | uniq` ; do # Fix them! sed -i 's/[[:space:]]*$//' "$FILE" git add "$FILE" done exit 

Su Mac OS (o, probabilmente, su qualsiasi BSD), i parametri del comando sed devono essere leggermente diversi. Prova questo:

 #!/bin/sh if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # Find files with trailing whitespace for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -E 's/:[0-9]+:.*//' | uniq` ; do # Fix them! sed -i '' -E 's/[[:space:]]*$//' "$FILE" git add "$FILE" done 

Salva questo file come .git/hooks/pre-commit – o cerca quello che c’è già, e incolla il chunk di fondo da qualche parte al suo interno. E ricorda di chmod a+x anche questo.

O per l’uso globale (tramite Git commit hooks – impostazioni globali ) puoi metterlo in $GIT_PREFIX/git-core/templates/hooks (dove GIT_PREFIX è / usr o / usr / local o / usr / share o / opt / local / condividi) ed esegui git init all’interno dei tuoi git init esistenti.

Secondo git help init :

L’esecuzione di git init in un repository esistente è sicura. Non sovrascriverà le cose che sono già lì. Il motivo principale per eseguire nuovamente git init è quello di acquisire nuovi modelli aggiunti.

Preferirei lasciare questo compito al tuo editor preferito.

Basta impostare un comando per rimuovere gli spazi finali durante il salvataggio.

Ho scritto questo hook pre-commit, che rimuove solo lo spazio bianco finale dalle righe che hai modificato / aggiunto, poiché i suggerimenti precedenti tendono a creare commit illeggibili se i file di destinazione hanno troppi spazi bianchi finali.

 #!/bin/sh if git rev-parse --verify HEAD >/dev/null 2>&1 ; then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi IFS=' ' files=$(git diff-index --check --cached $against -- | sed '/^[+-]/d' | perl -pe 's/:[0-9]+:.*//' | uniq) for file in $files ; do diff=$(git diff --cached $file) if test "$(git config diff.noprefix)" = "true"; then prefix=0 else prefix=1 fi echo "$diff" | patch -R -p$prefix diff=$(echo "$diff" | perl -pe 's/[ \t]+$// if m{^\+}') out=$(echo "$diff" | patch -p$prefix -f -s -t -o -) if [ $? -eq 0 ]; then echo "$diff" | patch -p$prefix -f -t -s fi git add $file done 

Si prega di provare i miei ganci pre-commit , può rilevare automaticamente spazi bianchi finali e rimuoverlo . Grazie!

può funzionare con GitBash(windows), Mac OS X and Linux !


Istantanea:

 $ git commit -am "test" auto remove trailing whitespace in foobar/main.m! auto remove trailing whitespace in foobar/AppDelegate.m! [master 80c11fe] test 1 file changed, 2 insertions(+), 2 deletions(-) 

Ecco una versione di Ubuntu + mac os x compatibile:

 #!/bin/sh # # A git hook script to find and fix trailing whitespace # in your commits. Bypass it with the --no-verify option # to git-commit # if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi # Find files with trailing whitespace for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | (sed -r 's/:[0-9]+:.*//' > /dev/null 2>&1 || sed -E 's/:[0-9]+:.*//') | uniq` ; do # Fix them! (sed -i 's/[[:space:]]*$//' "$FILE" > /dev/null 2>&1 || sed -i '' -E 's/[[:space:]]*$//' "$FILE") git add "$FILE" done # Now we can commit exit 

Divertiti

Usando gli attributi git e l’installazione dei filtri con git config

OK, questo è un nuovo approccio per risolvere questo problema … Il mio approccio è di non usare alcun hook, ma piuttosto usare filtri e attributi git. Ciò che questo ti permette di fare, è l’installazione, su ogni macchina su cui ti sviluppi, una serie di filtri che elimineranno lo spazio bianco aggiuntivo e le righe in bianco extra alla fine dei file prima di eseguirli. Quindi imposta un file .gitattributes che indichi i tipi di file a cui il filtro deve essere applicato. I filtri hanno due fasi, clean che viene applicata quando si aggiungono i file all’indice e macchia che viene applicata quando li si aggiunge alla directory di lavoro.

Dì al tuo git di cercare un file di attributi globali

Per prima cosa, comunica alla tua configurazione globale di utilizzare un file di attributi globali:

 git config --global core.attributesfile ~/.gitattributes_global 

Crea filtri globali

Ora crea il filtro:

 git config --global filter.fix-eol-eof.clean fixup-eol-eof %f git config --global filter.fix-eol-eof.smudge cat git config --global filter.fix-eol-eof.required true 

Aggiungi la magia scripting sed

Infine, metti lo script fixup-eol-eof da qualche parte nel tuo percorso e rendilo eseguibile. Lo script usa sed per eseguire alcune modifiche al volo (rimuovi spazi e spazi vuoti alla fine delle righe e righe vuote estranee alla fine del file)

fixup-eol-eof dovrebbe apparire così:

 #!/bin/bash sed -e 's/[ ]*$//' -e :a -e '/^\n*$/{$d;N;ba' -e '}' $1 

il mio succo di questo

Indica a quali tipi di file applicare il filtro appena creato

Infine, crea o apri ~ / .gitattributes_global nel tuo editor preferito e aggiungi righe come:

 pattern attr1 [attr2 [attr3 […]]] 

Quindi, se vogliamo risolvere il problema dello spazio bianco, per tutti i nostri file sorgente c aggiungeremo una riga simile a questa:

 *.c filter=fix-eol-eof 

Discussione sul filtro

Il filtro ha due fasi, la fase pulita che viene applicata quando le cose vengono aggiunte all’indice o registrate, e la fase di sbavatura quando git mette le cose nella tua directory di lavoro. Qui, il nostro sbavature sta semplicemente eseguendo il contenuto tramite il comando cat che dovrebbe lasciarle invariate, con l’eccezione di aggiungere eventualmente un carattere di fine riga finale se non ce n’è uno alla fine del file. Il comando clean è il filtraggio degli spazi bianchi che ho messo insieme dalle note su http://sed.sourceforge.net/sed1line.txt . Sembra che debba essere inserito in uno script di shell, non riesco a capire come iniettare il comando sed, incluso il risanamento delle righe extra estranee alla fine del file direttamente nel file git-config. (È ansible sbarazzarsi degli spazi finali finali, tuttavia, senza la necessità di uno script sed separato, basta impostare filter.fix-eol-eof su qualcosa come sed 's/[ \t]*$//' %f dove il \t è una scheda effettiva, premendo la scheda.)

Require = true fa sì che venga sollevato un errore se qualcosa va storto, per tenerti lontano dai guai.

Per favore perdonami se la mia lingua riguardo a git è imprecisa. Penso di avere una buona conoscenza dei concetti, ma sto ancora imparando la terminologia.

Stavo pensando a questo oggi. Questo è tutto ciò che ho finito per fare un progetto java:

 egrep -rl ' $' --include *.java * | xargs sed -i 's/\s\+$//g' 

il ciclo for per i file utilizza la variabile di shell $ IFS. nello script dato, i nomi di file con un carattere al loro interno che si trova anche nella variabile $ IFS saranno visti come due file diversi nel ciclo for. Questo script lo risolve: il modificatore in modalità multilinea come dato sed-manual non sembra funzionare di default sulla mia finestra di Ubuntu, quindi ho cercato una diversa implementazione e l’ho trovata con un’etichetta iteratrice, essenzialmente inizierà solo la sostituzione sul ultima riga del file se ho capito bene.

 #!/bin/sh # # A git hook script to find and fix trailing whitespace # in your commits. Bypass it with the --no-verify option # to git-commit # if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi SAVEIFS="$IFS" # only use new-line character as seperator, introduces EOL-bug? IFS=' ' # Find files with trailing whitespace for FILE in $( git diff-index --check --cached $against -- \ | sed '/^[+-]/d' \ | ( sed -r 's/:[0-9]+:.*//' || sed -E 's/:[0-9]+:.*//' ) \ | uniq \ ) do # replace whitespace-characters with nothing # if first execution of sed-command fails, try second one( MacOSx-version) ( sed -i ':a;N;$!ba;s/\n\+$//' "$FILE" > /dev/null 2>&1 \ || \ sed -i '' -E ':a;N;$!ba;s/\n\+$//' "$FILE" \ ) \ && \ # (re-)add files that have been altered to git commit-tree # when change was a [:space:]-character @EOL|EOF git-history becomes weird... git add "$FILE" done # restore $IFS IFS="$SAVEIFS" # exit script with the exit-code of git's check for whitespace-characters exec git diff-index --check --cached $against -- 

[1] modello sed-subsition: come posso sostituire una newline (\ n) usando sed? .

Per gli utenti di testo sublime .

Impostare correttamente come segue in Impostazione-Configurazione utente .

"trim_trailing_white_space_on_save": true

Questo non rimuove lo spazio automaticamente prima di un commit, ma è piuttosto facile da effettuare. Ho inserito il seguente script perl in un file chiamato git-wsf (git whitespace fix) in una directory in $ PATH, così posso:

git wsf | sh

e rimuove tutti gli spazi bianchi solo dalle righe di file che git segnala come diff.

 #! /bin/sh git diff --check | perl -x $0 exit #! /usr/bin/perl use strict; my %stuff; while (<>) { if (/trailing whitespace./) { my ($file,$line) = split(/:/); push @{$stuff{$file}},$line; } } while (my ($file, $line) = each %stuff) { printf "ex %s <  

Leggermente in ritardo ma poiché questo potrebbe aiutare qualcuno là fuori, ecco qui.

Apri il file in VIM. Per sostituire le tabs con spazi bianchi, digitare quanto segue nella riga di comando di vim

 :%s#\t# #gc 

Per sbarazzarsi degli altri spazi bianchi finali

 :%s#\s##gc 

Questo praticamente lo ha fatto per me. È noioso se hai molti file da modificare. Ma ho trovato più facile degli hook pre-commit e lavorare con più editor.

Per eliminare gli spazi bianchi finali alla fine della riga in un file, utilizzare ed :

 test -s file && printf '%s\n' H ',g/[[:space:]]*$/s///' 'wq' | ed -s file 

Probabilmente questo non risolverà direttamente il tuo problema, ma potresti volerli impostare tramite git-config nello spazio del tuo progetto reale, che modifica ./.git/config in contrapposizione a ~ / .gitconfig. Bello mantenere le impostazioni coerenti tra tutti i membri del progetto.

 git config core.whitespace "trailing-space,space-before-tab" git config apply.whitespace "trailing-space,space-before-tab"