在保留历史的同时合并Git子模块

时间:2013-05-05 22:37:14

标签: git git-submodules

我们有一个存储库,两年前已经将几个目录提取为子模块。

由于git子模块引起了太多麻烦,因此决定将提取恢复为子模块并将目录恢复到父存储库。

现在的问题是,这样做的最佳方式是什么 - 同时保留所有历史记录

我在考虑将子模块添加为远程,然后cherry-pick进行所有更改。但为此,我需要告诉git它不应该处理相对于当前目录的提交路径,而不是父代repo的root。

cherry-pick或任何其他聪明的方法无法做到这一点吗?

非常感谢!

1 个答案:

答案 0 :(得分:1)

您可以使用手册页中的示例或稍加修改的版本in this answer,使用git filter-branch执行此操作。这是git v1.8.2中的手册页版本:

To move the whole tree into a subdirectory, or remove it from there:

git filter-branch --index-filter \
    'git ls-files -s | sed "s-\t\"*-&newsubdir/-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD

首先,在父仓库中将每个子模块添加为远程,然后将每个子模块的master分支签出为本地跟踪分支(例如submoduleA-mastersubmoduleB-master等)。 Git会发出警告,因为分支机构不共享历史记录,但是否则会让您继续。将子模块分支的历史记录重写到相应的子目录中,并将其合并到父代的master中。最后,您将为这些子目录提供一系列合并提交,并在父级仓库中提供一个有凝聚力的单一历史记录。

这听起来要复杂得多。如果出现问题,请务必进行备份。编写整个内容的脚本,以便您可以尝试它,直到您做对了。每个子模块的粗略执行顺序是:

git remote add submodule submodule_remote
git checkout -b submodule-master submodule/master
git filter-branch ...        # With the index-filter described above.
                                 # Depending on length of history, this could
                                 # take quite a while to process/
git checkout master          # Get back on parent's master.

现在你面临着一个选择。您是否重写了父级以删除子模块的所有痕迹?如果是后者,请使用适合solutiongit version从父存储库中删除子模块,然后git merge submodule-master。如果您想要删除历史记录中的所有子模块提交,也可以使用git filter-branch重写父级。

我曾经为35个不同的存储库做过这个。这里有一个提示:在AWS中几小时的集群计算上花费10美元。 git filter-branch极受RAM限制。您的笔记本电脑在20小时内无法完成的事情,AWS集群计算实例可以在午餐时间完成。这是一种非常简单,廉价的方式来进行这样的操作。

最后一点说明。如果您使用BSD sed,那么手册页中的\t替换很可能会失败。 Jeff King的perl version将解决该问题:

git filter-branch --index-filter '
  git ls-files -s |
    perl -pe "s{\t\"?}{$&newsubdir/}" |
    GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
  mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD