在我们已将大多数项目模块化的git环境中,我们面临着每个存储库一个项目或每个存储库设计问题多个项目的问题。让我们考虑一个模块化项目:

myProject/
   +-- gui
   +-- core
   +-- api
   +-- implA
   +-- implB


今天,每个存储库有一个项目。它赋予了



release单个组件


tag单个组件

但也给branch组件带来了麻烦通常分支api需要core中的等效分支,并且可能还有其他组件。

鉴于我们希望对单个组件进行release处理,我们仍然可以通过在每个存储库设计中利用多个项目来获得类似的灵活性。

有什么经验以及您如何/为什么解决这些问题?

评论

我现在有一个非常相似的问题。我需要发布项目的不同版本,因此它们将需要位于不同的存储库中。不过,这是一场噩梦。如果有一种方法可以只分支子目录,那就太好了。

每个模块需要有单独的版本号。我们使用git-describe。

可能重复使用多个Git存储库,而不是一个包含来自不同团队的许多应用程序的存储库?

stackoverflow.com/a/29665889

您说“每个存储库一个项目”,然后列出一个带有多个文件夹的项目(名为myProject)。但是,然后您在谈论分支文件夹api和core,就好像它们是存储库而不是文件夹一样。

#1 楼

one project per repository的三个主要缺点(如上所述)。如果它们确实是截然不同的项目,那么它们就不那么正确了,但是从它的声音来看,更改一个项目通常需要更改另一个项目,这确实会夸大这些问题:



很难发现何时引入错误。当将存储库分解为子存储库时,像git bisect这样的工具变得更加难以使用。有可能,这并不是那么容易,这意味着在危机时期寻找错误会变得更加困难。

跟踪功能的整个历史要困难得多。诸如git log之类的历史记录遍历命令只是不会输出具有破裂存储库结构的有意义的历史记录。您可以通过子模块或子树或通过其他可编写脚本的方法获得一些有用的输出,但这与键入tig --grep=<caseID>git log --grep=<caseID>并扫描所有您关心的提交并不相同。您的历史记录变得更难理解,因此在您真正需要它时就没用了。

新开发人员花更多时间学习版本控制的结构,然后才能开始编码。每个新工作都需要选择程序,但是分解项目存储库意味着他们必须选择代码结构之外的VC结构。以我的经验,这对于刚接触git的开发人员来说尤其困难,这些开发人员来自使用单个存储库的更传统的集中式商店。

最后,这是机会成本的计算。在一个前雇主那里,我们将主要申请分为35个不同的子存储库。在它们之上,我们使用了一组复杂的脚本来搜索历史记录,确保它们之间的状态(即生产分支与开发分支)是相同的,然后分别或批量部署它们。

太过分了。至少对我们来说太多了。管理开销使我们的功能变得不那么灵活,使部署变得更加困难,使新开发者的教学花费了太多时间,到最后,我们几乎不记得为什么我们首先破坏了存储库。一个美丽的春天,我花了10美元在EC2中花了一个下午的集群计算时间。我将回购协议与几个git filter-branch调用一起编织回去。我们从不回头。

评论


除了主题之外,作为存储库管理器,没有什么比在系统上花费更多时间才能愉快的事情了,该系统可以在两个小时内完成您的笔记本电脑在20小时内无法完成的工作,而且价格低于午餐的价格。有时我真的很喜欢互联网。

–克里斯托弗(Christopher)
2012年8月17日15:47

您如何将这些单独的项目作为单独的版本发布?还是您不需要这样做?那是我的问题。如果需要创建项目A的V1和项目B的V2,请使用。

– Andrew T Finnell
2012年8月18日在12:16

要在“每个存储库一个项目”和“多个存储库”之间移动,请考虑使用git-subtree(在stackoverflow.com/a/17864475/15585上有很好的解释)

–阻止
2013年12月6日20:30

我编写了一个脚本来自动完成常见用例:github.com/Oakleon/git-join-repos

–chrishiestand
15-10-17在0:18



@Calmarius-提交消息比用户更重要。历史告诉您为什么要进行更改。如果您不知道为什么首先添加死代码,则删除潜在的死代码将更加困难。

–内森·科夫纳(Nathan Kovner)
4月6日19:58

#2 楼

Christopher在列举每个存储库一个项目的模型的缺点方面做得很好。我想讨论一些您考虑采用多存储库方法的原因。在我工作过的许多环境中,采用多存储库方法是一种合理的解决方案,但是要决定要拥有多少存储库以及在何处进行削减并不是一件容易的事。

在目前的职位上,我将拥有十多年历史的庞然大物单一存储库CVS存储库迁移到了许多git存储库中。自从最初的决定以来,存储库的数量已经增加(通过其他团队的行动),以至于我怀疑我们的存储量将超过最佳存储量。一些新员工建议合并存储库,但我对此表示反对。 Wayland项目也有类似的经验。我最近在一次演讲中看到,他们曾经有200多个git存储库,而领导对此表示歉意。查看他们的网站,我现在看到他们是5点,这似乎很合理。请务必注意,加入和拆分存储库是一项可管理的任务,并且可以进行实验(在合理的范围内)。

那么何时可能需要多个存储库?


单个存储库太大,无法高效使用。
您的存储库是松散耦合或分离的。
开发人员通常只需要一个或一小部分存储库即可进行开发。
您通常希望独立地开发存储库,只需要偶尔进行同步。
您想鼓励更多的模块化。
不同的团队在不同的存储库上工作。

要点2和3仅在点1成立时才有意义。通过拆分存储库,我显着减少了异地同事遭受的延迟,减少了磁盘消耗并改善了网络流量。

