如何将git add patch -p模式与diff的ignore-all-space结合使用?

39

如何在 Git add 中使用 patch 模式,但忽略空格变更。

这种情况适用于当您重新格式化文件并对其进行更改时。我希望先单独提交真正的代码更改(如 git diff -w path 所示),然后再提交格式化更改。


1
git add --patch 中的 'g' 选项将会有所帮助;空白字符变化将在选择补丁列表中为空。 - William Pursell
git 的哪个版本中添加了 g 选项? - Sam
1
它出现在1.6.1-rc1-37版本中,因此第一个正式发布它的版本应该是1.6.2。(实际上它不是一个“选项”,而是当显示一个hunk时使用的导航菜单选择。) - William Pursell
请注意,g 命令不会执行我所想的操作:我将“空格更改”解释为“插入或删除空行”。'g' 命令将显示这些类型更改的空白标头,但不会显示空格的插入/删除。 - William Pursell
1
可能是仅添加非空格更改的重复问题。 - George Hilliard
4个回答

25

以下内容是对相关问题的改编。

git diff -w --no-color | git apply --cached --ignore-whitespace

它的好处在于您不需要使用stash、临时文件或对工作目录执行reset --hard

补充说明

上述解决方案仅适用于除空格之外的更改。这没有解决补丁问题,尽管在此情况下使用--patch进行分段并不直观。

补丁选项1:在文本编辑器中编辑差异

有许多方法可以使用文本编辑器实现此操作。 Vim 特别适合此操作。

在您的存储库根目录中启动 Vim。

在正常模式下,通过以下方式将差异加载到空缓冲区中...

:r !git diff -w --no-color
:set ft=diff  # if you want syntax highlighting

修改差异并删除您不想暂存的部分。

要将vim缓冲区的内容暂存,运行vim ex命令...

:w !git apply --cached --ignore-whitespace

如果你是 Vim 爱好者,你也可以使用可视模式进行暂存!

:<',>'w !git apply --cached --ignore-whitespace

您可以使用ex命令提交已暂存的更改...
:!git commit -m "message"
# or
:!git commit

清空缓存区,读取未暂存的更改并重复执行。
:bd! | set ft=diff | r !git diff -w --no-color

最终,你只剩下空格改动需要提交。

如果你不使用Vim,你也可以将git diff导出到文件中,编辑该文件并保存,然后再将该文件导入到git apply中。反复提交和重复此过程,虽然有点繁琐,但却是可行的。

选项 2:补丁重置

这与git add --patch相反,但一旦你使用了它来暂存了非空格改动...

git diff -w --no-color | git apply --cached --ignore-whitespace

你可以在补丁模式下取消暂存块。
git reset --patch .

记住,您正在删除想要保留的已暂存更改。重复此步骤并根据需要提交,直到只剩下空格更改为止。

4
那太棒了。当 Visual Studio 破坏了 web.config 后,这非常有用。 - kenchilada
它怎么会有这么多赞?它完全没有包括git add --patch,允许用户按块查看所有更改,但忽略空格更改? - Chucky
我完全忽略了“修补程序”这个词,Chucky。更新了一些替代你解决方案的方法。 谢谢! - Justin C
在某些复杂情况下,当上下文存在被git diff忽略的空格错误时,此方法可能会失败。使用diff和apply上的unidiff-zero选项可以通过删除所有上下文来提供帮助:https://dev59.com/SWAf5IYBdhLWcg3woj7N#63904526 - Johnson Steward

3
如果您想执行git add --patch,但像提问者所要求的那样忽略所有空格,您可以在一个命令中完成此操作:
git diff -w --no-color | git apply --cached --ignore-whitespace && git checkout -- . && git reset && git add -p

git diff -w --no-color 命令会创建一个差异(diff)

git apply --cached --ignore-whitespace 命令则应用该差异,并忽略空格,然后将其加入索引(index)

git checkout -- . 命令会移除未加入索引的 "空格" 更改

git reset 命令将索引重置为仅包含非空格更改

git add -p 命令以打补丁(patch)模式添加非空格更改

