scripts/svn_helper.py
changeset 44 9d3a0f98df34
parent 30 636baa95715c
child 52 25d8f4623447
equal deleted inserted replaced
43:fdb9a6d839ae 44:9d3a0f98df34
    19 ls(): returns list of selected directory entries from an SVN repository
    19 ls(): returns list of selected directory entries from an SVN repository
    20 lsDirs(): wrapper around ls() that only returns node_kind.dir entries
    20 lsDirs(): wrapper around ls() that only returns node_kind.dir entries
    21 lsFiles(): wrapper around ls() that only returns node_kind.files entries
    21 lsFiles(): wrapper around ls() that only returns node_kind.files entries
    22 exists(): returns True if repo_path exists in the svn repository
    22 exists(): returns True if repo_path exists in the svn repository
    23 
    23 
    24 DEF_SVN_REPO: default HTTPS path to the SoC project SVN repo
       
    25 PYSVN_ALL_NODE_KINDS: all directory entry node_kinds supported by pysvn
    24 PYSVN_ALL_NODE_KINDS: all directory entry node_kinds supported by pysvn
    26 PYSVN_FILE_DIR_NODE_KINDS: actual file and directory node_kinds
    25 PYSVN_FILE_DIR_NODE_KINDS: actual file and directory node_kinds
    27 """
    26 """
    28 
    27 
    29 __authors__ = [
    28 __authors__ = [
    30   # alphabetical order by last name, please
    29   # alphabetical order by last name, please
    31   '"Todd Larsen" <tlarsen@google.com>',
    30   '"Todd Larsen" <tlarsen@google.com>',
    32 ]
    31 ]
    33 
    32 
    34 
    33 
       
    34 import os
    35 import pysvn
    35 import pysvn
    36 
    36 
    37 
    37 from trunk.scripts import settings
    38 #: default HTTPS path to the SoC project SVN repo
    38 
    39 DEF_SVN_REPO = 'https://soc.googlecode.com/svn/'
       
    40 
    39 
    41 #: all of the directory entry node_kinds supported py pysvn
    40 #: all of the directory entry node_kinds supported py pysvn
    42 PYSVN_ALL_NODE_KINDS = set([pysvn.node_kind.none, pysvn.node_kind.dir,
    41 PYSVN_ALL_NODE_KINDS = set([pysvn.node_kind.none, pysvn.node_kind.dir,
    43                             pysvn.node_kind.file, pysvn.node_kind.unknown])
    42                             pysvn.node_kind.file, pysvn.node_kind.unknown])
    44 
    43 
    45 #: actual file and directory node_kinds (includes dir and file, but excludes
    44 #: actual file and directory node_kinds (includes dir and file, but excludes
    46 #: the "non-file" none and unknown)
    45 #: the "non-file" none and unknown)
    47 PYSVN_FILE_DIR_NODE_KINDS = set([pysvn.node_kind.dir, pysvn.node_kind.file])
    46 PYSVN_FILE_DIR_NODE_KINDS = set([pysvn.node_kind.dir, pysvn.node_kind.file])
    48 
    47 
    49 
    48 
    50 def ls(client, repo_path, keep_kinds=PYSVN_FILE_DIR_NODE_KINDS, **kwargs):
    49 _client = None
       
    50 
       
    51 
       
    52 def getPySvnClient():
       
    53   """Returns the module-global pysvn Client object (creating one if needed).
       
    54   """
       
    55   global _client
       
    56 
       
    57   if not _client:
       
    58     _client = pysvn.Client()
       
    59 
       
    60   return _client
       
    61 
       
    62 
       
    63 def formatDirPath(path):
       
    64   """Appends trailing separator to non-empty path if it is missing.
       
    65 
       
    66   Args:
       
    67     path:  path string, which may be with or without a trailing separator,
       
    68       or even empty or None
       
    69 
       
    70   Returns:
       
    71     path unchanged if path evaluates to False or already ends with a trailing
       
    72     separator; otherwise, a / separator is appended
       
    73   """
       
    74   if path and not path.endswith('/'):
       
    75     path = path + '/'
       
    76   return path
       
    77 
       
    78 
       
    79 def formatDirPaths(*args):
       
    80   """Apply formatDirPath() to all supplied arguments, returning them in order.
       
    81   """
       
    82   return tuple([formatDirPath(arg) for arg in args])
       
    83 
       
    84 
       
    85 def getCanonicalSvnPath(path):
       
    86   """Returns the supplied svn repo path *without* the trailing / character.
       
    87 
       
    88   Some pysvn methods raise exceptions if svn directory URLs end with a
       
    89   trailing / ("non-canonical form") and some do not.  Go figure...
       
    90   """
       
    91   if path and path.endswith('/'):
       
    92     path = path[:-1]
       
    93   return path
       
    94 
       
    95 
       
    96 def useLocalOsSep(path):
       
    97   """Return path with all / characters replaced with os.sep, to be OS-agnostic.
       
    98   """
       
    99   return path.replace('/', os.sep)
       
   100 
       
   101 
       
   102 def getExpandedWorkingCopyPath(path, wc_root=None):
       
   103   """Returns expanded, local, native filesystem working copy path.
       
   104 
       
   105   Args:
       
   106     path: path to expand and convert to local filesystem directory separators
       
   107     wc_root: if present, prepended to path first
       
   108   """
       
   109   path = useLocalOsSep(path)
       
   110 
       
   111   if wc_root:
       
   112     # prepend (Windows-compatible) working copy root if one was supplied
       
   113     path = os.path.join(useLocalOsSep(wc_root), path)
       
   114 
       
   115   path = settings.getExpandedPath(path)
       
   116 
       
   117   if not path.endswith(os.sep):
       
   118     path = path + os.sep
       
   119 
       
   120   return path
       
   121 
       
   122 
       
   123 def encodeRevision(rev):
       
   124   """Encode supplied revision into a pysvn.Revision instance.
       
   125 
       
   126   This function is currently very simplistic and does not produce all possible
       
   127   types of pysvn.Revision object.  See below for current limitations.
       
   128 
       
   129   Args:
       
   130     rev: integer revision number or None
       
   131 
       
   132   Returns:
       
   133     HEAD pysvn.Revision object if rev is None,
       
   134     otherwise a pysvn.opt_revision_kind.number pysvn.Revision object created
       
   135     using the supplied integer revision number
       
   136   """
       
   137   if rev is None:
       
   138     return pysvn.Revision(pysvn.opt_revision_kind.head)
       
   139 
       
   140   return pysvn.Revision(pysvn.opt_revision_kind.number, int(rev))
       
   141 
       
   142 
       
   143 def ls(repo_path, client=None, keep_kinds=PYSVN_FILE_DIR_NODE_KINDS, **kwargs):
    51   """Returns a list of (possibly recursive) svn repo directory entries.
   144   """Returns a list of (possibly recursive) svn repo directory entries.
    52 
   145 
    53   Args:
   146   Args:
    54     client: pysvn Client instance
       
    55     repo_path: absolute svn repository path URL, including the server and
   147     repo_path: absolute svn repository path URL, including the server and
    56       directory path within the repo
   148       directory path within the repo
       
   149     client: pysvn Client instance; default is None, which will use the pysvn
       
   150       Client created by first call to getPySvnClient() (or create one if
       
   151       necessary)
    57     keep_kinds: types of directory entries to keep in the returned list; a
   152     keep_kinds: types of directory entries to keep in the returned list; a
    58       collection of pysvn.node_kind objects; default is
   153       collection of pysvn.node_kind objects; default is
    59       PYSVN_FILE_DIR_NODE_KINDS
   154       PYSVN_FILE_DIR_NODE_KINDS
    60     **kwargs: keyword arguments passed on to Client.list(), including:
   155     **kwargs: keyword arguments passed on to Client.list(), including:
    61       recurse: indicates if return results should include entries from
   156       recurse: indicates if return results should include entries from
    65     list of (Unicode, coming from pysvn) strings representing the entries
   160     list of (Unicode, coming from pysvn) strings representing the entries
    66     of types indicated by keep_kinds; strings are altered to match the
   161     of types indicated by keep_kinds; strings are altered to match the
    67     output of the actual 'svn ls' command: repo_path prefix is removed,
   162     output of the actual 'svn ls' command: repo_path prefix is removed,
    68     directories end with the / separator.
   163     directories end with the / separator.
    69   """
   164   """
       
   165   if not client:
       
   166     client = getPySvnClient()
       
   167 
    70   raw_entries = client.list(repo_path, **kwargs)
   168   raw_entries = client.list(repo_path, **kwargs)
    71   entries = []
   169   entries = []
    72 
   170 
    73   # Find shortest repos_path that is a 'dir' entry; will be prefix of all
   171   # Find shortest repos_path that is a 'dir' entry; will be prefix of all
    74   # other entries, since Client.list() always returns repo_path as one of
   172   # other entries, since Client.list() always returns repo_path as one of
    83 
   181 
    84       if len(entry_path) < len(shortest_path):
   182       if len(entry_path) < len(shortest_path):
    85         shortest_path = entry_path
   183         shortest_path = entry_path
    86 
   184 
    87   # normalize the path name of entry_prefix to include a trailing separator
   185   # normalize the path name of entry_prefix to include a trailing separator
    88   entry_prefix = shortest_path
   186   entry_prefix = formatDirPath(shortest_path)
    89 
       
    90   if not entry_prefix.endswith('/'):
       
    91     entry_prefix = entry_prefix + '/'
       
    92 
   187 
    93   for svn_list,_ in raw_entries:
   188   for svn_list,_ in raw_entries:
    94     # only include requested node kinds (dir, file, etc.)
   189     # only include requested node kinds (dir, file, etc.)
    95     if svn_list.kind not in keep_kinds:
   190     if svn_list.kind not in keep_kinds:
    96       continue
   191       continue
   115     entries.append(entry_path)
   210     entries.append(entry_path)
   116 
   211 
   117   return entries
   212   return entries
   118 
   213 
   119 
   214 
   120 def lsDirs(client, repo_path, **kwargs):
   215 def lsDirs(repo_path, **kwargs):
   121   """Wrapper around ls() that only returns node_kind.dir entries.
   216   """Wrapper around ls() that only returns node_kind.dir entries.
   122   """
   217   """
   123   return ls(client, repo_path, keep_kinds=(pysvn.node_kind.dir,), **kwargs)
   218   return ls(repo_path, keep_kinds=(pysvn.node_kind.dir,), **kwargs)
   124 
   219 
   125 
   220 
   126 def lsFiles(client, repo_path, **kwargs):
   221 def lsFiles(repo_path, **kwargs):
   127   """Wrapper around ls() that only returns node_kind.files entries.
   222   """Wrapper around ls() that only returns node_kind.files entries.
   128   """
   223   """
   129   return ls(client, repo_path, keep_kinds=(pysvn.node_kind.file,), **kwargs)
   224   return ls(repo_path, keep_kinds=(pysvn.node_kind.file,), **kwargs)
   130 
   225 
   131 
   226 
   132 def exists(client, repo_path):
   227 def exists(repo_path, client=None):
   133   """Returns True if repo_path exists in the svn repository."""
   228   """Returns True if repo_path exists in the svn repository."""
       
   229   if not client:
       
   230     client = getPySvnClient()
       
   231 
   134   try:
   232   try:
   135     raw_entries = client.list(repo_path)
   233     raw_entries = client.list(repo_path)
   136     return True
   234     return True
   137   except pysvn._pysvn.ClientError:
   235   except pysvn._pysvn.ClientError:
   138     # Client.list() raises an exception if the path is not present in the repo
   236     # Client.list() raises an exception if the path is not present in the repo
   139     return False
   237     return False
       
   238 
       
   239 
       
   240 def branchItems(src, dest, items, rev=None, client=None):
       
   241   """Branch a list of items (files and/or directories).
       
   242 
       
   243   Using the supplied pysvn client object, a list of items (expected to be
       
   244   present in the src directory) is branched from the absolute svn repo src
       
   245   path URL to the relative working client dest directory.
       
   246 
       
   247   Args:
       
   248     src: absolute svn repository source path URL, including the server and
       
   249       directory path within the repo
       
   250     dest: relative svn repository destination path in the current working copy
       
   251     items: list of relative paths of items in src/ to branch to dest/ (no item
       
   252       should begin with the / separator)
       
   253     client: pysvn Client instance; default is None, which will use the pysvn
       
   254       Client created by first call to getPySvnClient() (or create one if
       
   255       necessary)
       
   256   """
       
   257   if not client:
       
   258     client = getPySvnClient()
       
   259 
       
   260   src = formatDirPath(src)
       
   261   dest = useLocalOsSep(formatDirPath(dest))
       
   262 
       
   263   for item in items:
       
   264     assert not item.startswith('/')
       
   265     src_item = getCanonicalSvnPath(src + item)
       
   266     # attempt to be compatible with Windows working copy paths
       
   267     item = useLocalOsSep(item)
       
   268     client.copy(src_item, dest + item, src_revision=encodeRevision(rev))
       
   269 
       
   270 
       
   271 def branchDir(src, dest, client=None, rev=None):
       
   272   """Branch one directory to another.
       
   273 
       
   274   Using the supplied pysvn client object, the absolute svn repo path URL src
       
   275   directory is branched to the relative working client dest directory.
       
   276 
       
   277   Args:
       
   278     src: absolute svn repository source path URL, including the server and
       
   279       directory path within the repo
       
   280     dest: relative svn repository destination path in the current working copy
       
   281     client: pysvn Client instance; default is None, which will use the pysvn
       
   282       Client created by first call to getPySvnClient() (or create one if
       
   283       necessary)
       
   284   """
       
   285   if not client:
       
   286     client = getPySvnClient()
       
   287 
       
   288   src = getCanonicalSvnPath(src)
       
   289   dest = useLocalOsSep(formatDirPath(dest))
       
   290 
       
   291   client.copy(src, dest, src_revision=encodeRevision(rev))
       
   292 
       
   293 
       
   294 def exportItems(src, dest, items, rev=None, client=None):
       
   295   """Export a list of items (files and/or directories).
       
   296 
       
   297   Using the supplied pysvn client object, a list of items (expected to be
       
   298   present in the src directory) is exported from the absolute svn repo src
       
   299   path URL to the local filesystem directory.
       
   300 
       
   301   Args:
       
   302     src: absolute svn repository source path URL, including the server and
       
   303       directory path within the repo
       
   304     dest: local filesystem destination path
       
   305     items: list of relative paths of items in src/ to export to dest/ (no item
       
   306       should begin with the / separator)
       
   307     client: pysvn Client instance; default is None, which will use the pysvn
       
   308       Client created by first call to getPySvnClient() (or create one if
       
   309       necessary)
       
   310   """
       
   311   if not client:
       
   312     client = getPySvnClient()
       
   313 
       
   314   src = formatDirPath(src)
       
   315   dest = useLocalOsSep(formatDirPath(dest))
       
   316 
       
   317   for item in items:
       
   318     assert not item.startswith('/')
       
   319     src_item = getCanonicalSvnPath(src + item)
       
   320     # attempt to be compatible with Windows local filesystem paths
       
   321     dest_item = useLocalOsSep(getCanonicalSvnPath(dest + item))
       
   322     client.export(src_item, dest_item, revision=encodeRevision(rev))
       
   323 
       
   324 
       
   325 def exportDir(src, dest, client=None, rev=None):
       
   326   """Export one directory to another.
       
   327 
       
   328   Using the supplied pysvn client object, the absolute svn repo path URL src
       
   329   directory is exported to the the local filesystem directory.
       
   330 
       
   331   Args:
       
   332     src: absolute svn repository source path URL, including the server and
       
   333       directory path within the repo
       
   334     dest: local filesystem destination path
       
   335     client: pysvn Client instance; default is None, which will use the pysvn
       
   336       Client created by first call to getPySvnClient() (or create one if
       
   337       necessary)
       
   338   """
       
   339   if not client:
       
   340     client = getPySvnClient()
       
   341 
       
   342   src = getCanonicalSvnPath(src)
       
   343   dest = useLocalOsSep(getCanonicalSvnPath(dest))
       
   344 
       
   345   client.export(src, dest, revision=encodeRevision(rev))