Package moap :: Package vcs :: Module svn
[hide private]
[frames] | no frames]

Source Code for Module moap.vcs.svn

  1  # -*- Mode: Python; test-case-name: moap.test.test_vcs_svn -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  """ 
  5  SVN functionality. 
  6  """ 
  7   
  8  import os 
  9  import commands 
 10  import re 
 11   
 12  from moap.util import util, log 
 13  from moap.vcs import vcs 
 14   
15 -def detect(path):
16 """ 17 Detect if the given source tree is using svn. 18 19 @return: True if the given path looks like a Subversion tree. 20 """ 21 if not os.path.exists(os.path.join(path, '.svn')): 22 log.debug('svn', 'Did not find .svn directory under %s' % path) 23 return False 24 25 for n in ['props', 'text-base']: 26 if not os.path.exists(os.path.join(path, '.svn', n)): 27 log.debug('svn', 'Did not find .svn/%s under %s' % (n, path)) 28 return False 29 30 return True
31
32 -class SVN(vcs.VCS):
33 name = 'Subversion' 34 35 meta = ['.svn'] 36
37 - def _getByStatus(self, path, status):
38 """ 39 @param status: one character indicating the status we want to get 40 all paths for. 41 """ 42 ret = [] 43 44 # ? should be escaped 45 if status == '?': 46 status = '\?' 47 48 oldPath = os.getcwd() 49 50 os.chdir(path) 51 cmd = "svn status --no-ignore" 52 output = commands.getoutput(cmd) 53 lines = output.split("\n") 54 matcher = re.compile('^' + status + '\s+(.*)') 55 for l in lines: 56 m = matcher.search(l) 57 if m: 58 relpath = m.expand("\\1") 59 ret.append(relpath) 60 61 # FIXME: would be nice to sort per directory 62 os.chdir(oldPath) 63 return ret
64
65 - def getAdded(self, path):
66 return self._getByStatus(path, 'A')
67
68 - def getDeleted(self, path):
69 return self._getByStatus(path, 'D')
70
71 - def getIgnored(self, path):
72 return self._getByStatus(path, 'I')
73
74 - def getUnknown(self, path):
75 return self._getByStatus(path, '?')
76
77 - def ignore(self, paths, commit=True):
78 oldPath = os.getcwd() 79 80 os.chdir(self.path) 81 # svn ignores files by editing the svn:ignore property on the parent 82 tree = self.createTree(paths) 83 toCommit = [] 84 for path in tree.keys(): 85 # read in old property 86 cmd = "svn propget svn:ignore %s" % path 87 (status, output) = commands.getstatusoutput(cmd) 88 lines = output.split("\n") 89 # svn 1.3.1 (r19032) 90 # $ svn propset svn:ignore --file - . 91 # svn: Reading from stdin is currently broken, so disabled 92 temp = util.writeTemp(lines + tree[path]) 93 # svn needs to use "." for the base directory 94 if path == '': 95 path = '.' 96 toCommit.append(path) 97 cmd = "svn propset svn:ignore --file %s %s" % (temp, path) 98 os.system(cmd) 99 os.unlink(temp) 100 101 if commit and toCommit: 102 cmd = "svn commit -m 'moap ignore' -N %s" % " ".join(toCommit) 103 os.system(cmd) 104 os.chdir(oldPath)
105
106 - def commit(self, paths, message):
107 # get all the parents as well 108 parents = [] 109 for p in paths: 110 while p: 111 p = os.path.dirname(p) 112 if p: 113 parents.append(p) 114 115 paths.extend(parents) 116 temp = util.writeTemp([message, ]) 117 paths = [os.path.join(self.path, p) for p in paths] 118 cmd = "svn commit --non-recursive --file %s %s" % ( 119 temp, " ".join(paths)) 120 log.debug('svn', 'Executing command: %s' % cmd) 121 status = os.system(cmd) 122 os.unlink(temp) 123 if status != 0: 124 return False 125 126 return True
127
128 - def diff(self, path):
129 # the diff can also contain svn-specific property changes 130 # we need to filter them to be a normal unified diff 131 # These blocks are recognizable because they go 132 # newline - Property changes on: - stuff - newline 133 # We parse in the C locale so we need to set it - see ticket #266 134 oldPath = os.getcwd() 135 os.chdir(self.path) 136 137 cmd = "LANG=C svn diff %s" % path 138 output = commands.getoutput(cmd) 139 os.chdir(oldPath) 140 141 return self.scrubPropertyChanges(output)
142
143 - def scrubPropertyChanges(self, output):
144 """ 145 Scrub the given diff output from property changes. 146 """ 147 reo = re.compile( 148 '^$\n' # starting empty line 149 '^Property changes on:.*?$\n' # Property changes line, non-greedy 150 '.*?' # all the other lines, non-greedy 151 '^$\n', # ending empty line 152 re.MULTILINE | re.DOTALL) # make sure we do multi-line 153 154 return reo.sub('', output)
155
156 - def getPropertyChanges(self, path):
157 ret = {} 158 159 cmd = "LANG=C svn diff %s" % path 160 # we add a newline so we can match each Property changes block by 161 # having it end on a newline, including the last block 162 output = commands.getoutput(cmd) + '\n' 163 164 # match Property changes blocks 165 reo = re.compile( 166 'Property changes on: (.*?)$\n' # Property changes line, non-greedy 167 '^_*$\n' # Divider line 168 '^(\w*: .*?' # Property name block, non-greedy 169 '(?:Property)?)' # and stop at a possible next block 170 '^$\n' # and end on empty line 171 , re.MULTILINE | re.DOTALL) # make sure we do multi-line 172 173 # match Name: blocks within a file's property changes 174 reop = re.compile( 175 '^\w*: (.*?)\n' # Property name block, non-greedy 176 , re.MULTILINE | re.DOTALL) # make sure we do multi-line 177 178 179 fileMatches = reo.findall(output) 180 for path, properties in fileMatches: 181 ret[path] = reop.findall(properties) 182 183 return ret
184
185 - def update(self, path):
186 cmd = "svn update %s" % path 187 status, output = commands.getstatusoutput(cmd) 188 if status != 0: 189 raise vcs.VCSException(output) 190 return output
191
192 - def getCheckoutCommands(self):
193 ret = [] 194 195 oldPath = os.getcwd() 196 os.chdir(self.path) 197 198 # FIXME: what if some files are at a different revision ? 199 cmd = "svn info %s" % self.path 200 status, output = commands.getstatusoutput(cmd) 201 if status != 0: 202 raise vcs.VCSException(output) 203 lines = output.split('\n') 204 url = None 205 baseRevision = None 206 for line in lines: 207 if line.startswith('URL: '): 208 url = line[4:] 209 if line.startswith('Revision: '): 210 baseRevision = int(line[10:]) 211 212 ret.append( 213 'svn checkout --non-interactive --revision %d %s checkout\n' % ( 214 baseRevision, url)) 215 216 # now check all paths that are at a different revision than the base 217 # one 218 cmd = "svn status -v" 219 status, output = commands.getstatusoutput(cmd) 220 if status != 0: 221 raise vcs.VCSException(output) 222 lines = output.split('\n') 223 224 matcher = re.compile('.{8}' # first 8 status columns 225 '\s+(\d+)' # current revision 226 '\s+(\d+)' # last commited version 227 '\s+(\w+)' # last committer 228 '\s+(.*)' # working copy path 229 ) 230 231 # FIXME: group same revisions for a speedup 232 for line in lines: 233 m = matcher.search(line) 234 if m: 235 revision = int(m.expand("\\1")) 236 path = m.expand("\\4") 237 if revision != baseRevision: 238 ret.append('svn update --non-interactive --non-recursive ' 239 '--revision %d %s' % ( 240 revision, os.path.join('checkout', path))) 241 242 os.chdir(oldPath) 243 244 return "\n".join(ret) + ('\n')
245 246 VCSClass = SVN 247