在提交之前让git自动删除尾随空格

时间:2009-02-26 18:52:21

标签: git whitespace githooks

我正在和我的团队一起使用git,并希望从我的差异,日志,合并等中删除空格更改。我假设最简单的方法是让git自动删除尾随空格(和来自所有提交的其他空白错误)。

我尝试在~/.gitconfig文件中添加以下内容,但在提交时它没有执行任何操作。也许它是为不同的东西而设计的。解决方案是什么?

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

我正在使用ruby,以防任何人有任何ruby特定的想法。在提交之前自动进行代码格式化将是下一步,但这是一个难题而不是真正导致大问题。

16 个答案:

答案 0 :(得分:103)

这些设置(core.whitespaceapply.whitespace)不会删除尾随空格,而是:

  • core.whitespace:检测它们并引发错误
  • apply.whitespace:并剥离它们,但仅限于修补程序,而不是“始终自动”

我相信 git hook pre-commit 可以做得更好(包括删除尾随空格)


请注意,在任何给定时间,您都可以选择不运行pre-commit挂钩:

  • 暂时:git commit --no-verify .
  • 永久:cd .git/hooks/ ; chmod -x pre-commit

警告:默认情况下,pre-commit脚本(如this one),“删除尾随”功能“,但”警告“功能如下:< / p>

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

然而,您可以 build a better pre-commit hook ,尤其是当您考虑:

  

在Git中提交只有一些更改添加到暂存区域仍然会导致“原子”修订版可能永远不会作为工作副本存在而且可能无法正常工作


例如,oldman建议in another answer一个pre-commit hook来检测并删除空格。
由于该钩子获取每个文件的文件名,我建议小心某些类型的文件:你不想删除.md(降价)文件中的尾随空格!

答案 1 :(得分:41)

你可以通过欺骗Git将你的变化视为一个补丁来欺骗Git为你修复空白。与&#34; pre-commit hook&#34;相反。解决方案,这些解决方案为Git添加了空格固定命令。

是的,这些都是黑客攻击。


强大的解决方案

以下Git别名取自 my ~/.gitconfig

强大的&#34;我的意思是这些别名运行没有错误 正确的事情,无论树或索引是否脏。但是,如果互动git rebase -i已在进行中,它们将无法运作;如果你关心这个角落的情况,请参阅my ~/.gitconfig以获取更多检查,其中最后描述的git add -e技巧应该有效。

如果要直接在shell中运行它们,而不创建Git 别名,只需复制并粘贴双引号之间的所有内容即可 (假设你的shell像Bash一样)。

修复索引但不修复树

以下fixws Git别名修复了索引中的所有空格错误, 如果有的话,但是没有碰到树:

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

如果有的话,我们的想法是在git fixws之前运行git commit 索引中的空白错误。

修复索引和树

以下fixws-global-tree-and-index Git别名修复了所有空格 索引和树中的错误,如果有的话:

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

要修复未版本控制文件中的空白,请执行

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

简单但不健全的解决方案

这些版本更容易复制和粘贴,但他们不会这样做 如果不满足他们的条件,那是对的。

修复以当前目录为根的子树(但如果索引不为空,则重置索引)

使用git add -e来&#34;编辑&#34;使用身份编辑器:的补丁:

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

修复并保留索引(但如果树是脏的或索引为空则失败)

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

修复树和索引(但如果索引不为空则重置索引)

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

export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .技巧

的说明

在我从this answer了解git rebase --whitespace=fix诀窍之前,我到处都在使用更复杂的git add诀窍。

如果我们手动完成:

  1. apply.whitespace设为fix(您只需执行此操作一次):

    git config apply.whitespace fix
    

    这告诉Git修复补丁中的空格。

  2. 说服Git将您的更改视为修补程序

    git add -up .
    

    a + 输入以选择每个文件的所有更改。您将收到有关Git修复您的空白错误的警告 (git -c color.ui=auto diff此时显示您的非索引更改正是空白错误。)

  3. 从工作副本中删除空格错误:

    git checkout .
    
  4. 带回您的更改(如果您还没准备好提交):

    git reset
    
  5. GIT_EDITOR=:表示使用:作为编辑器,并作为命令 :是身份。

答案 2 :(得分:29)

我找到了一个git pre-commit hook that removes trailing whitespace

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

答案 3 :(得分:19)

在Mac OS(或可能是任何BSD)上,sed命令参数必须略有不同。试试这个:

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

将此文件另存为.git/hooks/pre-commit - 或查找已存在的文件,并将底部块粘贴到其中。并记住chmod a+x也是如此。

或者全球使用(通过Git commit hooks - global settings),您可以将其放入$GIT_PREFIX/git-core/templates/hooks(其中GIT_PREFIX是/ usr或/ usr / local或/ usr / share或/ opt / local / share)在现有的回购中运行git init

根据git help init

  

在现有存储库中运行git init是安全的。它不会覆盖已存在的东西。重新运行git init的主要原因是选择新添加的模板。

