我们的 Git 源存储库有多个模块。每个模块驻留在不同的目录中。每个模块都有一个专门的开发人员.
例如
src
|--module A --
|--module B --
|--module C --
现在我们有 2 个分支。假设工作分支是"开发",其他分支是"其他"(最不活跃的分支)。我们想将"其他"合并为"发展"。是否可以明智地执行此目录?
例如,我现在只想合并"模块 B"。然后对合并的代码进行新的更改并提交"开发"。然后一段时间后(比如 2-3 个月),不同的开发人员需要将模块 A 和 C 从"其他"合并到"开发"。
即简单地说,是否可以通过子目录将 Git 合并分解为多个步骤?
请注意,"其他"分支有大量提交。因此,识别导致"模块 B"更改的所有提交并逐个挑选是不可行的。
到目前为止我能想到的答案是
- 启动另一个 -> 开发合并
- 仅在"模块 B"中解决合并冲突。将"模块 B"目录备份到其他位置。
- 丢弃 #1 开始合并。
- 在开发上创建一个新分支 ->删除"模块 B"目录 ->将上面 #2 支持的"模块 B"复制到此分支 ->提交 ->合并以开发分支 ->继续我在"模块 B"中的新更改
缺点是,
将来完成"其他->开发"合并时,"模块B"仍然会有合并冲突。这些冲突将不得不被忽略(即必须在 git gui 中使用"使用本地文件"选项)。然后,在"模块 B"的"其他"分支中所做的任何新更改都必须手动挑选/复制。
简短的回答是否定的。 但这个答案是(a)不令人满意,(b)遗漏了你能做些什么来实现你的最终目标。
我认为这最好通过制作一些图纸来说明。 让我们简化你的问题,以Git看待它们的方式展示事物,逐个提交。 我们从一些提交序列结束时的一些常见提交开始:
...--F--G--H <-- main
我们现在创建一些分支(暂时完全停止使用名称main
),在这些侧分支上,我们进行一些提交:
I--...--J <-- branch1
/
...--G--H
K--...--L <-- branch2
这里可能有很多关于两个非main
分支的提交,即使我们只显示两个。
当你合并这些时,Git实现合并结果的方式是这样的:
Git 提取H
的快照,- 然后提取
J
的快照,并将两者进行比较以查看branch1
上发生了什么变化。
Git 提取 - 然后提取
L
的快照,并将两者进行比较以查看branch2
上发生了什么变化。
H
的快照,在您的情况下,这是...很多变化。:-) 它太多了,你不想全部处理它们。但是,Git将坚持处理所有这些问题。 如果你遇到冲突,git merge
会在工作中间停下来并退出并迫使你解决所有冲突。 不管你怎么做,一旦你完成了,Git 就会相信生成的文件是正确的合并结果。
要在解决所有问题后完成合并,请运行git merge --continue
或git commit
。 这使得一个新的合并提交M
:
I--...--J
/
...--G--H M
/
K--...--L
提交M
,像每次提交一样,具有所有文件的完整快照。根据定义,这是此合并的正确结果。这就是为什么简短的回答是否定的。
现在开始诀窍
但是——这就是为什么简单的"否"答案是不完整的——你会注意到我在上面省略了所有分支名称。 假设,在开始合并之前,我们创建一个新的分支名称,并将其作为我们当前的分支名称,如下所示:
I--...--J <-- branch1, merge-A (HEAD)
/
...--G--H
K--...--L <-- branch2
当我们完成合并时,我们将得到这个:
I--...--J <-- branch1
/
...--G--H M <-- merge-A (HEAD)
/
K--...--L <-- branch2
请注意,没有更改任何现有提交,只有名称merge-A
已更改,以指向新的合并提交M
。
这意味着您现在可以使用所有三个分支继续工作,无论您喜欢什么程度。 然后,在第二天(或周或月),您可以返回到branch1
并创建另一个新名称,例如merge-B
,并将其设置为当前分支:
I--...--J <-- branch1, merge-B (HEAD)
/
...--G--H
K--...--L <-- branch2
(这假设branch1
和branch2
没有移动。 如果它们已经移动,那么找到提交J
和L
的哈希ID并在此处使用它们可能是明智的。 请注意,它们可以作为合并提交的两个父级找到M
,它仍然存在- 我只是选择暂时停止绘制它。
我们现在运行相同类型的git merge
,进行提交MB
或M2
,其中我们实际上只解析模块 B 文件。 就Git而言,这是此合并的正确结果,即模块 A 和模块 C 文件都正确合并(无论我们在MB
快照中卡在它们中的内容)。我们必须记住模块 A 和 C 是错误合并的,就像我们制作M
时一样,我们必须记住模块 B 和 C 被错误合并。 但无论如何,我们都会得到这个结果:
I--...--J <-- branch1
/
...--G--H MB <-- merge-B (HEAD)
/
K--...--L <-- branch2
MB
是一个新的提交,与M
分开(如果我们想开始这样称呼它,则MA
)。
稍后,我们可以重复此操作以进行MC
合并。
如果我们试图将它们全部吸引进来,它会变得非常混乱:
...--J____
|
| MA
| /
| X
/
X MB
/ /
| X
| /
| / MC
|/___/
...--K
(显然,这使用图形绘制工具会更好)。
您现在可以自由地从三个MA
、MB
和MC
提交中提取特定文件,并将它们组合起来生成一些新的合并提交。 具体如何做到这一点取决于你自己。 您在记录此内容的新提交方面所做的确切内容也取决于您。 我自己可能会只使用父J
和L
进行新的合并提交 - 即第四次合并,其方式与这三个合并中的每一个相同,但通过提取三个合并结果来构建其快照。
请注意,这不是"免费">
如果在合并三个单独的MA
、MB
和MC
后继续开发,您仍然会坚持在MA
后、MB
后和MC
后提交中进行合并。 这需要再次运行git merge
并获得更多合并冲突。 在MA
、MB
和MC
中记录非常明确的合并消息可能非常重要,甚至可能在随后的一些提交中,以提醒您自己和/或其他人将来仍有工作要做。 然后,当您对合并所有三个模块进行"正确"合并时,您需要执行额外的合并工作,也许将 post-MA
、post-MB
和 post-MC
提交的分支提示提交记录为这些不同合并的第二次提交。
最终的图表将非常纠结和混乱。 这在这里是不可避免的。 使用章鱼合并,就最终结果而言,章鱼的许多腿中的每一条腿都可能具有相同的"地位"或"优先级",但这也是一种令人困惑的工作方式。 Git 的内置-s octopus
策略(这是 Git 通常进行章鱼合并的方式)是否可以处理这个问题是值得怀疑的;如果不能,您可以使用git commit-tree
手动进行这些合并提交,或者仅使用常规的一次合并两次来构建最终结果。
这种章鱼合并的好处(如果有的话)不是很大。 这是一张手工ASCII绘图,在三个M
合并的基础上构建:
...--J____
|
| MA
| /
| X
/
X MB--MM <-- main
/ / /
| X /
| / /
| / MC
|/___/
...--K
但请注意,自从三个单独的M
合并以来,没有发生任何工作。 在完成了MM
(所有模块的主合并)之后,现在是时候处理这项工作了。