Faça o git remover automaticamente o espaço em branco antes de cometer

Estou usando o git com minha equipe e gostaria de remover as mudanças de espaço dos meus diffs, logs, mesclagens, etc. Estou assumindo que a maneira mais fácil de fazer isso seria o git remover automaticamente o espaço em branco (e outros erros de espaço em branco) ) de todos os commits à medida que são aplicados.

Eu tentei adicionar o seguinte ao arquivo ~/.gitconfig mas ele não faz nada quando eu me comprometo. Talvez seja projetado para algo diferente. Qual é a solução?

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

Estou usando ruby ​​no caso de alguém ter alguma ideia específica de ruby. A formatação automática de código antes de confirmar seria o próximo passo, mas isso é um problema difícil e não está realmente causando um grande problema.

Essas configurações ( core.whitespace e apply.whitespace ) não estão lá para remover espaços em branco à direita, mas para:

  • core.whitespace : detectá-los e gerar erros
  • apply.whitespace : e tira-los, mas apenas durante patch, não “sempre automaticamente”

Acredito que o git hook pre-commit do git hook pre-commit faria um trabalho melhor para isso (inclui a remoção do espaço em branco)


Observe que, a qualquer momento, você pode optar por não executar o gancho de pre-commit :

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

Aviso: por padrão, um script de pre-commit (como este ), não possui um recurso “remove trailing”, mas um recurso de “aviso” como:

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

No entanto, você pode criar um gancho de pre-commit melhor , especialmente quando considerar que:

Confirmar no Git com apenas algumas alterações adicionadas à área de teste ainda resulta em uma revisão “atômica” que pode nunca ter existido como uma cópia de trabalho e pode não funcionar .


Por exemplo, oldman propõe em outra resposta um hook pre-commit que detecta e remove espaços em branco.
Como esse hook obtém o nome do arquivo de cada arquivo, eu recomendaria ter cuidado com certos tipos de arquivos: você não deseja remover espaços em branco no arquivo .md (markdown)!

Você pode enganar o Git para consertar o espaço em branco para você, enganando o Git para tratar suas mudanças como um patch. Em contraste com as soluções “pre-commit hook”, essas soluções adicionam comandos de correção de espaço em branco ao Git.

Sim, estes são hacks.


Soluções robustas

Os aliases do Git a seguir são tirados do meu ~/.gitconfig .

Por “robusto” quero dizer que esses aliases são executados sem erro, fazendo a coisa certa, independentemente de a tree ou o índice estarem sujos. No entanto, eles não funcionam se um git rebase -i interativo do git rebase -i já estiver em andamento; veja meu ~/.gitconfig para checagens adicionais se você se preocupa com este caso de canto, onde o truque git add -e descrito no final deve funcionar.

Se você quiser executá-los diretamente no shell, sem criar um alias do Git, apenas copie e cole tudo entre aspas duplas (supondo que seu shell seja do tipo Bash).

Corrigir o índice, mas não a tree

O seguinte alias do fixws Git corrige todos os erros de espaço em branco no índice, se houver algum, mas não toca na tree:

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

A idéia é executar o git fixws antes do git commit se você tiver erros de espaço em branco no índice.

Corrigir o índice e a tree

O seguinte fixws-global-tree-and-index Git corrige todos os erros de espaço em branco no índice e na tree, se houver:

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

Para também corrigir espaços em branco em arquivos não versionados, faça

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

Soluções simples mas não robustas

Essas versões são mais fáceis de copiar e colar, mas não fazem a coisa certa se as condições do lado não forem atendidas.

Corrigir a subtree com raiz no diretório atual (mas redefine o índice se não estiver vazio)

Usando git add -e para “editar” os patches com o editor de identidade::

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

Corrigir e preservar o índice (mas falhará se a tree estiver suja ou o índice estiver vazio)

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

Corrigir a tree e o índice (mas redefine o índice se não estiver vazio)

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

Explicação da export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue . truque

Antes de aprender sobre o git rebase --whitespace=fix do git rebase --whitespace=fix truque dessa resposta, eu estava usando o truque mais complicado de git add todos os lugares.

Se fizéssemos manualmente:

  1. Defina apply.whitespace para fix (você só precisa fazer isso uma vez):

     git config apply.whitespace fix 

    Isso diz ao Git para consertar os espaços em branco nos patches .

  2. Convença o Git a tratar suas alterações como um patch :

     git add -up . 

    Pressione a tecla + para selecionar todas as alterações para cada arquivo. Você receberá um aviso sobre o Git corrigindo seus erros de espaço em branco.
    ( git -c color.ui=auto diff neste momento revela que suas alterações não indexadas são exatamente os erros de espaço em branco).

  3. Remova os erros de espaço em branco da sua cópia de trabalho:

     git checkout . 
  4. Traga de volta suas mudanças (se você não estiver pronto para comê-las):

     git reset 

O GIT_EDITOR=: significa usar : como editor, e como um comando : é a identidade.

Eu encontrei um gancho de pré-commit do git que remove espaços em branco à direita .

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

No Mac OS (ou, provavelmente, qualquer BSD), os parâmetros do comando sed precisam ser ligeiramente diferentes. Tente isto:

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

Salve este arquivo como .git/hooks/pre-commit – ou procure o que já está lá, e cole o pedaço de baixo em algum lugar dentro dele. E lembre-se de chmod a+x também.

Ou para uso global (via Git commit hooks – configurações globais ) você pode colocar em $GIT_PREFIX/git-core/templates/hooks (onde GIT_PREFIX é / usr ou / usr / local ou / usr / share ou / opt / local / compartilhar) e execute git init dentro de seus repositorys existentes.

De acordo com o git help init :

