我认为最好的解决方法是修补Mercurial的Git子存储库支持,使其始终使用Git的递归选项(例如,当克隆基于Git的子存储库时使用
git clone --recursive
,在拉取更新的基于Git的子存储库后使用
git pull --recurse-submodules && git submodule update
等)。我知道Git开发人员明确选择不自动初始化子模块之一的工作流程,因为他们想要支持的工作流程之一是“我永远不想看到任何子模块”,但也许“始终初始化所有子存储库”更符合默认Mercurial操作模式(我不是很了解Mercurial用户,所以我对默认的Mercurial风格没有什么概念)。
在此之前,您可能可以通过将
subrepo/.gitmodules
条目转换为
.hgsub
条目来解决问题。手动很容易做到,但如果重要的话,您可能可以通过使用
git config
从
.git/config
和/或
.gitmodules
中提取路径和URL来自动执行它。如果您正在处理经常更改的
.gitmodules
文件,则可能会感到不舒服(每次更改
.gitmodules
都必须非常努力地同步
.hgsub
)。
我使用了四个存储库进行了测试:
- gitsub——一个“叶子”存储库(没有Git子模块)
- gitsuper——一个Git“超级项目”;
gitsub/
是gitsub作为子模块
- hgsuper2——一个Mercurial“超级项目”;
gitsuper/
是gitsuper作为子存储库,
gitsuper/gitsub
是gitsub作为子存储库。
- hgsuper2-clone——一个克隆的Mercurial“超级项目”;
gitsuper/
是gitsuper作为子存储库,
gitsuper/gitsub
是gitsub作为子存储库。
我像这样构建和测试它们:
- 创建 gitsub,添加并提交一些内容。
- 创建 gitsuper。
- 添加一些内容。
git submodule add url-of-gitsub gitsub && git submodule init
git commit -m 'added gitsub'
- 创建 hgsuper2。
- 添加一些内容。
git clone --recursive url-of-gitsuper gitsuper
echo 'gitsuper = [git]url-of-gitsuper' >> .hgsub
echo 'gitsuper/gitsub = [git]url-of-gitsub' >> .hgsub
这最后两步可以从 gitsuper/.git/config
和 gitsuper/.gitmodules
中自动完成。
hg add .hgsub && hg commit -m 'added Git subrepositories'
- 从 hgsuper2 克隆 hgsuper2-clone。
它会获取 gitsuper/
和 gitsuper/gitsub/
中的适当内容。
- 更新并提交新的内容到 gitsub。
- 更新 gitsuper。
- 添加或更改一些内容并暂存它们。
(cd gitsub && git pull origin master)
git add gitsub && git commit -m 'updated gitsuper content (also gitsub)'
- 在 hgsuper2 中,从 Git 子仓库中拉取更改。
(cd gitsuper && git pull --recurse-submodules && git submodule update)
通过拉取更新了 gitsuper/
和 gitsuper/gitsub/
中的内容。
hg commit -m 'updated gitsuper (and its contents)'
- 拉入到 hgsuper2-clone。
hg pull -u
来自 Git 的内容已经更新。
我的测试已经通过(使用Mercurial 1.8.1和Git 1.7.4.1),但我注意到了一个错误。Mercurial创建并检出了一个名字奇怪的Git分支(origin/master
,即refs/heads/origin/master
),而不是使用分离的HEAD(就像Git子模块一样)或仅使用master
(即refs/heads/master
)。有时它也会有点卡住,导致出现如下错误:
fatal: git checkout: branch origin/master already exists
abort: git checkout error 128 in gitsuper
我通过进入相关的Git仓库(基于Git的Mercurial子仓库)并使用
git checkout HEAD~0 && git branch -D origin/master
删除分支来解决了这个问题(第一个命令将HEAD分离,而且更重要的是移动到分支之外,以便下一个命令可以删除它)。只要您没有在Git仓库中进行任何本地更改,这个解决方法是完全安全的。
另一个小问题是,在发出Git子模块命令之前,您需要运行
git submodule init
让Git知道其子模块。如果您在由Mercurial创建的Git超级仓库中操作Git子模块,则子模块已经克隆到正确的位置,但是它们是由Mercurial建立的,因此在
.git/config
中没有相应的条目。
同样,如果您计划从基于Git的Mercurial子仓库内部管理Git内容的更改,则应该注意在提交Mercurial“超级项目”之前始终添加任何Git子模块,并从Git子仓库中提交和推送。否则,您可能会遇到这样一种情况:Mercurial使用
gitsuper和
gitsub的某种组合,而
gitsuper本身引用了不同版本的
gitsub。换句话说,由于您将通过将Git子模块作为Mercurial子仓库管理来绕过Git的子模块代码,因此您需要小心地保持Git对子模块的视图与Mercurial的视图同步。