多个git仓库的最佳实践

9
我有大约20个不同的代码库。许多是独立的,编译为库,但其中一些之间存在依赖关系。依赖关系解析和分支很复杂。
假设我有一个“超级项目”,它仅汇总所有其他代码库。它专门用于运行测试--没有实际开发在这里进行。
/superproject  [master, HEAD]
    /a         [master, HEAD]
    /b         [master, HEAD]
    /c         [master, HEAD]
    /...

现在,为了针对每个具体的功能或修复进行开发(a),特别是其中一些需要特定版本的项目才能编译或运行的功能(b v2.0c 3.0),我必须创建一个新的分支:
/superproject  [branch-a, HEAD]  <-- branch for 'a' project
    /a         [master]  <-- new commits here
    /b         [v2.0]
    /c         [v3.0]

对于b,可能需要其他东西,比如a v0.9c v3.1

/superproject  [branch-b, HEAD]  <-- branch for 'b' project
    /a         [v0.9]   <-- older version than 'a'
    /b         [master] <-- new commits go here
    /c         [v3.1]   <-- newer version than 'a'

当实施涉及功能分支、热修复分支、发布分支等常见git工作流时,这变得更加复杂和棘手。我被建议使用(或不使用) git-submodules, git-subtree, google的git-repo, git-slave等。如何管理如此复杂的项目的持续集成?编辑:如何在不模拟所有其他依赖项目的情况下运行测试?特别是当所有项目可能使用不同版本时。在git子模块提交后触发Jenkins测试

1
我实际上不鼓励这样的架构。像那样拥有一个仓库只会使维护者感到困惑,而且测试应该按项目进行。 - Makoto
上面的各个文件夹是不同的.git仓库,而不是一个单一的大仓库 - 否则它们就不能有不同的分支和标签。 - AlBlue
真正的问题是如何在不必模拟所有其他依赖项目的情况下运行测试?特别是当所有项目可能使用不同的版本时。 - betodelrio
2个回答

6

如果可能的话,请使用并行克隆来处理多个分支。相比每次切换都要执行checkout、clean和check-for-stale-detritus以及recreate-caches,cd简单得多。


就记录测试环境而言,您所描述的正是子模块在每个细节上所做的事情。对于这么简单的东西,我建议您在不使用子模块命令的情况下进行设置,并在您感到舒适且子模块问题列表中的首要项目是击键计数时告知它您的设置。

从您的问题设置开始,以下是如何设置自己以记录子项目中的干净构建:

cd $superproject
git init .
git add a b c etc
git commit -m "recording test state for $thistest"

就这样了。你已经提交了一组提交ID列表,即这些仓库中当前检出提交的ID。实际内容在这些仓库中,而不是在当前仓库中,但这就是Git在文件和子模块之间的全部差别。 .gitmodules文件包含随机注释以帮助克隆者,主要是一个建议的存储库,该存储库应包含必要的提交,并且有关命令默认值的随机注释,但它正在做的事情很容易理解。

想在路径foo处检出正确的提交吗?

(commit=`git rev-parse :foo`; cd foo; git checkout $commit)

rev-parse从索引中获取foo的内容ID,cd和checkout执行此操作。

以下是查找所有子模块以及应在其中检出什么以重新创建已暂存的(即已索引的)环境的方法:

git ls-files -s | grep ^16

检查当前索引列表中子模块的内容以及实际检出的内容:

echo $(git rev-parse :$submodule; (cd $submodule; git rev-parse HEAD))

然后你就完成了。检查所有子模块中正确的提交?

git ls-files -s | grep ^16 | while read mode commit stage path; do
        (cd "$path"; git checkout $commit)
done

有时您需要携带本地补丁并将其应用于每个检出:
git ls-files -s | grep ^16 | while read mode commit stage path; do
        (cd $path; git rebase $commit)
done

等等类似的操作。针对此类操作有git submodule命令可用,但它们并没有做出你在上面看到的任何操作。对于所有其他操作也是一样的,你可以将它们转换为近乎单行的操作,就像上面那些例子一样。

子模块并没有什么神秘的。


通常使用许多工具中的任何一个进行持续集成,我会将此留给其他人来解决。


很好的答案。如何在不使用超级项目子模块的情况下跟踪版本依赖关系?项目a需要特定版本的b。我能将该约束设置为a本身的一部分吗? - betodelrio
啊,好的,我要送孩子去营地了,之前的评论有点仓促。是的,你可以用这种方式处理依赖关系——对于这个问题,没有子模块工具,但同样只需要一行代码就能解决——为了添加一个实际上位于其他位置的子模块,你需要到其他位置并输入 git config worktree .. 来确定工作树的位置,在使用它而不是最初的子模块更新时,你需要输入 echo gitdir: /path/to/projecta/.git >$theprojectbpath_in_a。试着操作一下,也许我明天会有更多时间帮你。 - jthill

3
作为作者,git slave可以在这种情况下使用。如何使用取决于您是否控制了仓库abc;也就是说,您可以使分支策略在它们之间同步,以便对每个人而言,v2分支都具有相同的含义。如果是这样的话,我强烈建议使用git slave,因为您实际上可以将其视为一个大型项目。
如果您无法强制执行公共分支和标签策略,则需要强制执行一项,这更接近于jthill提出的git子模块工作流程的轻量级版本。具体来说,您可以拥有自己的仓库跟踪abc,并在每个仓库中创建一个branch a分支,该分支将对应于每个从属仓库的正确分支。与git子模块一样,您必须手动更新每个仓库(在此情况下合并)。然而,您不需要执行超级项目中的提交确认步骤。使用此技术并不是让从属项目在进行自身开发时共享相同的分支名称的强力案例,但这将起作用。
正如jthill所说,持续集成基本上与如何管理项目无关。

不,我不能强制在子模块(从属仓库)中使用相同的分支/标签。 gits 看起来比我想要实现的更复杂,但我会尝试一下。谢谢:D - betodelrio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接