Git:将两个存储库合并为一个

时间:2017-02-21 14:33:29

标签: git merge repository git-history-graph

我有一个存储库A(原始存储库)具有完整的提交集。在某个时间点,决定不再使用存储库A,并且从头开始创建一个新的清理存储库B,使用复制+粘贴(而不是将A的内容合并到干净的B中,以便将所有回购A的内容放入干净的B中)保持承诺历史)。我现在想要实现的是在第三个新的清理存储库C中粘贴存储库的提交和历史记录(历史记录非常重要)。最终目标是存储库C应该包含来自源存储库A和B按时间顺序排列,就像存储库B根本没有创建一样好像工作已在存储库A中进行。

所以目前的情况是:

repo A:提交A1>提交A2>提交A3> ...>提交

repo 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

然后解决了冲突。自从历史上以某种方式搞砸了部分工作。

然后尝试了我认为最干净的方法 - 只将回购A合并到回购C然后樱桃挑选回购B的承诺范围从B2 ... Bk。它的工作原理,但樱桃挑选合并提交会减慢合并过程。

我可以复制一个主题,请告诉我,如果我这样做,因为爬过很多线程,并且大部分看到如何通过将它们放在最终仓库中的不同文件夹中而不是这种情况下将两个仓库添加到第三个仓库。如果你可以分享最合适,最正确和最适合git的方法。

非常感谢。

2 个答案:

答案 0 :(得分:3)

如果您的历史非常线性(即A和B各自只有master),那就太糟糕了。如果有很多分支要与之竞争,那么它会更多地参与其中(请参阅下面的更新),但这仍然会提供一个起点:

首先,创建您的C仓库。

git clone /url/for/repo/A C
cd C

现在从B

中获取所有对象
git remote add B /url/for/repo/B
git fetch B

现在我们应该在新的C repo中有这两个历史。将B repo提交重新引导到A历史记录

git rebase --onto master --root B/master

现在您需要更新master参考,并且可能需要清理一下

git branch -f master
git remote remove B

现在A列为您的origin遥控器;你可能要么推动它或将其作为原点删除,这取决于你是否打算让A包含未来的所有内容。

  • 更新:好的,这里有更多信息,以防您的历史不是线性的,因为它可能永远不会......首先让我们关注拓扑,然后在refs上一两个字......

拓扑方面,实际上有三种情况:

1)B

中的分支和合并

因此,假设您在B中有一个分支提示(如果没有,请仔细阅读,然后再查看方案2),这会追溯到单个根,您可以移植到A中的单个分支尖端(如果没有看到下面的情景3,那么回过头来看。)

A1 ---- A2 ---- A3  <--- (master)
   \          /
     A4 -- A5 

---------------------------

B1 ---- B2 ---- B3 <--- (master)
   \          /
     B4 -- B5

A中可能有分支机构,但最终它们已被合并,您不必担心它们。 B中也可能有分支,虽然它们被合并回来但如果rebase试图使它们成为线性的,它们可能会造成麻烦。

首先创建C并导入两个历史记录,如上所述。 (最后我会建议对此程序略有不同,与裁判有关......但让我们回过头来看。)

现在,您有两种选择。最简单的到目前为止是使用filter-branch(但这可能很耗时)。找到要移植到的提交的哈希值(上面标记为A3)并运行

git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master

(其中xxxxxxxxxx是哈希值)。

