.NET 依赖管理和标记/分支

9
我的公司在管理构建、发布和分支方面遇到了一些问题...我们的基本设置是,我们维护了4个应用程序,其中2个是WPF应用程序,另外2个是ASP.NET应用程序,这4个应用程序共享公共库,因此它们目前都在一个文件夹/trunk/{app1, app2, app3, app4}中。
这使得很难单独分支/标记一个应用程序,因为您同时分支了所有4个应用程序,因此我们想将其分开成类似{app1,app2,app3,app4}/{trunk,tags,branches}的形式,但是这时我们遇到了共享库放置的问题?
我们不能将共享库作为SVN外部引用,因为这样当您分支/标记时,该分支仍然引用主干共享库,而不是将它们一起分支。
有什么技巧?想法?
我们目前正在使用svn和cruisecontrol.net。
编辑:就目前而言,共享库经常发生更改,这就是为什么我们不能将它们作为svn外部引用到主干上,因为我们可能会在分支中更改它们。因此,我们不能将它们用作二进制引用。
当库被静态构建而不包括源代码时,测试和调试也非常困难。
7个回答

6
我想这完全取决于共享库的稳定性。我更喜欢将共享库视为自己的项目,在CruiseControl中像其他项目一样构建。然后,四个主要应用程序将具有对共享库的二进制引用。
这种方法的主要优点在于现在应用程序的稳定性,因为共享库是静态的。除非明确更新二进制文件以使用新版本,否则对库进行更改不会影响应用程序。分支会随之带来二进制引用。您不会遇到一个看似无害的更改破坏其他三个应用程序的情况。

由于共享库目前经常在更改,我们不能将其作为svn外部引用到主干,因为我们可能会在分支中更改它们。因此,我们不能将它们用作二进制引用。 - sontek
如果您要添加到共享库中,我建议将新代码放在应用程序分支中,然后仅在其他应用程序需要时将其移动到共享库中。在应用程序中进行开发,直到完全测试通过,然后仅在需要时向其他应用程序公开。通常不应更改共享库,除非是修复错误 - 现有类/方法的功能/接口通常应保持不变。开闭原则 - 添加功能,不要更改现有内容。 - Brian Frantz
这些共享库是我们应用程序的核心,如果发现错误或出现新功能,则需要进行更改。例如,其中一个共享库是一些在两个应用程序中使用的asp.net用户控件,因此最近我正在重新设计信用卡/支付控件以添加付款计划功能(付款计划是新的业务需求)。显然,我不想更改主干中的付款控件,因为那会干扰其他人的工作。因此,我需要分支直到它正常工作,然后将其合并回主干。 - sontek
如果所有的应用程序都如此紧密耦合,那么也许它们应该成为一个巨大的解决方案?或者也许是两个解决方案 - 网络和桌面?然后你可以分支整个组。无论哪种方式,我建议重新评估你的整个方法,看看是否有更好的适合你所要做的事情。 - Brian Frantz
一个巨大的解决方案通常更容易。或者,如果“巨大”的解决方案变得太大,那么可以有许多解决方案,仅引用足够的项目以使它们的部分编译通过。然而请记住,即使您使用很多小项目,仍应将所有项目构建为一个单独的巨大解决方案。在我的工作中,我们制作了一个小实用程序,读取小型解决方案并在我们的自动化构建过程中生成一个巨大的解决方案。这样,我们就不必在添加或删除项目时维护巨大的解决方案了。 - JohannesH

4

能否说明一下为什么您不喜欢同时分支所有四个应用程序?

这会使得单独分支/标记一个应用程序非常困难,因为您同时在分支所有四个应用程序。

通常我会直接将所有项目放在主干(trunk)下,就像您目前正在做的那样。然后当我创建发布分支或功能分支时,我只需要忽略其他一起进行的项目。请记住,复制是廉价的,所以它们不会占用服务器空间。

具体来说,这是我描述的源代码树布局:

  • 主干(trunk)
    • WPF1
    • WPF2
    • ASP.NET 1
    • ASP.NET 2
    • lib1
    • lib2
  • 分支(branches)
    • WPF1 v 1.0
      • WPF1
      • WPF2
      • ASP.NET 1
      • ASP.NET 2
      • lib1
      • lib2
    • WPF1 v 1.1
      • WPF1
      • WPF2
      • ASP.NET 1
      • ASP.NET 2
      • lib1
      • lib2
    • lib1付款计划
      • WPF1
      • WPF2
      • ASP.NET 1
      • ASP.NET 2
      • lib1
      • lib2

