diff -r 962eb403165b -r 2af1b9de62b3 hgext/convert/subversion.py --- a/hgext/convert/subversion.py Mon Apr 14 14:34:38 2008 +0200 +++ b/hgext/convert/subversion.py Mon Apr 14 23:04:34 2008 +0200 @@ -185,6 +185,8 @@ self.ra = self.transport.ra self.ctx = self.transport.client self.base = svn.ra.get_repos_root(self.ra) + # Module is either empty or a repository path starting with + # a slash and not ending with a slash. self.module = self.url[len(self.base):] self.rootmodule = self.module self.commits = {} @@ -535,24 +537,6 @@ svn.ra.reparent(self.ra, svn_url.encode(self.encoding)) def expandpaths(self, rev, paths, parents): - def get_entry_from_path(path, module=self.module): - # Given the repository url of this wc, say - # "http://server/plone/CMFPlone/branches/Plone-2_0-branch" - # extract the "entry" portion (a relative path) from what - # svn log --xml says, ie - # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" - # that is to say "tests/PloneTestCase.py" - if path.startswith(module): - relative = path[len(module):] - if relative.startswith('/'): - return relative[1:] - else: - return relative - - # The path is outside our tracked tree... - self.ui.debug('%r is not under %r, ignoring\n' % (path, module)) - return None - entries = [] copyfrom = {} # Map of entrypath, revision for finding source of deleted revisions. copies = {} @@ -563,24 +547,25 @@ self.reparent(self.module) for path, ent in paths: - entrypath = get_entry_from_path(path, module=self.module) + entrypath = self.getrelpath(path) entry = entrypath.decode(self.encoding) kind = svn.ra.check_path(self.ra, entrypath, revnum) if kind == svn.core.svn_node_file: - if ent.copyfrom_path: - copyfrom_path = get_entry_from_path(ent.copyfrom_path) - if copyfrom_path: - self.ui.debug("Copied to %s from %s@%s\n" % - (entrypath, copyfrom_path, - ent.copyfrom_rev)) - # It's probably important for hg that the source - # exists in the revision's parent, not just the - # ent.copyfrom_rev - fromkind = svn.ra.check_path(self.ra, copyfrom_path, ent.copyfrom_rev) - if fromkind != 0: - copies[self.recode(entry)] = self.recode(copyfrom_path) entries.append(self.recode(entry)) + if not ent.copyfrom_path or not parents: + continue + # Copy sources not in parent revisions cannot be represented, + # ignore their origin for now + pmodule, prevnum = self.revsplit(parents[0])[1:] + if ent.copyfrom_rev < prevnum: + continue + copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule) + if not copyfrom_path: + continue + self.ui.debug("copied to %s from %s@%s\n" % + (entrypath, copyfrom_path, ent.copyfrom_rev)) + copies[self.recode(entry)] = self.recode(copyfrom_path) elif kind == 0: # gone, but had better be a deleted *file* self.ui.debug("gone from %s\n" % ent.copyfrom_rev) @@ -590,8 +575,8 @@ # a root revision. uuid, old_module, fromrev = self.revsplit(parents[0]) - basepath = old_module + "/" + get_entry_from_path(path, module=self.module) - entrypath = old_module + "/" + get_entry_from_path(path, module=self.module) + basepath = old_module + "/" + self.getrelpath(path) + entrypath = basepath def lookup_parts(p): rc = None @@ -647,7 +632,7 @@ # parent in the same commit? (probably can). Could # cause problems if instead of revnum -1, # we have to look in (copyfrom_path, revnum - 1) - entrypath = get_entry_from_path("/" + child, module=old_module) + entrypath = self.getrelpath("/" + child, module=old_module) if entrypath: entry = self.recode(entrypath.decode(self.encoding)) if entry in copies: @@ -680,7 +665,7 @@ # parent in the same commit? (probably can). Could # cause problems if instead of revnum -1, # we have to look in (copyfrom_path, revnum - 1) - entrypath = get_entry_from_path("/" + child, module=self.module) + entrypath = self.getrelpath("/" + child) # print child, self.module, entrypath if entrypath: # Need to filter out directories here... @@ -691,39 +676,30 @@ # Copies here (must copy all from source) # Probably not a real problem for us if # source does not exist - - # Can do this with the copy command "hg copy" - # if ent.copyfrom_path: - # copyfrom_entry = get_entry_from_path(ent.copyfrom_path.decode(self.encoding), - # module=self.module) - # copyto_entry = entrypath - # - # print "copy directory", copyfrom_entry, 'to', copyto_entry - # - # copies.append((copyfrom_entry, copyto_entry)) - - if ent.copyfrom_path: - copyfrom_path = ent.copyfrom_path.decode(self.encoding) - copyfrom_entry = get_entry_from_path(copyfrom_path, module=self.module) - if copyfrom_entry: - copyfrom[path] = ent - self.ui.debug("mark %s came from %s\n" % (path, copyfrom[path])) - - # Good, /probably/ a regular copy. Really should check - # to see whether the parent revision actually contains - # the directory in question. - children = self._find_children(self.recode(copyfrom_path), ent.copyfrom_rev) - children.sort() - for child in children: - entrypath = get_entry_from_path("/" + child, module=self.module) - if entrypath: - entry = entrypath.decode(self.encoding) - # print "COPY COPY From", copyfrom_entry, entry - copyto_path = path + entry[len(copyfrom_entry):] - copyto_entry = get_entry_from_path(copyto_path, module=self.module) - # print "COPY", entry, "COPY To", copyto_entry - copies[self.recode(copyto_entry)] = self.recode(entry) - # copy from quux splort/quuxfile + if not ent.copyfrom_path or not parents: + continue + # Copy sources not in parent revisions cannot be represented, + # ignore their origin for now + pmodule, prevnum = self.revsplit(parents[0])[1:] + if ent.copyfrom_rev < prevnum: + continue + copyfrompath = ent.copyfrom_path.decode(self.encoding) + copyfrompath = self.getrelpath(copyfrompath, pmodule) + if not copyfrompath: + continue + copyfrom[path] = ent + self.ui.debug("mark %s came from %s:%d\n" + % (path, copyfrompath, ent.copyfrom_rev)) + children = self._find_children(ent.copyfrom_path, ent.copyfrom_rev) + children.sort() + for child in children: + entrypath = self.getrelpath("/" + child, pmodule) + if not entrypath: + continue + entry = entrypath.decode(self.encoding) + copytopath = path + entry[len(copyfrompath):] + copytopath = self.getrelpath(copytopath) + copies[self.recode(copytopath)] = self.recode(entry, pmodule) return (util.unique(entries), copies) @@ -732,6 +708,13 @@ from_revnum, to_revnum = to_revnum, from_revnum self.child_cset = None + + def isdescendantof(parent, child): + if not child or not parent or not child.startswith(parent): + return False + subpath = child[len(parent):] + return len(subpath) > 1 and subpath[0] == '/' + def parselogentry(orig_paths, revnum, author, date, message): """Return the parsed commit object or None, and True if the revision is a branch root. @@ -755,10 +738,21 @@ if root_paths: path, ent = root_paths[-1] if ent.copyfrom_path: + # If dir was moved while one of its file was removed + # the log may look like: + # A /dir (from /dir:x) + # A /dir/a (from /dir/a:y) + # A /dir/b (from /dir/b:z) + # ... + # for all remaining children. + # Let's take the highest child element from rev as source. + copies = [(p,e) for p,e in orig_paths[:-1] + if isdescendantof(ent.copyfrom_path, e.copyfrom_path)] + fromrev = max([e.copyfrom_rev for p,e in copies] + [ent.copyfrom_rev]) branched = True newpath = ent.copyfrom_path + self.module[len(path):] # ent.copyfrom_rev may not be the actual last revision - previd = self.latest(newpath, ent.copyfrom_rev) + previd = self.latest(newpath, fromrev) if previd is not None: prevmodule, prevnum = self.revsplit(previd)[1:] if prevnum >= self.startrev: @@ -771,8 +765,7 @@ paths = [] # filter out unrelated paths for path, ent in orig_paths: - if not path.startswith(self.module): - self.ui.debug("boring@%s: %s\n" % (revnum, path)) + if self.getrelpath(path) is None: continue paths.append((path, ent)) @@ -885,6 +878,26 @@ rpath = '/'.join([self.base, path]).strip('/') return ['%s/%s' % (path, x) for x in svn.client.ls(rpath, optrev(revnum), True, self.ctx, pool).keys()] + def getrelpath(self, path, module=None): + if module is None: + module = self.module + # Given the repository url of this wc, say + # "http://server/plone/CMFPlone/branches/Plone-2_0-branch" + # extract the "entry" portion (a relative path) from what + # svn log --xml says, ie + # "/CMFPlone/branches/Plone-2_0-branch/tests/PloneTestCase.py" + # that is to say "tests/PloneTestCase.py" + if path.startswith(module): + relative = path.rstrip('/')[len(module):] + if relative.startswith('/'): + return relative[1:] + elif relative == '': + return relative + + # The path is outside our tracked tree... + self.ui.debug('%r is not under %r, ignoring\n' % (path, module)) + return None + pre_revprop_change = '''#!/bin/sh REPOS="$1"