如何在 Powershell(vs bash)中正确生成和应用 git 补丁?

Posted

技术标签:

【中文标题】如何在 Powershell(vs bash)中正确生成和应用 git 补丁?【英文标题】:How to generate and apply git patches correctly in Powershell (vs bash)? 【发布时间】:2022-01-13 21:51:01 【问题描述】:

请注意以下简短场景(在 Powershell 中):

PS> git diff -U3 -r -M HEAD -- .\Metadata\LegacyTypeModules\xyz.Web.Main.draft.json | Out-File -Encoding ascii c:\temp\1.diff

PS> git apply --cached C:\temp\1.diff
error: patch failed: Metadata/LegacyTypeModules/xyz.Web.Main.draft.json:69
error: Metadata/LegacyTypeModules/xyz.Web.Main.draft.json: patch does not apply

这会失败,因为文件中的最后一行不以 CRLF 结尾:

但是,在 bash 中运行时,完全相同的命令可以工作:

$ git diff -U3 -r -M HEAD -- Metadata/LegacyTypeModules/xyz.Web.Main.draft.json > /c/Temp/2.diff

$ git apply --cached /c/Temp/2.diff

P11F70F@L-R910LPKW MINGW64 /c/xyz/tip (arch/1064933)

这两个补丁的区别是:

所以问题似乎发生了,因为 Powershell 使用 CRLF 终止通过管道的每一行,而 bash 保留原始行结尾。

我理解为什么会发生这种情况 - Powershell 使用对象进行操作,并且对象是字符串不包括 EOL 字符。写入文件时,Powershell 将对象转换为字符串(在字符串的情况下,转换是 nop)并使用默认的 EOL 序列来分隔行。

是不是说Powershell根本不能用在EOL敏感的场景中?

【问题讨论】:

【参考方案1】:

您可以尝试使用join 将 CRLF 替换为 unix EOL:

(git command arguments . . .) -join "`n" | out-file c:\temp\1.diff -NoNewline

【讨论】:

很好,尽管您可能希望((git command arguments . . .) -join "`n") + "`n" | out-file c:\temp\1.diff -NoNewline 确保输出文件有一个尾随 LF。 @mklement0 对。如果需要,尽管我认为这并不重要。 在 Unix 世界中,文本文件通常应该有一个尾随换行符。 (例如:问题截图中的补丁内容抱怨文件末尾缺少换行符)。【参考方案2】:

标准差异(也称为补丁)以 LF 行结尾终止行。那是因为这是 POSIX 为diff 的输出指定的内容。所有行都必须包含一个 LF 行结尾。

当补丁中的 CR 在 LF 之前时,它被认为是要修补的内容的一部分。因此,您遇到的补丁可能不适用,因为旧内容被列为具有 CRLF 行结尾,而事实并非如此。

不幸的是,PowerShell 在这方面完全被破坏了,它的管道损坏了通过它们的数据。对于任何类型的二进制数据尤其如此。如果您使用任何类型的设计为在 Unix 上运行的工具,例如 Git,则需要避免使用 PowerShell 的管道。

【讨论】:

【参考方案3】:

确实:

PowerShell 总是将外部程序的输出解码为文本(使用[Console]::OutputEncoding

然后,当行变得可用时,它逐行通过管道发送解码的输出。

诸如Out-File 之类的文件输出 cmdlet 然后总是使用 platform-native 换行序列 - Windows 上的 CRLF - 在写入目标文件时终止每个(字符串化)输入对象(使用默认字符编码(或通过-Encoding 指定的编码),在技术上与用于解码外部程序输出的编码无关。 p>

换句话说:从 PowerShell 7.2 开始,PowerShell 管道(和重定向)支持通过传递原始二进制数据 - 未来的原始数据- 数据支持正在GitHub issue #1908 中讨论。

解决方法

手动使用 LF 换行符 ("`n") 加入和终止解码的输出行,并将生成的多行字符串按原样 (-NoNewLine) 写入目标文件,如图所示在zdan's helpful answer。

在这个简单的例子中,委派给cmd.exe /c是最简单的,因为cmd.exe的管道和重定向是原始字节管道强>:

cmd /c @'
git diff -U3 -r -M HEAD -- .\Metadata\LegacyTypeModules\xyz.Web.Main.draft.json > c:\temp\1.diff
'@

请注意使用 here-string 以提高任何嵌入式引用的可读性和易用性(此处不适用)。

【讨论】:

以上是关于如何在 Powershell(vs bash)中正确生成和应用 git 补丁?的主要内容,如果未能解决你的问题,请参考以下文章

在 Windows 上,Git Bash vs Windows Powershell vs 命令提示符有啥区别

如何在 Powershell 中执行相当于 $PROGPATH/program 的 bash?

如何从 powershell 中为 bash 设置环境变量?

IDE VScode如何开启多个终端powershell或者Git Bash?如何横向显示?

如何在Windows上为Google云平台添加docker-registry secret(就是在PowerShell中,不是Bash)?

如何运行具有详细输出的 PowerShell 脚本?