答案 4 :(得分:10)

我宁愿把这个任务留给你最喜欢的编辑。

设置命令以在保存时删除尾随空格。

答案 5 :(得分:9)

我编写了这个预提交钩子,它只删除你已经更改/添加的行的空白空格,因为如果目标文件有太多的尾随空格,之前的建议往往会创建不可读的提交

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

答案 6 :(得分:8)

请尝试my pre-commit hooks,它可以自动检测尾随空格, 将其删除 。谢谢!

它可以在GitBash(windows), Mac OS X and Linux

下工作

快照:

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

答案 7 :(得分:8)

使用git属性,并使用git config

过滤设置

好的,这是解决这个问题的新方法......我的方法是不使用任何钩子,而是使用过滤器和git属性。这允许你做的是,在你开发的每台机器上设置一组过滤器,这些过滤器将在提交之前剥离文件末尾的额外尾随空格和额外空白行。然后设置一个.gitattributes文件,该文件说明应该将过滤器应用于哪些类型的文件。过滤器有两个阶段,在将文件添加到索引时应用了clean,以及在将它们添加到工作目录时应用的涂抹。

告诉你的git寻找全局属性文件

首先,告诉您的全局配置使用全局属性文件:

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

创建全局过滤器

现在,创建过滤器:

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

添加sed脚本魔术

最后,将fixup-eol-eof脚本放在路径上的某个位置,并使其可执行。该脚本使用sed进行一些动态编辑(删除行末尾的空格和空格,以及文件末尾的无关空行)

fixup-eol-eof应如下所示:

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

my gist of this

告诉git将新创建的过滤器应用于

的文件类型

最后,在您喜欢的编辑器中创建或打开〜/ .gitattributes_global并添加以下行:

pattern attr1 [attr2 [attr3 […]]]

因此,如果我们想解决空白问题,对于我们所有的c源文件,我们会添加一行如下所示:

*.c filter=fix-eol-eof

过滤器的讨论

过滤器有两个阶段,即将事物添加到索引或签入时应用的清洁阶段,以及当git将东西放入工作目录时的涂抹阶段。在这里,我们的涂抹只是通过cat命令运行内容,它应该保持不变,除了可能添加一个尾随换行符(如果文件末尾没有一个)。 clean命令是我从http://sed.sourceforge.net/sed1line.txt的笔记拼凑而成的空白过滤。它似乎必须放入shell脚本,我无法弄清楚如何注入sed命令,包括将文件末尾的无关额外行的卫生直接导入git-config文件。 (你 CAN 摆脱了尾随空白,但是,无需单独的sed脚本,只需将filter.fix-eol-eof设置为sed 's/[ \t]*$//' %f \t所在的位置{{1}}是一个实际的标签,按Tab键。)

如果出现问题,require = true会导致错误,以免让您免于麻烦。

如果我关于git的语言不准确,请原谅我。我想我对这些概念有了很好的把握,但仍在学习术语。

答案 8 :(得分:6)

这是一个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

玩得开心

答案 9 :(得分:5)

今天想到这个。这就是我最终为java项目所做的一切:

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

答案 10 :(得分:2)

for-loop for files使用$ IFS shell变量。 在给定的脚本中,其中包含字符的文件名也位于$ IFS变量中,将被视为for循环中的两个不同文件。这个脚本修复了它: 多线模式修饰符作为给定的sed-manual默认情况下在我的ubuntu框中似乎不起作用,所以我寻求一个不同的实现并发现它带有一个迭代标签,基本上它只会在文件的最后一行开始替换如果我理解正确的话。

#!/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] sed-subsition模式:How can I replace a newline (\n) using sed?

答案 11 :(得分:2)

Sublime Text 用户。

设置用户配置中正确设置以下内容。

"trim_trailing_white_space_on_save": true

答案 12 :(得分:1)

这不会在提交之前删除自动空格,但它很容易生效。我将以下perl脚本放在$ PATH的目录中名为git-wsf(git whitespace fix)的文件中,以便我可以:

git wsf | SH

并且它会从git报告为差异的文件行中删除所有的空格。

#! /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";
}

答案 13 :(得分:0)

稍晚但是因为这可能有助于那里的人,所以这里。

在VIM中打开文件。 要用空格替换制表符,请在vim命令行中键入以下内容

:%s#\t#    #gc

摆脱其他尾随空格

:%s#\s##gc

这对我来说非常有用。如果要编辑大量文件,这很乏味。但我发现它比预先提交钩子和使用多个编辑器更容易。

答案 14 :(得分:0)

要以便携方式删除文件末尾的尾随空格,请使用ed

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

答案 15 :(得分:-1)

这可能不会直接解决您的问题,但您可能希望在实际项目空间中通过git-config设置它们,编辑./.git/config而不是〜/ .gitconfig。很高兴保持所有项目成员之间的设置一致。

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