大多数颠覆工具使用 /TRUNK, /分支和 /标签创建默认存储库布局。该文档还建议不要为每个项目使用单独的存储库,以便更容易共享代码。

遵循该建议导致我拥有以下布局的存储库:

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

等等,您明白了。随着时间的流逝,我发现这种结构有些笨拙,在我看来,对这些建议有另一种解释,例如:

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

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

有帮助吗?

解决方案 5

我决定咬子弹并重组我的存储库。我写了一个小程序来协助(下图)。我遵循的步骤是:

  1. 制作原始存储库的备份副本。
  2. svn checkout 整个存储库. 。这花费了很长时间和很多磁盘空间。
  3. 从下面的工作副本中从下面运行该程序。
  4. 检查修改后的工作副本,并整理剩下的问题(例如。 svn delete 过时的 树干, 标签分支 文件夹)
  5. svn commit 回到存储库。

整个过程花了一些时间,但是我决定采用这种方法,因为修改工作副本比黑客入侵现场存储库要安全得多,我可以选择简单地丢弃工作副本,如果一切都出错,请解决任何问题在工作副本中,并将整个重组作为单个修订。

这是我用来进行移动的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");
        }
    }
}

其他提示

我发现 颠覆存储库布局 博客文章总结了这一点:

(...)社区已经采用了几种常见的布局作为最佳实践,因此可以将其视为建议。如果您的存储库可以被公众访问,那么遵循这些惯例可能会使访问其他颠覆存储库的用户更容易找到所需的东西。

有两个常用的布局:

trunk
branches
tags

第一个布局是包含单个项目或一组彼此紧密相关的项目的存储库的最佳选择. 。这种布局很有用,因为它易于分支或标记整个项目或一组具有单个命令的项目:

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

这可能是最常用的存储库布局,并且由许多开源项目(例如颠覆本身和子开机)使用。这是大多数托管网站(例如tigris.org,sourceforge.net和Google代码)的布局,因为这些网站上的每个项目都有自己的存储库。

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

ProjectA
   trunk
   branches
   tags
ProjectB
   trunk
   branches
   tags

在此布局中,每个项目都会收到一个顶级文件夹,然后在其下方创建了TRUNK/BRANCE/TAGS文件夹。这实际上与第一个布局是相同的布局,而是只是将每个项目放在自己的存储库中,而是一个存储库中。 Apache Software Foundation使用此布局作为其存储库,该存储库将其所有项目包含在一个存储库中。

使用此布局,每个项目都有自己的分支机构和标签,很容易使用一个命令为该项目中的文件创建它们,类似于先前显示的文件:

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

在本布局中,您无法轻松地做的是创建一个包含Projecta和ProjectB文件的分支或标签。您仍然可以做到这一点,但是它需要多个命令,还必须决定是否要为涉及多个项目的分支和标签制作一个特殊的文件夹。如果您需要做很多事情,则可能需要考虑第一个布局。

因此,解释:

  • 将第一个布局用于单个或多个 有关的 项目。
  • 使用第二个布局 非相关 项目。

整个帖子值得阅读。

第二个布局是要走的方式。一个充分的理由是允许或否认开发人员与其中一个项目合作。

我更喜欢第二。第二个项目之间的权限有所不同,这将更容易实施。

我非常喜欢第二个,如果需要的话,请使用Maven或Ant/Ivy摄取其他项目的工件。

我也更喜欢每个存储库或少数相关存储库有一个项目。

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

备份/还原操作最初要复杂一些,因为您必须循环浏览所有存储库才能进行热拷贝,但是在不幸的事件中,您只需要还原一个存储库 - 其他存储库不需要离线或丢失任何数据。随着项目的死亡,可以简单地删除存储库,从而为您节省以后的备份空间。

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

正如反到后指出的那样,如果您想使用svndumpfilter选择性地导出一个整体存储库可能会造成巨大的痛苦 - 导致其死亡的更改路径的数量可能很高。

升级存储库模式以将来的SVN版本需要更多的努力 - 您必须这样做 n 时代而不是一次...但是可以脚本脚本,您不需要一次协调每个人。

如果有人使用密码,并且您必须删除密码,则可以在一个存储库中快速进行转储/过滤器/重新加载,同时不影响其他团队。

一个建议,如果您走这条路线 - 每个回购有所不同,而不是一个大型文件,同样,它更容易管理,并提供一些时间戳将要旧的舒适感 - 如果有什么不对劲,您可以寻找一些时间戳最近的变化更容易。

参考 存储库布局 来自SVNBook

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

/
   trunk/
   branches/
   tags/

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

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

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

当然,您可以自由地忽略这些常见的布局。您可以创建任何最适合您或您的团队的变体。请记住,无论您选择什么,这都不是永久的承诺。您可以随时重组存储库。由于分支机构和标签是普通目录,因此SVN MOVE命令可以随心所欲移动或重命名。从一个布局切换到另一种布局只是发出一系列服务器端移动的问题。如果您不喜欢在存储库中组织的方式,那么只需兼顾目录。

但是请记住,在移动目录很容易执行时,您也需要体贴其他用户。您的杂耍可能会迷失现有工作副本的用户。如果用户具有特定存储库目录的工作副本,并且您的SVN MOVE子命令从最新的修订中删除了路径,那么当用户接下来运行SVN更新时,她被告知她的工作副本代表不再存在的路径。然后,她被迫切换到新位置。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top