如何将Git补丁应用于具有不同名称和路径的文件?

143

我有两个代码仓库。在一个仓库中,我修改了文件./hello.test,然后提交了更改,并使用git format-patch -1 HEAD从该提交创建了一个补丁文件。现在,我有第二个仓库,其中包含一个与hello.test具有相同内容但以不同名称放置在另一个不同目录下的文件:./blue/red/hi.test。我应该如何将上述补丁应用于hi.test文件?我尝试使用git am --directory='blue/red' < patch_file,但是它会抱怨文件名不相同(我以为 Git 并不关心这个问题?)。我知道我可以编辑补丁文件以应用于特定文件,但我正在寻找命令解决方案。


相关问题:https://dev59.com/T3A75IYBdhLWcg3wT3L8 - 0 _
Meld对于这种事情绝对是非常棒的选择... - Niko
5个回答

144
你可以使用git diff创建补丁,然后使用patch工具应用它,该工具允许你指定要应用差异的文件。
例如:
cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file

8
好的,没问题。但是,请问有没有办法使用Git命令来实现这一点,以保持提交数据(日期和时间、提交作者、提交信息)不变? - mart1n
2
有可能你可以通过使用 amapply 来解决问题,但我找不到相关内容。如果你发现自己需要频繁地重复修改某些内容,那么使用子模块或者利用你所选择的编程语言提供的代码共享机制(例如在 Ruby 中,你可以将重复的代码提取为 gem)可能是更好的解决方案。 - georgebrock
1
这实际上与文档相关(源文件是XML)。子模块并不是一个真正的选择,因为我必须在我们现有的基础设施中为它们提出充分的理由。 - mart1n
2
在Windows上,可以使用Git for Windows中包含的git-bashgit-bash包括几个Unix命令,包括patch - mihca
patch 命令中的 -p1 标志是否必需?似乎没有加这个标志也可以正常工作。 - erwaman

89

有一个简单的解决方案,不需要手动编辑补丁,也不需要外部脚本。

在第一个存储库中(如果你只想选择一个提交,则可以添加-1选项),这可能还会导出一系列提交:

git format-patch --relative <committish> --stdout > ~/patch

在第二个代码库中:

git am --directory blue/red/ ~/patch

git format-patch中不使用--relative选项,另一种解决方案是使用git am中的-p<n>选项来从补丁路径中剥离n个目录,正如类似问题的答案所提到的。

也可以运行git format-patch --relative <committish>,不带--stdout选项,它将生成一组.patch文件。这些文件可以直接用git amgit am --directory blue/red/ path/to/*.patch进行处理。


13
这仍然依赖于文件名相同的事实,对吗? - mart1n
5
需要注意的是,--directory选项似乎要求您相对于仓库根目录指定完整路径的目录,例如--directory=./,而在进入仓库子目录后进行切换目录操作是行不通的。 - Reid
2
使用 --3way 选项有助于解决 does not exist in index 问题: git am --3way --directory (相对路径) (补丁) - Brent Bradburn
在这两个命令中都使用“-k”键,以便不会剥离提交消息的第一行。 - ruvim
1
使用 --3way 不仅有助于处理“不存在于索引中”的错误(正如 @nobar 指出的那样),而且还可以让您清晰地处理合并冲突。与其让有冲突的文件不受影响,不如添加一个冲突块,然后再进行解决。 - Daniel Wolf
显示剩余3条评论

9

在@georgebrock的答案基础上,我提供了一个解决方案:

首先,像往常一样创建补丁文件(例如git format-patch commitA..commitB)。

然后,确保目标仓库是干净的(没有更改或未跟踪的文件),并像这样应用补丁:

cd second-repo
git am ~/00*.patch

对于每个补丁文件,您将会得到一个错误提示,如“错误:XYZ不存在于索引中”。您现在可以手动应用此补丁文件:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

对于每个补丁文件,您需要执行以下三个步骤。

这将保留原始提交消息等内容,而无需使用任何特殊的 git format-patch 命令或编辑补丁文件。


1
好的回答,我认为这是任何“非标准”补丁操作的最佳基础。我分三步完成。**(1) 提交到文本** - git format-patch -1 commitA --stdout > thing.diff ; (2) 编辑补丁文件 直到它能够满足我的需求; (3) 文本提交 git am --3way thing.diff,它的优点是您可以接受应用干净的补丁部分,并使用git的标准冲突解决过程来处理不适用的部分。 - We Are All Monica
使用当前的 git 版本,可以通过 git am --show-current-patch=diff 显示当前的 diff,因此 patch 步骤可以更改为 git am --show-current-patch=diff | patch --directory blue/red。这样不用记住每个提交中要指定哪个 patch 文件,节省了时间。 - Emil Styrke

2
我理解在你的情况下,这两个文件是完全相同的,因此补丁很可能会成功。
然而,如果您想将补丁应用于类似但不完全相同的文件,或者想进行交互式修补,则需要使用三方合并工具。
比如说,如果您修改了文件A,让我们将A~1表示为前一个版本,并且您想将A~1到A之间的差异应用于文件B。
打开一个三方合并工具,例如Beyond Compare,左侧面板的路径是A,中间面板是公共祖先,所以路径是A~1,右侧面板的路径是B。然后,下面的面板显示了将A~1到A之间的差异应用于文件B的结果。
以下图示说明了这个概念。

enter image description here


0

提醒一下:我最近在尝试从Github下载补丁并将其应用于本地文件(该文件是新位置中的“覆盖”)时遇到了问题。

git am无法应用补丁,因为文件“不在索引中”或“脏”。但是,我发现简单的patch命令可以应用补丁。它会提示我输入要打补丁的文件名。

总之,任务完成了...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接