【问题标题】:Applying patches across repositories with different commit histories跨具有不同提交历史的存储库应用补丁
【发布时间】:2019-09-04 21:12:55
【问题描述】:

假设我有两个相似的存储库 A 和 B,但提交历史不同。

一个例子可能是两个 Python Flask 应用程序,或者两个 Ruby on Rails 应用程序,它们共享 很多 个相似文件,但并不完全相同。

我对存储库 A 进行了更改,我想也将其应用于存储库 B。将其应用于 B 可能会有一些冲突,但没关系,我想看看它们是什么并解决他们。

我尝试了以下从 repo A 生成补丁

> cd ~/git/repoA/
> git format-patch HEAD~
0001-My-Example-Commit.patch
> mv 0001-My-Example-Commit.patch ~/git/repoB

然后我尝试将补丁应用到 repo B

> cd ~/git/repoB
> git am 0001-My-Example-Commit.patch
Applying: My Example Commit
error: patch failed: Gemfile:20
error: Gemfile: patch does not apply
error: patch failed: Gemfile.lock:125
error: Gemfile.lock: patch does not apply
error: patch failed: app/assets/stylesheets/application.scss:29
error: app/assets/stylesheets/application.scss: patch does not apply
....
....
error: patch failed: spec/views/application/_mobile_navigation.html.erb_spec.rb:44
error: spec/views/application/_mobile_navigation.html.erb_spec.rb: patch does not apply
Patch failed at 0001 Using Devise for authentication
hint: Use 'git am --show-current-patch' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

如图所示,它会导致几个错误/冲突。没关系,我将尝试查看冲突是什么并修复它们,就像我在常规合并中所做的那样:

git status
On branch test-git-apply
You are in the middle of an am session.
  (fix conflicts and then run "git am --continue")
  (use "git am --skip" to skip this patch)
  (use "git am --abort" to restore the original branch)

nothing to commit, working tree clean

Git 甚至没有将更改应用为差异,因此没有差异/修改文件,我什至看不到冲突是什么或尝试修复它们

有没有办法强制 git 显示冲突?

谢谢!

编辑

我知道--directory 选项存在,但我认为它不适用于这里,因为我的补丁文件已经相对于同一个根目录生成。例如

diff --git a/Gemfile b/Gemfile
index c970c34..ffc812d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -20,12 +20,17 @@ gem "webpacker", "~> 3.5", ">= 3.5.5"
 #
 gem "bcrypt", "~> 3.1", ">= 3.1.10"
 gem "cancancan", "~> 3.0"
....

