Home | History | Annotate | Download | only in tools
      1 '''
      2 Copyright 2011 Google Inc.
      3 
      4 Use of this source code is governed by a BSD-style license that can be
      5 found in the LICENSE file.
      6 '''
      7 
      8 import fnmatch
      9 import os
     10 import re
     11 import subprocess
     12 
     13 PROPERTY_MIMETYPE = 'svn:mime-type'
     14 
     15 # Status types for GetFilesWithStatus()
     16 STATUS_ADDED                 = 0x01
     17 STATUS_DELETED               = 0x02
     18 STATUS_MODIFIED              = 0x04
     19 STATUS_NOT_UNDER_SVN_CONTROL = 0x08
     20 
     21 
     22 if os.name == 'nt':
     23   SVN = 'svn.bat'
     24 else:
     25   SVN = 'svn'
     26 
     27 
     28 def Cat(svn_url):
     29     """Returns the contents of the file at the given svn_url. 
     30 
     31     @param svn_url URL of the file to read
     32     """
     33     proc = subprocess.Popen([SVN, 'cat', svn_url],
     34                             stdout=subprocess.PIPE,
     35                             stderr=subprocess.STDOUT)
     36     exitcode = proc.wait()
     37     if not exitcode == 0:
     38         raise Exception('Could not retrieve %s. Verify that the URL is valid '
     39                         'and check your connection.' % svn_url)
     40     return proc.communicate()[0]
     41 
     42 
     43 class Svn:
     44 
     45     def __init__(self, directory):
     46         """Set up to manipulate SVN control within the given directory.
     47 
     48         @param directory
     49         """
     50         self._directory = directory
     51 
     52     def _RunCommand(self, args):
     53         """Run a command (from self._directory) and return stdout as a single
     54         string.
     55 
     56         @param args a list of arguments
     57         """
     58         print 'RunCommand: %s' % args
     59         proc = subprocess.Popen(args, cwd=self._directory,
     60                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     61         (stdout, stderr) = proc.communicate()
     62         if proc.returncode is not 0:
     63             raise Exception('command "%s" failed in dir "%s": %s' %
     64                             (args, self._directory, stderr))
     65         return stdout
     66 
     67     def GetInfo(self):
     68         """Run "svn info" and return a dictionary containing its output.
     69         """
     70         output = self._RunCommand([SVN, 'info'])
     71         svn_info = {}
     72         for line in output.split('\n'):
     73           if ':' in line:
     74             (key, value) = line.split(':', 1)
     75             svn_info[key.strip()] = value.strip()
     76         return svn_info
     77 
     78     def Checkout(self, url, path):
     79         """Check out a working copy from a repository.
     80         Returns stdout as a single string.
     81 
     82         @param url URL from which to check out the working copy
     83         @param path path (within self._directory) where the local copy will be
     84         written
     85         """
     86         return self._RunCommand([SVN, 'checkout', url, path])
     87 
     88     def ListSubdirs(self, url):
     89         """Returns a list of all subdirectories (not files) within a given SVN
     90         url.
     91 
     92         @param url remote directory to list subdirectories of
     93         """
     94         subdirs = []
     95         filenames = self._RunCommand([SVN, 'ls', url]).split('\n')
     96         for filename in filenames:
     97             if filename.endswith('/'):
     98                 subdirs.append(filename.strip('/'))
     99         return subdirs
    100 
    101     def GetNewFiles(self):
    102         """Return a list of files which are in this directory but NOT under
    103         SVN control.
    104         """
    105         return self.GetFilesWithStatus(STATUS_NOT_UNDER_SVN_CONTROL)
    106 
    107     def GetNewAndModifiedFiles(self):
    108         """Return a list of files in this dir which are newly added or modified,
    109         including those that are not (yet) under SVN control.
    110         """
    111         return self.GetFilesWithStatus(
    112             STATUS_ADDED | STATUS_MODIFIED | STATUS_NOT_UNDER_SVN_CONTROL)
    113 
    114     def GetFilesWithStatus(self, status):
    115         """Return a list of files in this dir with the given SVN status.
    116 
    117         @param status bitfield combining one or more STATUS_xxx values
    118         """
    119         status_types_string = ''
    120         if status & STATUS_ADDED:
    121             status_types_string += 'A'
    122         if status & STATUS_DELETED:
    123             status_types_string += 'D'
    124         if status & STATUS_MODIFIED:
    125             status_types_string += 'M'
    126         if status & STATUS_NOT_UNDER_SVN_CONTROL:
    127             status_types_string += '\?'
    128         status_regex_string = '^[%s].....\s+(.+)$' % status_types_string
    129         stdout = self._RunCommand([SVN, 'status'])
    130         status_regex = re.compile(status_regex_string, re.MULTILINE)
    131         files = status_regex.findall(stdout)
    132         return files
    133 
    134     def AddFiles(self, filenames):
    135         """Adds these files to SVN control.
    136 
    137         @param filenames files to add to SVN control
    138         """
    139         self._RunCommand([SVN, 'add'] + filenames)
    140 
    141     def SetProperty(self, filenames, property_name, property_value):
    142         """Sets a svn property for these files.
    143 
    144         @param filenames files to set property on
    145         @param property_name property_name to set for each file
    146         @param property_value what to set the property_name to
    147         """
    148         if filenames:
    149             self._RunCommand(
    150                 [SVN, 'propset', property_name, property_value] + filenames)
    151 
    152     def SetPropertyByFilenamePattern(self, filename_pattern,
    153                                      property_name, property_value):
    154         """Sets a svn property for all files matching filename_pattern.
    155 
    156         @param filename_pattern set the property for all files whose names match
    157                this Unix-style filename pattern (e.g., '*.jpg')
    158         @param property_name property_name to set for each file
    159         @param property_value what to set the property_name to
    160         """
    161         all_files = os.listdir(self._directory)
    162         matching_files = sorted(fnmatch.filter(all_files, filename_pattern))
    163         self.SetProperty(matching_files, property_name, property_value)
    164 
    165     def ExportBaseVersionOfFile(self, file_within_repo, dest_path):
    166         """Retrieves a copy of the base version (what you would get if you ran
    167         'svn revert') of a file within the repository.
    168 
    169         @param file_within_repo path to the file within the repo whose base
    170                version you wish to obtain
    171         @param dest_path destination to which to write the base content
    172         """
    173         self._RunCommand([SVN, 'export', '--revision', 'BASE',
    174                           file_within_repo, dest_path])
    175