服务器上的复制很便宜,但是在开发人员的计算机上结账和复制并不便宜。大多数开发人员喜欢保留整个存储库的检出状态,以防他们必须与某人共同工作的分支,他们已经将其拉下来了,但是当您有几个分支和标记时,这开始变得非常耗时。但到目前为止,这似乎是最好的解决方案 :) - sontek
2
在您的情况下,我会检查主干或单个分支,无论我正在工作。然后使用 switch 来处理另一个分支。我可能会检出两个工作副本,这样我就可以将一个留在我的主要工作分支上,使用另一个在其他分支上进行小工作时进行切换。另一个要考虑的是浅层检出:从根目录检出整个存储库,但在需要时不要向下递归该检出到分支。我发现这更麻烦,因为从您的检出中删除分支很麻烦。 - Don Kirkby
2
我同意这是正确的方法。开发人员保持整个代码库已检出是可以改变的行为 - 没有理由使用次优解决方案来满足实际上是懒惰的需求。除此之外,当有人正在处理实验性的WPF 1.1分支时,我不想在我只是尝试处理一个特定分支时等待获取所有这些更新。 - gregmac
1
我曾尝试过参与使用Brians和Dons分离方法的项目,我认为按照Don的建议做实际上更容易,并且在某些方面也更正确。如果你不能一直掌握Brian建议的方法,很快就会导致版本问题。 - JohannesH

4
我们正在启动一个开源项目,试图解决这个问题。如果有人对此感兴趣并想进行评论或贡献,可以访问以下网址:

http://refix.codeplex.com


请参阅Maven替代方案的问题以获取一组管理二进制依赖项的.NET工具。然而,我认为您可以使用这些工具来管理第三方二进制文件获得最大的效益。个人而言,如果我的团队撰写了库,则更喜欢从源代码构建库。 - Don Kirkby

1

我同意@Brian Frantz的观点。没有理由不将共享库视为自己的项目,每天构建,并使您的项目对每日构建进行二进制依赖。

但是,即使您想将它们保留为源代码依赖项并与应用程序一起构建,为什么不使用SVN externals方法呢?当您分支特定应用程序时,无需分支共享库,除非您需要该分支的单独副本。但这意味着它不再是共享库了,对吧?


由于共享库正在频繁更改,因此我们不能将它们用作svn外部引用到主干,因为我们可能会在分支中更改它们。因此,我们不能将它们用作二进制引用。我们分支的原因是因为我们可能会进行重要更改,这些更改会影响共享库,但我们不希望在更改修复并在分支中测试之前破坏其他项目,然后当我们将其合并回主干时,我们会在其他应用程序中修复对它的调用。 - sontek
那是一场灾难性的配方。当你分支两个应用程序以及它们共享的库时会发生什么?合并共享库的更改可能会(而且很可能会)成为后勤噩梦。(我曾经历过这种情况:-) - Franci Penov
显然这是一个问题,但我们必须依靠开发人员之间的良好沟通,他们很少需要在完全相同的代码行上工作,如果他们确实触及了相同的代码行并且发生冲突,我们有 diff 工具来找出问题所在,这只是团队开发的一部分 :)我认为最好将代码分支化,这样他们更改的所有内容都在该分支中,不会影响其他开发人员,唯一需要分支的时候是当我们要进行重大更改时 :) - sontek
不应该在客户端应用程序的分支中进行共享库的重大更改。应该将其放入共享库分支中,该分支具有所有客户端应用程序的稳定版本,以便您完全控制共享库中正在发生的变化。 - Franci Penov

1

多年来,我尝试了几种方法来解决这个问题,但老实说,并没有最佳解决方案。

我们的团队目前处于一个巨大的开发阶段,每个人基本上都需要随时使用最新的共享库。因此,我们在每个人的C:驱动器上都有一个名为SharedLibs\Latest的文件夹,它会自动与我们共享库的最新开发版本同步。每个应该从中受益的项目都有绝对文件引用到这个文件夹。当人们推出共享库的新版本时,各个项目会透明地接收到它们。

除了最新的文件夹外,我们还有一个名为SharedLibs\Releases的文件夹,其中包含每个共享库每个版本的层次结构文件夹。随着项目的成熟和接近发布候选阶段,共享库引用将指向这些稳定的文件夹。

最大的缺点是这个结构必须存在于任何项目中才能进行构建。如果有人想要在10年后构建一个应用程序,他们仍然需要这个结构。需要注意的是,这些文件夹也需要存在于构建/CI服务器上。

在此之前,每个解决方案都有一个位于源代码控制下的 lib 文件夹,其中包含二进制文件。每个项目所有者都负责传播新的共享 dll。由于大多数人拥有多个项目,因此对于仍处于非稳定阶段的项目,事情经常会失掉。此外,TFS 似乎无法很好地跟踪二进制文件的更改。如果 TFS 能更好地跟踪 dll,我们可能会使用共享 libs 解决方案/项目,而不是现在采用的文件系统方法。

