2012-04-17 8 views
24

私たちにはたくさんのディレクトリとファイルを持つsvnリポジトリがあり、ビルドシステムはすべてのsvn:externalsプロパティをブランチに対して再帰的に見つけることができる必要がありますリポジトリ内で、チェックアウトする前に現在、私たちが使用します。すぐにリモートのsvnリポジトリのすべてのsvn:externalsのリストを取得

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

をこれは非常に時間がかかるを証明し、実際の帯域幅の豚でいます。クライアントはリポジトリ内のすべての小道具をすべて受け取っているように見え、ローカルでフィルタリングを行っているように見えます(ただし、wiresharkでこれを確認していません)。これを行うより速い方法がありますか?好ましくは、所望のデータのみを返すようにサーバを取得する何らかの方法が好ましい。

答えて

0

-Rスイッチのために遅いです。リポジトリパス内のすべてのディレクトリが再帰的にプロパティを検索されますが、これは多くの作業です。

+0

ええ、私はそれがなぜそれをするのか考え出しましたが、誰かがより速い方法を持っていたかどうか疑問に思っていました。それは有望な見ていない。 – NeoSkye

+0

私はそれが簡単だと思います:あなたが再帰的にそれを行う必要があるならば、それはちょうど遅くなります。 – Argeman

2

私はついに解決策を思いつきました。私は、要求を複数の小さなsvn要求に分割し、それらのタスクのそれぞれをスレッドプールによって実行することにしました。このような種類のsvnサーバは、私たちの場合はsvnサーバがLAN上にあり、このクエリはフルビルド時にのみ作成されるため、問題はないようです。

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 
+0

ThreadPoolの依存関係はここにありますhttps://gist.github.com/metal3d/5075460 – ceilfors

+0

スレッドで実行すると 'os.popen()'に問題があります。それはただ静かに死ぬ。私はスレッドでそれを実行して、このスクリプトからすべてのスレッド部分を削除しました。私はここでスピードを犠牲にしていますが、このスクリプトはリポジトリが大きすぎると静かに死んでしまう 'propget -R'と比較してより信頼性があります。 – ceilfors

+1

ThreadPool https:// gistを使用しないバージョンです。github.com/ceilfors/741d8152106a310dd454 – ceilfors

25

前述のとおり、ネットワーク帯域幅を消費します。ただし、それらのリポジトリがホストされているサーバーにアクセスできる場合は、file://プロトコルで実行できます。より速く、ネットワークを消費しないことが証明されています。

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

あなたの場所で全体の作業コピーを持っていた場合にも、また、あなたのWCの中でそれを実行することができます。

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

希望すれば、より速く結果を得ることができます。

+1

すばらしい解決策!多くの時間を節約! – donV

+1

残念ながら、私たちのビルドシステムはSVNサーバーに直接アクセスすることはできませんので、これで問題は解決されませんが、そのようなアクセス権を持っていれば本当に良い解決策ですので、私はあなたの答えをアップ投票しています。 – NeoSkye

+0

SVN Server 1.4.6を使用していて、 'file://'を使用すると、数秒後に* Aborted *が返されます。 http://svn.haxx.se/users/archive-2007-04/0500.shtmlに似ています – ceilfors

0

ない理想的なソリューション(副作用を有していてもよい)と、あなたの問題に答えていないが、

あなたは、すべての外部定義を書き換えると、一つの共通の、知られている場所に(書き換え)を追加することができます - あなたは排除するでしょう。この方法を変更後のPGで再帰

0

あなたは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を使用します。 Y OUは、この(実際のアドレスは削除)のようにそれを使用することができます(私のリポジトリは非常に小さいですが)、パフォーマンスに関しては

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 

、これは非常に高速に動作します。あなたはそれを自分でチェックしなければなりません。

関連する問題