我已经使用Git或颠覆来完成重构/重新创建现有代码存储库的任务。在这种特殊情况下,存储库历史并不一定重要。经过分析,我发现在确定一个好的布局时有些问题。我读过很多博客和帖子,但我仍然不确定什么是最好的布局。
现有的存储库包含一组包含文件、一组相互依赖的库,它们中的许多依赖于包含文件集。此外,还有两个应用程序项目依赖于库集。此外,还有一组脚本使用其中一个应用程序和一些附加的配置信息。我画了一张图表来说明情况:
+---------->include files
| ^
| |
library A -----> library B <----- library C <----- library D
^ ^ | ^
| | | |
| +--------------------------------+ |
| |
application 1 application 2 --------------------+
^
|
script -----> configuration information
目标是要有一个布局,其中每个组件都可以尽可能独立地开发,并且要有一个版本(对于外部客户),该版本包含一组具有定义的标记版本的所有组件,以便能够及时返回并为特定版本构建软件。
我提出了以下结构:
trunk/
include/
lib/
library A/
library B/
library C/
library D/
app/
application 1/
application 2/
tags/
1.0/
include/
lib/
library A/
library B/
library C/
library D/
app/
application 1/
application 2/
1.1/
include/
lib/
library A/
library B/
library C/
library D/
app/
application 1/
application 2/
...
每次创建一个新版本时,我只需将整个存储库复制到标记中的一个新子目录。
此解决方案的问题在于库本身没有单独的标记目录,而且我只希望有一个包含标记组件的版本,并且此解决方案不显示哪些组件在版本中具有哪些标记版本。我已经考虑过使用单独的存储库,然后创建一个主存储库,其中有一个releases子目录,我在其中使用“svn:externals”和一个特定的tags子目录链接所有必需的组件,但是不同的库和include文件相互依赖,我看不到如何将代码划分为单独的实体。
有什么想法吗?
=2011年1月28日继续提问===============
好的,我画了一张我如何规划新布局的图。目标是将
使用svn:externals方法在一个
例如,我将svn:externals设置为
trunk/projects/lib/library2/依赖于^/tags/projects/include/std/1.3。
trunk/
projects/
include/
std/
lib/
library1/
dependencies/
std/ --> tags/projects/include/std/1.2
library2/
dependencies/
std/ --> tags/projects/include/std/1.2
library1/ --> tags/projects/lib/library1/1.4.3
library3/
dependencies/
std/ --> tags/projects/include/std/1.3
library1/ --> tags/projects/lib/library1/1.4
app/
application1/
dependencies/
library3/ --> tags/projects/lib/library3/1.1
application2/
dependencies/
library1/ --> tags/projects/lib/library1/2.1
application3/
dependencies/
std/ --> tags/projects/include/std/1.2
library2/ --> tags/projects/lib/library2/1.5.2
config/
configuration1/
dependencies/
application1/ --> tags/projects/app/application1/2.3
configuration2/
dependencies/
application1/ --> tags/projects/app/application1/1.6
configuration2/
dependencies/
application2/ --> tags/projects/app/application1/1.6
tags/
projects/
include/
std/
1.2/
1.3/
lib/
library1/
1.4.3/
1.4/
2.1/
library2/
1.5.2/
library3/
1.1/
app/
application1/
1.6/
2.3/
branches/
...
剩余问题:
这个设计可行吗,或者你认为有什么主要的缺点吗?
如果将库复制到标记目录,会发生什么情况?这会复制
SVN:外部属性也是。这会引起问题吗,还是需要这种行为?
我可以为所有外部对象指定一个显式的修订,但是标记无论如何都不应该更改,所以目录不够吗?
将存储库拆分为开发存储库和使用svn:external的存储库是否是更好的解决方案?参见问题中的“用例”答案What's the benefits of "svn:externals"?。
最佳答案
我建议把分类法翻个底朝外。在Subversion中,我建议使用这样的分类法:
companyname/
include/
trunk/
tags/
branches/
libraryA/
trunk/
tags/
branches/
libraryB/
trunk/
tags/
branches/
libraryC
trunk/
tags/
branches/
libraryD/
trunk/
tags/
branches/
application1
trunk/
tags/
branches/
application2
trunk/
tags/
branches/
在git中,我建议您为include、librarya、libraryb、application1等创建一个单独的git repo。
此结构允许您在不同部分之间创建任何类型的依赖关系(例如,application1中的分支可能依赖于librarya项目的不稳定版本,而application1中的头可能依赖于librarya项目的稳定版本)。
这种结构也适用于大多数构建工具,如maven、rake、buildr、ant等。
您提供的分类法看起来是应用程序部署版本的良好结构,但不是版本控制的良好结构。根据经验,我认为如果你使用我建议的版本控制结构,然后在打包/部署/发布应用程序时使用生成脚本(或生成工具)创建列出的结构,你会更好。
更新:要详细说明工作周期可能如何进行:
例如,假设我们已经完成了application1的bug修复(我们称这个版本为1.0.0)。最新和最大的更改签入到application1/trunk中。此版本的application1依赖于libraryd v0.5.0。您更新application1/trunk/readme.txt时会注意到此版本依赖于libraryd v0.5.0。也许,更重要的是,application/trunk中的构建脚本知道如何签出libraryd/tags/0.5.0。接下来,在application1/tags/1.0.0中创建一个标记(它只是当前状态下trunk的副本)。
现在,假设一周过去了,另一个开发人员将libraryd更新到1.3.0版。您需要增强应用程序1。所以,在application1/trunk中进行更改。然后更新application1/trunk/readme.txt,说明您现在依赖于libraryd v1.3.0(同样,application1v1.3.0的新构建脚本将签出libraryd/tags/1.3.0)。将application1/trunk复制到application1/tags/1.1.0。
现在,如果需要的话,您总是可以恢复到application1/tags/1.0.0(并且确信它将从libraryd/tags/0.5.0中提取代码)。application/tags/1.1.0将使用libraryd版本1.3.0。
在subversion和git中,标记都是在给定时间点对一组文件的引用。这意味着标记不会占用太多空间,所以我说标记要早并且经常;-)