Subversion存储库布局

时间:2010-04-09 22:03:05

标签: svn repository-design

大多数subversion工具使用/ trunk,/ branches和/ tags创建默认存储库布局。该文档还建议不要为每个项目使用单独的存储库,以便可以更轻松地共享代码。

根据该建议,我得到了一个具有以下布局的存储库:

/trunk
      /Project1
      /Project2
/branches
         /Project1
         /Project2
/tags
     /Project1
     /Project2

等等,你明白了。随着时间的推移,我发现这个结构有点笨拙,我发现对建议有另一种解释,例如:

/Project1
         /trunk
         /branches
         /tags
/Project2
         /trunk
         /branches
         /tags       

那么,人们使用哪种布局,为什么?或者 - 还有另一种方法可以做我完全错过的事情吗?

6 个答案:

答案 0 :(得分:32)

我发现Subversion Repository Layout博客文章很好地总结了这一点:

  

(...)有几种常见的布局   已被采纳的   社区作为最佳做法和   因此可以将这些视为   建议。如果您的存储库是   以下是公众可以访问的   这些约定可能会使它更容易   对于已访问其他用户的用户   Subversion存储库找到了什么   他们正在寻找。

     

有两种常用的布局:

trunk
branches
tags
     

第一种布局是最佳选择   对于包含a的存储库   单个项目或一组项目   与每个人紧密相关   其他即可。这种布局很有用,因为   分支或标记它很简单   整个项目或一组项目   用一个命令:

svn copy url://repos/trunk url://repos/tags/tagname -m "Create tagname"
     

这可能是最常见的   使用的存储库布局和使用   很多开源项目,比如   Subversion本身和Subclipse。这个   是大多数托管网站的布局   像Tigris.org,SourceForge.net和   Google代码跟随每个项目   这些网站是自己的   库中。

     

下一个布局是最佳选择   包含无关的存储库   或松散相关的项目

ProjectA
   trunk
   branches
   tags
ProjectB
   trunk
   branches
   tags
     

在此布局中,每个项目都会收到   一个顶级文件夹,然后是   trunk / branches / tags文件夹   在它下面创造。这真的是   与第一个布局相同的布局,它是   只是而不是把每个   他们在自己的存储库中项目   都在一个存储库中。该   Apache Software Foundation使用它   他们的存储库的布局   包含他们所有的项目   单一存储库。

     

通过这种布局,每个项目都有它   自己的分支和标签很容易   为其中的文件创建它们   项目使用一个命令,类似于   之前显示的那个:

svn copy url://repos/ProjectA/trunk url://repos/ProjectA/tags/tagname -m "Create tagname"
     

你不能轻易做到这一点   layout是创建一个分支或标记   包含来自ProjectA和的文件   项目B。你仍然可以做到,但它   需要多个命令和你   还必须决定你是否要去   为...创建一个特殊的文件夹   分支和标签涉及   多个项目。如果你要去   需要做很多事,你可能想要   考虑第一个布局。

所以,换句话说:

  • 对单个或多个相关项目使用第一个布局。
  • 非相关项目使用第二种布局。

整篇文章值得一读。

答案 1 :(得分:8)

第二种布局是要走的路。一个很好的理由是允许或拒绝开发人员使用其中一个项目。

答案 2 :(得分:5)

我更喜欢第二个。第二,如果两个项目的人员权限不同,则更容易实现。

答案 3 :(得分:2)

我非常喜欢第二种,如果需要,可以使用maven或ant / ivy从其他项目中摄取工件。

我还希望每个存储库或少量相关存储库都有一个项目。

它简化了访问控制,这在存储库级别比存储库中的路径级别更容易 - 特别是在针对LDAP进行身份验证时。

备份/恢复操作最初有点复杂,因为您必须遍历所有存储库才能执行热拷贝,但在不幸的事件中,您只需要还原一个存储库 - 其他存储库不需要脱机或松散任何数据。随着项目的消亡,可以简单地删除存储库,从而为将来的备份节省空间。

当每个存储库只有一个项目(或少量相关项目)时,Hook脚本变得更简单,您不必检查受影响的路径以有条件地在钩子中执行操作。

正如retracile指出的那样,如果您想要使用svndumpfilter选择性地导出,那么一个整体存储库可能会非常痛苦 - 导致其死亡的更改路径的数量可能很高。

为svn的未来版本升级存储库架构需要更多的努力 - 你必须 n 次而不是一次......但它可以编写脚本,你不需要一次协调所有人

如果有人提交密码,你必须删除密码,你可以在一个仓库中快速进行转储/过滤/重新加载,同时不影响其他团队。

如果你采用这条路线,那么有一条建议 - 每个仓库有一个不同的.conf文件而不是一个巨大的文件,再次管理更容易,并且提供一些时间戳会变旧的安慰 - 如果有什么不对劲你可以更容易地寻找最近的变化。

答案 4 :(得分:1)