可以将以上命令封装到别名(alias)中:

alias gwap=“git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero && git checkout -- . && git reset && git add -p”

如果你使用像我一样基于Unix的系统:

gwap= !git diff -U0 -w --no-color | git apply --cached --ignore-whitespace --unidiff-zero && git checkout -- . && git reset && git add -p 

(注意我添加了选项-U0--unidiff-zero,以解决上下文匹配问题,根据此评论。)
来源:https://til.hashrocket.com/posts/696df00135-remove-whitespace-changes-then-git-add-p

也许将空格更改存储起来,然后在执行此命令后再弹出它们会更安全。我不确定如何在不将所有内容都存储在索引中的情况下完成这个操作。有人有任何想法吗? - Chucky
我不知道有任何选择性存储的方法,而且我已经广泛地寻找了这种能力。然而,如果您的仅限于空格的更改是由格式化工具(例如 gofmtrustfmt)引起的,则此解决方案非常有效。提交后,我只需重新运行格式化工具即可恢复空格更改。 - Justin C
git diff | git apply 之后,也许可以使用 git stash --keep-index 来保存空格更改。它会存储所有更改,但保留索引不变。执行 git reset && git add -p,直到完成提交,然后使用 git stash pop 恢复空格更改。灵感来自于 这个答案 - Justin C
我尝试了一下,Justin。但是当弹出存储时,会发生合并冲突。 - Chucky
我早就预料到了 :/ ,我猜暂存文件将会足够。在执行git checkout -- .之前,运行git diff -U0 --no-color > some-temp-file,最后,使用<some-temp-file git apply --cached --unidiff-zero && rm some-temp-file进行操作。 - Justin C

1
注意:此答案已经过时。6年后,Justin的另一个答案更好。建议使用git apply --cached 我建议简单地往返一个差异。
想法:
git diff --ignore-all-space | (git reset --hard && git apply)

警告:由于git reset的存在,这样做非常危险(它不会保留二进制文件的更改)。也许您想要一个类似于bash函数的东西。

function cleanup_patch()
{
    if [ $# -lt 1 ]; then 
        echo 'Must provide explicit paths (wildcards allowed)'; 
    else
        git diff --ignore-all-space -- "$@" |
            (git checkout HEAD -- "$@" &&
             git apply)
    fi
}

据我所知,diff命令中看似有用的--binary选项并不遵守忽略空格标志。


@Sam:如果我的回答对你有帮助,那就足够了。为什么使用临时文件会更好?你所描述的似乎正是cleanup_patch函数所做的事情,只不过增加了一些繁琐的步骤。也许我漏掉了什么。 - sehe
将输出到文件仅仅是在你不经常使用diff和patch时的安全保障。你可以检查补丁看起来是否合理,如果你在重置/检出后出现问题,至少可以手动应用补丁的更改。也就是说,我是一个新手。无论如何,你难道不认为检出特定路径比重置整个工作目录更安全吗?哦,现在我看到你的shell函数实际上做到了这一点X_X我会继续大声思考。你不应该在apply中使用--ignore-whitespace标志吗? - Sam
@Sam:在应用时忽略空格是多余的。很高兴你注意到了bash函数中的checkout。如果你想的话,可以使用临时文件。如果我怀疑它,我的偏好是commit/reset HEAD^或使用git stash。然而,我通常手动清理补丁。 - sehe
那么为什么 apply 有那个标志呢? - Sam
1
@AnkurLoriya 这是故意的,但我同意这使得一行代码的方法变得太冒险了。另一个答案应该是被接受的答案。我会在几分钟内添加一个注释。 - sehe
显示剩余3条评论

1

一个更强大且多才多艺的版本是 @"Justin C" 的答案:

anw = !git diff -U0 -w --no-color -- \"$@\" | git apply --cached --ignore-whitespace --unidiff-zero "#"
  • 不带参数 - 添加所有已跟踪文件的非空白更改
  • 给定文件/目录 - 仅添加这些位置中的非空白更改

有关更多信息,请参见this answer


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