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.
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!.
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
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.
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"
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.
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