我决定咬紧牙关并重新构建我的存储库。我写了一个小程序来帮忙(下面)。我遵循的步骤是:

  1. 制作原始存储库的备份副本。
  2. svn checkout 整个存储库。这需要很长时间和大量磁盘空间。
  3. 在上一步的工作副本上运行下面的程序。
  4. 检查修改后的工作副本并整理任何遗留问题(例如svn delete过时的主干标记分支文件夹)
  5. svn commit返回存储库。
  6. 整个过程需要时间,但我决定采用这种方法,因为修改工作副本比黑客攻击实时存储库要安全得多,而且如果一切都出错,我可以选择简单地丢弃工作副本,修复工作副本中的任何问题,并将整个重组作为单个修订提交。

    这是我用来移动的C#代码。需要SharpSvn库。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Text;
    using SharpSvn;
    
    /**
     * 
     * Program operation:
     * 1. Parse command line to determine path to working copy root
     * 2. Enumerate folders in the /trunk 
     * 3. Restructure each project folder in /trunk
     * 
     * 
     * Restructure a Project:
     * 1. Get the project name (folder name in /trunk/{Project})
     * 2. SVN Move /trunk/{Project} to /{Project}/trunk
     * 3. Reparent Project, branches
     * 4. Reparent Project, tags
     * 
     * Reparent(project, folder)
     * If /{folder}/{Project} exists
     *   SVN Move /{folder}/{Project} to /{Project}/{Folder}
     * else
     *   Create folder /{Project}/{Folder}
     *   SVN Add /{Project}/{Folder}
     * 
     **/
    
    namespace TiGra.SvnRestructure
    {
        /// <summary>
        /// Restructures a Subversion repository from
        ///     /trunk|branches|tags/Project
        /// to
        ///     /Project/trunk|branches|tags
        /// </summary>
        internal class Program
        {
            private static string WorkingCopy;
            private static string SvnUri;
            private static string Branches;
            private static string Tags;
            private static string Trunk;
    
            private static SvnClient svn;
            private static List<string> Projects;
    
            private static void Main(string[] args)
            {
                ProcessCommandLine(args);
                CreateSvnClient();
                EnumerateProjectsInTrunk();
                RestructureProjects();
                Console.ReadLine();
            }
    
            private static void RestructureProjects()
            {
                foreach (var project in Projects)
                {
                    RestructureSingleProject(project);
                }
            }
    
            private static void RestructureSingleProject(string projectPath)
            {
                var projectName = Path.GetFileName(projectPath);
                var projectNewRoot = Path.Combine(WorkingCopy, projectName);
                bool hasBranches = Directory.Exists(Path.Combine(Branches, projectName));
                bool hasTags = Directory.Exists(Path.Combine(Tags, projectName));
                Reparent(Path.Combine(Trunk, projectName), Path.Combine(projectNewRoot, "trunk"));
                if (hasBranches)
                    Reparent(Path.Combine(Branches, projectName), Path.Combine(projectNewRoot, "branches"));
                if (hasTags)
                    Reparent(Path.Combine(Tags, projectName), Path.Combine(projectNewRoot, "tags"));
            }
    
            private static void Reparent(string oldPath, string newPath)
            {
                Console.WriteLine(string.Format("Moving {0} --> {1}", oldPath, newPath));
                svn.Move(oldPath, newPath, new SvnMoveArgs(){CreateParents = true});
            }
    
            private static void EnumerateProjectsInTrunk()
            {
                var list = EnumerateFolders("trunk");
                Projects = list;
            }
    
            /// <summary>
            /// Enumerates the folders in the specified subdirectory.
            /// </summary>
            /// <param name="trunk">The trunk.</param>
            private static List<string> EnumerateFolders(string root)
            {
                var fullPath = Path.Combine(WorkingCopy, root);
                var folders = Directory.GetDirectories(fullPath, "*.*", SearchOption.TopDirectoryOnly).ToList();
                folders.RemoveAll(s => s.EndsWith(".svn")); // Remove special metadata folders.
                return folders;
            }
    
            private static void CreateSvnClient()
            {
                svn = new SharpSvn.SvnClient();
            }
    
            /// <summary>
            /// Processes the command line. There should be exactly one argument,
            /// which is the path to the working copy.
            /// </summary>
            private static void ProcessCommandLine(string[] args)
            {
                if (args.Length != 1)
                    throw new ArgumentException("There must be exactly one argument");
                var path = args[0];
                if (!Directory.Exists(path))
                    throw new ArgumentException("The specified working copy root could not be found.");
                WorkingCopy = path;
                Branches = Path.Combine(WorkingCopy, "branches");
                Tags = Path.Combine(WorkingCopy, "tags");
                Trunk = Path.Combine(WorkingCopy, "trunk");
            }
        }
    }
    

答案 5 :(得分:0)

请参阅svnbook中的Repository Layout

  

有一些标准的,推荐的方法来组织内容   存储库。大多数人创建一个trunk目录来保存“main”   “开发行”,包含分支副本的分支目录,   以及包含标签副本的标签目录。

/
   trunk/
   branches/
   tags/
     

如果存储库包含多个项目,则管理员通常会编制索引   他们按项目布局。

     

这是一个这样的布局的例子:

/
   paint/
      trunk/
      branches/
      tags/
   calc/
      trunk/
      branches/
      tags/
     

当然,您可以自由地忽略这些常见布局。你可以创建   任何类型的变化,无论对您或您的团队最有效。   请记住,无论您选择什么,它都不是永久的承诺。   您可以随时重新组织您的存储库。因为分支和   标签是普通目录,svn move命令可以移动或重命名   但是你希望他们。从一种布局切换到另一种布局只是一个   发布一系列服务器端移动的问题;如果你不喜欢   事情在存储库中组织,只是玩杂耍   周围的目录。

     

请记住,虽然移动目录很容易,但是   需要考虑其他用户。你的杂耍可以   使用现有工作副本的用户迷失方向。如果用户有工作   特定存储库目录和svn move子命令的副本   从最新修订版中删除路径,然后在用户下一步时删除   运行svn update,她被告知她的工作副本代表一条路径   不再存在。然后她被迫转向新的   位置。