如何构建由多个库和应用程序组成的存储库
我赢得了使用 Git 或 Subversion 重组/重新创建现有代码存储库的任务。在这种特殊情况下,存储库历史记录不一定重要。在分析情况后,我发现确定良好布局存在一些问题。我已经阅读了很多博客和线程,但我仍然不确定什么是最好的布局。
现有存储库包含一组包含文件、一组相互部分依赖的库,并且其中许多库依赖于该组包含文件。此外,还有两个应用程序项目依赖于该组库。此外,还有一组脚本使用其中一个应用程序和一些附加配置信息。我画了一张图来阐明情况:
+---------->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/
...
每次创建新版本时,我只需将整个存储库复制到标签中的新子目录中。
此解决方案的问题在于,库本身没有单独的标记目录,并且我只想拥有由标记组件组成的版本,并且此解决方案不显示版本中哪些组件具有哪些标记版本。我考虑过使用单独的存储库,然后创建一个主存储库,其中有一个发布子目录,我在其中使用“svn:externals”和特定标签子目录链接所有必要的组件,但不同的库和包含文件相互依赖,我不这样做不知道如何将代码划分为单独的实体。
有什么想法吗?
===============问题于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:externals 属性也是如此。这会导致问题吗?或者这种行为是我们所希望的吗?
- 我可以为所有外部指定一个显式修订,但标签无论如何都不应该改变,所以目录还不够吗?
- 将存储库分为开发存储库和使用 svn:external 的存储库是否是更好的解决方案?请参阅问题中的答案“用例”“svn:externals”有什么好处? 。
I've won the task of restructuring/recreating an existing code repository, either using Git or Subversion. The repository history is not necessarily important in this special case. After analyzing the situation I've found some problems determining a good layout. I've read through a lot of blogs and threads, but I'm still unsure as to what is the best layout.
The existing repository contains a set of include files, a set of libraries that are partially dependent on each other, and many of them depend on the set of include files. Additionally, there are two application projects that depend on the set of libraries. Furthermore, there is a set of scripts that makes use of one of the applications and some additional configuration information. I've drawn a graph to clarify the situation:
+---------->include files
| ^
| |
library A -----> library B <----- library C <----- library D
^ ^ | ^
| | | |
| +--------------------------------+ |
| |
application 1 application 2 --------------------+
^
|
script -----> configuration information
The goal is to have a layout where each component can be developed as independently as possible, and to have a release (for external customers) that contains a set of all components at defined tag versions, so that it is possible to go back in time and build the software for a specific release.
I've come up with the following structure:
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/
...
Each time I create a new release I would simply copy the whole repository to a new subdirectory in tags.
The problem with this solution is that the libraries do not have separate tag directories for themselves, and that I only want to have a release that consists of tagged components, and this solution does not display which components have which tag versions in a release. I have considered using separate repositories and then create a master repository that has a releases subdirectory where I link in all necessary components with `svn:externals' and a specific tags subdirectory, but the different libraries and include files depend on each other and I don't see how to partition the code into separate entities.
Any ideas?
=============== question continued on 28-1-2011 ===============
Ok, I've drawn a graph of how I plan the new layout. The goal is to link the
tags of various dependencies with the svn:externals method within one
repository, for example I'd set svn:externals in
trunk/projects/lib/library2/dependencies on ^/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/
...
Remaining questions:
- Is this design workable, or do you see any major drawbacks?
- What happens if I copy a library to a tags directory? This would copy the
svn:externals property as well. Will this cause problems, or is this behaviour desired? - I could specify an explicit revision for all externals, but a tag should not change anyways, so wouldn't the directory suffice?
- Would a repository split into a development repository and a repository that uses svn:external be a better solution? See answer `use cases' in question What's the benefits of "svn:externals"?.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我建议彻底改变分类法。在 subversion 中,我建议这样的分类法:
在 git 中,我建议您应该为 include、libraryA、libraryB、application1 等创建一个单独的 git 存储库...
此结构将允许您在不同部分之间创建任何类型的依赖关系(例如,application1 中的分支可能依赖于libraryA 项目的不稳定版本,而application1 中的HEAD 可能依赖于libraryA 项目的稳定版本)。
这种结构也适用于大多数构建工具,如 maven、rake、buildr、ant 等。
您提供的分类法看起来对于应用程序的部署版本来说是一个很好的结构,但对于版本控制来说不是一个好的结构。根据经验,我认为如果您使用像我建议的版本控制结构一样的结构,然后使用构建脚本(或构建工具)来创建您在打包/部署/时列出的结构,效果会更好。运送您的应用程序。
更新:详细说明一下工作周期可能会如何进行:
例如,假设我们完成了 Application1 的错误修复(我们将此版本称为 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(同样,application1 v1.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 中,标签是对给定时间点的一组文件的引用,这意味着标签不会占用太多空间,所以我说尽早且经常进行标签;-)
I suggest turning the taxonomy inside out. In subversion, I suggest a taxonomy like this:
In git, I suggest you should create a separate git repo for include, libraryA, libraryB, application1, etc...
This structure will allow you to create any kind of dependencies between the different parts (for example, a branch in application1 could depend on an unstable version of the libraryA project, while the HEAD in application1 could depend on a stable version of the libraryA project).
This structure also works well with most build tools like maven, rake, buildr, ant, etc.
The taxonomy you presented looks like it's a good structure for a deployed version of your application, but not a good structure for version control. From experience, I think you will be better off if you use a structure like the one I suggested for version control, and then use a build script (or build tool) to create the structure you listed when it comes time to package/deploy/ship your app.
UPDATE: To elaborate a bit on how work cycle might go:
So, for example, let's say we finished implementing bug fixes for Application1 (let's call this version 1.0.0). The latest and greatest changes are checked into application1/trunk. This version of Application1 depends on libraryD v0.5.0. You update application1/trunk/README.txt with a note that this version depends on libraryD v0.5.0. Perhaps, more importantly, the build script inside application/trunk knows how to checkout libraryD/tags/0.5.0. Next, create a tag (which is simply a copy of trunk in the current state) in application1/tags/1.0.0.
Now, let's say a week goes by and another developer updates libraryD to version 1.3.0. You need to enhance application1. So, make changes in application1/trunk. Then update application1/trunk/README.txt to say that you now depend on libraryD v1.3.0 (and, similarly, the new build script for application1 v1.3.0 will checkout libraryD/tags/1.3.0). Copy application1/trunk to application1/tags/1.1.0.
Now you can always revert to application1/tags/1.0.0 if needed (and be confident that it will pull code from libraryD/tags/0.5.0. application/tags/1.1.0 will use libraryD version 1.3.0.
In both subversion and git, a tag is a reference back to a set of files at a given point in time. This means that tags don't take up much space, so I say tag early and often ;-)
一个重要的问题是所有库和应用程序是否紧密耦合到一个产品中,或者它们是否可以彼此独立运行。 IMO 只有当所有库和应用程序都是一个产品时,将它们放入单个 git 存储库才有意义,因为除了 svn 之外,您无法仅查看 git 树的一部分。当库和应用程序独立时,您可以为每个库/应用程序创建一个存储库,并通过子模块将它们粘合在一起。
Git 子模块与 svn extarnals 类似,但它们始终引用目标的特定修订版,而不仅仅是简单的 url。因此,当您运行 git checkout deadbeef 时,您始终会从创建 commit deadbeef 时获取子模块状态,无论哪个是引用的存储库的头。因此,git 标签还获取每个子模块的状态,这与 svn 不同,svn 没有固定版本的 url。
An important question is if all libraries and applications are tight coupled into a single product, or if they can live independent from each other. IMO Putting them into a single git repository only makes sense, if all libs and apps are one single product, since other than svn you can't check out only a part of a git tree. When the libs and apps are independent, you can create a repo for each lib/app, and glue them together via submodules.
Git submodules are similar to svn extarnals, but they reference always a specific revision of the target, never only a plain url. So when you run
git checkout deadbeef
, you always get the submodule state from the point when commit deadbeef was created, regardless which is the head of the referenced repo. So a git tag also takes the state of each submodule, unlike svn with a not revision-pinned url.