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