只添加非空格更改

400

我已经设置了文本编辑器,在保存文件时自动删除末尾空格。我正在为一个开源项目做贡献,该项目在末尾空格方面存在严重问题。

每次我尝试提交补丁之前,都必须手动忽略所有的仅包含空格的更改,只选择相关信息。不仅如此,当我运行git rebase时,由于这些空格问题,通常会遇到几个问题。

因此,我希望能够以类似git add -p的方式将只有非空格变化添加到索引中,但不必自己选择所有变化。

是否有人知道如何做到这一点?

编辑:我不能更改项目的工作方式,并且他们经过邮件列表讨论后决定忽略此问题。

12个回答

459

@Frew的解决方案并不完全符合我的需求,所以我为这个完全相同的问题创建了这个别名:

alias.addnw=!sh -c 'git diff -U0 -w --no-color --src-prefix=a/ --dst-prefix=b/ "$@" | git apply --cached --ignore-whitespace --unidiff-zero -'

或者你可以直接运行:

git diff -U0 -w --no-color --src-prefix=a/ --dst-prefix=b/ | git apply --cached --ignore-whitespace --unidiff-zero -

更新

根据this comment,添加了选项-U0--unidiff-zero以解决上下文匹配问题。

基本上它应用了补丁,而没有空格改变的情况下将会被应用。您会发现,在使用 git addnw 您的/文件名后,仍然会有未暂存的更改,这是留下的空格。

虽然不需要--no-color选项,但由于我始终设置颜色,因此必须使用它。无论如何,谨慎总比后悔强。

警告

虽然这个技巧可以直接使用,但如果您尝试使用它来删除带有--ignore-blank-lines选项的空白行更改,则事情会变得复杂。使用这个选项时,git diff将会删除一些块,使得结果的补丁是错误的,因为目标文件中的行号将会偏移。


7
这对我很有效,但是我必须使用“git apply --ignore-whitespace”,否则补丁将无法应用,这是很明显的原因。 - jupp0r
130
应该给git add添加一个选项,如git add -w来执行此操作。 - Jarl
7
这给我带来了“补丁无法应用”和“查找时出错”的问题... 有什么想法吗? - DTI-Matt
18
对我没有起作用。出现了“补丁无法应用”的错误。 - Jerry Saravia
16
如果您因上下文中的空格而收到“patch failed”错误消息,如@bronson所指出的那样,此修订后的命令可以解决问题(它生成没有上下文的补丁):git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero。这不会有风险,因为索引已经是最新的,所以它是一个可靠的基准补丁。 - void.pointer
显示剩余23条评论

44
创建一个仅包含实际更改(不包括只有空格变化的行)的补丁文件,然后清理您的工作区并应用该补丁文件:

git diff > backup
git diff -w > changes
git reset --hard
patch < changes

检查剩余的差异,然后像平常一样添加和提交。
相当于Mercurial的操作是这样的:

hg diff > backup
hg diff -w > changes
hg revert --all
hg import --no-commit changes


5
@jww,原帖的核心问题是“如何避免将仅包含空格的更改提交到源代码控制”。原帖作者使用的是 Git,但这也适用于我使用过的所有源代码管理系统。此回答展示了正确的步骤,如果有人正在使用 Mercurial,可以参考此方法。我想其他人可能也会为使用 Sublesion 等系统的人贡献解决方案。 - Steve Pitchers
4
@jww和@pagid:我编辑了我的答案,专门针对Git进行了解释,使用了与我解决Mercurial问题的方法相同的方法。在我看来,StackOverflow不仅仅是另一个问答论坛 - 它还有作为知识库的角色。除了原始发布者之外的其他人可能会从给出的答案中受益,他们的情况各不相同。这就是为什么我认为传达一般原则的答案是有效的,而不仅仅是针对单个特定情况的答案。 - Steve Pitchers
13
这实际上是我见过的最干净、最易懂和最稳固的方法。 - Kzqai
1
@Gangnus,这意味着要将“changes”文件的内容输入到“patch”命令中。你是否可能使用的是不支持这种重定向方式的有缺陷的shell?详细信息请参考链接:https://www.tldp.org/LDP/abs/html/io-redirection.html - Steve Pitchers
1
如果您下载Git BashCygwin Bash,则该脚本将在Windows上运行。或者,如果您使用的是Windows 10,则可以通过新子系统更加简单! - Steve Pitchers
显示剩余10条评论

40

这对我有效:

如果你想保留一个stash,这个方法可行。

git stash && git stash apply && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

我不喜欢暂存,但是在git + cygwin中我遇到了一个bug,导致我丢失了一些修改。为了确保这些修改被记录在reflog中,我设置了以下操作:

