将多个git repos重写为一个

时间:2017-06-02 10:41:04

标签: git

我有三个不相关的存储库,我想通过将每个存储库移动到不同的子目录来重写到单个存储库中。我正在做这样的事情:

some_url=git@github.com/blah/" repos='a b c d e f g' git init git commit --allow-empty -m 'Unified repo base commit' for repo in $repos; do git fetch $some_url/$repo git checkout -b rewrite-$repo # prefix commit messages with original repo name git filter-branch -f --msg-filter "/bin/echo -n '${repo}: ' && cat" # the rewrite just does `mv * $repo/` for all files in each $repo git filter-branch -f --tree-filter "mkdir -p ${repo}; find -mindepth 1 -maxdepth 1 ! -name ${repo} | -exec -- mv '{}' ${repo} ';'" git checkout master # rebase the rewritten branch in git rebase rewrite-$repo done

但是,我在rebase上遇到了合并冲突。据我所知,这些冲突不在不同的回购中,而是在为一个回购重绕历史时。它几乎看起来没有以正确的顺序应用补丁(例如,在冲突点,文件处于与提交之前的原始仓库中的状态不同的状态)。

我无法理解这是怎么发生的 - 天真地,因为所有的回购都被重写为使用不同的子目录,似乎没有重叠的可能性,并且对rebase的重绕不应该改变任何东西。我尝试了一个类似的解决方案,使用git format-patch和git am导入,但同样的事情发生了。尝试使用cpan的Git::FastExport::Stitch时再次相同。

我最终决定重复正常合并所有回购(令人恼火的是,如果没有共同的祖先,似乎不可能进行章鱼合并),但我仍然很想知道发生了什么以及如何正确地做到这一点。

1 个答案:

答案 0 :(得分:1)

更新 - 添加了一些关于为什么通过合并进行变基数会出现问题的问题,以及基于评论的其他一些说明

我的猜测是个人回购'历史不是严格线性的(即有分支和合并点)?如果是这样的话,rebase可能会造成混乱(见下文)。重新整理任何非平凡的历史往往是一个坏主意。

如果你真的想要将一个(看似随意的)线性顺序强加给回购项目的项目,你可能需要做更多的filter-branch工作。给定

A --- B --- C <--(repo1)

D --- E --- F <--(repo2)
 \        /
  G ---- H

你可以filter-branch repo2使用tree-filter添加C的子目录加parent-filter来移植DC。重复每个回购。

如果你想要章鱼合并,请将repo分支在空的&#34;初始提交&#34; (您将使用commit --allow-empty创建)而不是独立生根每个。 (但是,如果你需要保留提交ID,那当然不是一个选项;在这种情况下,获得章鱼合并最多是困难的。)

回到为什么rebase可能成为一个问题:基本上有两个问题。

首先,它最多移动一个分支头。如果您有其他参考(标签,多个分支),您必须单独移动它们。 (我说&#34;手动&#34;,但是如果有很多这样的话你可以找到一种方法来编写脚本...但是显而易见的解决方案 - 运行多个rebase操作 - 没有做你想要的事。)

更重要的是,它并没有很好地处理合并。默认情况下,它想要产生一个线性历史记录,它似乎无法跟踪它在回到&#34;另一方面时所做的事情。合并。

您可以通过指定--preserve-merges来修复大约80%的问题,通过合并进行变基,这会尝试修剪和删除子树&#34;因为它是&#34;而不是将子树中的历史线性化。但如果合并未通过默认合并策略100%自动完成,则会发生以下两种情况之一:

如果您很幸运,当rebase尝试重做合并时,它会因冲突而失败,并且您将被咨询以重做冲突解决方案(当然,这可能是由其他人完成的,或者其他什么,但至少你有机会修复它)。最糟糕的情况是合并不会因冲突而失败,但仍然不对(因为原始合并是通过--no-commit编辑完成的,或者是使用默认合并以外的其他方式完成的战略导致不同的结果)。在这种情况下,rebase不知道它是不对的,并且会在你的历史被打破的情况下默默地继续下去。

出于这个原因,如果你确实改变了一个非平凡的历史,你可能不得不使用--preserve-merges 彻底验证最终结果,例如:通过提交将新子树与旧子树进行比较。 (这并不容易,但在你的情况下,它只是比较一个特定的子树,所以至少是可行的。)