2012-04-17 3 views
24

우리는 많은 디렉토리와 파일을 가진 svn 저장소를 가지고 있으며 우리의 빌드 시스템은 모든 svn : externals 속성을 지점에 대해 재귀 적으로 찾을 수 있어야합니다 그것을 체크 아웃하기 전에 저장소에 저장하십시오. 현재 우리는 다음을 사용합니다 :원격 svn 저장소에 대한 모든 svn : externals 목록을 빨리 얻으십시오

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

이것은 매우 시간이 오래 걸리고 진짜 대역폭 돼지입니다. 클라이언트가 repo에있는 모든 항목을 소포 받고 로컬로 필터링을 수행하고있는 것처럼 보입니다 (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

또한, 당신은 또한 당신의 화장실 내에서 실행할 수 있습니다.

희망 결과를 빨리 얻을 수 있기를 바랍니다. 당신이 제거됩니다 이런 식으로 -

+1

그레이트 솔루션! 많은 시간을 절약했습니다! – donV

+1

불행히도, 우리의 빌드 시스템은 SVN 서버에 직접 액세스 할 수 없기 때문에 문제가 발생하지는 않습니다. 그러나 당신이 그런 종류의 액세스 권한을 가지고 있다면 정말 좋은 해결책이기 때문에 답변을 upvoting하고 있습니다. – NeoSkye

+0

SVN Server 1.4.6을 사용하고 있으며'file : //'을 사용하면 몇 초 후에 * Aborted *가 발생합니다. http://svn.haxx.se/users/archive-2007-04/0500.shtml – ceilfors

0

하지 이상적인 솔루션 (부작용이있을 수 있습니다)이 아닌 문제에 대답하지만,

당신은 하나의 공통 알려진 장소에 (다시) 모든 외관 정의를 다시 작성하고 추가 할 수 있습니다 변경 후 페이지에서 재귀

0

파이썬과 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)) 

이 모두 파이썬에서 작동해야이 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 

, 이것은 매우 빠른 작동 (내 저장소가 아주 작은하지만). 직접 확인해야합니다.

관련 문제