请看一下Apache NPanday + Maven Release + Nexus Repository Manager(我是一个提交者;一个用户要求我们让NPanday变得更加可见,因为它非常棒:-))。 - Lars Corneliussen

1

Apache NPanday + Apache Maven Release

...可能解决你的问题

它为你提供了依赖管理(传递解析)、强大的版本支持以及在包括 SVN 在内的 14 种以上版本控制系统上自动标记/分支。

如果需要更详细的说明,请告诉我。

我认为你无法避免将版本控制和共享库分发作为单独的构件,但是 Maven 可以帮助你轻松完成这些工作!

而且你总可以使用一些技巧来将所有内容都放在一个解决方案中 :-)

一个示例工作流程:

  1. Dev1在本地使用Maven构建A
  2. 将代码提交
  3. 构建服务器构建A并将所谓的快照版本部署到存储库管理器(例如Nexus)
  4. Dev2加载B,NPanday会自动从存储库管理器中解析A-libs(无需获取源代码和构建)
  5. Dev1想要发布A:Maven Release将创建一个分支或标签以及您的源代码,完成版本(删除SNAPSHOT)并将构件部署到存储库管理器。
  6. Dev2现在可以升级B以使用A的最终版本(更改XML中的条目或使用VS插件进行操作)
  7. 现在Dev2可以发布B,再次自动创建标签或分支并部署已构建的构件。

如果您想从构建输出提供压缩包,则Maven Assembly Plugin将帮助您完成此操作。


0

您可以在独立模式下使用Apache/IVY。

http://ant.apache.org/ivy/history/latest-milestone/standalone.html

我需要强调“独立运行”模式。如果你在谷歌上搜索示例,你会发现很多不是独立运行的。

基本上,IVY就是这样工作的。

你发布二进制文件(或任何类型的文件,但从这一点开始我会说二进制文件)......作为小的二进制包。

以下是伪代码,请不要依赖我的记忆。

java.exe ivy.jar -publish MyBinaryPackageOne.xml --revision 1.2.3.4 (<<其中.xml指的是组成一个包的N个文件。)

“包”只是一组文件。你可以在一个包中包含.dll、.xml和.pdb文件(我用DotNet构建程序集时所做的)。或者其他什么。IVY对文件类型没有限制。如果你想把Word文档放在那里,你可以,但SharePoint更适合文档。

当你修复代码中的错误时,你会增加版本号。

java.exe ivy.jar -publish MyBinaryPackageOne.xml --revision 1.2.3.5

然后你可以从IVY中检索你想要的内容。

java.exe ivy.jar -retrieve PackagesINeed.xml 

PackagesINeed.xml将包含您想要的软件包信息。

例如 “我想要版本为'1.2+的MyBinaryPackageOne” (在xml中定义)

当您构建框架二进制文件时...您会发布到IVY。 然后,当您开发和构建代码时...您从IVY检索。

简而言之,IVY是一个文件存储库(不是源代码)。

然后,Ivy成为您的二进制文件的权威来源。

没有“嘿,开发者Joe有我们需要的二进制文件”之类的废话。

.......

优点: 1. 您不需要将二进制文件保存在源代码控制中(因此不会使您的源代码控制膨胀)。 2. 您只需拥有一个二进制文件的权威来源。 3. 通过 XML 配置,您可以指定库所需的版本。(例如,如果 MyBinaryPackageOne 的版本 2(2.0.0.0)已发布到 IVY(假设与 1.2.x.y 有不兼容的更改),则您可以在检索时(XML 配置文件中)定义只需要“1.2+”,这样您的项目就会忽略任何 2+ 版本……除非您更改配置包。)
高级: 如果您有一个构建机器(例如 CruiseControl.NET)……您可以编写逻辑,在每次构建后将新构建的二进制文件发布到 IVY 中。(这就是我所做的。)
我使用 SVN 修订版作为构建号的最后一位数字。 如果我的 SVN 修订版是“3333”,那么我会运行类似于以下命令: java.exe ivy.jar -publish MyBinaryPackageOne.xml --revision 1.2.3.3333

因此,每当检索修订版“1.2.3+”的包时...我将获得最新版本的构建。 在这种情况下,我将获得包的1.2.3.3333版本。

令人遗憾的是,IVY始于2005年(好消息)...但NUGET直到2010年才发布?(2011年?) 在这一点上,微软落后了5-6年,我个人认为。

我永远不会回到把二进制文件放入源代码控制中。

IVY非常出色。它经过时间的考验。它解决了依赖关系管理的问题。

需要花费一点时间来适应它吗? 是的。

但最终它是值得的。

这是我的两分钱。

.................

但是第二个想法是学习如何使用NUGET与本地(即您公司的本地)存储库。

这与IVY大致相同。

但是经过研究NUGET后,我仍然喜欢IVY。


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