Executar git init em um repository existente é seguro. Não irá sobrescrever coisas que já estão lá. A principal razão para executar novamente o git init é pegar novos modelos adicionados.

Eu prefiro deixar essa tarefa para o seu editor favorito.

Basta definir um comando para remover espaços à direita ao salvar.

Eu escrevi esse gancho de pré-consolidação, que remove apenas o espaço em branco à direita das linhas que você alterou / adicionou, já que as sugestões anteriores tendem a criar confirmações ilegíveis se os arquivos de destino tiverem muito espaço em branco à direita.

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

Por favor, tente meus ganchos pré-commit , ele pode detectar automaticamente espaços em branco e removê-los . Obrigado!

pode funcionar com o GitBash(windows), Mac OS X and Linux !


Instantâneo:

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

Aqui está uma versão compatível com o 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 

Diverta-se

Usando atributos git e filtros setup com git config

OK, esta é uma nova abordagem para resolver este problema … Minha abordagem é não usar ganchos, mas sim usar filtros e atributos git. O que isso permite que você faça, é configurar, em cada máquina que você desenvolve, um conjunto de filtros que irá remover espaços em branco adicionais e linhas extras em branco no final dos arquivos antes de enviá-los. Em seguida, configure um arquivo .gitattributes que indique os tipos de arquivos aos quais o filtro deve ser aplicado. Os filtros têm duas fases, clean, que é aplicada ao adicionar arquivos ao índice, e smudge, aplicado ao adicioná-los ao diretório de trabalho.

Diga ao seu git para procurar por um arquivo de atributos global

Primeiro, informe sua configuração global para usar um arquivo de atributos globais:

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

Crie filtros globais

Agora, crie o 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 

Adicione a magia de script sed

Por fim, coloque o script fixup-eol-eof em algum lugar do seu caminho e torne-o executável. O script usa sed para fazer alguma edição on the fly (remover espaços e espaços em branco no final das linhas e linhas em branco estranhas no final do arquivo)

O fixup-eol-eof deve ficar assim:

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

minha essência disso

Diga ao git quais tipos de arquivos para aplicar o filtro recém-criado

Por fim, crie ou abra ~ / .gitattributes_global no seu editor favorito e adicione linhas como:

 pattern attr1 [attr2 [attr3 […]]] 

Portanto, se quisermos corrigir o problema de espaço em branco, para todos os nossos arquivos de código-fonte, adicionaríamos uma linha como esta:

 *.c filter=fix-eol-eof 

Discussão do filtro

O filtro tem duas fases, a fase de limpeza que é aplicada quando as coisas são adicionadas ao índice ou check-in, e a fase de manchas quando o git coloca coisas em seu diretório de trabalho. Aqui, nosso borrão está apenas executando o conteúdo através do comando cat que deve deixá-los inalterados, com a exceção de possivelmente adicionar um caractere de nova linha à direita se não houver um no final do arquivo. O comando clean é a filtragem de espaços em branco que eu compilei a partir de notas em http://sed.sourceforge.net/sed1line.txt . Parece que ele deve ser colocado em um shell script, não consegui descobrir como injetar o comando sed, incluindo o saneamento das linhas extras externas no final do arquivo diretamente no arquivo git-config. (Você pode se livrar de espaços em branco, no entanto, sem a necessidade de um script sed separado, basta definir o filter.fix-eol-eof para algo como sed 's/[ \t]*$//' %f onde o \t é uma guia atual, pressionando a tecla tab.)

O require = true faz com que um erro seja levantado se algo der errado, para mantê-lo longe de problemas.

Por favor, perdoe-me se a minha linguagem relativa ao git for imprecisa. Eu acho que tenho uma boa compreensão dos conceitos, mas ainda estou aprendendo a terminologia.

Estava pensando sobre isso hoje. Isso é tudo o que acabei fazendo em um projeto java:

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

o loop for para arquivos usa a variável de shell $ IFS. no script fornecido, nomes de arquivos com um caractere neles que também estão na variável $ IFS serão vistos como dois arquivos diferentes no loop for. Este script corrige isso: modificador de modo de múltiplas linhas como determinado sed-manual não parece funcionar por padrão na minha checkbox Ubuntu, então eu busquei uma implementação diferente e achei isso com uma etiqueta de iteração, essencialmente ele só irá iniciar a substituição no última linha do arquivo, se eu entendi corretamente.

 #!/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] padrão sed-subsition: Como posso replace uma nova linha (\ n) usando sed? .

Para usuários do Sublime Text .

Defina seguindo corretamente em você Configuração – configuração do usuário .

"trim_trailing_white_space_on_save": true

Isso não remove os espaços em branco automaticamente antes de um commit, mas é muito fácil de efetuar. Eu coloquei o seguinte script perl em um arquivo chamado git-wsf (git whitespace fix) em um diretório em $ PATH para que eu possa:

git wsf | sh

e remove todos os espaços em branco somente de linhas de arquivos que criam relatórios como um 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 < 

Um pouco tarde, mas como isso pode ajudar alguém lá fora, aqui vai.

Abra o arquivo no VIM. Para replace guias por espaços em branco, digite o seguinte na linha de comando do vim

 :%s#\t# #gc 

Para se livrar de outros espaços em branco à direita

 :%s#\s##gc 

Isso praticamente fez isso por mim. É tedioso se você tiver muitos arquivos para editar. Mas achei mais fácil do que pre-commit hooks e trabalhando com vários editores.

Para excluir espaços em branco no final da linha em um arquivo portável, use ed :

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

Isso provavelmente não irá resolver diretamente o seu problema, mas você pode querer configurar os via git-config no seu espaço real do projeto, que edita ./git/config ao invés de ~ / .gitconfig. É bom manter as configurações consistentes entre todos os membros do projeto.

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