367 def gettags(self): |
367 def gettags(self): |
368 tags = {} |
368 tags = {} |
369 if self.tags is None: |
369 if self.tags is None: |
370 return tags |
370 return tags |
371 |
371 |
372 start = self.revnum(self.head) |
372 # svn tags are just a convention, project branches left in a |
373 try: |
373 # 'tags' directory. There is no other relationship than |
374 for entry in get_log(self.url, [self.tags], self.startrev, start): |
374 # ancestry, which is expensive to discover and makes them hard |
375 orig_paths, revnum, author, date, message = entry |
375 # to update incrementally. Worse, past revisions may be |
376 for path in orig_paths: |
376 # referenced by tags far away in the future, requiring a deep |
377 if not path.startswith(self.tags+'/'): |
377 # history traversal on every calculation. Current code |
|
378 # performs a single backward traversal, tracking moves within |
|
379 # the tags directory (tag renaming) and recording a new tag |
|
380 # everytime a project is copied from outside the tags |
|
381 # directory. It also lists deleted tags, this behaviour may |
|
382 # change in the future. |
|
383 pendings = [] |
|
384 tagspath = self.tags |
|
385 start = svn.ra.get_latest_revnum(self.ra) |
|
386 try: |
|
387 for entry in get_log(self.url, [self.tags], start, self.startrev): |
|
388 origpaths, revnum, author, date, message = entry |
|
389 copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p,e |
|
390 in origpaths.iteritems() if e.copyfrom_path] |
|
391 copies.sort() |
|
392 # Apply moves/copies from more specific to general |
|
393 copies.reverse() |
|
394 |
|
395 srctagspath = tagspath |
|
396 if copies and copies[-1][2] == tagspath: |
|
397 # Track tags directory moves |
|
398 srctagspath = copies.pop()[0] |
|
399 |
|
400 for source, sourcerev, dest in copies: |
|
401 if not dest.startswith(tagspath + '/'): |
378 continue |
402 continue |
379 ent = orig_paths[path] |
403 for tag in pendings: |
380 source = ent.copyfrom_path |
404 if tag[0].startswith(dest): |
381 rev = ent.copyfrom_rev |
405 tagpath = source + tag[0][len(dest):] |
382 tag = path.split('/')[-1] |
406 tag[:2] = [tagpath, sourcerev] |
383 tags[tag] = self.revid(rev, module=source) |
407 break |
|
408 else: |
|
409 pendings.append([source, sourcerev, dest.split('/')[-1]]) |
|
410 |
|
411 # Tell tag renamings from tag creations |
|
412 remainings = [] |
|
413 for source, sourcerev, tagname in pendings: |
|
414 if source.startswith(srctagspath): |
|
415 remainings.append([source, sourcerev, tagname]) |
|
416 continue |
|
417 # From revision may be fake, get one with changes |
|
418 tagid = self.latest(source, sourcerev) |
|
419 if tagid: |
|
420 tags[tagname] = tagid |
|
421 pendings = remainings |
|
422 tagspath = srctagspath |
|
423 |
384 except SubversionException, (inst, num): |
424 except SubversionException, (inst, num): |
385 self.ui.note('no tags found at revision %d\n' % start) |
425 self.ui.note('no tags found at revision %d\n' % start) |
386 return tags |
426 return tags |
387 |
427 |
388 def converted(self, rev, destrev): |
428 def converted(self, rev, destrev): |