【问题讨论】:

    标签: git diff patch


    【解决方案1】:

    没有冲突1补丁根本无法应用。例如,补丁可能会说:第 87 行读取“foo”。在随后的第 88 行,将“bar”更改为“baz”。第 89 行读作“quux”。 但是您的第 87 行到第 89 行没有读作“foo”、“bar”和“quux”,并且附近也没有包含该序列的行。程序员自己来决定如何处理补丁。

    hint: Use 'git am --show-current-patch' to see the failed patch
    

    所以,使用它。阅读补丁。检查您的文件并决定如何处理此补丁。


    1这里不能有冲突,因为补丁本身并不代表对同一行的两组不同更改。当您有 两个 差异时会发生冲突:一个说在第 88 行将 bar 更改为 baz(这是可能的),另一个说在第 88 行将 bar 更改为 rab(这也是可能的) .这两个更改冲突。

    补丁只提供一个变化。它要么适用,要么不适用。


    修补的替代方法

    这里有两个替代选项。从低效到高效的订单:

    • 确保来自 repo A 的补丁是由 git format-patch --full-index 生成的。使用git am时,使用git am -3(或将am.threeWay配置为true)。这样,A 上的差异将包括文件父版本的完整 blob 哈希。如果该 blob(文件的父版本)在 repo B 中可用,Git 将能够使用 blob-hash-and-patch 来重建真正合并所需的三个输入:一个通用的基本版本和两个修改的 -从基础版本。

    • 或者,在 repo B 中使用git remote add 添加对 repo A 的直接访问权限。选择一个远程名称,以便稍后提醒您这是什么(或之后立即删除远程)。然后使用这个远程名称运行 git fetch 以将来自 repo A 的提交带入 repo B,以便所有提交在 B 中本地可用。现在,您可以运行:而不是 git format-patchgit am

      git cherry-pick <hash>
      

      对于有问题的提交。 Git 现在将使用来自 repo A 的两个提交作为 merge-base 和 their-change 进行完整的三向合并:

      git diff <parent of hash> <hash>   # what they changed
      

      以及您当前的提交作为您的更改:

      git diff <parent of hash> HEAD     # what we changed
      

      并将它们与适当的冲突结合起来。这里的第二个差异命令是“我们改变了什么”或合并的--ours 部分;这里的第一个 diff 命令是“他们改变了什么”,因此是合并的 --theirs 部分。

    请注意,git am -3 或设置 am.threeWay 只是一种尝试完全合并的方法,希望:

    index a1539a7ce682f10a5aff3d1fee4e86530e058c89..98f88a28d3227c436ecf1765f75b7f4e8e336834 100755
    

    给出一个出现在您的存储库中的哈希 ID。执行 git fetch 实际上得到你有那个哈希 ID 的 blob,如果你还没有它,那么就不需要单纯的希望和愿望。

    【讨论】:

    • 我明白你在说什么,但在你的例子中我觉得很奇怪 git 仍然无法尝试对第 87-89 行进行这些更改,而只是强制与已经存在的内容发生冲突(使用&gt;&gt;&gt;&gt;&gt;&gt;&lt;&lt;&lt;&lt;&lt;&lt;)。然后我,作者,可以手动接受一个或另一个。不过感谢您的见解,谢谢!
    • 问题在于,要构造一个适当的冲突,Git 必须找到一个 第三个 文件,即你的两个待修补文件都有更改的共同祖先,并且您正在应用的补丁程序发生了变化。也就是说,例如,您正在应用的补丁说“对第 87-89 行进行此更改” - 所以 Git 需要找到将这些行作为第 87-89 行的文件,以便它可以查找 your 文件中的哪些行对应。由于 index 行,Git 可以对一些 Git 补丁执行此操作:index 行包含原始文件的哈希 ID。
    • 如果你的补丁有 index 行,并且你有那个原始文件(作为存储在你的存储库中的 blob),并且你使用 git apply --3waygit am --3way,Git 将尝试这样做。如果您使用git format-patch 生成补丁,它们将具有索引行(并且您可以使用--full-index 来防止可能使它们模棱两可的缩写),但这些可能与您的存储库中的实际 blob 不对应。
    • 请注意,如果您 直接 访问 repo A 并希望从中挑选一些提交到不相关的 repo B,您可以git fetch A 的提交进入 repo B,然后在 repo B 中运行git cherry-pick。此策略进行三向合并,并为您提供所需的冲突行。
    • 非常感谢更新和详细的大纲。这种解释很有意义,添加另一个 remote 并执行 git fetch 以合并更改的方法正是我所需要的。
    【解决方案2】:

    注意,你看:

    hint: Use 'git am --show-current-patch' to see the failed patch
    

    但是,正如 Git 2.25(2020 年第一季度)中所述,这并不完全正确。

    参见Junio C Hamano (gitster)commit 80736d7(2019 年 10 月 23 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit f1e2666,2019 年 11 月 10 日)

    doc: am --show-current-patch 提供完整的电子邮件信息

    现有的措辞给人的印象是它只给出了$GIT_DIR/rebase-apply/patch 文件的内容,即正确的补丁,但该选项实际上发出了正在处理的整个电子邮件消息(iow,输出之一来自“git mailsplit”的文件)

    因此,您可能必须从该电子邮件中提取导致冲突的确切部分。

    【讨论】:

      猜你喜欢
      • 2018-03-18
      • 1970-01-01
      • 2018-02-07
      • 1970-01-01
      • 2021-03-14
      • 1970-01-01
      • 2018-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多