尽管涉及两个子部分,但我认为这是一个综合问题,因为它被分解成部分的方式并不重要。只要最终结果保留了检查,研究和构建/测试历史版本的所有有意义的历史和能力,我就可以通过不同的方式实现我想要的目标。目标是退出hg和迄今为止使用过的subrepo模型,然后转移到git中的统一树,但不会牺牲历史。
我开始使用的是一个Mercurial存储库,它包含一些顶级代码和许多有趣历史所在的子存储库。 subrepos有一些分支/合并,但没有什么太疯狂。我想要实现的最终结果是单个git存储库,没有子模块,例如:
对于原始顶级hg repo中的每个提交,都有一个git提交,它会检出完全相同的树,并且检查出相应的hg提交及其所有引用subrepo提交。
这些对应于连续顶级hg提交的git提交是彼此的后代,其提交对应于其间的所有相关子提交。
我有关如何实现这一点的基本思想是迭代所有顶级hg提交,并且对于每个更改.hgsubstate
的顶级提交,也迭代从旧版本到旧版本的所有路径子模块的新版本(可能涉及分支)。在每一步:
git-write-tree
和git-commit-tree
使用相应的hg提交中的author,3nd,date和commit消息,使用所需的父级生成提交。这应该有用吗?有没有更好的方法来实现我想要的,也许首先用hg做subrepo崩溃?我不清楚的最重要的事情是如何执行所需的迭代,因此如何实现它的实用建议会很棒。
一个额外的约束:原始回购涉及无法发布的内容(这是基本转换完成后的另一个git-filter-branch
步骤)所以涉及上传回购以供第三方处理的解决方案不可行。
答案 0 :(得分:6)
您所写的内容可能会或可能不会解决问题。但这并不简单。主要问题是您需要按顺序提交,以便您的子目录和主仓库保持一致。我以小规模重新创建了这个问题,并且能够在subrepos之间保持一致。)
我的解决方案:
使用hg convert扩展,我将主仓库转换为没有子目录的仓库(以及相关信息)。
cd main
awk '{ print $1}' .hgsub | xargs -n 1 echo 'exclude' > ../filemap
echo exclude .hgsub >> ../filemap
echo exclude .hgsubstate >> ../filemap
cd ..
hg convert --filemap filemap main mainConv
cd mainConv
hg update
使用--filemap中的重命名转换subrepo。
cd ..
echo rename . subRepo > subFileMap
hg convert --filemap main/subRepo subRepoConv
cd subRepoConv
hg update
将子回版拉到已转换的主仓库。
cd ../mainConv
hg pull -f ../subRepoConv
在拉动时你会注意到回购中的多个头(因为subrepo有自己的头)。合并他们:
hg heads
hg merge <RevID from subrepo (not main repo)>
hg ci -mMergeOfSubRepo
你必须重复3&amp;每个子流程都有4个。
但是承诺不会被排序。所以按照这里的顺序排列它们https://stackoverflow.com/a/16012597:
cd ..
hg clone -r 0 mainConv mainOrdered
cd mainOrdered
for REV in `hg log -R ../main -r 'sort(1:tip, date)' --template '{rev}\n'`
do
hg pull ../main -r $REV
done
现在使用http://repo.or.cz/w/fast-export.git将此有序的mercurial repo转换为git:
cd ..
git clone git://repo.or.cz/fast-export.git
git init mainGit
cd mainGit
../fast-export/hg-fast-export.sh -r ../mainOrdered
git checkout HEAD
答案 1 :(得分:4)
是。您最好的选择是使用git commit-tree
手动创建提交。有许多转换工具,但它们永远不会给你你想要的。另一方面,手写脚本将为您提供所需的所有灵活性。
我已写过许多这些脚本,包括git remote-hg
本身。
答案 2 :(得分:2)
不相关的offtopic
我确定,你选择了最糟糕的迁移理念(从Mercurial到Git),但它是你的选择和你的责任
迁移课程
我对Git的了解相当薄弱,因此对于Mercurial + subrepo - &gt;单片Git我只能看到和描述这样的方式:
Mercurial + subrepo - &gt;单片Mercurial - &gt;单片Git回购
答案 3 :(得分:1)
这就是我为解决类似问题所做的工作:
git checkout -b
中为每个subrepo存储库命名git read-tree --prefix=pathsubrepo/ -u subrepobranch
这或多或少是我做的更详细一点(改编自bash历史......但实际上没有运行)
第1步
cd ~
git clone git://repo.or.cz/fast-export.git
git init parent_repo
cd parent_repo
~/fast-export/hg-fast-export.sh -r /path/to/old/mercurial/parent
git checkout HEAD
cd ~
git init subrepo1
cd subrepo1
~/fast-export/hg-fast-export.sh -r /path/to/old/mercurial/parent/subrepo1
git checkout HEAD
cd ~
git init subrepo2
cd subrepo2
~/fast-export/hg-fast-export.sh -r /path/to/old/mercurial/parent/subrepo2
git checkout HEAD
第2步
cd ~/parent_repo
git remote add sub1 $HOME/subrepo1/
git remote add sub2 $HOME/subrepo2/
第3步
cd ~/parent_repo
git checkout -b sub1master sub1/master
git checkout -b sub2master sub2/master
第4步
cd ~/parent_repo
git read-tree --prefix=subrepo1/ -u sub1master
git read-tree --prefix=subrepo1/ -u sub2master
完成后,您可以git branch -D sub1master
和git branch -D sub2master
,因为您不再需要它们了。
答案 4 :(得分:1)
似乎我在问题中遗漏的问题和对可能解决方案的讨论是对所涉及的图论的正确理解。像&#34;这样的想法遍历从旧版本到新版本的所有路径&#34;没有真正明确的定义,或者至少没有反映我期望他们反映的内容。从更严格的角度来看,我认为我有一种方法可行。
首先,问题是:子版本修订仅代表历史中给定点的子树状态。我想将它们映射到代表整个组合树状态的修订版。然后可以以有意义的方式将子汇率DAG与顶级DAG合并。
对于给定的subrepo修订版R,我们可以询问顶级repo(或者父级repo,如果我们有多个subrepos级别)修订包括R或R的任何后代。假设一个根,这一组修订版有一个Lowest Common Ancestor(或者可能不止一个),这似乎是一个很好的候选人。实际上,如果我们与R一起使用的顶级修订版S不是使用R或其后代的修订版的共同祖先(但映射是合理的),那么R将具有后代R&#39;其相关的顶级修订版S&#39;不是S的后代。换句话说,从subrepo派生的历史将在顶级树的修订之间产生令人困惑/无意义的跳跃。
现在,如果我们想要选择一个共同的祖先,那么从使这些修订版本可以检出,构建和测试的角度来看,最低的一个是有意义的,并且从给出一个合理的想法是什么的角度来看顶级回购(和其他子目录)的状态是在subrepo的变化时。整个顶级DAG的根本当然也可以工作,但它不会提供可以检查的有意义的,可用的修订;选择根将是等效的(从可用性的角度来看)到一个天真的repo-merge,每个subrepo有一个根,只要顶级repo更新它使用的修订版,就会从subrepo历史中合并。
因此,如果我们可以使用LCA为每个子版本修订版R分配顶级修订版T(R),那么它将如何转化为
每当子版本修订版R对于R的每个父P都具有与T(P)不同的T(R)时,它有效地将来自顶级回购(和其他子版本)的新变化合并到子历史记录中。转换应将此表示为两个提交:
实际的subrepo提交R,使用旧的顶级修订。如果R具有单个父P(不是合并提交),则这将是T(P)。如果R有多个父母,那么不清楚是否可以选择使用哪一个父母,但任何父母P的T(P)应该合理。
合并提交合并返回与R关联的顶级repo提交T(R)的转换C(T(R)),其中C(T(R))本身刚刚合并(1)上方。
除了引用(1)作为合并父项的C(T(R))之外,转换中对R的所有其他引用应使用(2)。这包括顶级仓库中T(R)的任何后代的转换,这些后代使用此子目录的修订版R,以及R本身的直接子代的转换。
我认为上述(尽管措辞不当)描述指定了合并顶级和子级别DAG所需的所有内容。每个子版本修订都获得树的完整版本,最终通过&#34; merge commit&#34;连接到转换后的repo的统一DAG中。 (当subrepo合并新的关联顶级修订时,以及顶级合并已更改的子版本修订时)。
然后,生成git repo的最后一步是简单地以拓扑排序的形式或通过深度优先步行重放合并的DAG,这样每个git commit-tree
已经拥有它需要的所有父修订版本本。
答案 5 :(得分:-1)
尝试Facebook的Hg&lt; - &gt; Git转换器:FbShipIt
。您所描述的大多数内容应该适用于此提交转换器工具,该工具可复制Mercurial和Git之间的提交。
FbShipIt
有一个警告:它不了解合并提交,但可以通过git rebase
解决。