web-dev-qa-db-fra.com

Obtenez rapidement la liste de tous les svn: externals pour un référentiel svn distant

Nous avons un référentiel svn avec beaucoup de répertoires et de fichiers et notre système de construction doit être capable de trouver toutes les propriétés svn: externals, de manière récursive pour une branche du référentiel, avant de l'extraire. Actuellement nous utilisons:

svn propget svn:externals -R http://url.of.repo/Branch

Cela a pris beaucoup de temps et constitue un véritable casse-tête. Il semble que le client reçoive tous les accessoires pour tout le contenu du référentiel et effectue le filtrage localement (bien que je ne l’aie pas confirmé avec Wireshark). Y a-t-il un moyen plus rapide de faire cela? De préférence, un moyen de faire en sorte que le serveur ne renvoie que les données souhaitées.

24
NeoSkye

Comme vous l'avez dit, cela consomme de la bande passante réseau. Toutefois, si vous avez accès au serveur sur lequel ces référentiels sont hébergés, vous pouvez l'exécuter via le protocole file://. Il est prouvé qu’il est plus rapide et ne consomme pas de réseau.

svn propget svn:externals -R file:///path/to/repo/Branch

En outre, si vous avez la copie de travail complète en place, vous pouvez également l'exécuter dans votre WC.

svn propget svn:externals -R /path/to/WC

J'espère que cela vous aidera à obtenir les résultats plus rapidement!.

25
Jeyanthan I

J'ai finalement trouvé une solution. J'ai décidé de scinder la demande en plusieurs petites requêtes svn, puis de faire de chacune d'elles une tâche exécutée par un pool de threads. Ce type de serveur claque le serveur svn, mais dans notre cas, le serveur svn est sur le réseau local et cette requête est uniquement effectuée lors de la compilation complète, de sorte que cela ne semble pas être un problème.

import os
import sys
import threading
import ThreadPool

thread_pool = ThreadPool.ThreadPool(8)
externs_dict = {}
externs_lock = threading.Lock()

def getExternRev( path, url ):
    cmd = 'svn info "%s"' % url
    pipe = os.popen(cmd, 'r')
    data = pipe.read().splitlines()

    #Harvest last changed rev
    for line in data:
        if "Last Changed Rev" in line:
            revision = line.split(":")[1].strip()
            externs_lock.acquire()
            externs_dict[path] = (url, revision)
            externs_lock.release()

def getExterns(url, base_dir):
    cmd = 'svn propget svn:externals "%s"' % url
    pipe = os.popen(cmd, 'r')
    data = pipe.read().splitlines()
    pipe.close()

    for line in data:
        if line:
            line = line.split()
            path = base_dir + line[0]
            url = line[1]
            thread_pool.add_task( getExternRev, path, url )

def processDir(url, base_dir):
    thread_pool.add_task( getExterns, url, base_dir )

    cmd = 'svn list "%s"' % url
    pipe = os.popen(cmd, 'r')
    listing = pipe.read().splitlines()
    pipe.close()

    dir_list = []
    for node in listing:
        if node.endswith('/'):
            dir_list.append(node)

    for node in dir_list:
        #externs_data.extend( analyzePath( url + node, base_dir + node ) )
        thread_pool.add_task( processDir, url+node, base_dir+node )

def analyzePath(url, base_dir = ''):
    thread_pool.add_task( processDir, url, base_dir )
    thread_pool.wait_completion()


analyzePath( "http://url/to/repository" )
print externs_dict
2
NeoSkye

Si cela ne vous dérange pas d'utiliser Python et la bibliothèque pysvn, voici un programme complet de ligne de commande que j'utilise pour les externes SVN:

"""
@file
@brief SVN externals utilities.
@author Lukasz Matecki
"""
import sys
import os
import pysvn
import argparse

class External(object):

    def __init__(self, parent, remote_loc, local_loc, revision):
        self.parent = parent
        self.remote_loc = remote_loc
        self.local_loc = local_loc
        self.revision = revision

    def __str__(self):
        if self.revision.kind == pysvn.opt_revision_kind.number:
            return """\
Parent:     {0}
Source:     {1}@{2}
Local name: {3}""".format(self.parent, self.remote_loc, self.revision.number, self.local_loc)
        else:
            return """\
Parent:     {0}
Source:     {1} 
Local name: {2}""".format(self.parent, self.remote_loc, self.local_loc)


