更改文件名时补丁的奇怪行为
Weird behavior of patch when filename is changed
最近在学习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
?
以上操作是在一台Linux机器上执行的bash
shell.
提前致谢。
这是 GNU 补丁的一个特性。由于您没有指定要修补的文件,因此必须以某种方式从输入中推断出它。基本上,如果您不指定路径(仅基本名称),它假定它必须用更短的名称修补文件,除非传递 --posix
命令行参数或设置 POSIXLY_CORRECT
环境变量:
patch --posix <p
# or
POSIXLY_CORRECT=1 patch <p
在您的情况下,在 a
和 b
之间,第一个被正确选择,但是对于 ab
和 b
,第二个被选为补丁目标(如 patching file b
行所示),但修补失败,因此出现错误。
您也可以通过明确指定补丁目标来修复此行为:
patch ab <p
深入研究文档
GNU 补丁使用以下逻辑(参见 patch's manual, "10.6 Multiple Patches in a File" section):
First, patch takes an ordered list of candidate file names as follows:
- If the header is that of a context diff, patch takes the old and new file names in the header. ...
...
Then patch selects a file name from the candidate list as follows:
- If some of the named files exist, patch selects the first name if conforming to POSIX, and the best name otherwise.
对于“uniform context format”,"old" 文件在 ---
之后提及(在您的情况下为 a
或 ab
),并且 "new" 文件在 +++
之后提到(在你的情况下是 b
)。
如果两个文件都存在并且 patch
未配置为 "confirming to POSIX"(例如,通过设置 POSIXLY_CORRECT
环境变量或 --posix
命令行参数,请参阅 "10.12 patch and the POSIX Standard" section of the manual),然后 patch
将从两个名称中选择 "the best" 个名称。 "Name" 这里包括从补丁文件中获取的完整路径(在你的情况下无关紧要)。详情稍后详述:
To determine the best of a nonempty list of file names, patch first takes all the names with the fewest path name components; of those, it then takes all the names with the shortest basename; of those, it then takes all the shortest names; finally, it takes the first remaining name.
"Name component"这里基本上是一个folder/file名称(例如/foo/bar/baz
有三个),"basename"只是文件的名称(baz
).
因此,如果名称为 a
(旧)和 b
(新)并且两个文件都存在,则没有 "best name",因此第一个被修补。
但是,如果名称为 ab
(旧)和 b
(新)并且两个文件都存在,则 b
为 "better",因此该工具会尝试修补它并失败。
我不知道为什么将此行为设置为默认行为。
最近在学习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
?
以上操作是在一台Linux机器上执行的bash
shell.
提前致谢。
这是 GNU 补丁的一个特性。由于您没有指定要修补的文件,因此必须以某种方式从输入中推断出它。基本上,如果您不指定路径(仅基本名称),它假定它必须用更短的名称修补文件,除非传递 --posix
命令行参数或设置 POSIXLY_CORRECT
环境变量:
patch --posix <p
# or
POSIXLY_CORRECT=1 patch <p
在您的情况下,在 a
和 b
之间,第一个被正确选择,但是对于 ab
和 b
,第二个被选为补丁目标(如 patching file b
行所示),但修补失败,因此出现错误。
您也可以通过明确指定补丁目标来修复此行为:
patch ab <p
深入研究文档
GNU 补丁使用以下逻辑(参见 patch's manual, "10.6 Multiple Patches in a File" section):
First, patch takes an ordered list of candidate file names as follows:
- If the header is that of a context diff, patch takes the old and new file names in the header. ...
...
Then patch selects a file name from the candidate list as follows:
- If some of the named files exist, patch selects the first name if conforming to POSIX, and the best name otherwise.
对于“uniform context format”,"old" 文件在 ---
之后提及(在您的情况下为 a
或 ab
),并且 "new" 文件在 +++
之后提到(在你的情况下是 b
)。
如果两个文件都存在并且 patch
未配置为 "confirming to POSIX"(例如,通过设置 POSIXLY_CORRECT
环境变量或 --posix
命令行参数,请参阅 "10.12 patch and the POSIX Standard" section of the manual),然后 patch
将从两个名称中选择 "the best" 个名称。 "Name" 这里包括从补丁文件中获取的完整路径(在你的情况下无关紧要)。详情稍后详述:
To determine the best of a nonempty list of file names, patch first takes all the names with the fewest path name components; of those, it then takes all the names with the shortest basename; of those, it then takes all the shortest names; finally, it takes the first remaining name.
"Name component"这里基本上是一个folder/file名称(例如/foo/bar/baz
有三个),"basename"只是文件的名称(baz
).
因此,如果名称为 a
(旧)和 b
(新)并且两个文件都存在,则没有 "best name",因此第一个被修补。
但是,如果名称为 ab
(旧)和 b
(新)并且两个文件都存在,则 b
为 "better",因此该工具会尝试修补它并失败。
我不知道为什么将此行为设置为默认行为。