跨具有不同提交历史的存储库应用补丁
Applying patches across repositories with different commit histories
假设我有两个相似但提交历史不同的存储库 A 和 B。
一个示例可能是两个 Python Flask 应用程序,或者 Rails 上的两个 Ruby 应用程序共享 lot 相似文件但并不完全相同。
我对存储库 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".
如图所示,结果有几个errors/conflicts。没关系,我会尝试查看冲突是什么并修复它们,就像我在常规合并中所做的那样:
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 甚至不将更改应用为 diff,因此没有 diffed/modified 文件,我什至看不到冲突是什么或尝试修复它们
有没有办法强制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"
....
没有冲突。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-patch
和 git am
, 你可以 运行:
git cherry-pick <hash>
对于有问题的提交。 Git 现在将进行完整的三向合并,使用来自 repo A 的两个提交作为合并基础和它们的更改:
git diff <parent of hash> <hash> # what they changed
并且您当前的提交作为您的更改:
git diff <parent of hash> HEAD # what we changed
并将它们结合起来,并酌情考虑冲突。这里的第二个 diff 命令是 "what we changed" 或合并的 --ours
部分;这里的第一个 diff 命令是 "what they changed",因此是合并的 --theirs
部分。
请注意,git am -3
或设置 am.threeWay
只是一种 尝试 完全合并的方法,希望:
index a1539a7ce682f10a5aff3d1fee4e86530e058c89..98f88a28d3227c436ecf1765f75b7f4e8e336834 100755
给出一个出现在您的存储库中的哈希 ID。做 git fetch
实际上 得到 你有那个散列 ID 的 blob,如果你还没有它,那么就不需要单纯的希望和愿望。
注意,你看:
hint: Use 'git am --show-current-patch' to see the failed patch
但是,正如 Git 2.25(2020 年第一季度)中所述,这并不完全正确。
见commit 80736d7 (23 Oct 2019) by Junio C Hamano (gitster
)。
(由 Junio C Hamano -- gitster
-- in commit f1e2666 合并,2019 年 11 月 10 日)
doc
: am --show-current-patch
gives an entire e-mail message
The existing wording gives an impression that it only gives the contents of the $GIT_DIR/rebase-apply/patch
file, i.e. the patch proper, but the option actually emits the entire e-mail message being processed (iow, one of the output files from "git mailsplit").
因此您可能必须从该电子邮件中提取导致冲突的确切部分。
假设我有两个相似但提交历史不同的存储库 A 和 B。
一个示例可能是两个 Python Flask 应用程序,或者 Rails 上的两个 Ruby 应用程序共享 lot 相似文件但并不完全相同。
我对存储库 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".
如图所示,结果有几个errors/conflicts。没关系,我会尝试查看冲突是什么并修复它们,就像我在常规合并中所做的那样:
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 甚至不将更改应用为 diff,因此没有 diffed/modified 文件,我什至看不到冲突是什么或尝试修复它们
有没有办法强制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"
....
没有冲突。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-patch
和git am
, 你可以 运行:git cherry-pick <hash>
对于有问题的提交。 Git 现在将进行完整的三向合并,使用来自 repo A 的两个提交作为合并基础和它们的更改:
git diff <parent of hash> <hash> # what they changed
并且您当前的提交作为您的更改:
git diff <parent of hash> HEAD # what we changed
并将它们结合起来,并酌情考虑冲突。这里的第二个 diff 命令是 "what we changed" 或合并的
--ours
部分;这里的第一个 diff 命令是 "what they changed",因此是合并的--theirs
部分。
请注意,git am -3
或设置 am.threeWay
只是一种 尝试 完全合并的方法,希望:
index a1539a7ce682f10a5aff3d1fee4e86530e058c89..98f88a28d3227c436ecf1765f75b7f4e8e336834 100755
给出一个出现在您的存储库中的哈希 ID。做 git fetch
实际上 得到 你有那个散列 ID 的 blob,如果你还没有它,那么就不需要单纯的希望和愿望。
注意,你看:
hint: Use 'git am --show-current-patch' to see the failed patch
但是,正如 Git 2.25(2020 年第一季度)中所述,这并不完全正确。
见commit 80736d7 (23 Oct 2019) by Junio C Hamano (gitster
)。
(由 Junio C Hamano -- gitster
-- in commit f1e2666 合并,2019 年 11 月 10 日)
doc
:am --show-current-patch
gives an entire e-mail messageThe existing wording gives an impression that it only gives the contents of the
$GIT_DIR/rebase-apply/patch
file, i.e. the patch proper, but the option actually emits the entire e-mail message being processed (iow, one of the output files from "git mailsplit").
因此您可能必须从该电子邮件中提取导致冲突的确切部分。