##// END OF EJS Templates
convert/subversion.py: str.rsplit is not available in Python 2.3
Bryan O'Sullivan -
r4927:5e89b0da default
parent child Browse files
Show More
@@ -1,621 +1,623
1 1 # Subversion 1.4/1.5 Python API backend
2 2 #
3 3 # Copyright(C) 2007 Daniel Holth et al
4 4 #
5 5 # Configuration options:
6 6 #
7 7 # convert.svn.trunk
8 8 # Relative path to the trunk (default: "trunk")
9 9 # convert.svn.branches
10 10 # Relative path to tree of branches (default: "branches")
11 11 #
12 12 # Set these in a hgrc, or on the command line as follows:
13 13 #
14 14 # hg convert --config convert.svn.trunk=wackoname [...]
15 15
16 16 import pprint
17 17 import locale
18 18
19 19 from mercurial import util
20 20
21 21 # Subversion stuff. Works best with very recent Python SVN bindings
22 22 # e.g. SVN 1.5 or backports. Thanks to the bzr folks for enhancing
23 23 # these bindings.
24 24
25 25 from cStringIO import StringIO
26 26
27 27 from common import NoRepo, commit, converter_source
28 28
29 29 try:
30 30 from svn.core import SubversionException, Pool
31 31 import svn.core
32 32 import svn.ra
33 33 import svn.delta
34 34 import svn
35 35 import transport
36 36 except ImportError:
37 37 pass
38 38
39 39 class CompatibilityException(Exception): pass
40 40
41 41 # SVN conversion code stolen from bzr-svn and tailor
42 42 class convert_svn(converter_source):
43 43 def __init__(self, ui, url, rev=None):
44 44 super(convert_svn, self).__init__(ui, url, rev=rev)
45 45
46 46 try:
47 47 SubversionException
48 48 except NameError:
49 49 msg = 'subversion python bindings could not be loaded\n'
50 50 ui.warn(msg)
51 51 raise NoRepo(msg)
52 52
53 53 self.encoding = locale.getpreferredencoding()
54 54 self.lastrevs = {}
55 55
56 56 latest = None
57 57 if rev:
58 58 try:
59 59 latest = int(rev)
60 60 except ValueError:
61 61 raise util.Abort('svn: revision %s is not an integer' % rev)
62 62 try:
63 63 # Support file://path@rev syntax. Useful e.g. to convert
64 64 # deleted branches.
65 url, latest = url.rsplit("@", 1)
66 latest = int(latest)
65 at = url.rfind('@')
66 if at >= 0:
67 latest = int(url[at+1:])
68 url = url[:at]
67 69 except ValueError, e:
68 70 pass
69 71 self.url = url
70 72 self.encoding = 'UTF-8' # Subversion is always nominal UTF-8
71 73 try:
72 74 self.transport = transport.SvnRaTransport(url = url)
73 75 self.ra = self.transport.ra
74 76 self.ctx = svn.client.create_context()
75 77 self.base = svn.ra.get_repos_root(self.ra)
76 78 self.module = self.url[len(self.base):]
77 79 self.modulemap = {} # revision, module
78 80 self.commits = {}
79 81 self.files = {}
80 82 self.uuid = svn.ra.get_uuid(self.ra).decode(self.encoding)
81 83 except SubversionException, e:
82 84 raise NoRepo("couldn't open SVN repo %s" % url)
83 85
84 86 try:
85 87 self.get_blacklist()
86 88 except IOError, e:
87 89 pass
88 90
89 91 self.last_changed = self.latest(self.module, latest)
90 92
91 93 self.head = self.revid(self.last_changed)
92 94
93 95 def setrevmap(self, revmap):
94 96 lastrevs = {}
95 97 for revid in revmap.keys():
96 98 uuid, module, revnum = self.revsplit(revid)
97 99 lastrevnum = lastrevs.setdefault(module, revnum)
98 100 if revnum > lastrevnum:
99 101 lastrevs[module] = revnum
100 102 self.lastrevs = lastrevs
101 103
102 104 def exists(self, path, optrev):
103 105 try:
104 106 return svn.client.ls(self.url.rstrip('/') + '/' + path,
105 107 optrev, False, self.ctx)
106 108 except SubversionException, err:
107 109 return []
108 110
109 111 def getheads(self):
110 112 # detect standard /branches, /tags, /trunk layout
111 113 optrev = svn.core.svn_opt_revision_t()
112 114 optrev.kind = svn.core.svn_opt_revision_number
113 115 optrev.value.number = self.last_changed
114 116 rpath = self.url.strip('/')
115 117 cfgtrunk = self.ui.config('convert', 'svn.trunk')
116 118 cfgbranches = self.ui.config('convert', 'svn.branches')
117 119 trunk = (cfgtrunk or 'trunk').strip('/')
118 120 branches = (cfgbranches or 'branches').strip('/')
119 121 if self.exists(trunk, optrev) and self.exists(branches, optrev):
120 122 self.ui.note('found trunk at %r and branches at %r\n' %
121 123 (trunk, branches))
122 124 oldmodule = self.module
123 125 self.module += '/' + trunk
124 126 lt = self.latest(self.module, self.last_changed)
125 127 self.head = self.revid(lt)
126 128 self.heads = [self.head]
127 129 branchnames = svn.client.ls(rpath + '/' + branches, optrev, False,
128 130 self.ctx)
129 131 for branch in branchnames.keys():
130 132 if oldmodule:
131 133 module = '/' + oldmodule + '/' + branches + '/' + branch
132 134 else:
133 135 module = '/' + branches + '/' + branch
134 136 brevnum = self.latest(module, self.last_changed)
135 137 brev = self.revid(brevnum, module)
136 138 self.ui.note('found branch %s at %d\n' % (branch, brevnum))
137 139 self.heads.append(brev)
138 140 elif cfgtrunk or cfgbranches:
139 141 raise util.Abort(_('trunk/branch layout expected, '
140 142 'but not found'))
141 143 else:
142 144 self.ui.note('working with one branch\n')
143 145 self.heads = [self.head]
144 146 return self.heads
145 147
146 148 def getfile(self, file, rev):
147 149 data, mode = self._getfile(file, rev)
148 150 self.modecache[(file, rev)] = mode
149 151 return data
150 152
151 153 def getmode(self, file, rev):
152 154 return self.modecache[(file, rev)]
153 155
154 156 def getchanges(self, rev):
155 157 self.modecache = {}
156 158 files = self.files[rev]
157 159 cl = files
158 160 cl.sort()
159 161 # caller caches the result, so free it here to release memory
160 162 del self.files[rev]
161 163 return cl
162 164
163 165 def getcommit(self, rev):
164 166 if rev not in self.commits:
165 167 uuid, module, revnum = self.revsplit(rev)
166 168 self.module = module
167 169 self.reparent(module)
168 170 stop = self.lastrevs.get(module, 0)
169 171 self._fetch_revisions(from_revnum=revnum, to_revnum=stop)
170 172 commit = self.commits[rev]
171 173 # caller caches the result, so free it here to release memory
172 174 del self.commits[rev]
173 175 return commit
174 176
175 177 def gettags(self):
176 178 tags = {}
177 179 def parselogentry(*arg, **args):
178 180 orig_paths, revnum, author, date, message, pool = arg
179 181 for path in orig_paths:
180 182 if not path.startswith('/tags/'):
181 183 continue
182 184 ent = orig_paths[path]
183 185 source = ent.copyfrom_path
184 186 rev = ent.copyfrom_rev
185 187 tag = path.split('/', 2)[2]
186 188 tags[tag] = self.revid(rev, module=source)
187 189
188 190 start = self.revnum(self.head)
189 191 try:
190 192 svn.ra.get_log(self.ra, ['/tags'], 0, start, 0, True, False,
191 193 parselogentry)
192 194 return tags
193 195 except SubversionException:
194 196 self.ui.note('no tags found at revision %d\n' % start)
195 197 return {}
196 198
197 199 # -- helper functions --
198 200
199 201 def revid(self, revnum, module=None):
200 202 if not module:
201 203 module = self.module
202 204 return (u"svn:%s%s@%s" % (self.uuid, module, revnum)).decode(self.encoding)
203 205
204 206 def revnum(self, rev):
205 207 return int(rev.split('@')[-1])
206 208
207 209 def revsplit(self, rev):
208 210 url, revnum = rev.encode(self.encoding).split('@', 1)
209 211 revnum = int(revnum)
210 212 parts = url.split('/', 1)
211 213 uuid = parts.pop(0)[4:]
212 214 mod = ''
213 215 if parts:
214 216 mod = '/' + parts[0]
215 217 return uuid, mod, revnum
216 218
217 219 def latest(self, path, stop=0):
218 220 'find the latest revision affecting path, up to stop'
219 221 if not stop:
220 222 stop = svn.ra.get_latest_revnum(self.ra)
221 223 try:
222 224 self.reparent('')
223 225 dirent = svn.ra.stat(self.ra, path.strip('/'), stop)
224 226 self.reparent(self.module)
225 227 except SubversionException:
226 228 dirent = None
227 229 if not dirent:
228 230 print self.base, path
229 231 raise util.Abort('%s not found up to revision %d' % (path, stop))
230 232
231 233 return dirent.created_rev
232 234
233 235 def get_blacklist(self):
234 236 """Avoid certain revision numbers.
235 237 It is not uncommon for two nearby revisions to cancel each other
236 238 out, e.g. 'I copied trunk into a subdirectory of itself instead
237 239 of making a branch'. The converted repository is significantly
238 240 smaller if we ignore such revisions."""
239 241 self.blacklist = set()
240 242 blacklist = self.blacklist
241 243 for line in file("blacklist.txt", "r"):
242 244 if not line.startswith("#"):
243 245 try:
244 246 svn_rev = int(line.strip())
245 247 blacklist.add(svn_rev)
246 248 except ValueError, e:
247 249 pass # not an integer or a comment
248 250
249 251 def is_blacklisted(self, svn_rev):
250 252 return svn_rev in self.blacklist
251 253
252 254 def reparent(self, module):
253 255 svn_url = self.base + module
254 256 self.ui.debug("reparent to %s\n" % svn_url.encode(self.encoding))
255 257 svn.ra.reparent(self.ra, svn_url.encode(self.encoding))
256 258
257 259 def _fetch_revisions(self, from_revnum = 0, to_revnum = 347):
258 260 def get_entry_from_path(path, module=self.module):
259 261 # Given the repository url of this wc, say
260 262 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch"
261 263 # extract the "entry" portion (a relative path) from what
262 264 # svn log --xml says, ie
263 265 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py"
264 266 # that is to say "tests/PloneTestCase.py"
265 267
266 268 if path.startswith(module):
267 269 relative = path[len(module):]
268 270 if relative.startswith('/'):
269 271 return relative[1:]
270 272 else:
271 273 return relative
272 274
273 275 # The path is outside our tracked tree...
274 276 self.ui.debug('Ignoring %r since it is not under %r\n' % (path, module))
275 277 return None
276 278
277 279 received = []
278 280 # svn.ra.get_log requires no other calls to the ra until it completes,
279 281 # so we just collect the log entries and parse them afterwards
280 282 def receivelog(*arg, **args):
281 283 received.append(arg)
282 284
283 285 self.child_cset = None
284 286 def parselogentry(*arg, **args):
285 287 orig_paths, revnum, author, date, message, pool = arg
286 288
287 289 if self.is_blacklisted(revnum):
288 290 self.ui.note('skipping blacklisted revision %d\n' % revnum)
289 291 return
290 292
291 293 self.ui.debug("parsing revision %d\n" % revnum)
292 294
293 295 if orig_paths is None:
294 296 self.ui.debug('revision %d has no entries\n' % revnum)
295 297 return
296 298
297 299 if revnum in self.modulemap:
298 300 new_module = self.modulemap[revnum]
299 301 if new_module != self.module:
300 302 self.module = new_module
301 303 self.reparent(self.module)
302 304
303 305 copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions.
304 306 copies = {}
305 307 entries = []
306 308 rev = self.revid(revnum)
307 309 parents = []
308 310
309 311 # branch log might return entries for a parent we already have
310 312 if (rev in self.commits or
311 313 (revnum < self.lastrevs.get(self.module, 0))):
312 314 return
313 315
314 316 try:
315 317 branch = self.module.split("/")[-1]
316 318 if branch == 'trunk':
317 319 branch = ''
318 320 except IndexError:
319 321 branch = None
320 322
321 323 paths = orig_paths.keys()
322 324 paths.sort()
323 325 for path in paths:
324 326 # self.ui.write("path %s\n" % path)
325 327 if path == self.module: # Follow branching back in history
326 328 ent = orig_paths[path]
327 329 if ent:
328 330 if ent.copyfrom_path:
329 331 # ent.copyfrom_rev may not be the actual last revision
330 332 prev = self.latest(ent.copyfrom_path, ent.copyfrom_rev)
331 333 self.modulemap[prev] = ent.copyfrom_path
332 334 parents = [self.revid(prev, ent.copyfrom_path)]
333 335 self.ui.note('found parent of branch %s at %d: %s\n' % \
334 336 (self.module, prev, ent.copyfrom_path))
335 337 else:
336 338 self.ui.debug("No copyfrom path, don't know what to do.\n")
337 339 # Maybe it was added and there is no more history.
338 340 entrypath = get_entry_from_path(path, module=self.module)
339 341 # self.ui.write("entrypath %s\n" % entrypath)
340 342 if entrypath is None:
341 343 # Outside our area of interest
342 344 self.ui.debug("boring@%s: %s\n" % (revnum, path))
343 345 continue
344 346 entry = entrypath.decode(self.encoding)
345 347 ent = orig_paths[path]
346 348
347 349 kind = svn.ra.check_path(self.ra, entrypath, revnum)
348 350 if kind == svn.core.svn_node_file:
349 351 if ent.copyfrom_path:
350 352 copyfrom_path = get_entry_from_path(ent.copyfrom_path)
351 353 if copyfrom_path:
352 354 self.ui.debug("Copied to %s from %s@%s\n" % (entry, copyfrom_path, ent.copyfrom_rev))
353 355 # It's probably important for hg that the source
354 356 # exists in the revision's parent, not just the
355 357 # ent.copyfrom_rev
356 358 fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev)
357 359 if fromkind != 0:
358 360 copies[self.recode(entry)] = self.recode(copyfrom_path)
359 361 entries.append(self.recode(entry))
360 362 elif kind == 0: # gone, but had better be a deleted *file*
361 363 self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
362 364
363 365 # if a branch is created but entries are removed in the same
364 366 # changeset, get the right fromrev
365 367 if parents:
366 368 uuid, old_module, fromrev = self.revsplit(parents[0])
367 369 else:
368 370 fromrev = revnum - 1
369 371 # might always need to be revnum - 1 in these 3 lines?
370 372 old_module = self.modulemap.get(fromrev, self.module)
371 373
372 374 basepath = old_module + "/" + get_entry_from_path(path, module=self.module)
373 375 entrypath = old_module + "/" + get_entry_from_path(path, module=self.module)
374 376
375 377 def lookup_parts(p):
376 378 rc = None
377 379 parts = p.split("/")
378 380 for i in range(len(parts)):
379 381 part = "/".join(parts[:i])
380 382 info = part, copyfrom.get(part, None)
381 383 if info[1] is not None:
382 384 self.ui.debug("Found parent directory %s\n" % info[1])
383 385 rc = info
384 386 return rc
385 387
386 388 self.ui.debug("base, entry %s %s\n" % (basepath, entrypath))
387 389
388 390 frompath, froment = lookup_parts(entrypath) or (None, revnum - 1)
389 391
390 392 # need to remove fragment from lookup_parts and replace with copyfrom_path
391 393 if frompath is not None:
392 394 self.ui.debug("munge-o-matic\n")
393 395 self.ui.debug(entrypath + '\n')
394 396 self.ui.debug(entrypath[len(frompath):] + '\n')
395 397 entrypath = froment.copyfrom_path + entrypath[len(frompath):]
396 398 fromrev = froment.copyfrom_rev
397 399 self.ui.debug("Info: %s %s %s %s\n" % (frompath, froment, ent, entrypath))
398 400
399 401 fromkind = svn.ra.check_path(self.ra, entrypath, fromrev)
400 402 if fromkind == svn.core.svn_node_file: # a deleted file
401 403 entries.append(self.recode(entry))
402 404 elif fromkind == svn.core.svn_node_dir:
403 405 # print "Deleted/moved non-file:", revnum, path, ent
404 406 # children = self._find_children(path, revnum - 1)
405 407 # print "find children %s@%d from %d action %s" % (path, revnum, ent.copyfrom_rev, ent.action)
406 408 # Sometimes this is tricky. For example: in
407 409 # The Subversion Repository revision 6940 a dir
408 410 # was copied and one of its files was deleted
409 411 # from the new location in the same commit. This
410 412 # code can't deal with that yet.
411 413 if ent.action == 'C':
412 414 children = self._find_children(path, fromrev)
413 415 else:
414 416 oroot = entrypath.strip('/')
415 417 nroot = path.strip('/')
416 418 children = self._find_children(oroot, fromrev)
417 419 children = [s.replace(oroot,nroot) for s in children]
418 420 # Mark all [files, not directories] as deleted.
419 421 for child in children:
420 422 # Can we move a child directory and its
421 423 # parent in the same commit? (probably can). Could
422 424 # cause problems if instead of revnum -1,
423 425 # we have to look in (copyfrom_path, revnum - 1)
424 426 entrypath = get_entry_from_path("/" + child, module=old_module)
425 427 if entrypath:
426 428 entry = self.recode(entrypath.decode(self.encoding))
427 429 if entry in copies:
428 430 # deleted file within a copy
429 431 del copies[entry]
430 432 else:
431 433 entries.append(entry)
432 434 else:
433 435 self.ui.debug('unknown path in revision %d: %s\n' % \
434 436 (revnum, path))
435 437 elif kind == svn.core.svn_node_dir:
436 438 # Should probably synthesize normal file entries
437 439 # and handle as above to clean up copy/rename handling.
438 440
439 441 # If the directory just had a prop change,
440 442 # then we shouldn't need to look for its children.
441 443 # Also this could create duplicate entries. Not sure
442 444 # whether this will matter. Maybe should make entries a set.
443 445 # print "Changed directory", revnum, path, ent.action, ent.copyfrom_path, ent.copyfrom_rev
444 446 # This will fail if a directory was copied
445 447 # from another branch and then some of its files
446 448 # were deleted in the same transaction.
447 449 children = self._find_children(path, revnum)
448 450 children.sort()
449 451 for child in children:
450 452 # Can we move a child directory and its
451 453 # parent in the same commit? (probably can). Could
452 454 # cause problems if instead of revnum -1,
453 455 # we have to look in (copyfrom_path, revnum - 1)
454 456 entrypath = get_entry_from_path("/" + child, module=self.module)
455 457 # print child, self.module, entrypath
456 458 if entrypath:
457 459 # Need to filter out directories here...
458 460 kind = svn.ra.check_path(self.ra, entrypath, revnum)
459 461 if kind != svn.core.svn_node_dir:
460 462 entries.append(self.recode(entrypath))
461 463
462 464 # Copies here (must copy all from source)
463 465 # Probably not a real problem for us if
464 466 # source does not exist
465 467
466 468 # Can do this with the copy command "hg copy"
467 469 # if ent.copyfrom_path:
468 470 # copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding),
469 471 # module=self.module)
470 472 # copyto_entry = entrypath
471 473 #
472 474 # print "copy directory", copyfrom_entry, 'to', copyto_entry
473 475 #
474 476 # copies.append((copyfrom_entry, copyto_entry))
475 477
476 478 if ent.copyfrom_path:
477 479 copyfrom_path = ent.copyfrom_path.decode(self.encoding)
478 480 copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module)
479 481 if copyfrom_entry:
480 482 copyfrom[path] = ent
481 483 self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path]))
482 484
483 485 # Good, /probably/ a regular copy. Really should check
484 486 # to see whether the parent revision actually contains
485 487 # the directory in question.
486 488 children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev)
487 489 children.sort()
488 490 for child in children:
489 491 entrypath = get_entry_from_path("/" + child, module=self.module)
490 492 if entrypath:
491 493 entry = entrypath.decode(self.encoding)
492 494 # print "COPY COPY From", copyfrom_entry, entry
493 495 copyto_path = path + entry[len(copyfrom_entry):]
494 496 copyto_entry = get_entry_from_path(copyto_path, module=self.module)
495 497 # print "COPY", entry, "COPY To", copyto_entry
496 498 copies[self.recode(copyto_entry)] = self.recode(entry)
497 499 # copy from quux splort/quuxfile
498 500
499 501 self.modulemap[revnum] = self.module # track backwards in time
500 502 # a list of (filename, id) where id lets us retrieve the file.
501 503 # eg in git, id is the object hash. for svn it'll be the
502 504 self.files[rev] = zip(entries, [rev] * len(entries))
503 505 if not entries:
504 506 return
505 507
506 508 # Example SVN datetime. Includes microseconds.
507 509 # ISO-8601 conformant
508 510 # '2007-01-04T17:35:00.902377Z'
509 511 date = util.parsedate(date[:18] + " UTC", ["%Y-%m-%dT%H:%M:%S"])
510 512
511 513 log = message and self.recode(message)
512 514 author = author and self.recode(author) or ''
513 515
514 516 cset = commit(author=author,
515 517 date=util.datestr(date),
516 518 desc=log,
517 519 parents=parents,
518 520 copies=copies,
519 521 branch=branch,
520 522 rev=rev.encode('utf-8'))
521 523
522 524 self.commits[rev] = cset
523 525 if self.child_cset and not self.child_cset.parents:
524 526 self.child_cset.parents = [rev]
525 527 self.child_cset = cset
526 528
527 529 self.ui.note('fetching revision log for "%s" from %d to %d\n' % \
528 530 (self.module, from_revnum, to_revnum))
529 531
530 532 try:
531 533 discover_changed_paths = True
532 534 strict_node_history = False
533 535 svn.ra.get_log(self.ra, [self.module], from_revnum, to_revnum, 0,
534 536 discover_changed_paths, strict_node_history,
535 537 receivelog)
536 538 for entry in received:
537 539 parselogentry(*entry)
538 540 except SubversionException, (_, num):
539 541 if num == svn.core.SVN_ERR_FS_NO_SUCH_REVISION:
540 542 raise NoSuchRevision(branch=self,
541 543 revision="Revision number %d" % to_revnum)
542 544 raise
543 545
544 546 def _getfile(self, file, rev):
545 547 io = StringIO()
546 548 # TODO: ra.get_file transmits the whole file instead of diffs.
547 549 mode = ''
548 550 try:
549 551 revnum = self.revnum(rev)
550 552 if self.module != self.modulemap[revnum]:
551 553 self.module = self.modulemap[revnum]
552 554 self.reparent(self.module)
553 555 info = svn.ra.get_file(self.ra, file, revnum, io)
554 556 if isinstance(info, list):
555 557 info = info[-1]
556 558 mode = ("svn:executable" in info) and 'x' or ''
557 559 mode = ("svn:special" in info) and 'l' or mode
558 560 except SubversionException, e:
559 561 notfound = (svn.core.SVN_ERR_FS_NOT_FOUND,
560 562 svn.core.SVN_ERR_RA_DAV_PATH_NOT_FOUND)
561 563 if e.apr_err in notfound: # File not found
562 564 raise IOError()
563 565 raise
564 566 data = io.getvalue()
565 567 if mode == 'l':
566 568 link_prefix = "link "
567 569 if data.startswith(link_prefix):
568 570 data = data[len(link_prefix):]
569 571 return data, mode
570 572
571 573 def _find_children(self, path, revnum):
572 574 path = path.strip("/")
573 575
574 576 def _find_children_fallback(path, revnum):
575 577 # SWIG python bindings for getdir are broken up to at least 1.4.3
576 578 pool = Pool()
577 579 optrev = svn.core.svn_opt_revision_t()
578 580 optrev.kind = svn.core.svn_opt_revision_number
579 581 optrev.value.number = revnum
580 582 rpath = '/'.join([self.base, path]).strip('/')
581 583 return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev, True, self.ctx, pool).keys()]
582 584
583 585 if hasattr(self, '_find_children_fallback'):
584 586 return _find_children_fallback(path, revnum)
585 587
586 588 self.reparent("/" + path)
587 589 pool = Pool()
588 590
589 591 children = []
590 592 def find_children_inner(children, path, revnum = revnum):
591 593 if hasattr(svn.ra, 'get_dir2'): # Since SVN 1.4
592 594 fields = 0xffffffff # Binding does not provide SVN_DIRENT_ALL
593 595 getdir = svn.ra.get_dir2(self.ra, path, revnum, fields, pool)
594 596 else:
595 597 getdir = svn.ra.get_dir(self.ra, path, revnum, pool)
596 598 if type(getdir) == dict:
597 599 # python binding for getdir is broken up to at least 1.4.3
598 600 raise CompatibilityException()
599 601 dirents = getdir[0]
600 602 if type(dirents) == int:
601 603 # got here once due to infinite recursion bug
602 604 # pprint.pprint(getdir)
603 605 return
604 606 c = dirents.keys()
605 607 c.sort()
606 608 for child in c:
607 609 dirent = dirents[child]
608 610 if dirent.kind == svn.core.svn_node_dir:
609 611 find_children_inner(children, (path + "/" + child).strip("/"))
610 612 else:
611 613 children.append((path + "/" + child).strip("/"))
612 614
613 615 try:
614 616 find_children_inner(children, "")
615 617 except CompatibilityException:
616 618 self._find_children_fallback = True
617 619 self.reparent(self.module)
618 620 return _find_children_fallback(path, revnum)
619 621
620 622 self.reparent(self.module)
621 623 return [path + "/" + c for c in children]
General Comments 0
You need to be logged in to leave comments. Login now