1

我们有一个存储库,其中几个目录已在两年前被提取为子模块。

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

现在的问题是,最好的方法是什么——同时保留所有历史

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

有没有办法用cherry-pick或任何其他聪明的方式做到这一点?

非常感谢!

4

1 回答 1

1

您可以使用手册页中的示例或此答案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

首先,将每个子模块添加为父 repo 中的远程,然后将每个子模块的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.

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

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

最后一点。如果您使用 BSD ,那么手册页中的替换sed很有可能会失败。\tJeff King 的perl 版本将解决这个问题:

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
于 2013-05-06T01:59:00.093 回答