这确实假设你有sed;它可以在Windows上的git bash环境中使用,或者在几乎任何* nix系统上使用。如果没有,你可以提出一个等效的过滤器。 (它所做的只是说&#34;如果输入是空行,请写出&#39; -p&#39;然后是我移植到的哈希;否则传递输入通过作为我的输出&#34;。)

如果由于某种原因你不能这样做,或者它似乎确实是性能问题,那么你可以尝试计划b:将--preserve-merges选项提供给rebase ..这将在很多时候做你想做的事。 但是有一些重要的警告。

基本上,如果合并引入了手动更改 - 或者因为需要手动冲突解决,或者因为合并是使用--no-commit并且以这种方式引入了手动更改 - 那么合并将不会被复制即使使用此选项,也可通过rebase正确使用。

在发生冲突的情况下,rebase应该停止并允许您重新应用手动解决方案(您可能可以使用原始合并提交的路径检出(checkout ... -- .)。但是在有人使用--no-commit rebase甚至没有意识到任何错误。

如果您知道这将是一个问题,但可以识别一个或两个问题合并,那么一个选项是重新定义每个问题合并的父项,然后手动重做合并,然后继续从那一点开始。

如果您不知道是否/哪里会出现问题,您可以尝试使用rebase,然后运行验证来比较提交。在做rebase之前

git checkout master
git tag old-B-master

然后尝试使用rebase

git rebase --preserve-branches --onto master --root B/master
git tag new-B-master

然后做任何级别的验证对你来说都是安全的。 (显然差异old-B-master反对new-B-master。当我做了这样的事情时,我写了一个脚本来递归遍历提交祖先比较提交提交。偏执狂?也许。)

除非非常,非常顺利,否则您最好还是回到filter-branch方法。

2)B

中的多个分支提示
A1 ---- A2 ---- A3  <--- (master)
   \          /
     A4 -- A5 

---------------------------

B1 ---- B2 <--- (master)
   \
     B4 -- B5 <--- (branch1)

也许你的B回购没有完全合并。这可能会或可能不会使事情变得复杂。如果您正在使用filter-branch,则可以同时处理多个参考号。您可能不能只说--all(因为这可能会捕获已经在A树中的引用,并且可能操作最终会失败),但您可以列出分支提示B树。

git filter-branch --parent-filter 'sed "s/^\$/-p xxxxxxxxxx/"' B/master B/branch1

如果您尝试使用rebase(或者如果您只想使用单个tip ref),则可以创建临时章鱼合并。

git checkout B/master
git checkout -b b-entry-point
git merge -s ours B/branch1 B/branch2 ...

生成的合并提交是临时的。 (您可以在移植后删除b-entry-point分支。)它只提供单个&#34;入口点&#34;进入B提交树。

3)A

中的多个分支提示
A --- Am <-- (master)
  \
    Ab1 <-- (branch1)
---------------------------------------
B1 ---- B2  <-- (master)

B3 ---- B4  <-- (branch1)

那么如果A首先没有完全合并怎么办?创建回购B时,您创建了一个新的提交B === Am吗?我猜是这样的,因为你不得不像多个历史树一样做一些奇怪的事情来包括Ab1的代表,如果你想要的话,你后来有点头疼重新合并...

如果您确实有多棵树要移植,那么我认为您只需要分别处理每棵树。没那么多可以改善这一点。

如果您有多个嫁接点,但自M重新合并以来,那么您可能不得不嫁给M父母中的每一个单独,然后重新合并为M',然后继续将M的子项移植到M'

好的,但裁判怎么办?

现在上面的情况很好,但是你可能有一个refs(在A和/或B中)你关心的不只是master分支。

这是filter-branch更好处理的事情之一;事实上,如果我没记错的话,rebase不会重写任何引用,除了分支提示它的变基(如果是远程分支引用,甚至不是)。

特别是如果使用filter-branch,您可能会发现通过克隆B并导入A形式的远程引用来创建C很方便(而不是相反,如上所示),这样您就可以{{1}为你重写本地引用。

即便如此,您可能会发现需要重新定位的远程引用组合。可以根据需要使用带有filter-branch选项的分支和标记命令,将本地引用与最适合您的最终状态的远程引用对齐。

答案 1 :(得分:0)

您可以通过一次提交来挑选一系列提交。

$ git merge A/master          # merge A/master into C/master
$ git cherry-pick B2^..Bk     # cherry-pick all B2 to Bk commits

或者,你可以改变。转到C repo master分支。您可以签出新的banch(比如rebase)然后合并B / master并将其重新绑定到A/master

$ git checkout -b rebase origin/master # checkout a new branch with clean C-remote/master history 

$ git merge B/master            # merge B/master into C/master
$ git rebase A/master           # rebase C/master onto A/master 

如果所有历史记录都正常,则将master替换为rebase分支。

$ git checkout rebase          # make sure current branch is rebase 
$ git branch -D master         # delete local master branch

$ git checkout -b master       # create & checkout a new 'master' branch      
$ git push -f orgin master     # update C-remote/master