【问题标题】:Weird behavior of patch when filename is changed更改文件名时补丁的奇怪行为
【发布时间】:2018-12-20 12:48:55
【问题描述】:

最近在学习diffpatch。我创建了两个文件,文件a 内容abc 和文件b 内容def。然后我使用了diff -u a b > ppatch < p,它的行为正确,如下图所示:

[joe@joe-pc c]$ ls
a  b
[joe@joe-pc c]$ more a
abc
[joe@joe-pc c]$ more b
def
[joe@joe-pc c]$ diff -u a b > p
[joe@joe-pc c]$ more p
--- a   2018-12-20 22:56:33.865661540 +0800
+++ b   2018-12-20 22:54:15.241516269 +0800
@@ -1 +1 @@
-abc
+def
[joe@joe-pc c]$ patch < p
patching file a
[joe@joe-pc c]$ more a
def
[joe@joe-pc c]$ more b
def
[joe@joe-pc c]$ ls
a  b  p
[joe@joe-pc c]$

但是如果我将文件名从a 更改为ab,就会发生一些奇怪的事情。 patch &lt; p 命令告诉我
patching file b Reversed (or previously applied) patch detected! Assume -R? [n]

[joe@joe-pc c]$ ls
ab  b
[joe@joe-pc c]$ more ab
abc
[joe@joe-pc c]$ more b
def
[joe@joe-pc c]$ diff -u ab b > p
[joe@joe-pc c]$ more p
--- ab  2018-12-20 22:57:29.767980973 +0800
+++ b   2018-12-20 22:54:15.241516269 +0800
@@ -1 +1 @@
-abc
+def
[joe@joe-pc c]$ patch < p
patching file b
Reversed (or previously applied) patch detected!  Assume -R? [n] ^C
[joe@joe-pc c]$

文件内容相同,但是为什么在第二种情况下patch找不到要修补的正确文件ab

以上操作是在带有bash shell的Linux机器上进行的。

提前致谢。

【问题讨论】:

  • 感谢您的建议。我认为屏幕截图更“真实”。现在我已将终端输出粘贴到我的问题中。
  • 很棒的问题。根据一些随机的[opennet.ru/cgi-bin/opennet/man.cgi?topic=patch](man补丁),它应该自动推断文件名是ab,但在你的情况下它使用b
  • 对我来说看起来像是一个错误:当我将文件命名为 ac/b 时它也不起作用,但与 ac/bd 一起使用。如果我完全删除第二个文件,它也会开始使用任何名称组合。

标签: diff patch


【解决方案1】:

这是 GNU 补丁的一个特性。由于您没有指定要修补的文件,因此必须以某种方式从输入中推断出它。基本上,如果您不指定路径(仅指定基本名称),则假定它必须使用较短的名称修补文件,除非传递了 --posix 命令行参数或设置了 POSIXLY_CORRECT 环境变量:

patch --posix <p
# or
POSIXLY_CORRECT=1 patch <p

在您的情况下,ab 之间的第一个被正确选择,但对于 abb,第二个被选择为补丁目标(正如 patching file b 行所建议的那样),但是补丁失败,因此出现错误。

您也可以通过明确指定补丁目标来解决此问题:

patch ab <p

深入研究文档

GNU 补丁使用以下逻辑(参见patch's manual, "10.6 Multiple Patches in a File" section):

首先,patch 获取候选文件名的有序列表,如下所示:

  • 如果标头是上下文差异的标头,patch 将采用标头中的旧文件名和新文件名。 ...

...

然后patch从候选列表中选择一个文件名如下:

  • 如果某些命名文件存在,如果符合 POSIX,patch 会选择第一个名称,否则选择最佳名称。

对于“uniform context format”,“旧”文件在---(在您的情况下为aab)之后提及,而“新”文件在+++ 之后提及(b 在你的情况)。

如果两个文件都存在并且patch 未配置为“确认到POSIX”(例如,通过设置POSIXLY_CORRECT 环境变量或--posix 命令行参数,请参阅"10.12 patch and the POSIX Standard" section of the manual),那么patch 将选择两个中的“最好”的名字。这里的“名称”包括从补丁文件获取的完整路径(在您的情况下无关紧要)。详情稍后具体:

为了确定最佳的非空文件名列表,patch 首先取所有路径名组件最少的名称;其中,它会采用所有具有最短基本名称的名称;其中,它采用所有最短的名称;最后,它取剩下的第一个名字。

这里的“名称组件”基本上是一个文件夹/文件名(例如/foo/bar/baz 有三个),“基本名称”只是文件的名称(baz)。 因此,如果名称是 a(旧)和 b(新)并且两个文件都存在,则没有“最佳名称”,因此第一个被修补。 但是如果名称是 ab(旧)和 b(新)并且两个文件都存在,那么 b 是“更好的”,因此该工具会尝试修补它并失败。

我不知道为什么将这种行为设为默认值。

【讨论】:

  • 哇,你的回答太棒了!感觉不直观,补丁文件中提供了新旧文件名的信息,但patch不遵循该规则...非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多