代码之家  ›  专栏  ›  技术社区  ›  Sean McMillan

用于分支共享库的存储库布局(或钩子)(并保持清醒)

  •  4
  • Sean McMillan  · 技术社区  · 16 年前

    我们正在使用Subversion(这个问题可能适用于许多版本控制系统,但Subversion是我真正关心的一个问题)。

    我们的存储库布局如下:

    (布局A)

    Web
      branches
      tags
      trunk
    Libraries
      Foo
        branches
        tags
        trunk
      Bar
        branches
        tags
        trunk
    WindowsClient
      branches
      tags
      trunk
    DB
      branches
      tags
      trunk
    

    问题是版本控制单元不等于开发单元——我必须进行多次签出以获得可构建的工件,当我进行分支时,我必须分支多个组件(并在多个地方签入)。

    这意味着我们可以转而采用这样的结构:

    (Layout B)

    Web
      branches
      tags
      trunk
        main
        libs
          Foo
          Bar
        DB
    WindowsClient
      branches
      tags
      trunk
        main
        libs
          Foo
          Baz
        DB
    

    但是我们有任何共享库的副本。我们可以在使用svn:externals时映射共享的lib,但这只是一个假象——当包含该项目时,它们不会被分支。

    最后一个选择是:

    (布局C)

    branches
    tags
    trunk
      Web
      Libraries
        Foo
        Bar
      WindowsClient
      DB
    

    这可以确保库与其包含的项目一起进行分支,但以分支单位是整个世界为代价。(这也意味着结账的单位是整个世界,这也很烦人。)

    我要的是存储库布局 (布局D) 这使我能够:

    • 同时分支项目及其依赖库
    • 在项目之间共享库

    如果我能在一次签出中签出项目及其库,那就太好了,但这并没有上面提到的那么重要。

    所以问题是:

    是否有布局D,它是什么,如何使用它?

    编辑: 因为似乎没有一个基本的布局可以提供这些属性,所以我会对某种钩子函数非常感兴趣。如果它能与Tortoissesvn(WindowsGUI)客户机一起工作,那就特别好了,因为这正是我们所使用的。

    7 回复  |  直到 16 年前
        1
  •  1
  •   retracile    16 年前

    使用选项C,然后像这样签出:

    svn co -N ...../branches/mybranch workingcopy
    cd workingcopy
    svn update Web Libraries
    

    现在,当您执行SVN操作时(包括 svn update “)它只会处理 Web Libraries 目录。

    也读到 sparse directories .

        2
  •  1
  •   Phil Miller    16 年前

    对于“如何为这个工作流程安排存储库”的问题,没有很好的答案。因为软件并不真正支持这一点。我建议使用你的布局B,将库代码分支,并切换相关的 svn:external 如果需要的话,或者如果分支需要引用库的非主干版本,请立即转到该分支。

    我本来打算建议Git更好地处理这个问题,但没什么大不了的。因为它的子模块引用的是与外部存储库稍有不同的单独存储库,并且存储库的每个副本都是一个“分支”,这可能会有一些改进。

        3
  •  1
  •   Wim Coenen    16 年前

    我们可以在使用中映射共享的libs 外部的,但那只是 错觉——它们不会被分开 当包含的项目为时。

    实际上,它们是分支的 如果它们在同一个存储库中,并且您使用了相对外部语法,例如 ^\mylib\trunk . 这些外部引用将更改为普通(复制)文件夹。你必须明确通过 --ignore-externals svn copy 为了抑制这种行为,否则您将得到类似布局B中的副本。 ( 编辑 我很确定它是这样运作的,但我似乎无法重现这种行为。我一定弄错了,对不起!)

    事实上,外部并不总是自动分支,这不一定是个问题。我将使用布局B svn:externals (不是副本),将项目分为 --忽略外部 ,然后在分支之后,适应 外部的 指向正确的库分支。

    您可以设置Externals以指向特定的修订版(这有利于严格控制;您可以决定何时升级到lbrary的新修订版),或者只跟踪头部(这有利于持续集成,假设您已经设置了构建服务器)。

        4
  •  0
  •   Chris Chilvers    16 年前

    我们解决这个问题的方法是,对于多个项目使用的共享外部库,共享库使用自己的主干/分支/标记进入自己的存储库。

    然后,我们有一个构建服务器构建和发布集成构建、里程碑和发布,并且二进制artefact被复制到一个共享位置,并存储在特定于版本的目录中(这些目录是备份的)。

    依赖项目的构建脚本的一部分(通常作为init/update而不是作为标准构建的一部分按需运行),然后检查新版本并获取二进制文件。这有一个优势,即在依赖项目之间对共享的人工制品进行一致的版本控制,并减少了构建时间,因为所有依赖项目都可以共享相同的版本。

    帮助我们当前使用的版本控制 Apache Ivy 它支持瞬时依赖项(即获取依赖项的依赖项)和版本约束(例如,此项目只应使用foo版本1.2.*)。

        5
  •  0
  •   Jim T    16 年前

    我建议是你对这些图书馆的处理方式导致了你的问题。如果开始将库视为独立的项目,则可以更改此设置。把它们看作是有它们自己存在的原因、它们自己的设计和它们自己的发布周期,就像您可能用于单元测试的第三方库、XML阅读器、DB访问等。

    当然,您经常会遇到这样的情况:项目中的某个特性需要库中的一个新特性。实现库功能和利用库功能是两个独立的任务——它们可能是一个业务任务,但它们是两个开发任务。没有必要仅仅因为这就是工作的来龙去脉就把这两个活动紧密地联系在一起。签出库,更改它,释放它,然后签出项目,并在项目中使用库的新版本。

    我强烈地认为,将库分离到它们自己的主干中是一件好事——当我看到单个主干下有多个独立可发布的项目时,我无法忍受。它有点设计不好,而且由于开发不成熟而陷入困境。但是要将它们分开,你必须能够独立地发布每个项目——对我来说,这就是拥有多个项目的原因。 方法 . 但这并不难做到:

    首先,项目使用外部引用库的特定发布版本。这是项目引用库的唯一方式。这样做意味着开发人员可以在不破坏任何使用库的项目的情况下生成库的新版本,因为所有项目都将引用以前的版本。当项目想要引入新版本的库时,他们就可以进行控制——开发人员可以选择何时需要投入精力用新版本测试他们的代码,以及何时需要承担修复新库引入的任何构建问题的痛苦。

    当您显式地更改像这样的库的版本时,您的项目中也会有一个条目显示“我正在使用 库X的版本”,它让您对项目中的历史有一个很好的了解,即什么时候工作,什么时候事情发生了变化。

    当然,这在理论上是很好的,但在实践中,开发人员有时不得不引用库中不稳定和未完成的版本。这很好——开发人员总是可以将他们的工作副本切换到指向库主干,而不是标记或某个开发分支,并使用那里的代码(如果必须的话,甚至可以在那里工作,brrr)。开关只是本地编辑,因此对提交的代码没有影响。如果项目开发在一个不稳定的分支上,那么您可以通过更改外部引用来决定使切换更持久,直到分支准备好重新集成为止,但这并不是通常在没有明确原因的情况下进行的操作。

    最后,对您的项目进行分支和标记就变成了对您的主项目进行分支或标记的一个简单案例——仅此而已。不用担心分支库——它们会照顾自己。对库进行更改的过程不会改变项目是否在主干、开发分支或维护版本中。而且,您的库本身也可以拥有完全独立于主项目的开发分支,以及多个支持的版本等,可以达到您需要和支持的复杂程度。

    通过在主干或开发分支上使用Externals,您可以使用一个签出来构建您需要的任何结构的工作区。因为所有库都在主根目录下,所以您可以多次签出多个版本,而不会在生成中发生冲突。

    我发现这个系统运行得很好,在把工作转移到一个不这样工作的地方之后,我发现自己渴望着以前的工作方式。存在一些问题,主要是与依赖库的库有关,以及是否具有递归外部性。我的观点是递归,除非它引起了问题(或者过度的痛苦),然后转移到一个“退化”模型,在这个模型中,项目必须知道某些“深层”依赖性,即使它不直接使用它们。另外,决定将外部定义放在何处并坚持使用它,没有什么比搜索那些SVN更烦人的了:不同项目中随机文件夹的外部属性。把它们放在树干的根部是很好的。

        6
  •  0
  •   Ian Ringrose    16 年前

    这是一个没有好的解决方案的难题; 高端解决方案是使用__ software product lines _管理系统(例如 pure::variants )。然而,我们中的大多数人并没有在源代码控制系统上花费这种匹配。

    所以我会和你一起去 莱图塔 _“,每个库单独版本。不过,我倾向于把__ 大旅行箱 _157;低于_156; 分支机构 _157;因为它是一个分支,我喜欢所有分支都与顶部保持相同的距离。

    下一步更取决于您的构建系统,我假设这里是Visual Studio。

    • 在每个产品分支树的根中
    • 创建一个BAT文件,该文件定义一个环境变量,该变量包含要使用的每个库的分支的名称。
    • 编辑Visual Studio项目文件以使用这些环境变量引用库
    • 在启动Visual Studio之前,从Visual Studio命令提示符运行批处理文件

    您还可以考虑编写自定义的msbuild文件,而不是使用批处理文件。或者编写一个工具,在更改库版本时编辑所有项目文件。

    如果您只有一个或两个共享库,并且一次只对一个产品进行更改,例如为正在进行的项目添加新方法。我会考虑为每个项目拥有不同的库分支,并使用SVN1.5合并跟踪来跟踪正在发生的事情。(当变更稳定时,合并到卡车,然后在需要时从卡车合并到每个项目分支)

    (如果你有100多个库,你必须跟踪每个库相互需要的witch版本。这开始变得非常复杂!)

    我不喜欢svn:external,因为您的本地PC上的文件系统不清楚发生了什么。然而,SVN:External是一个可行的解决方案。

        7
  •  0
  •   gavinb    16 年前

    经历过类似的问题,我知道你的痛苦。在具有层次结构组件的存储库中管理依赖关系是一个困难的问题。

    我们的项目有几个产品(无论您发送给客户的是什么产品)由不同的组件(其中许多组件是共享的)组成。我们每个部件都有自己的 tags/branches/trunk 三部曲,很像你的布局A(这毕竟是推荐的方式)。

    我们确实使用过 svn:externals 为每种产品提供一种指定依赖组件(和子组件等)的方法,并且最初它工作得相当好。但最终我们遇到了一些问题,例如当您进行分支时会发生什么,如果一个产品需要在某个版本上固定一个depdency,那么如何通过externals传播标签以进行配置管理(这样您就可以真正地重建同一棵树!)等等。所以 外部的 解决一些问题,但介绍其他问题。

    我最后写了一些脚本来处理这个问题,但还是有点费解。幸运的是,您可以使用python subversion绑定来编写python应用程序来操作属性,这样您就可以执行诸如通过依赖组件传播标记等操作。

    有一个项目旨在解决依赖模块的这个问题,称为 Piston .对于这类问题,它看起来是一个非常好的通用工具。我并没有在生产中部署它,但当时看起来它可以满足我们的大部分需求。当然,它看起来比外部系统提供的解决方案更灵活(这仍然是一个非常手工的过程)。

    底线: 您可以坚持使用布局A,并使用活塞来管理依赖项,以便将库的所有正确版本组装到您的工作目录中。