4和5更微妙。当您将客户端和服务器的存储库分开时,这使得协调客户端和服务器代码之间的更改更加昂贵。这可能是积极的,因为这鼓励了两者之间的分离接口。

即使在多存储库项目的不利方面,也可以这样做很多可敬的工作-Wayland和Boost来了心神。我认为尚未就最佳做法达成共识,因此需要做出一些判断。用于多个存储库(git-subtree,git-submodule等)的工具仍在开发和试验中。我的建议是尝试并务实。

评论


参考支持该主张的参考,该答案将更加有用:“加入和拆分存储库是一项可管理的任务。”

–通配符
15年11月30日在16:06

多个存储库也可以对抗模块化,因为它们使更改共享代码变得更加困难。跨仓库依赖关系使集成更加困难,可以更轻松地破坏代码(即使您有很好的工具来检查它),破坏仓库外代码的威胁会阻碍重构接口,这是您做事最强大的工具之一更加模块化。

– cjs
18年4月20日在0:15

有关MicroServices和DDD设计的所有内容都保存在这里。您应该最小化共享代码。

– Arwin
19年2月28日在14:01

#3 楼

当我们使用GitHub时,实际上在一个存储库中有多个项目,但要确保这些项目/模块已正确模块化(我们使用-api和-core约定+ Maven +静态和运行时检查,甚至有可能一天到OSGi进行引导) 。

它保存什么?好吧,如果我们要在多个项目中进行较小的更改,则不必发出多个“拉取请求”。问题和Wiki保持集中化等。

我们仍然将每个模块/项目都视为一个适当的独立项目,并将其分别构建和集成到我们的CI服务器中。

评论


很有意思。我怀疑这是github上的常见模型。如果您要面对单独的组件发行版,您是否采用了子模块之类的功能,或者发行/标记了整个存储库?

– JohanSjöberg
2012年8月17日14:09



子模块(如果需要的话),但现在我们从父版本开始进行版本控制。

–马丁·弗伯格(Martijn Verburg)
2012年8月17日在16:40

在我目前的雇主处,我们使用类似的策略,并将有关项目中最新提交的元数据打包到工件的各种清单文件中(即git log -1-的结果)。真的很棒。这个答案值得更多的赞扬。

–克里斯托弗(Christopher)
2014年2月22日在4:30

“它节省了什么?如果我们在多个项目中进行较小的更改,则不必发出多个Pull Requests。” -您能举个例子吗?直观地讲,这表明您没有成功确保适当的模块化...

– Arwin
3月30日8:59

@MartijnVerburg那么我们同意。如果您实际上有一个整体,而很多移动部件无法用版本化的NuGet包等进行有意义的分隔,那么最好将所有部件放在一起。但随后,单仓库也可以使用。但是,您在回答中说“没有一个回购协议,但要确保这些项目/模块正确地模块化”,然后我不明白。

– Arwin
4月4日12:32

#4 楼

对我而言,使用一个或多个存储库的主要区别在于以下问题的答案:


同一团队开发的多个部分具有相同的发布周期,相同的顾客?然后,没有理由拆分一个存储库。
多个部分是否彼此高度依赖?因此,由于彼此之间的高度依赖,因此拆分模型,控制器和UI(即使它们是不同的部分)也不是很明智。但是,如果2个部分之间只有很小的依赖关系,而这种依赖关系是由一个稳定的接口(每隔几年才更改一次)来实现的,那么将2个部分划分为2个存储库是明智的。

例如,我有一个小型应用程序(仅客户端),该应用程序检查Subversion存储库的“质量”。有一个核心实现,可以从命令行启动,并且可以与Java 6一起很好地工作。但是我已经开始实现一个UI,该UI使用JavaFX作为Java 8的一部分。因此,我拆分了2个,并创建了一个第二个存储库(具有第二个构建过程),具有不同的时间表,...

我喜欢上面的答案(投票赞成),但我认为它们并不是全部真实的故事。因此,我也想添加用于拆分存储库的参数。因此,真正的答案(何时拆分)可能在中间的某个地方...

#5 楼

git-subtree(请参阅Atlassian博客,中型博客或内核链接)可能很适合您所拥有的。因此,您的每个顶级项目都会使用一组可能不同版本的子树。

#6 楼

根据您的示例,应该根据存储库之间的相互依赖性来设置存储库。设计MicroServices和域驱动设计的所有理由都适用于此:在某些情况下,重复的代码是可以接受的,可以使用接口,除非确实需要,否则不要破坏兼容性等等。

现在我认为用户界面应独立于后端。因此,UI项目存储库通常应包含UI代码和客户端控制器。客户端控制器将以抽象方式与服务控制器连接。他们将使用与服务分开版本化的服务客户端/ api抽象,以便可以在不中断客户端的情况下更新服务(可能有多个不同的客户端)。

所以服务本身应该是它自己的存储库。在我看来,该服务只是某些单点业务逻辑的包装。因此,业务逻辑通常应与托管它的服务技术分开。另一方面,存储库实现通常紧密地连接到业务逻辑,以至于可以将其集成在同一存储库中。但是,即使您的飞行里程也可能有所不同。

当然,简单的项目在技术或支持多个堆栈方面不太可能发生很大变化,其中所有UI都可以从与后端相同的来源托管,后端服务通常仅由同一客户端使用,可以从更紧密集成的存储库中受益。

在这种情况下,将完整的垂直存储在一个存储库中可能会很好,而专注于确保功能域在其自己的存储库中是独立的。这样,您仍然具有较小的存储库的最大优点,而其他方面的开销却很少。