web-dev-qa-db-fra.com

Assurez-vous que git supprime automatiquement les espaces blancs de fin avant de commettre

J'utilise git avec mon équipe et souhaite supprimer les modifications d'espaces de mes diffs, journaux, fusions, etc. Je suppose que la meilleure façon de le faire serait que git supprime automatiquement les espaces finaux ) de tous les commits tels qu’ils sont appliqués.

J'ai essayé d'ajouter ce qui suit dans le fichier ~/.gitconfig, Mais il ne fait rien quand je commets. Peut-être que c'est conçu pour quelque chose de différent. Quelle est la solution?

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

J'utilise Ruby au cas où quelqu'un aurait des idées spécifiques Ruby. Le formatage automatique du code avant la validation serait l'étape suivante, mais c'est un problème difficile et non causant vraiment un gros problème.

210
mloughran

Ces paramètres (core.whitespace et apply.whitespace) ne sont pas là pour supprimer les espaces finaux, mais pour:

  • core.whitespace: les détecte et génère des erreurs
  • apply.whitespace: et les dépouille, mais seulement pendant le patch, pas "toujours automatiquement"

Je crois que le git hook pre-commit ferait un meilleur travail pour cela (inclut la suppression des espaces blancs de fin)


Notez qu’à tout moment, vous pouvez choisir de ne pas exécuter le pre-commit crochet:

  • temporairement: git commit --no-verify .
  • en permanence: cd .git/hooks/ ; chmod -x pre-commit

Attention: par défaut, un pre-commit script (comme celui-ci ), a pas une "fonctionnalité de suppression", mais un "avertissement" tel que:

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

Vous pourriez cependant construire un meilleur pre-commit hook, surtout si vous considérez que:

S'engager dans Git avec seulement quelques modifications ajoutées à la zone de transfert entraîne toujours une révision "atomique" qui peut ne jamais avoir existé en tant que copie de travail et risque de ne pas fonctionner .


Par exemple, oldman propose dans une autre réponse a pre-commit hook qui détecte et supprime les espaces.
Étant donné que ce hook reçoit le nom de fichier de chaque fichier, il est conseillé de faire attention à certains types de fichiers: vous ne souhaitez pas supprimer les espaces finaux dans .md fichiers (démarques)!

106
VonC

Vous pouvez convaincre Git de réparer les espaces pour vous, en incitant Git à traiter vos modifications comme un correctif. Contrairement aux solutions de "pré-commit hook", ces solutions ajoutent des commandes de correction des espaces à Git.

Oui, ce sont des hacks.


Des solutions robustes

Les alias Git suivants sont tirés de my ~/.gitconfig .

Par "robuste", je veux dire que ces alias fonctionnent sans erreur, en faisant le bon choix, que l'arbre ou l'index soit sale. Cependant, ils ne fonctionnent pas si un git rebase -i est déjà en cours; voir mon ~/.gitconfig pour des vérifications supplémentaires si vous vous souciez de ce coin de cas, où le git add -e Le truc décrit à la fin devrait marcher.

Si vous souhaitez les exécuter directement dans le shell, sans créer d'alias Git, copiez et collez tout ce qui se trouve entre les guillemets (en supposant que votre shell ressemble à Bash).

Corrige l'index mais pas l'arbre

Le fixws alias Git suivant corrige toutes les erreurs d'espacement, s'il y en a, mais ne touche pas l'arborescence:

# 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'idée est de courir git fixws avant git commit _ si vous avez des erreurs d'espacement dans l'index.

Fixer l'index et l'arbre

Le suivant fixws-global-tree-and-index L’alias Git corrige toutes les erreurs d’espacement des blancs dans l’index et dans l’arbre, le cas échéant:

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

Pour corriger également les espaces dans les fichiers non versionnés, procédez comme suit:

git add --intent-to-add <unversioned files> && git fixws-global-tree-and-index

Des solutions simples mais pas robustes

Ces versions sont plus faciles à copier et à coller, mais elles ne font pas la bonne chose si leurs conditions secondaires ne sont pas remplies.

