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

我使用git与我的团队,并希望从我的差异,日志,合并等删除空格的变化。我假设最简单的方法来做到这一点是让git自动删除尾随空白(和其他空白错误)从所有提交应用。

我曾尝试通过~/.gitconfig文件添加以下内容,但在提交时它不会执行任何操作。 也许它是为不同的东西devise的。 有什么解决scheme?

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

我使用ruby,以防有人有任何ruby的具体想法。 提交之前的自动代码格式化将是下一步,但这是一个难题,并没有真正造成大问题。

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

  • core.whitespace :检测它们,并引发错误
  • apply.whitespace :并剥离它们,但只在补丁期间,而不是“始终自动”

我相信git hook pre-commit会做得更好(包括删除尾随的空白)


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

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

警告:默认情况下, pre-commit脚本(如这个 ) 没有 “删除尾随”function,而是“警告”function,如:

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

然而,你可以build立一个更好的pre-commit钩子 ,特别是当你考虑到:

在Git中只join了一些修改,但是仍然会导致一个“primefaces”的修改,这个修改可能永远不会作为工作副本存在,并且可能无法工作


例如, oldman 在另一个答案中提出了一个检测和删除空格的pre-commit钩子 。
由于该钩子获取每个文件的文件名,我build议小心某些types的文件:你不想删除.md (markdown)文件中的尾随空格!

你可以欺骗Git去修正你的空白,通过欺骗Git把你的修改作为补丁来处理。 与“预先提交钩子”解决scheme相比,这些解决scheme将添加空格的命令添加到Git中。

是的,这些都是黑客。


强大的解决scheme

下面的Git别名是从我的~/.gitconfig

通过“健壮”我的意思是这些别名运行没有错误,做正确的事情,无论树或索引是否脏。 但是,如果交互式git rebase -i已经在进行,则它们不起作用。 看看我的~/.gitconfig的额外检查,如果你关心这个angular落的情况下,最后描述的git add -e技巧应该工作。

如果你想直接在shell中运行它们,而不创build一个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 commit之前运行git fixws ,如果你在索引中有空白错误的话。

修复索引和树

以下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 

简单但不稳健的解决scheme

这些版本更容易复制和粘贴,但如果他们的条件不满足,他们不会做正确的事情。

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

使用git add -e用标识编辑器“编辑”补丁:

 (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 .说明export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue .

在我学习git rebase --whitespace=fix 这个答案的技巧之前,我使用的是比较复杂的git add trick。

如果我们手动完成:

  1. 设置apply.whitespacefix (你只需要做一次):

     git config apply.whitespace fix 

    这告诉Git 修补补丁中的空白。

  2. 说服Git把你的改变作为一个补丁

     git add -up . 

    点击+ input以select每个文件的所有更改。 你会得到一个Git修正你的空白错误的警告。
    git -c color.ui=auto diff在这一点上显示你的非索引改变正是空白错误)。

  3. 从您的工作副本中删除空白错误:

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

     git reset 

GIT_EDITOR=:表示使用:作为编辑器,作为命令:是身份。

我发现了一个git pre-commit钩子去除了尾随的空白 。

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

在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提交钩子 – 全局设置 ),你可以把它放在$GIT_PREFIX/git-core/templates/hooks (其中GIT_PREFIX是/ usr或/ usr / local或/ usr / share或/ opt / local /共享),并在现有的回购站内运行git init

根据git help init

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

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

只需设置一个命令来保存时删除尾随空格。

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

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

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

玩的开心

请尝试我的预先提交挂钩 ,它可以自动检测尾随空白, 并将其删除 。 谢谢!

它可以在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(-) 

使用git属性,并使用git config过滤设置

好的,这是解决这个问题的新方法…我的方法是不使用任何钩子,而是使用filter和git属性。 你可以这样做,在你开发的每台机器上设置一组filter,在提交文件之前,会在文件末尾去掉多余的空白和额外的空白行。 然后设置一个.gitattributes文件,说明filter应该应用于哪些types的文件。 这些filter有两个阶段,在向索引添加文件时使用clean,在将其添加到工作目录时应用了smudge。

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

首先,告诉你的全局configuration使用全局属性文件:

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

创build全局filter

现在,创buildfilter:

 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脚本放在你的path上,并使其可执行。 该脚本使用sed来进行一些即时编辑(删除行尾的空格和空格以及文件尾部的多余的空行)

fixup-eol-eof应该是这样的:

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

我的这个主旨

告诉git哪些文件types应用你新创build的filter

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

 pattern attr1 [attr2 [attr3 […]]] 

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

 *.c filter=fix-eol-eof 

讨论filter

这个filter有两个阶段,当添加到索引或签入时应用的清理阶段,以及当git将东西放入工作目录时的涂抹阶段。 在这里,我们的涂抹只是通过cat命令运行内容,应该保持不变,除了可能添加一个尾随的换行符,如果没有一个在文件的末尾。 干净的命令是我在http://sed.sourceforge.net/sed1line.txt从笔记拼凑在一起的空白过滤。; 看来它必须放在一个shell脚本中,我不知道如何注入sed命令,包括在文件末尾多余的额外行的清理直接到git-config文件中。 (你可以删除尾随空白,但是,不需要单独的sed脚本,只需将filter.fix-eol-eof为像sed 's/[ \t]*$//' %f \t是一个实际的选项卡,通过按Tab。)

如果出现错误,require = true会导致错误,从而避免麻烦。

如果我的git语言不准确,请原谅我。 我认为我对这些概念有相当好的把握,但是仍然在学习术语。

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

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

文件的for循环使用$ IFS shellvariables。 在给定的脚本中,带有一个字符的文件名也会在$ IFSvariables中被看作for循环中的两个不同的文件。 这个脚本解决了这个问题:多行模式修改器作为给定的sed手册似乎不工作默认情况下在我的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 -- 

sed-subsition模式: 如何使用sedreplace换行符(\ n)? 。

对于崇高的文本用户。

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

"trim_trailing_white_space_on_save": true

稍迟,但由于这可能会帮助那里的人,在这里。

在VIM中打开文件。 要使用空格replace制表符,请在vim命令行中input以下内容

 :%s#\t# #gc 

摆脱其他尾随的空白

 :%s#\s##gc 

这几乎为我做了。 如果你有很多文件需要编辑,那就太单调乏味了。 但是我发现它比预先提交钩子和使用多个编辑器更容易。

要删除文件尾部的尾部空格,请使用ed

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

这不会在提交之前自动删除空格,但是很容易实现。 我把下面的perl脚本放在一个名为git-wsf(git whitespace fix)的文件中,放在$ PATH的目录中,这样我就可以:

git wsf | SH

只会从git报告为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"; } 

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

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