446 # directory. It also lists deleted tags, this behaviour may |
446 # directory. It also lists deleted tags, this behaviour may |
447 # change in the future. |
447 # change in the future. |
448 pendings = [] |
448 pendings = [] |
449 tagspath = self.tags |
449 tagspath = self.tags |
450 start = svn.ra.get_latest_revnum(self.ra) |
450 start = svn.ra.get_latest_revnum(self.ra) |
451 for entry in self._getlog([self.tags], start, self.startrev): |
451 stream = self._getlog([self.tags], start, self.startrev) |
452 origpaths, revnum, author, date, message = entry |
452 try: |
453 copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e |
453 for entry in stream: |
454 in origpaths.iteritems() if e.copyfrom_path] |
454 origpaths, revnum, author, date, message = entry |
455 # Apply moves/copies from more specific to general |
455 copies = [(e.copyfrom_path, e.copyfrom_rev, p) for p, e |
456 copies.sort(reverse=True) |
456 in origpaths.iteritems() if e.copyfrom_path] |
457 |
457 # Apply moves/copies from more specific to general |
458 srctagspath = tagspath |
458 copies.sort(reverse=True) |
459 if copies and copies[-1][2] == tagspath: |
459 |
460 # Track tags directory moves |
460 srctagspath = tagspath |
461 srctagspath = copies.pop()[0] |
461 if copies and copies[-1][2] == tagspath: |
462 |
462 # Track tags directory moves |
463 for source, sourcerev, dest in copies: |
463 srctagspath = copies.pop()[0] |
464 if not dest.startswith(tagspath + '/'): |
464 |
465 continue |
465 for source, sourcerev, dest in copies: |
466 for tag in pendings: |
466 if not dest.startswith(tagspath + '/'): |
467 if tag[0].startswith(dest): |
467 continue |
468 tagpath = source + tag[0][len(dest):] |
468 for tag in pendings: |
469 tag[:2] = [tagpath, sourcerev] |
469 if tag[0].startswith(dest): |
|
470 tagpath = source + tag[0][len(dest):] |
|
471 tag[:2] = [tagpath, sourcerev] |
|
472 break |
|
473 else: |
|
474 pendings.append([source, sourcerev, dest]) |
|
475 |
|
476 # Filter out tags with children coming from different |
|
477 # parts of the repository like: |
|
478 # /tags/tag.1 (from /trunk:10) |
|
479 # /tags/tag.1/foo (from /branches/foo:12) |
|
480 # Here/tags/tag.1 discarded as well as its children. |
|
481 # It happens with tools like cvs2svn. Such tags cannot |
|
482 # be represented in mercurial. |
|
483 addeds = dict((p, e.copyfrom_path) for p, e |
|
484 in origpaths.iteritems() |
|
485 if e.action == 'A' and e.copyfrom_path) |
|
486 badroots = set() |
|
487 for destroot in addeds: |
|
488 for source, sourcerev, dest in pendings: |
|
489 if (not dest.startswith(destroot + '/') |
|
490 or source.startswith(addeds[destroot] + '/')): |
|
491 continue |
|
492 badroots.add(destroot) |
470 break |
493 break |
471 else: |
494 |
472 pendings.append([source, sourcerev, dest]) |
495 for badroot in badroots: |
473 |
496 pendings = [p for p in pendings if p[2] != badroot |
474 # Filter out tags with children coming from different |
497 and not p[2].startswith(badroot + '/')] |
475 # parts of the repository like: |
498 |
476 # /tags/tag.1 (from /trunk:10) |
499 # Tell tag renamings from tag creations |
477 # /tags/tag.1/foo (from /branches/foo:12) |
500 remainings = [] |
478 # Here/tags/tag.1 discarded as well as its children. |
|
479 # It happens with tools like cvs2svn. Such tags cannot |
|
480 # be represented in mercurial. |
|
481 addeds = dict((p, e.copyfrom_path) for p, e |
|
482 in origpaths.iteritems() |
|
483 if e.action == 'A' and e.copyfrom_path) |
|
484 badroots = set() |
|
485 for destroot in addeds: |
|
486 for source, sourcerev, dest in pendings: |
501 for source, sourcerev, dest in pendings: |
487 if (not dest.startswith(destroot + '/') |
502 tagname = dest.split('/')[-1] |
488 or source.startswith(addeds[destroot] + '/')): |
503 if source.startswith(srctagspath): |
|
504 remainings.append([source, sourcerev, tagname]) |
489 continue |
505 continue |
490 badroots.add(destroot) |
506 if tagname in tags: |
491 break |
507 # Keep the latest tag value |
492 |
508 continue |
493 for badroot in badroots: |
509 # From revision may be fake, get one with changes |
494 pendings = [p for p in pendings if p[2] != badroot |
510 try: |
495 and not p[2].startswith(badroot + '/')] |
511 tagid = self.latest(source, sourcerev) |
496 |
512 if tagid and tagname not in tags: |
497 # Tell tag renamings from tag creations |
513 tags[tagname] = tagid |
498 remainings = [] |
514 except SvnPathNotFound: |
499 for source, sourcerev, dest in pendings: |
515 # It happens when we are following directories |
500 tagname = dest.split('/')[-1] |
516 # we assumed were copied with their parents |
501 if source.startswith(srctagspath): |
517 # but were really created in the tag |
502 remainings.append([source, sourcerev, tagname]) |
518 # directory. |
503 continue |
519 pass |
504 if tagname in tags: |
520 pendings = remainings |
505 # Keep the latest tag value |
521 tagspath = srctagspath |
506 continue |
522 finally: |
507 # From revision may be fake, get one with changes |
523 stream.close() |
508 try: |
|
509 tagid = self.latest(source, sourcerev) |
|
510 if tagid and tagname not in tags: |
|
511 tags[tagname] = tagid |
|
512 except SvnPathNotFound: |
|
513 # It happens when we are following directories |
|
514 # we assumed were copied with their parents |
|
515 # but were really created in the tag |
|
516 # directory. |
|
517 pass |
|
518 pendings = remainings |
|
519 tagspath = srctagspath |
|
520 |
|
521 return tags |
524 return tags |
522 |
525 |
523 def converted(self, rev, destrev): |
526 def converted(self, rev, destrev): |
524 if not self.wc: |
527 if not self.wc: |
525 return |
528 return |