git add . && git commit -am 'tmp' && git reset HEAD^ && git diff -w > foo.patch && git checkout . && git apply foo.patch && rm foo.patch

我们基本上创建了一个不包括空格更改的差异(diff),撤销我们所有的更改,然后应用该差异。


1
你可能想要执行 git stash 而不是 checkout,以备份你的更改,至少在测试之前这样做。 - Paŭlo Ebermann
1
你最终会得到很多暂存区,而且基本上你并不真正需要做所有这些。 它能够工作,但我认为有点凌乱。 - Colin Hebert
3
我同意Colin的看法。如果脚本正常运行,那么就不需要创建备份。不过,可以考虑运行stash,然后再用stash pop命令。如果需要的话,可以还原弹出的备份,但是否则就不会留下大量备份。这样会留下一个额外的文件。 - Casebash
1
跳过二进制文件怎么样?当尝试应用上述代码片段时,我收到错误提示,说补丁无法应用,需要完整的索引行!让我困惑的是,我一开始甚至没有触碰这些文件/二进制文件! - tver3305
1
我认为在第一条命令的结尾处,“git rm foo.patch”应该改为“rm foo.patch”。除此之外非常有帮助,谢谢。 - Jack Casey
显示剩余4条评论

17

根据评论中的用户所说,最受欢迎的答案在某些情况下不起作用,这是由于修补上下文中存在空白。

我将命令修改如下:

$ git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero

这会生成一个没有上下文的补丁。由于补丁的寿命很短,所以这应该不是问题。

对应的别名,再次修订了其他用户已经提供的内容:

addw = !sh -c 'git diff -U0 -w --no-color "$@" | git apply --cached --ignore-whitespace --unidiff-zero' -

为了仅忽略缩进更改,我不得不使用--ignore-space-change而不是-wgit diff -U0 --ignore-space-change --no-color | git apply --cached --unidiff-zero - Andrew
1
提醒一句,不要使用--ignore-blank-lines的这个巧妙无上下文的技巧,否则如果您要忽略的一些"空格"变化是删除或添加空行,则会发现补丁块在错误的偏移处进行修补。 - elbeardmorez

13

将以下内容添加到您的 .gitconfig 文件中:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"

感谢@Colin Herbert的回答启发了我。

语法解释

最后一个#必须被引用,以便它不会在.gitconfig内被视为注释字符,而是被传递并在shell内被视为注释字符——它插入在git apply结束和由git自动放置在命令行末尾的用户提供的参数之间。这些参数在这里是不需要的——我们不希望git apply使用它们,因此前面有注释字符。您可能希望将此命令作为GIT_TRACE=1 git anw运行,以查看它的运行情况。

--表示参数的结束,允许存在文件名为-w或类似于git diff开关的情况。

$@周围的转义双引号是必需的,以保留任何用户提供的引用参数。如果未转义"字符,则它将被.gitconfig解析器所消耗,而不能到达shell。

注意:.gitconfig别名解析不将单引号视为任何特殊字符——它的特殊字符仅为"\\n;(在"引用字符串之外)。这就是为什么"必须始终被转义,即使它看起来在一个单引号的字符串中(git对此完全无动于衷)。

这很重要,例如,如果您有一个方便的别名来在工作树的根目录中执行bash命令。错误的表达式是:

sh = !bash -c '"$@"' -

虽然正确的是:

sh = !bash -c '\"$@\"' -

很好。这使我能够一次添加一个文件。除了为参数添加根目录外,是否有一种方法可以使其像“git add -A”一样工作? - Chucky

7
以下内容如何:

如下:

git add `git diff -w --ignore-submodules |grep "^[+][+][+]" |cut -c7-`

反引号内的命令获取具有非空格更改的文件名称。

2
如果没有使用子模块,可以只执行git add \git diff -w |grep '^+++' |cut -c7-``。 - karmakaze

2
类似于@void.pointer的答案,但是用于修复最近的提交。
git reset --mixed HEAD^
git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero -

这将使空格更改未暂存,其余部分已暂存。

0

这是我的黑客。

git diff -w | grep "diff --git a/*" | sed -r 's#diff --git a/(.*) b(.*)#\1#g' | xargs git add 

git diff -w 只显示有非空格更改的文件。


0
我已经设置了文本编辑器,可以在保存文件时自动删除末尾的空格。
你的编辑器没有项目/目录特定的设置吗?你可以为这个项目禁用空格偏好设置。这似乎是一个更简单的解决方案...

0

对我来说它有效:

git config apply.whitespace fix

每次提交之前使用以下命令:

git add -up .

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接