我们有一个存储库,两年前已经将几个目录提取为子模块。
由于git子模块引起了太多麻烦,因此决定将提取恢复为子模块并将目录恢复到父存储库。
现在的问题是,这样做的最佳方式是什么 - 同时保留所有历史记录。
我在考虑将子模块添加为远程,然后cherry-pick
进行所有更改。但为此,我需要告诉git它不应该处理相对于当前目录的提交路径,而不是父代repo的root。
用cherry-pick
或任何其他聪明的方法无法做到这一点吗?
非常感谢!
答案 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-master
,submoduleB-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.
现在你面临着一个选择。您是否重写了父级以删除子模块的所有痕迹?如果是后者,请使用适合solution的git 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