34 Stimmen

Subversion-Repository-Layout

Die meisten Subversion-Tools erstellen eine Standard-Repository-Struktur mit /trunk, /branches und /tags. Die Dokumentation empfiehlt auch, keine separaten Repositories für jedes Projekt zu verwenden, damit der Code leichter geteilt werden kann.

Dem Rat zu folgen hat dazu geführt, dass ich ein Repository mit der folgenden Struktur habe:

/trunk
      /Projekt1
      /Projekt2
/branches
         /Projekt1
         /Projekt2
/tags
     /Projekt1
     /Projekt2

und so weiter, du verstehst schon. Im Laufe der Zeit habe ich diese Struktur ein wenig umständlich gefunden und es ist mir eingefallen, dass es eine alternative Interpretation der Empfehlungen gibt, wie zum Beispiel:

/Projekt1
         /trunk
         /branches
         /tags
/Projekt2
         /trunk
         /branches
         /tags       

Also, welche Struktur verwenden die Leute und warum? Oder - gibt es vielleicht eine andere Möglichkeit, Dinge zu tun, die mir vollkommen entgangen ist?

0 Stimmen

Wie hilft es Ihnen, Code freizugeben, wenn Sie dasselbe Repository für mehrere Projekte verwenden?

0 Stimmen

Du sagst mir ;-) Das steht im Subversion-Handbuch.

34voto

Pascal Thivent Punkte 548176

Ich finde, dass der Blogeintrag zum Subversion-Repository-Layout dies ziemlich gut zusammenfasst:

(...) es gibt mehrere gängige Layouts, die von der Community als bewährte Praktiken übernommen wurden und daher könnte man diese als Empfehlungen betrachten. Wenn Ihr Repository öffentlich zugänglich ist, kann es für Benutzer, die auf andere Subversion-Repositories zugegriffen haben, einfacher sein, zu finden, wonach sie suchen.

Es gibt zwei häufig verwendete Layouts:

trunk
branches
tags

Dieses erste Layout ist die beste Option für ein Repository, das ein einzelnes Projekt oder eine Gruppe von Projekten enthält, die eng miteinander verbunden sind. Dieses Layout ist nützlich, weil es einfach ist, das gesamte Projekt oder eine Gruppe von Projekten mit einem einzigen Befehl zu branchen oder zu taggen:

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

Dies ist wahrscheinlich das am häufigsten verwendete Repository-Layout und wird von vielen Open-Source-Projekten wie Subversion selbst und Subclipse verwendet. Dies ist das Layout, dem die meisten Hosting-Seiten wie Tigris.org, SourceForge.net und Google Code folgen, da jedem Projekt auf diesen Seiten sein eigenes Repository zugewiesen wird.

Das nächste Layout ist die beste Option für ein Repository, das nicht verwandte oder locker miteinander verbundene Projekte enthält.

ProjektA
   trunk
   branches
   tags
ProjektB
   trunk
   branches
   tags

In diesem Layout erhält jedes Projekt einen Ordner auf oberster Ebene und dann werden die trunk/branches/tags-Ordner darunter erstellt. Eigentlich ist dies dasselbe Layout wie das erste Layout, es ist nur so, dass anstatt jedes Projekt in seinem eigenen Repository zu platzieren, sie alle in einem einzigen Repository sind. Die Apache Software Foundation verwendet dieses Layout für ihr Repository, das alle ihre Projekte in einem einzigen Repository enthält.

Mit diesem Layout hat jedes Projekt seine eigenen branches und tags und es ist einfach, sie für die Dateien in diesem Projekt mit einem Befehl zu erstellen, ähnlich wie bei dem zuvor gezeigten:

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

