文件名更改时补丁的怪异行为

时间:2018-12-20 12:48:56

标签: diff patch

最近,我正在学习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 < 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机器上执行的。

谢谢。

1 个答案:

答案 0 :(得分:2)

这是GNU补丁程序的功能。由于您尚未指定要修补的文件,因此必须以某种方式从输入中推导出来。基本上,如果您不指定路径(仅指定基本名称),则除非使用--posix命令行参数或设置了POSIXLY_CORRECT环境变量,否则它将假定必须使用较短的名称来修补文件。 :

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

在您的情况下,在ab之间正确选择了第一个,但对于abb,则选择了第二个作为补丁目标(如{ {1}}行提示),但修补失败,因此出现错误。

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

patching file b

挖掘文档

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

  

首先,补丁程序采用候选文件名称的有序列表,如下所示:

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

...

     

然后补丁从候选列表中选择一个文件名,如下所示:

     
      
  • 如果存在某些命名文件,则修补程序将选择符合POSIX的名字,否则选择最佳名字。
  •   

对于“ uniform context format”,在“ patch ab <p (在您的情况下为---a)后面提到“旧”文件,在后面提到“新”文件ab(在您的情况下为+++)。

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

  

要确定最好的非空文件名列表,patch首先采用路径名组成部分最少的所有名称;其中,然后取所有具有最短基本名称的名称;其中,所有名字都用最短的名字表示;最后,它使用剩下的第一个名字。

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

我不知道为什么将此行为设为默认行为。