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

在保留历史记录的同时合并Git子模块,git,git-submodules,Git,Git Submodules,我们有一个存储库,其中两个目录在两年前被提取为子模块 由于git子模块造成了太多的麻烦,因此决定将提取作为子模块进行恢复,并将目录带回父存储库 现在的问题是,在保留所有历史的同时,最好的方法是什么 我正在考虑将子模块添加为远程模块,然后cherry picking所有更改。但为此,我需要告诉git,它不应该处理相对于当前目录的提交路径,而不是相对于父repo的根的提交路径 难道没有人能用樱桃摘或其他聪明的方法做到这一点吗 非常感谢 您可以使用手册页中的示例或稍加修改的版本,使用git filte

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

由于git子模块造成了太多的麻烦,因此决定将提取作为子模块进行恢复,并将目录带回父存储库

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

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

难道没有人能用樱桃摘或其他聪明的方法做到这一点吗


非常感谢

您可以使用手册页中的示例或稍加修改的版本,使用
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中的远程模块,然后将每个子模块的
主节点
分支签出为本地跟踪分支(例如
子模块a主节点
子模块b主节点
,等等)。Git将抛出一个警告,因为这些分支不共享历史记录,但会让您继续。将子模块分支的历史记录重写到相应的子目录中,并将其合并到父级的
主目录中。最后,您将有一系列子目录的合并提交,以及父repo中的一个内聚的、单一的历史记录

这听起来比实际情况复杂得多。确保进行备份以防出现问题。编写整个过程的脚本,这样你就可以尝试,直到你做对为止。每个子模块的大致执行顺序为:

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 merge submodule master
。如果您还想从历史记录中删除所有子模块提交,那么也可以使用
git filter branch
重写父模块

我曾经为35个不同的存储库这样做过。这里有一个小贴士:在AWS上花10美元进行几个小时的集群计算
git过滤器分支
非常受RAM限制。一个AWS集群计算实例可能在午餐时间内完成一件笔记本电脑无法在20小时内完成的事情。这是一种非常简单、廉价的方式来进行这样的操作

最后一个音符。如果使用BSD
sed
,手册页中的
\t
替换很可能会失败。Jeff 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

您可以使用手册页中的示例或稍加修改的版本,使用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中的远程模块,然后将每个子模块的
主节点
分支签出为本地跟踪分支(例如
子模块a主节点
子模块b主节点
,等等)。Git将抛出一个警告,因为这些分支不共享历史记录,但会让您继续。将子模块分支的历史记录重写到相应的子目录中,并将其合并到父级的
主目录中。最后,您将有一系列子目录的合并提交,以及父repo中的一个内聚的、单一的历史记录

这听起来比实际情况复杂得多。确保进行备份以防出现问题。编写整个过程的脚本,这样你就可以尝试,直到你做对为止。每个子模块的大致执行顺序为:

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 merge submodule master
。如果您还想从历史记录中删除所有子模块提交,那么也可以使用
git filter branch
重写父模块

我曾经为35个不同的存储库这样做过。这里有一个小贴士:在AWS上花10美元进行几个小时的集群计算
git过滤器分支
非常受RAM限制。一个AWS集群计算实例可能在午餐时间内完成一件笔记本电脑无法在20小时内完成的事情。这是一种非常简单、廉价的方式来进行这样的操作

最后一个音符。如果使用BSD
sed
,手册页中的
\t
替换很可能会失败。Jeff 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