Быстро получить список всех svn: externals для удаленного репозитория svn
У нас есть репозиторий svn с большим количеством каталогов и файлов, и наша система сборки должна быть в состоянии найти все свойства svn: externals, рекурсивно для ветки в репозитории, прежде чем проверять ее. В настоящее время мы используем:
svn propget svn:externals -R http://url.of.repo/Branch
Это оказалось чрезвычайно трудоемким и является реальной полосой свиньи. Похоже, что клиент получает все реквизиты для всего в репо и делает фильтрацию локально (хотя я не подтвердил это с помощью wirehark). Есть ли более быстрый способ сделать это? Предпочтительно, чтобы какой-либо способ заставить сервер возвращать только нужные данные.
Ответы
Ответ 1
Как вы упомянули, он потребляет пропускную способность сети. Однако, если у вас есть доступ к серверу, на котором размещены эти репозитории, вы можете запустить его по протоколу file://
. Доказано, что это быстрее и не потребляет сеть.
svn propget svn:externals -R file:///path/to/repo/Branch
Кроме того, если у вас была вся рабочая копия, вы также можете запустить ее в своем WC.
svn propget svn:externals -R /path/to/WC
Надеюсь, это поможет вам быстрее достичь результатов!
Ответ 2
Наконец-то я придумал решение. Я решил разбить запрос на несколько небольших запросов svn, а затем сделать каждую из этих задач задачей пула потоков. Этот тип шлемов svn-сервера, но в нашем случае сервер svn находится в локальной сети, и этот запрос выполняется только во время полной сборки, поэтому это не проблема.
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
Ответ 3
Он медленный из-за переключателя -R; все каталоги в вашем пути репозитория ищут свойство рекурсивно, что очень много.
Ответ 4
Не идеальное решение (может иметь побочные эффекты), а не ответ на вашу проблему, но
Вы можете переписать все определения externals и добавить (переписать) в одном общем известном месте - таким образом вы устраните рекурсию в pg после изменения
Ответ 5
Если вы не против использования Python и библиотеки pysvn, вот полная программа командной строки, которую я использую для внешних 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 __name__ == "__main__":
sys.exit(main(sys.argv))
Это должно работать как на Python 2, так и на Python 3.
Вы можете использовать его так (удаленные фактические адреса):
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
Что касается производительности, это работает довольно быстро (хотя мой репозиторий довольно мал). Вы должны проверить это для себя.
Ответ 6
не уверен, где я нашел этот драгоценный камень, но весьма полезно видеть внешние с собственными внешними:
Windows:
svn status . | findstr /R "^X"
Linux/Unix:
svn status . | grep -E "^X"