Was Sie in diesem Layout nicht einfach machen können, ist ein Branch oder Tag zu erstellen, der Dateien von sowohl ProjektA und ProjektB enthält. Sie können es immer noch tun, aber es erfordert mehrere Befehle und Sie müssen auch entscheiden, ob Sie einen speziellen Ordner für die branches und tags machen, die mehrere Projekte betreffen. Wenn Sie das häufig machen müssen, sollten Sie vielleicht das erste Layout in Betracht ziehen.

Also, um es anders auszudrücken:

  • Verwenden Sie das erste Layout für ein einzelnes oder mehrere verwandte Projekte.
  • Verwenden Sie das zweite Layout für nicht verwandte Projekte.

Der ganze Beitrag ist das Lesen wert.

8voto

David Espart Punkte 11102

Das zweite Layout ist der richtige Weg. Ein guter Grund dafür ist, einem Entwickler zu erlauben oder zu verweigern, mit einem der Projekte zu arbeiten.

5voto

Andrew B Punkte 829

Ich bevorzuge die zweite. Mit der zweiten ist es einfacher zu implementieren, wenn die Berechtigungen der Personen zwischen den beiden Projekten unterschiedlich sind.

3voto

thekbb Punkte 7508

Ich bevorzuge sehr die zweite Möglichkeit, maven oder ant/ivy zu verwenden, um die Artefakte aus anderen Projekten zu übernehmen, wenn nötig.

Ich ziehe es auch vor, ein einziges Projekt pro Repository zu haben oder eine kleine Anzahl von verwandten Repositories.

Es vereinfacht die Zugriffskontrolle, die auf Repository-Ebene einfacher ist als auf Pfad-Ebene innerhalb des Repositorys - insbesondere bei der Authentifizierung gegen LDAP.

Backup-/Restore-Vorgänge sind anfangs etwas komplizierter, da Sie alle Repositories durchgehen müssen, um eine Hot-Copy durchzuführen, aber im Unglücksfall müssen Sie nur ein Repo wiederherstellen - die anderen müssen nicht offline genommen oder Daten verloren gehen. Nachdem Projekte gestorben sind, können die Repositories einfach gelöscht werden, um Platz bei zukünftigen Backups zu sparen.

Hook-Skripte werden einfacher, wenn es nur ein Projekt (oder eine kleine Anzahl von verwandten Projekten) pro Repository gibt. Sie müssen den betroffenen Pfad nicht überprüfen, um bedingt Maßnahmen in Ihrem Hook zu ergreifen.

Wie retracile bemerkte, wird ein monolithisches Repository wahrscheinlich zu einer großen Herausforderung, wenn Sie jemals selektiv exportieren möchten, indem Sie svndumpfilter verwenden - die Anzahl der geänderten Pfade, die dazu führen, dass es abstürzt, ist wahrscheinlich hoch.

Das Upgraden des Repository-Schemas für zukünftige Versionen von svn erfordert mehr Aufwand - Sie müssen es n-Mal tun anstatt nur einmal... aber es kann automatisiert werden und Sie müssen nicht alle gleichzeitig koordinieren.

Wenn jemand ein Passwort commitet und Sie es löschen müssen, können Sie das Dump/Filter/Reload schnell in einem Repo durchführen, ohne andere Teams zu beeinträchtigen.

Ein Ratschlag, wenn Sie diesen Weg gehen - haben Sie eine separate .conf-Datei pro Repository anstatt eine große, es ist einfacher zu verwalten und bietet auch die Gewissheit, dass einige Zeitstempel alt sein werden - wenn etwas nicht stimmt, können Sie nach kürzlichen Änderungen leichter suchen.

1voto

Tim Long Punkte 13230

Ich beschloss, in den sauren Apfel zu beißen und mein Repository umzubauen. Ich schrieb ein kleines Programm zur Unterstützung (siehe unten). Die Schritte, die ich befolgte, waren:

  1. Erstellen Sie eine Sicherungskopie des Original-Repository.
  2. svn checkout das gesamte Repository. Dies dauerte lange und brauchte viel Festplattenspeicher.
  3. Führen Sie das unten stehende Programm im Arbeitskopie des vorherigen Schritts aus.
  4. Überprüfen Sie die modifizierte Arbeitskopie und bereinigen Sie alle übrig gebliebenen Probleme (z.B. svn delete des veralteten trunk, tags und branches Ordner).
  5. svn commit zurück ins Repository.