Corrige le sous-arbre enraciné dans le répertoire courant (mais réinitialise l'index s'il n'est pas vide)

En utilisant git add -e pour "éditer" les patchs avec l’éditeur d’identité ::

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

Corrige et préserve l'index (mais échoue si l'arborescence est sale ou si l'index est vide)

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

Corrige l'arborescence et l'index (mais réinitialise l'index s'il n'est pas vide)

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

Explication de la export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue . tour

Avant que je connaisse le git rebase --whitespace=fix astuce de cette réponse J'utilisais le plus compliqué git add astuce partout.

Si nous l'avons fait manuellement:

  1. Ensemble apply.whitespace à fix (il suffit de le faire une fois):

    git config apply.whitespace fix
    

    Ceci dit à Git de corriger les espaces dans des correctifs .

  2. Convainquez Git de traiter vos modifications comme un correctif :

    git add -up .
    

    Frappé a+entersélectionner toutes les modifications pour chaque fichier. Vous recevrez un avertissement concernant la correction par Git de vos erreurs d'espaces.
    (git -c color.ui=auto diff à ce stade indique que vos modifications non indexées correspondent exactement aux erreurs d’espace).

  3. Supprimez les erreurs d'espacement de votre copie de travail:

    git checkout .
    
  4. Ramenez vos modifications (si vous n'êtes pas prêt à les valider):

    git reset
    

Le GIT_EDITOR=: signifie utiliser : en tant qu'éditeur et en tant que commande : est l'identité.

43
ntc2

J'ai trouvé un git crochet de pré-validation qui supprime les espaces finaux .

#!/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
29
cmcginty

Sous Mac OS (ou probablement sous BSD), les paramètres de la commande sed doivent être légèrement différents. Essaye ça:

#!/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

Enregistrez ce fichier sous .git/hooks/pre-commit - ou cherchez celui qui est déjà là et collez le morceau du bas quelque part à l'intérieur. Et rappelez-vous de chmod a+x ça aussi.

Ou pour une utilisation globale (via hooks de commit Git - paramètres globaux ), vous pouvez le mettre dans $GIT_PREFIX/git-core/templates/hooks (où GIT_PREFIX est/usr ou/usr/local ou/usr/share ou/opt/local/share) et exécutez git init dans votre dépôt existant.

Selon git help init:

L'exécution de git init dans un référentiel existant est sûre. Cela n'écrasera pas les choses qui sont déjà là. La principale raison de la réexécution de git init est de récupérer les nouveaux modèles ajoutés.

19
AlexChaffee

Je préfère laisser cette tâche à votre éditeur préféré.

Il suffit de définir une commande pour supprimer les espaces de fin lors de la sauvegarde.

10
Giacomo

S'il vous plaît essayez mes pré-validations hooks , il peut détecter automatiquement les espaces de fin et retirez-le. Je vous remercie!

cela peut fonctionner sous GitBash(windows), Mac OS X and Linux!


Instantané:

$ 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(-)
9
oldman

J'ai écrit ce crochet de pré-validation, qui supprime uniquement les espaces blancs de fin des lignes que vous avez modifiées/ajoutées, car les suggestions précédentes ont tendance à créer des validations illisibles si les fichiers cibles comportent trop d'espaces blancs de fin.

#!/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
9
urandom

Utilisation d'attributs git et de la configuration des filtres avec git config

OK, c’est une nouvelle façon de résoudre ce problème… Mon approche est de ne pas utiliser de hooks, mais plutôt d’utiliser des filtres et des attributs git. Ce que cela vous permet de faire, c’est de configurer, sur chaque machine sur laquelle vous développez, un ensemble de filtres qui vont supprimer l’espace blanc de fin supplémentaire et les lignes vides supplémentaires à la fin des fichiers avant de les valider. Ensuite, configurez un fichier .gitattributes qui indique les types de fichiers auxquels le filtre doit être appliqué. Les filtres ont deux phases, clean qui est appliquée lors de l’ajout de fichiers à l’index et smudge lors de leur ajout au répertoire de travail.

Dites à votre git de rechercher un fichier d'attributs global

Tout d’abord, indiquez à votre configuration globale d’utiliser un fichier d’attributs globaux:

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

Créer des filtres globaux

Maintenant, créez le filtre:

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

Ajouter la magie de script sed

Enfin, mettez le fixup-eol-eof script quelque part sur votre chemin, et le rendre exécutable. Le script utilise sed pour effectuer des modifications à la volée (supprimer les espaces et les blancs à la fin des lignes et les lignes blanches superflues à la fin du fichier).

fixup-eol-eof devrait ressembler à ceci:

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

mon résumé de ceci

Indiquez à git les types de fichiers auxquels appliquer le filtre que vous venez de créer.

Enfin, créez ou ouvrez ~/.gitattributes_global dans votre éditeur favori et ajoutez des lignes telles que:

pattern attr1 [attr2 [attr3 […]]]

Donc, si nous voulons résoudre le problème des espaces, nous ajouterons une ligne qui ressemble à ceci pour tous nos fichiers source c:

*.c filter=fix-eol-eof

Discussion du filtre

Le filtre a deux phases, la phase de nettoyage qui est appliquée lorsque des éléments sont ajoutés à l'index ou archivés, et la phase de maculage lorsque git place des éléments dans votre répertoire de travail. Ici, notre maculage n’exécute que le contenu via la commande cat, ce qui devrait les laisser inchangés, à l’exception de l’ajout possible d’un caractère de fin de ligne s'il n’existait pas de caractère de fin de ligne. La commande de nettoyage est le filtrage des espaces que j'ai bricolé à partir de notes sur http://sed.sourceforge.net/sed1line.txt . Il semble que cela doit être mis dans un script Shell, je ne pouvais pas comprendre comment injecter la commande sed, y compris la désinfection des lignes supplémentaires superflues à la fin du fichier directement dans le fichier git-config. (Vous [~ # ~] pouvez [~ # ~] vous débarrasser des espaces vides, cependant, sans qu'il soit nécessaire de créer un script sed séparé, le filter.fix-eol-eofà quelque chose comme sed 's/[ \t]*$//' %f où le \t est un onglet réel, en appuyant sur tab.)

Require = true provoque une erreur en cas d'erreur, pour vous éviter des ennuis.

S'il vous plaît pardonnez-moi si mon langage concernant git est imprécis. Je pense avoir une assez bonne compréhension des concepts, mais j'apprends toujours la terminologie.

8
zbeekman

Voici une version compatible Ubuntu + Mac OS X:

#!/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

S'amuser

6
sdepold

J'y pensais aujourd'hui. C'est tout ce que j'ai fini par faire pour un Java projet:

egrep -rl ' $' --include *.Java *  | xargs sed -i 's/\s\+$//g'
5
Grant Murphy

Pour Sublime Text utilisateurs.

Définissez les paramètres suivants correctement dans votre configuration Setting-User .

"trim_trailing_white_space_on_save": true

2
Haris Krajina

la boucle for for files utilise la variable $ IFS Shell. dans le script donné, les noms de fichiers contenant un caractère qui se trouve également dans la variable $ IFS seront considérés comme deux fichiers différents dans la boucle for. Ce script le corrige: le modificateur de mode multi-lignes donné par sed-manual ne semble pas fonctionner par défaut sur ma boîte Ubuntu, aussi je recherchais une implémentation différente et le trouvai avec une étiquette itérative. dernière ligne du fichier si je l’ai bien compris.

#!/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] pattern sed-subsition: Comment remplacer une nouvelle ligne (\ n) par sed? .

2
imme

Cela ne supprime pas les espaces automatiquement avant une validation, mais il est assez facile à effectuer. Je mets le script Perl suivant dans un fichier nommé git-wsf (correctif de l'espace blanc) dans un répertoire dans $ PATH afin que je puisse:

git wsf | sh

et il supprime tous les espaces uniquement des lignes de fichiers que git affiche sous la forme d'un 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 <<EOT\n", $file;
    for (@$line) {
        printf '%ds/ *$//'."\n", $_;
    }
    print "wq\nEOT\n";
}
1
davidc

Pour supprimer les espaces de fin de ligne dans un fichier, utilisez ed:

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

Un peu tard, mais comme cela pourrait aider quelqu'un, voilà.

Ouvrez le fichier dans VIM. Pour remplacer les tabulations par des espaces, tapez ce qui suit dans la ligne de commande vim

:%s#\t#    #gc

Se débarrasser des autres espaces de fuite

:%s#\s##gc

C'est à peu près le fait pour moi. C'est fastidieux si vous avez beaucoup de fichiers à éditer. Mais j’ai trouvé que c’était plus facile que les hooks de pré-commit et le travail avec plusieurs éditeurs.

0
hriddle