Iva*_*van 4 git merge repository git-history-graph
我有一个存储库 A(原始存储库),具有完整的提交集。在某个时间点,决定不再使用存储库 A,并从头开始创建一个新的干净存储库 B,使用复制 + 粘贴将所有存储库 A 的内容放入其中(而不是通过将 A 的内容合并到干净的 B 中,以便保留提交历史记录)。我现在想要实现的是将存储库的提交和历史记录(粘合历史记录非常重要)粘合到第三个新的干净存储库 C 中。最终目标是存储库 C 应该包含源存储库 A 和B 按时间顺序排列,就好像存储库 B 根本没有创建,例如,好像工作已经在存储库 A 中进行。
所以目前的情况是:
仓库 A:提交 A1 > 提交 A2 > 提交 A3 > ... > 提交 An
仓库 B:提交 B1 (= A1 + A2 + A3 + ... + An) > 提交 B2 (= 提交 An+1) > ... > 提交 Bk
尝试了以下方法:
git remote add -f A <repo_A_url>
git merge A/master
git remote add -f B <repo_B_url>
git merge B/master
Run Code Online (Sandbox Code Playgroud)
然后解决了矛盾。部分有效,因为历史不知何故被搞乱了。
然后尝试了我认为最干净的方法 - 仅将存储库 A 合并到存储库 C 中,然后挑选存储库 B 的提交范围从 B2 ... Bk。它有效,但挑选合并提交会减慢合并过程。
我可能会复制一个主题,请告诉我是否这样做,因为爬行了许多线程,并且主要看到如何通过将两个存储库放置在最终存储库中的不同文件夹中来将它们添加到第三个存储库,但情况并非如此。如果您可以分享最合适、语义正确且对 git 友好的方法。
十分感谢。
如果您的历史记录非常线性(即仅master在 A 和 B 中),那么这不会太糟糕。如果有很多分支需要处理,它将涉及更多(请参阅下面的更新),但这仍然提供了一个起点:
首先,创建您的 C 存储库。
git clone /url/for/repo/A C
cd C
Run Code Online (Sandbox Code Playgroud)
现在从 B 获取所有对象
git remote add B /url/for/repo/B
git fetch B
Run Code Online (Sandbox Code Playgroud)
现在我们应该在新的 C 存储库中拥有这两个历史记录。将 B 存储库提交重新设置到 A 历史记录上
git rebase --onto master --root B/master
Run Code Online (Sandbox Code Playgroud)
现在您需要更新master参考并且可能需要清理一下
git branch -f master
git remote remove B
Run Code Online (Sandbox Code Playgroud)
现在 A 被列为您的origin遥控器;您可能想要推送它或将其作为原点删除,具体取决于您是否打算让 A 包含未来的所有内容。
从拓扑角度来看,实际上存在三种总体场景:
1)B中的分支和合并
因此,假设您在 B 中有一个分支尖端(如果没有,请通读此内容,然后查看场景 2),它可以追溯到单个根,您可以将其移植到 A 中的单个分支尖端(如果没有,请参阅下面的场景 3)回到这个)。
A1 ---- A2 ---- A3 <--- (master)
\ /
A4 -- A5
---------------------------
B1 ---- B2 ---- B3 <--- (master)
\ /
B4 -- B5
Run Code Online (Sandbox Code Playgroud)
A 中可能有分支,但最终它们已合并回来,您不必担心它们。B 中也可能有分支,尽管它们被合并回来,但如果 rebase 试图使它们线性化,它们可能会造成麻烦。
首先创建 C 并导入两个历史记录,就像我们上面所做的那样。(最后,我会建议对这个过程稍作修改,与参考文献有关......但让我们回到这一点。)
现在,您有两个选择。到目前为止最简单的是使用filter-branch(但可能很耗时)。找到您将要移植的提交的哈希值(上面标记为 A3)并运行
git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master
Run Code Online (Sandbox Code Playgroud)
(其中 xxxxxxxxxx 是哈希值)。
这确实假设你有 sed;它可以在 Windows 上的 git bash 环境中使用,或者在几乎任何 *nix 系统上使用。如果没有,您可以想出一个等效的过滤器。(它所做的只是说“如果输入是空行,则写出'-p',后跟我要嫁接的散列;否则将输入作为我的输出传递”。)
如果由于某种原因你不能这样做,或者如果它确实是一个性能问题,那么你可以尝试计划 b:将选项提供给--preserve-merges... rebase,这在很多时候都会满足你的要求。 但有一些重要的警告。
基本上,如果合并引入了手动更改 - 要么是因为需要手动解决冲突,要么是因为合并是通过--no-commit这种方式完成的,并且以这种方式引入了手动更改 - 那么即使使用此选项,合并也不会通过 rebase 正确再现。
在发生冲突的情况下,变基应该停止并让您重新应用手动解决方案(您可以通过checkout ... -- .原始合并提交的路径签出()来完成此操作)。但在有人使用--no-commit变基的情况下甚至不会意识到有什么不对劲。
如果您知道这将是一个问题,但可以识别一两个问题合并,那么一个选择是对每个问题合并的父级进行变基,然后手动重做合并,然后从该点继续。
如果您不知道是否/哪里会出现问题,您可以尝试变基,然后运行验证来比较提交。在进行变基之前
git checkout master
git tag old-B-master
Run Code Online (Sandbox Code Playgroud)
然后尝试变基
git rebase --preserve-branches --onto master --root B/master
git tag new-B-master
Run Code Online (Sandbox Code Playgroud)
然后进行您认为安全的任何级别的验证。(显然old-B-master与new-B-master最低限度的。当我做这样的事情时,我编写了一个脚本来递归地遍历提交祖先,逐个提交进行比较。偏执?也许吧。)
除非这一切进展得非常非常顺利,否则你最好还是回到原来的filter-branch方法。
2)B中的多个分支提示
A1 ---- A2 ---- A3 <--- (master)
\ /
A4 -- A5
---------------------------
B1 ---- B2 <--- (master)
\
B4 -- B5 <--- (branch1)
Run Code Online (Sandbox Code Playgroud)
也许您的 B 存储库未完全合并。这可能会也可能不会使事情变得复杂。如果您使用filter-branch,它可以同时处理许多参考。您可能不能只是说--all(因为这可能会捕获树中已有的引用A,并且操作可能最终会失败),但您可以列出树中的分支提示B。
git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master B/branch1
Run Code Online (Sandbox Code Playgroud)
如果您尝试使用变基(或者您只想使用单个提示引用),您可以创建一个临时的章鱼合并。
git checkout B/master
git checkout -b b-entry-point
git merge -s ours B/branch1 B/branch2 ...
Run Code Online (Sandbox Code Playgroud)
生成的合并提交是临时的。(您可以在移植后删除 b-entry-point 分支。)它只是为提交树提供了一个“入口点” B。
3)A中的多个分支提示
A --- Am <-- (master)
\
Ab1 <-- (branch1)
---------------------------------------
B1 ---- B2 <-- (master)
B3 ---- B4 <-- (branch1)
Run Code Online (Sandbox Code Playgroud)
那么如果 A 一开始就没有完全合并怎么办?当您创建存储库 B 时,您是否只创建了一个新提交B === Am?我猜是这样,因为你必须做一些奇怪的事情,比如多个历史树来包含Ab1,并且如果你想重新合并,你以后会有点头痛......
如果您确实有多棵树需要嫁接,那么我认为您只需单独处理每一棵树即可。没有太多的事情可以改善这一点。
如果您有多个嫁接点,但它们已在 重新合并M,那么您可能必须分别嫁接每个M父级,然后将合并重新创建为M',然后继续将子级嫁接M到M'。
好吧,但是裁判呢?
现在上面的内容很好,但是除了分支之外,您可能还有您关心的参考文献(在 A 和/或 B 中)master。
这是处理得更好的事情之一filter-branch;事实上,如果我没记错的话,rebase不会重写任何引用,除了其变基的分支提示(如果它是远程分支引用,则甚至不会重写)。
特别是如果使用filter-branch,您可能会发现通过克隆 B 并导入来自 A 的远程引用(而不是相反,如上所示)来创建 C 很方便,这样您就可以为filter-branch您重写本地引用。
即便如此,您可能会发现需要重新定位的远程引用的某些组合。-f可以根据需要使用带有该选项的分支和标记命令,将本地引用与最适合您的最终状态的远程引用对齐。
| 归档时间: |
|
| 查看次数: |
1435 次 |
| 最近记录: |