533 svn_url = self.base + module |
535 svn_url = self.base + module |
534 self.ui.debug("reparent to %s\n" % svn_url.encode(self.encoding)) |
536 self.ui.debug("reparent to %s\n" % svn_url.encode(self.encoding)) |
535 svn.ra.reparent(self.ra, svn_url.encode(self.encoding)) |
537 svn.ra.reparent(self.ra, svn_url.encode(self.encoding)) |
536 |
538 |
537 def expandpaths(self, rev, paths, parents): |
539 def expandpaths(self, rev, paths, parents): |
538 def get_entry_from_path(path, module=self.module): |
|
539 # Given the repository url of this wc, say |
|
540 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch" |
|
541 # extract the "entry" portion (a relative path) from what |
|
542 # svn log --xml says, ie |
|
543 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" |
|
544 # that is to say "tests/PloneTestCase.py" |
|
545 if path.startswith(module): |
|
546 relative = path[len(module):] |
|
547 if relative.startswith('/'): |
|
548 return relative[1:] |
|
549 else: |
|
550 return relative |
|
551 |
|
552 # The path is outside our tracked tree... |
|
553 self.ui.debug('%r is not under %r, ignoring\n' % (path, module)) |
|
554 return None |
|
555 |
|
556 entries = [] |
540 entries = [] |
557 copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions. |
541 copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions. |
558 copies = {} |
542 copies = {} |
559 |
543 |
560 new_module, revnum = self.revsplit(rev)[1:] |
544 new_module, revnum = self.revsplit(rev)[1:] |
561 if new_module != self.module: |
545 if new_module != self.module: |
562 self.module = new_module |
546 self.module = new_module |
563 self.reparent(self.module) |
547 self.reparent(self.module) |
564 |
548 |
565 for path, ent in paths: |
549 for path, ent in paths: |
566 entrypath = get_entry_from_path(path, module=self.module) |
550 entrypath = self.getrelpath(path) |
567 entry = entrypath.decode(self.encoding) |
551 entry = entrypath.decode(self.encoding) |
568 |
552 |
569 kind = svn.ra.check_path(self.ra, entrypath, revnum) |
553 kind = svn.ra.check_path(self.ra, entrypath, revnum) |
570 if kind == svn.core.svn_node_file: |
554 if kind == svn.core.svn_node_file: |
571 if ent.copyfrom_path: |
|
572 copyfrom_path = get_entry_from_path(ent.copyfrom_path) |
|
573 if copyfrom_path: |
|
574 self.ui.debug("Copied to %s from %s@%s\n" % |
|
575 (entrypath, copyfrom_path, |
|
576 ent.copyfrom_rev)) |
|
577 # It's probably important for hg that the source |
|
578 # exists in the revision's parent, not just the |
|
579 # ent.copyfrom_rev |
|
580 fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev) |
|
581 if fromkind != 0: |
|
582 copies[self.recode(entry)] = self.recode(copyfrom_path) |
|
583 entries.append(self.recode(entry)) |
555 entries.append(self.recode(entry)) |
|
556 if not ent.copyfrom_path or not parents: |
|
557 continue |
|
558 # Copy sources not in parent revisions cannot be represented, |
|
559 # ignore their origin for now |
|
560 pmodule, prevnum = self.revsplit(parents[0])[1:] |
|
561 if ent.copyfrom_rev < prevnum: |
|
562 continue |
|
563 copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule) |
|
564 if not copyfrom_path: |
|
565 continue |
|
566 self.ui.debug("copied to %s from %s@%s\n" % |
|
567 (entrypath, copyfrom_path, ent.copyfrom_rev)) |
|
568 copies[self.recode(entry)] = self.recode(copyfrom_path) |
584 elif kind == 0: # gone, but had better be a deleted *file* |
569 elif kind == 0: # gone, but had better be a deleted *file* |
585 self.ui.debug("gone from %s\n" % ent.copyfrom_rev) |
570 self.ui.debug("gone from %s\n" % ent.copyfrom_rev) |
586 |
571 |
587 # if a branch is created but entries are removed in the same |
572 # if a branch is created but entries are removed in the same |
588 # changeset, get the right fromrev |
573 # changeset, get the right fromrev |
589 # parents cannot be empty here, you cannot remove things from |
574 # parents cannot be empty here, you cannot remove things from |
590 # a root revision. |
575 # a root revision. |
591 uuid, old_module, fromrev = self.revsplit(parents[0]) |
576 uuid, old_module, fromrev = self.revsplit(parents[0]) |
592 |
577 |
593 basepath = old_module + "/" + get_entry_from_path(path, module=self.module) |
578 basepath = old_module + "/" + self.getrelpath(path) |
594 entrypath = old_module + "/" + get_entry_from_path(path, module=self.module) |
579 entrypath = basepath |
595 |
580 |
596 def lookup_parts(p): |
581 def lookup_parts(p): |
597 rc = None |
582 rc = None |
598 parts = p.split("/") |
583 parts = p.split("/") |
599 for i in range(len(parts)): |
584 for i in range(len(parts)): |
678 for child in children: |
663 for child in children: |
679 # Can we move a child directory and its |
664 # Can we move a child directory and its |
680 # parent in the same commit? (probably can). Could |
665 # parent in the same commit? (probably can). Could |
681 # cause problems if instead of revnum -1, |
666 # cause problems if instead of revnum -1, |
682 # we have to look in (copyfrom_path, revnum - 1) |
667 # we have to look in (copyfrom_path, revnum - 1) |
683 entrypath = get_entry_from_path("/" + child, module=self.module) |
668 entrypath = self.getrelpath("/" + child) |
684 # print child, self.module, entrypath |
669 # print child, self.module, entrypath |
685 if entrypath: |
670 if entrypath: |
686 # Need to filter out directories here... |
671 # Need to filter out directories here... |
687 kind = svn.ra.check_path(self.ra, entrypath, revnum) |
672 kind = svn.ra.check_path(self.ra, entrypath, revnum) |
688 if kind != svn.core.svn_node_dir: |
673 if kind != svn.core.svn_node_dir: |
689 entries.append(self.recode(entrypath)) |
674 entries.append(self.recode(entrypath)) |
690 |
675 |
691 # Copies here (must copy all from source) |
676 # Copies here (must copy all from source) |
692 # Probably not a real problem for us if |
677 # Probably not a real problem for us if |
693 # source does not exist |
678 # source does not exist |
694 |
679 if not ent.copyfrom_path or not parents: |
695 # Can do this with the copy command "hg copy" |
680 continue |
696 # if ent.copyfrom_path: |
681 # Copy sources not in parent revisions cannot be represented, |
697 # copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding), |
682 # ignore their origin for now |
698 # module=self.module) |
683 pmodule, prevnum = self.revsplit(parents[0])[1:] |
699 # copyto_entry = entrypath |
684 if ent.copyfrom_rev < prevnum: |
700 # |
685 continue |
701 # print "copy directory", copyfrom_entry, 'to', copyto_entry |
686 copyfrompath = ent.copyfrom_path.decode(self.encoding) |
702 # |
687 copyfrompath = self.getrelpath(copyfrompath, pmodule) |
703 # copies.append((copyfrom_entry, copyto_entry)) |
688 if not copyfrompath: |
704 |
689 continue |
705 if ent.copyfrom_path: |
690 copyfrom[path] = ent |
706 copyfrom_path = ent.copyfrom_path.decode(self.encoding) |
691 self.ui.debug("mark %s came from %s:%d\n" |
707 copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module) |
692 % (path, copyfrompath, ent.copyfrom_rev)) |
708 if copyfrom_entry: |
693 children = self._find_children(ent.copyfrom_path, ent.copyfrom_rev) |
709 copyfrom[path] = ent |
694 children.sort() |
710 self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path])) |
695 for child in children: |
711 |
696 entrypath = self.getrelpath("/" + child, pmodule) |
712 # Good, /probably/ a regular copy. Really should check |
697 if not entrypath: |
713 # to see whether the parent revision actually contains |
698 continue |
714 # the directory in question. |
699 entry = entrypath.decode(self.encoding) |
715 children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev) |
700 copytopath = path + entry[len(copyfrompath):] |
716 children.sort() |
701 copytopath = self.getrelpath(copytopath) |
717 for child in children: |
702 copies[self.recode(copytopath)] = self.recode(entry, pmodule) |
718 entrypath = get_entry_from_path("/" + child, module=self.module) |
|
719 if entrypath: |
|
720 entry = entrypath.decode(self.encoding) |
|
721 # print "COPY COPY From", copyfrom_entry, entry |
|
722 copyto_path = path + entry[len(copyfrom_entry):] |
|
723 copyto_entry = get_entry_from_path(copyto_path, module=self.module) |
|
724 # print "COPY", entry, "COPY To", copyto_entry |
|
725 copies[self.recode(copyto_entry)] = self.recode(entry) |
|
726 # copy from quux splort/quuxfile |
|
727 |
703 |
728 return (util.unique(entries), copies) |
704 return (util.unique(entries), copies) |
729 |
705 |
730 def _fetch_revisions(self, from_revnum, to_revnum): |
706 def _fetch_revisions(self, from_revnum, to_revnum): |
731 if from_revnum < to_revnum: |
707 if from_revnum < to_revnum: |
732 from_revnum, to_revnum = to_revnum, from_revnum |
708 from_revnum, to_revnum = to_revnum, from_revnum |
733 |
709 |
734 self.child_cset = None |
710 self.child_cset = None |
|
711 |
|
712 def isdescendantof(parent, child): |
|
713 if not child or not parent or not child.startswith(parent): |
|
714 return False |
|
715 subpath = child[len(parent):] |
|
716 return len(subpath) > 1 and subpath[0] == '/' |
|
717 |
735 def parselogentry(orig_paths, revnum, author, date, message): |
718 def parselogentry(orig_paths, revnum, author, date, message): |
736 """Return the parsed commit object or None, and True if |
719 """Return the parsed commit object or None, and True if |
737 the revision is a branch root. |
720 the revision is a branch root. |
738 """ |
721 """ |
739 self.ui.debug("parsing revision %d (%d changes)\n" % |
722 self.ui.debug("parsing revision %d (%d changes)\n" % |
753 orig_paths.sort() |
736 orig_paths.sort() |
754 root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)] |
737 root_paths = [(p,e) for p,e in orig_paths if self.module.startswith(p)] |
755 if root_paths: |
738 if root_paths: |
756 path, ent = root_paths[-1] |
739 path, ent = root_paths[-1] |
757 if ent.copyfrom_path: |
740 if ent.copyfrom_path: |
|
741 # If dir was moved while one of its file was removed |
|
742 # the log may look like: |
|
743 # A /dir (from /dir:x) |
|
744 # A /dir/a (from /dir/a:y) |
|
745 # A /dir/b (from /dir/b:z) |
|
746 # ... |
|
747 # for all remaining children. |
|
748 # Let's take the highest child element from rev as source. |
|
749 copies = [(p,e) for p,e in orig_paths[:-1] |
|
750 if isdescendantof(ent.copyfrom_path, e.copyfrom_path)] |
|
751 fromrev = max([e.copyfrom_rev for p,e in copies] + [ent.copyfrom_rev]) |
758 branched = True |
752 branched = True |
759 newpath = ent.copyfrom_path + self.module[len(path):] |
753 newpath = ent.copyfrom_path + self.module[len(path):] |
760 # ent.copyfrom_rev may not be the actual last revision |
754 # ent.copyfrom_rev may not be the actual last revision |
761 previd = self.latest(newpath, ent.copyfrom_rev) |
755 previd = self.latest(newpath, fromrev) |
762 if previd is not None: |
756 if previd is not None: |
763 prevmodule, prevnum = self.revsplit(previd)[1:] |
757 prevmodule, prevnum = self.revsplit(previd)[1:] |
764 if prevnum >= self.startrev: |
758 if prevnum >= self.startrev: |
765 parents = [previd] |
759 parents = [previd] |
766 self.ui.note('found parent of branch %s at %d: %s\n' % |
760 self.ui.note('found parent of branch %s at %d: %s\n' % |
883 path = path.strip('/') |
876 path = path.strip('/') |
884 pool = Pool() |
877 pool = Pool() |
885 rpath = '/'.join([self.base, path]).strip('/') |
878 rpath = '/'.join([self.base, path]).strip('/') |
886 return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()] |
879 return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()] |
887 |
880 |
|
881 def getrelpath(self, path, module=None): |
|
882 if module is None: |
|
883 module = self.module |
|
884 # Given the repository url of this wc, say |
|
885 # "http://server/plone/CMFPlone/branches/Plone-2_0-branch" |
|
886 # extract the "entry" portion (a relative path) from what |
|
887 # svn log --xml says, ie |
|
888 # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" |
|
889 # that is to say "tests/PloneTestCase.py" |
|
890 if path.startswith(module): |
|
891 relative = path.rstrip('/')[len(module):] |
|
892 if relative.startswith('/'): |
|
893 return relative[1:] |
|
894 elif relative == '': |
|
895 return relative |
|
896 |
|
897 # The path is outside our tracked tree... |
|
898 self.ui.debug('%r is not under %r, ignoring\n' % (path, module)) |
|
899 return None |
|
900 |
888 pre_revprop_change = '''#!/bin/sh |
901 pre_revprop_change = '''#!/bin/sh |
889 |
902 |
890 REPOS="$1" |
903 REPOS="$1" |
891 REV="$2" |
904 REV="$2" |
892 USER="$3" |
905 USER="$3" |