Der gesamte Prozess dauerte zwar lange, aber ich entschied mich für diesen Ansatz, weil das Ändern einer Arbeitskopie sicherer ist als das Hacken eines Live-Repositorys und ich die Möglichkeit hatte, die Arbeitskopie einfach wegzuwerfen, wenn etwas schief ging, jedes Problem in der Arbeitskopie zu beheben und die gesamte Umstrukturierung als eine einzelne Revision zu commiten.

Hier ist der von mir verwendete C# Code zur Verschiebung. Erfordert die SharpSvn-Bibliothek.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using SharpSvn;

/**
 * 
 * Programmoperation:
 * 1. Ausführen des Befehlszeilenparsers, um den Pfad zum Wurzelverzeichnis der Arbeitskopie zu bestimmen
 * 2. Enumerieren der Ordner im /trunk 
 * 3. Umstrukturieren jedes Projektordners im /trunk
 * 
 * 
 * Umstrukturieren eines Projekts:
 * 1. Den Projektnamen erhalten (Ordnername in /trunk/{Projekt})
 * 2. SVN Move /trunk/{Projekt} nach /{Projekt}/trunk
 * 3. Neuzuordnen von Projekt, branches
 * 4. Neuzuordnen von Projekt, tags
 * 
 * Reparent(project, folder)
 * Wenn /{Ordner}/{Projekt} vorhanden ist
 *   SVN Move /{Ordner}/{Projekt} nach /{Projekt}/{Ordner}
 * sonst
 *   Ordner erstellen /{Projekt}/{Ordner}
 *   SVN Add /{Projekt}/{Ordner}
 * 
 **/

namespace TiGra.SvnRestructure
{
    /// 
    /// Strukturiert ein Subversion-Repository neu von
    ///     /trunk|branches|tags/Projekt
    /// zu
    ///     /Projekt/trunk|branches|tags
    /// 
    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 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("Bewegt {0} --> {1}", oldPath, newPath));
            svn.Move(oldPath, newPath, new SvnMoveArgs(){CreateParents = true});
        }

        private static void EnumerateProjectsInTrunk()
        {
            var list = EnumerateFolders("trunk");
            Projects = list;
        }

        /// 
        /// Enumeriert die Ordner im angegebenen Unterordner.
        /// 
        /// Der trunk.
        private static List EnumerateFolders(string root)
        {
            var fullPath = Path.Combine(WorkingCopy, root);
            var folders = Directory.GetDirectories(fullPath, "*.*", SearchOption.TopDirectoryOnly).ToList();
            folders.RemoveAll(s => s.EndsWith(".svn")); // Entferne spezielle Metadatenordner.
            return folders;
        }

        private static void CreateSvnClient()
        {
            svn = new SharpSvn.SvnClient();
        }

        /// 
        /// Verarbeitet die Befehlszeile. Es sollte genau ein Argument geben, das der Pfad zur Arbeitskopie ist.
        /// 
        private static void ProcessCommandLine(string[] args)
        {
            if (args.Length != 1)
                throw new ArgumentException("Es muss genau ein Argument vorhanden sein");
            var path = args[0];
            if (!Directory.Exists(path))
                throw new ArgumentException("Das angegebene Wurzelverzeichnis der Arbeitskopie konnte nicht gefunden werden.");
            WorkingCopy = path;
            Branches = Path.Combine(WorkingCopy, "branches");
            Tags = Path.Combine(WorkingCopy, "tags");
            Trunk = Path.Combine(WorkingCopy, "trunk");
        }
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X