def find_externals(client, repo_path, external_path=None):
    """
    @brief Find SVN externals.
    @param client (pysvn.Client) The client to use.
    @param repo_path (str) The repository path to analyze.
    @param external_path (str) The URL of the external to find; if omitted, all externals will be searched.
    @returns [External] The list of externals descriptors or empty list if none found.
    """
    repo_root = client.root_url_from_path(repo_path)

    def parse(ext_prop):
        for parent in ext_prop:
            external = ext_prop[parent]
            for line in external.splitlines():
                path, name = line.split()
                path = path.replace("^", repo_root)
                parts = path.split("@")
                if len(parts) > 1:
                    url = parts[0]
                    rev = pysvn.Revision(pysvn.opt_revision_kind.number, int(parts[1]))
                else:
                    url = parts[0]
                    rev = pysvn.Revision(pysvn.opt_revision_kind.head)
                retval = External(parent, url, name, rev)
                if external_path and not external_path == url:
                    continue
                else:
                    yield retval

    for entry in client.ls(repo_path, recurse=True):
        if entry["kind"] == pysvn.node_kind.dir and entry["has_props"] == True:
            externals = client.propget("svn:externals", entry["name"])
            if externals:
                for e in parse(externals):
                    yield e


def check_externals(client, externals_list):
    for i, e in enumerate(externals_list):
        url = e.remote_loc
        rev = e.revision
        try:
            info = client.info2(url, revision=rev, recurse=False)
            props = info[0][1]
            url = props.URL
            print("[{0}] Existing:\n{1}".format(i + 1, "\n".join(["   {0}".format(line) for line in str(e).splitlines()])))
        except:
            print("[{0}] Not found:\n{1}".format(i + 1, "\n".join(["   {0}".format(line) for line in str(e).splitlines()])))

def main(cmdargs):
    parser = argparse.ArgumentParser(description="SVN externals processing.",
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     prefix_chars='-+')

    SUPPORTED_COMMANDS = ("check", "references")

    parser.add_argument(
        "action",
        type=str,
        default="check",
        choices=SUPPORTED_COMMANDS,
        help="""\
the operation to execute:
   'check' to validate all externals in a given location;
   'references' to print all references to a given location""")

    parser.add_argument(
        "url",
        type=str,
        help="the URL to operate on")

    parser.add_argument(
        "--repo", "-r",
        dest="repo",
        type=str,
        default=None,
        help="the repository (or path within) to perform the operation on, if omitted is inferred from url parameter")

    args = parser.parse_args()

    client = pysvn.Client()

    if args.action == "check":
        externals = find_externals(client, args.url)
        check_externals(client, externals)
    Elif args.action == "references":
        if args.repo:
            repo_root = args.repo
        else:
            repo_root = client.root_url_from_path(args.url)
        for i, e in enumerate(find_externals(client, repo_root, args.url)):
            print("[{0}] Reference:\n{1}".format(i + 1, "\n".join(["   {0}".format(line) for line in str(e).splitlines()])))

if __== "__main__":
    sys.exit(main(sys.argv))

Cela devrait fonctionner à la fois dans Python 2 et dans Python 3. Vous pouvez l'utiliser comme ceci (adresses réelles supprimées):

python svn_externals.py references https://~~~~~~~~~~~~~~/cmd_utils.py
[1] Reference:
   Parent:     https://~~~~~~~~~~~~~~/BEFORE_MK2/scripts/utils
   Source:     https://~~~~~~~~~~~~~~/tools/python/cmd_utils.py
   Local name: cmd_utils.py
[2] Reference:
   Parent:     https://~~~~~~~~~~~~~~/VTB-1425_PCU/scripts/utils
   Source:     https://~~~~~~~~~~~~~~/tools/python/cmd_utils.py
   Local name: cmd_utils.py
[3] Reference:
   Parent:     https://~~~~~~~~~~~~~~/scripts/utils
   Source:     https://~~~~~~~~~~~~~~/tools/python/cmd_utils.py
   Local name: cmd_utils.py

En ce qui concerne les performances, cela fonctionne assez rapidement (bien que mon référentiel soit assez petit). Vous devez vérifier par vous-même.

0
lutecki

je ne sais pas où j'ai trouvé ce petit bijou, mais il est très utile de voir les externes avec leurs propres externes :

Windows:
    svn status . | findstr /R "^X"

Linux/Unix:
    svn status . | grep -E "^X"
0
Marcel Petrick

C'est lent à cause du commutateur -R; Tous les répertoires de votre chemin de référentiel sont recherchés de manière récursive pour la propriété, ce qui représente un travail considérable.

0
Argeman

Pas la solution idéale (peut avoir des effets secondaires) et ne répondez pas à votre problème, mais

Vous pouvez réécrire toutes les définitions externes et ajouter (réécrire) dans un endroit commun et connu - de cette façon, vous éliminerez la récurrence dans pg après modification

0
Lazy Badger