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

发布于 2024-07-14 18:37:45 字数 382 浏览 15 评论 0原文

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

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

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

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

I'm using Git with my team and would like to remove white space changes from my diffs, logs, merges, etc. I'm assuming that the easiest way to do this would be for Git to automatically remove trailing white space (and other white space errors) from all commits as they are applied.

I have tried to add the following to the ~/.gitconfig file, but it doesn't do anything when I commit. Maybe it's designed for something different. What's the solution?

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

I'm using Ruby in case anyone has any Ruby specific ideas. Automatic code formatting before committing would be the next step, but that's a hard problem and is not really causing a big problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(17

扭转时空 2024-07-21 18:37:45

这些设置(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 脚本(例如 这个),不是“删除尾随”功能”,而是“警告”功能,例如

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

:然而可以构建一个更好的预提交钩子,特别是当您考虑到:

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


例如,oldman 提议另一个答案 a 预提交 hook检测并删除空格。
由于该钩子获取每个文件的文件名,因此我建议对某些类型的文件小心:您不想删除 .md (markdown)文件中的尾随空格!


另一种方法,由 hakre评论

在 Markdown 中,您可以在行尾有两个空格,并且通过在 \n 之前添加“\”而不将其作为尾随空格。

然后是内容过滤器驱动程序:

git config --global filter.space-removal-at-eol.clean 'sed -e "s/ \+$//"'
# register in .gitattributes 
*.md filter=space-removal-at-eol

Those settings (core.whitespace and apply.whitespace) are not there to remove trailing whitespace but to:

  • core.whitespace: detect them, and raise errors
  • apply.whitespace: and strip them, but only during patch, not "always automatically"

I believe the git hook pre-commit would do a better job for that (includes removing trailing whitespace)


Note that at any given time you can choose to not run the pre-commit hook:

  • temporarily: git commit --no-verify .
  • permanently: cd .git/hooks/ ; chmod -x pre-commit

Warning: by default, a pre-commit script (like this one), has not a "remove trailing" feature", but a "warning" feature like:

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

You could however build a better pre-commit hook, especially when you consider that:

Committing in Git with only some changes added to the staging area still results in an “atomic” revision that may never have existed as a working copy and may not work.


For instance, oldman proposes in another answer a pre-commit hook which detects and remove whitespace.
Since that hook get the file name of each file, I would recommend to be careful for certain type of files: you don't want to remove trailing whitespace in .md (markdown) files!


Another approach, suggested by hakre in the comments:

You can have two spaces at end of line in markdown and not have it as trailing whitespace by adding "\" before \n.

Then a content filter driver:

git config --global filter.space-removal-at-eol.clean 'sed -e "s/ \+$//"'
# register in .gitattributes 
*.md filter=space-removal-at-eol
梦回旧景 2024-07-21 18:37:45

您可以欺骗 Git 将您的更改视为补丁,从而为您修复空白。 与“预提交挂钩”解决方案相比,这些解决方案向 Git 添加了空格修复命令。

是的,这些都是黑客行为。


稳健的解决方案

以下 Git 别名取自
我的~/.gitconfig

我所说的“稳健”是指这些别名运行时没有错误,
正确的事情,无论树或索引是否脏。 但是,如果交互式 git rebase -i 已经在进行中,它们将不起作用; 有关其他信息,请参阅 我的 ~/.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 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

简单但不可靠的解决方案

这些版本更容易复制和粘贴,但它们不执行
如果不满足他们的附带条件,那就是正确的事情。

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

使用 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 . 技巧

在我从 这个答案 我到处都在使用更复杂的 git add 技巧。

如果我们手动执行:

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

    git config apply.whitespace 修复 
      

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

  2. 说服 Git 将您的更改视为补丁

    git add -up 。 
      

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

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

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

    git 重置 
      

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

You can trick Git into fixing the whitespace for you, by tricking Git into treating your changes as a patch. In contrast to the "pre-commit hook" solutions, these solutions add whitespace-fixing commands to Git.

Yes, these are hacks.


Robust solutions

The following Git aliases are taken from
my ~/.gitconfig.

By "robust" I mean that these aliases run without error, doing
the right thing, regardless of whether the tree or index are dirty. However, they don't work if an interactive git rebase -i is already in progress; see my ~/.gitconfig for additional checks if you care about this corner case, where the git add -e trick described at the end should work.

If you want to run them directly in the shell, without creating a Git
alias, just copy and paste everything between the double quotes
(assuming your shell is Bash like).

Fix the index but not the tree

The following fixws Git alias fixes all whitespace errors in the index,
if any, but doesn't touch the 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"

The idea is to run git fixws before git commit if you have
whitespace errors in the index.

Fix the index and the tree

The following fixws-global-tree-and-index Git alias fixes all whitespace
errors in the index and the tree, if any:

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

To also fix whitespace in unversioned files, do

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

Simple but not robust solutions

These versions are easier to copy and paste, but they don't do the
right thing if their side conditions are not met.

Fix the sub-tree rooted at the current directory (but resets the index if it's not empty)

Using git add -e to "edit" the patches with the identity editor ::

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

Fix and preserve the index (but fails if the tree is dirty or the index is empty)

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

Fix the tree and the index (but resets the index if it's not empty)

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

Explanation of the export GIT_EDITOR=: && git -c apply.whitespace=fix add -ue . trick

Before I learned about the git rebase --whitespace=fix trick from this answer I was using the more complicated git add trick everywhere.

If we did it manually:

  1. Set apply.whitespace to fix (you only have to do this once):

    git config apply.whitespace fix
    

    This tells Git to fix whitespace in patches.

  2. Convince Git to treat your changes as a patch:

    git add -up .
    

    Hit a+enterto select all changes for each file. You'll get a warning about Git fixing your whitespace errors.
    (git -c color.ui=auto diff at this point reveals that your non-indexed changes are exactly the whitespace errors).

  3. Remove the whitespace errors from your working copy:

    git checkout .
    
  4. Bring back your changes (if you aren't ready to commit them):

    git reset
    

The GIT_EDITOR=: means to use : as the editor, and as a command
: is the identity.

独木成林 2024-07-21 18:37:45

我发现了一个 Git 预提交挂钩,可以删除尾随空白

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

I found a Git pre-commit hook that removes trailing white space.

#!/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
鹿港小镇 2024-07-21 18:37:45

在 macOS(或者可能是任何 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 post-commit 挂钩应用于所有当前和未来的存储库),您可以将其放入 $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 的主要原因是获取新添加的模板。

On macOS (or, likely, any BSD), the sed command parameters have to be slightly different. Try this:

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

Save this file as .git/hooks/pre-commit -- or look for the one that's already there, and paste the bottom chunk somewhere inside it. And remember to chmod a+x it too.

Or for global use (via Applying a git post-commit hook to all current and future repos) you can put it in $GIT_PREFIX/git-core/templates/hooks (where GIT_PREFIX is /usr or /usr/local or /usr/share or /opt/local/share) and run git init inside your existing repos.

According to git help init:

Running git init in an existing repository is safe. It will not overwrite things that are already there. The primary reason for rerunning git init is to pick up newly added templates.

甜是你 2024-07-21 18:37:45

使用 Git 属性,并通过 Git 配置设置过滤器

好吧,这是解决这个问题的新方法……我的方法是不使用任何钩子,而是使用过滤器和 Git 属性。 这允许您在开发的每台机器上设置一组过滤器,这些过滤器将在提交文件之前去除文件末尾的额外尾随空格和额外空行。

然后设置一个 .gitattributes 文件,说明过滤器应应用于哪些类型的文件。 过滤器有两个阶段,clean 将文件添加到索引时应用,smudge 将文件添加到工作目录时应用。

让您的 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

我的要点

告诉 Git 将新创建的过滤器应用到哪些文件类型

最后,创建或打开文件 ~/.在您最喜欢的文本编辑器中打开 .gitattributes_global 并添加如下行:

pattern attr1 [attr2 [attr3 […]]]

因此,如果我们想解决空白问题,对于所有 C 源文件,我们将添加如下行:

*.c filter=fix-eol-eof

过滤器的讨论

过滤器有两个阶段。 clean 阶段在将内容添加到索引或签入时应用,而 smudge 阶段则在 Git 将内容放入工作目录时应用。

在这里,我们的污点只是通过 cat 命令运行内容,这应该使它们保持不变,但如果文件末尾没有换行符,可能会添加一个尾随换行符。

clean 命令是我根据 http://sed.sourceforge.net/ 上的注释拼凑而成的空白过滤。 sed1line.txt。 看来必须放到shell脚本里了。 我不知道如何注入 sed 命令,包括将文件末尾的无关额外行直接清理到 git-config 文件中。 (但是,您可以去掉尾随空白,而不需要单独的 sed 脚本。只需将 filter.fix-eol-eof 设置为类似 /[ \t]*$//' %f 其中 \t 是一个实际的制表符,按 Tab。)

sed ' s 如果出现问题,>require = true 会引发错误,以免您遇到麻烦。

Using Git attributes, and filters setup with Git configuration

OK, this is a new tack on solving this problem… My approach is to not use any hooks, but rather use filters and Git attributes. This allows you to set up, on each machine you develop on, a set of filters that will strip extra trailing white space and extra blank lines at the end of files before committing them.

Then set up a .gitattributes file that says which types of files the filter should be applied to. The filters have two phases, clean which is applied when adding files to the index, and smudge which is applied when adding them to the working directory.

Tell your Git to look for a global attributes file

First, tell your global configuration to use a global attributes file:

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

Create global filters

Now, create the filter:

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

Add the sed scripting magic

Finally, put the fixup-eol-eof script somewhere on your path, and make it executable. The script uses sed to do some on the fly editing (remove spaces and blanks at the end of lines, and extraneous blank lines at the end of the file)

fixup-eol-eof should look like this:

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

My gist of this

Tell Git which file types to apply your newly created filter to

Lastly, create or open file ~/.gitattributes_global in your favorite text editor and add lines like:

pattern attr1 [attr2 [attr3 […]]]

So if we want to fix the white space issue, for all of our C source files we would add a line that looks like this:

*.c filter=fix-eol-eof

Discussion of the filter

The filter has two phases. The clean phase which is applied when things are added to the index or checked in, and the smudge phase when Git puts stuff into your working directory.

Here, our smudge is just running the contents through the cat command which should leave them unchanged, with the exception of possibly adding a trailing newline character if there wasn’t one at the end of the file.

The clean command is the white space filtering which I cobbled together from notes at http://sed.sourceforge.net/sed1line.txt. It seems that it must be put into a shell script. I couldn’t figure out how to inject the sed command, including the sanitation of the extraneous extra lines at the end of the file directly into the git-config file. (You can get rid of trailing blanks, however, without the need of a separate sed script. Just set the filter.fix-eol-eof to something like sed 's/[ \t]*$//' %f where the \t is an actual tab, by pressing Tab.)

The require = true causes an error to be raised if something goes wrong, to keep you out of trouble.

所有深爱都是秘密 2024-07-21 18:37:45

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

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

I'd rather leave this task to your favorite editor.

Just set a command to remove trailing spaces when saving.

演多会厌 2024-07-21 18:37:45

我编写了这个预提交挂钩,它仅从您更改/添加的行中删除尾随空格,因为如果目标文件有太多尾随空格,前面的建议往往会创建不可读的提交。

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

I wrote this pre-commit hook, which only removes the trailing white space from the lines which you've changed/added, since the previous suggestions tend to create unreadable commits if the target files have too much trailing white space.

#!/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
柠檬色的秋千 2024-07-21 18:37:45

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

它可以在 Git Bash (Windows)、Mac OS X 和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(-)

Please try my pre-commit hooks. It can auto detect trailing white space and remove it.

It can work under Git Bash (Windows), Mac OS X and Linux!


Snapshot:

$ 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(-)
廻憶裏菂餘溫 2024-07-21 18:37:45

这是 Ubuntu 和 Mac OS X 兼容版本:

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# 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

Here is an Ubuntu and Mac OS X compatible version:

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# 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
你的心境我的脸 2024-07-21 18:37:45

我今天正在思考这个问题。 这就是我最终为 Java 项目所做的一切:

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

I was thinking about this today. This is all I ended up doing for a Java project:

egrep -rl ' 
 --include *.java *  | xargs sed -i 's/\s\+$//g'
冷弦 2024-07-21 18:37:45

对于 Sublime Text 用户。

在您的设置-用户配置中正确设置以下内容。

"trim_trailing_white_space_on_save": true

For Sublime Text users.

Set the following properly in your Setting-User configuration.

"trim_trailing_white_space_on_save": true
夜唯美灬不弃 2024-07-21 18:37:45

这不会在提交之前自动删除空格,但它很容易实现。 我将以下 Perl 脚本放入名为 git-wsf (Git 空格修复)的文件中$PATH 中的一个目录,这样我就可以:

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";
}

This doesn't remove white space automatically before a commit, but it is pretty easy to effect. I put the following Perl script in a file named git-wsf (Git white space fix) in a directory in $PATH, so I can:

git wsf | sh

And it removes all white space only from lines of files that Git reports as a 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";
}
梦里寻她 2024-07-21 18:37:45

文件的 for 循环使用 $IFS shell 变量。
在给定的脚本中,其中包含 $IFS 变量中的字符的文件名将在 for 循环中被视为两个不同的文件。

此脚本修复了它: sed 手册中给出的多行模式修饰符似乎没有默认情况下在我的 Ubuntu 机器上工作,所以我寻求不同的实现,并发现它带有迭代标签,本质上它只会在我正确理解的情况下开始替换文件的最后一行。

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# 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 separator, introduces EOL-bug?
IFS='
'
# Find files with trailing white space
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 (Mac OS X 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 white space characters
exec git diff-index --check --cached $against --

1 sed 替换模式:如何使用 sed 替换换行符 (\n)?

The for loop for files uses the $IFS shell variable.
In the given script, filenames with a character in them that also is in the $IFS-variable will be seen as two different files in the for loop.

This script fixes it: multiline-mode modifier as given in the sed manual doesn't seem to work by default on my Ubuntu box, so I sought for a different implementation and found this with an iterating label, essentially it will only start substitution on the last line of the file if I've understood it correctly.

#!/bin/sh
#

# A Git hook script to find and fix trailing white space
# 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 separator, introduces EOL-bug?
IFS='
'
# Find files with trailing white space
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 (Mac OS X 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 white space characters
exec git diff-index --check --cached $against --

1 sed-substitution pattern: How can I replace a newline (\n) using sed?

乙白 2024-07-21 18:37:45

Python 脚本获得相同的结果。

import subprocess                                                                 
                                                                              
def get_trailing_lines():                                                         
                                                                              
    result = subprocess.run([                                                     
                            'git',                                            
                            'diff',                                           
                            '--check'                                         
                        ], capture_output=True)                               
                                                                              
    return result.stdout.decode().split('\n')                                     
                                                                              
                                                                              
def modify_line(file_path, l_num):                                                
                                                                              
    f_lines = open(file_path).readlines()                                         
    f_lines[l_num] = f_lines[l_num].rstrip()+'\n'\                                
                     if '\n' in f_lines[l_num] else f_lines[l_num].rstrip()    
                                                                              
    with open(file_path, "w") as w_fp:                                            
        w_fp.writelines(f_lines)                                                  
                                                                              
                                                                              
if __name__ == '__main__':                                                        
                                                                              
    l = get_trailing_lines()                                                      
    for m, d in zip(l[::2], l[1::2]):                                             
        f_path, l_no, *_ = m.split(":")                                           
        modify_line(f_path, int(l_no)-1)                                          

Python Script for the same result.

import subprocess                                                                 
                                                                              
def get_trailing_lines():                                                         
                                                                              
    result = subprocess.run([                                                     
                            'git',                                            
                            'diff',                                           
                            '--check'                                         
                        ], capture_output=True)                               
                                                                              
    return result.stdout.decode().split('\n')                                     
                                                                              
                                                                              
def modify_line(file_path, l_num):                                                
                                                                              
    f_lines = open(file_path).readlines()                                         
    f_lines[l_num] = f_lines[l_num].rstrip()+'\n'\                                
                     if '\n' in f_lines[l_num] else f_lines[l_num].rstrip()    
                                                                              
    with open(file_path, "w") as w_fp:                                            
        w_fp.writelines(f_lines)                                                  
                                                                              
                                                                              
if __name__ == '__main__':                                                        
                                                                              
    l = get_trailing_lines()                                                      
    for m, d in zip(l[::2], l[1::2]):                                             
        f_path, l_no, *_ = m.split(":")                                           
        modify_line(f_path, int(l_no)-1)                                          
葬シ愛 2024-07-21 18:37:45

这可能不会直接解决您的问题,但您可能想通过 git-config< /a> 在您的实际项目空间中,它编辑文件 ./.git/config 而不是文件 ~/.gitconfig。 最好保持所有项目成员之间的设置一致。

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

This probably won't directly solve your problem, but you might want to set those via git-config in your actual project space, which edits file ./.git/config as opposed to file ~/.gitconfig. It is nice to keep the settings consistent among all project members.

git config core.whitespace "trailing-space,space-before-tab"
git config apply.whitespace "trailing-space,space-before-tab"
鹿港小镇 2024-07-21 18:37:45

在 Vim 中打开文件。 要将制表符替换为空格,请在 Vim 命令行中输入以下内容:

:%s#\t#    #gc

删除其他尾随空格

:%s#\s##gc

这对我来说几乎做到了。 如果您有很多文件需要编辑,这会很乏味。 但我发现它比预提交挂钩和使用多个文本编辑器更容易。

Open the file in Vim. To replace tabs with white spaces, type the following on the Vim command line:

:%s#\t#    #gc

To get rid of other trailing white spaces

:%s#\s##gc

This pretty much did it for me. It's tedious if you have a lot of files to edit. But I found it easier than pre-commit hooks and working with multiple text editors.

榆西 2024-07-21 18:37:45

要可移植地删除文件中行尾的尾随空格,请使用 ed

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

To delete trailing white space at the end of lines in a file portably, use ed:

test -s file &&
   printf '%s\n' H ',g/[[:space:]]*$/s///' 'wq' | ed -s file
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文