最近,我正在学习diff
和patch
。我创建了两个文件,文件a
的内容为abc
,文件b
的内容为def
。然后我使用了diff -u a b > p
和patch < 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机器上执行的。
谢谢。
答案 0 :(得分:2)
这是GNU补丁程序的功能。由于您尚未指定要修补的文件,因此必须以某种方式从输入中推导出来。基本上,如果您不指定路径(仅指定基本名称),则除非使用--posix
命令行参数或设置了POSIXLY_CORRECT
环境变量,否则它将假定必须使用较短的名称来修补文件。 :
patch --posix <p
# or
POSIXLY_CORRECT=1 patch <p
在您的情况下,在a
和b
之间正确选择了第一个,但对于ab
和b
,则选择了第二个作为补丁目标(如{ {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
会“更好”,因此该工具会尝试对其进行修补并失败。
我不知道为什么将此行为设为默认行为。