Mercurial > hgsubversion
diff fetch_command.py @ 73:9c1b53abefcb
fetch_command: support svn copy detection in stupid mode
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Wed, 05 Nov 2008 13:37:08 +0100 |
parents | 2e30b59a9c19 |
children | 450d5d9d3b80 |
line wrap: on
line diff
--- a/fetch_command.py +++ b/fetch_command.py @@ -217,6 +217,94 @@ def make_diff_path(b): return 'trunk' return 'branches/' + b +def makecopyfinder(r, branchpath, rootdir): + """Return a function detecting copies. + + Returned copyfinder(path) returns None if no copy information can + be found or ((source, sourcerev), sourcepath) where "sourcepath" is the + copy source path, "sourcerev" the source svn revision and "source" is the + copy record path causing the copy to occur. If a single file was copied + "sourcepath" and "source" are the same, while file copies dectected from + directory copies return the copied source directory in "source". + """ + # filter copy information for current branch + branchpath = branchpath + '/' + fullbranchpath = rootdir + branchpath + copies = [] + for path, e in r.paths.iteritems(): + if not e.copyfrom_path: + continue + if not path.startswith(branchpath): + continue + if not e.copyfrom_path.startswith(fullbranchpath): + # ignore cross branch copies + continue + dest = path[len(branchpath):] + source = e.copyfrom_path[len(fullbranchpath):] + copies.append((dest, (source, e.copyfrom_rev))) + + copies.sort() + copies.reverse() + exactcopies = dict(copies) + + def finder(path): + if path in exactcopies: + return exactcopies[path], exactcopies[path][0] + # look for parent directory copy, longest first + for dest, (source, sourcerev) in copies: + dest = dest + '/' + if not path.startswith(dest): + continue + sourcepath = source + '/' + path[len(dest):] + return (source, sourcerev), sourcepath + return None + + return finder + +def getcopies(svn, hg_editor, branch, branchpath, r, files, parentid): + """Return a mapping {dest: source} for every file copied into r. + """ + if parentid == revlog.nullid: + return {} + + # Extract svn copy information, group them by copy source. + # The idea is to duplicate the replay behaviour where copies are + # evaluated per copy event (one event for all files in a directory copy, + # one event for single file copy). We assume that copy events match + # copy sources in revision info. + svncopies = {} + finder = makecopyfinder(r, branchpath, svn.subdir) + for f in files: + copy = finder(f) + if copy: + svncopies.setdefault(copy[0], []).append((f, copy[1])) + if not svncopies: + return {} + + # cache changeset contexts and map them to source svn revisions + parentctx = hg_editor.repo.changectx(parentid) + ctxs = {} + def getctx(svnrev): + if svnrev in ctxs: + return ctxs[svnrev] + changeid = hg_editor.get_parent_revision(svnrev + 1, branch) + ctx = None + if changeid != revlog.nullid: + ctx = hg_editor.repo.changectx(changeid) + ctxs[svnrev] = ctx + return ctx + + # check svn copies really make sense in mercurial + hgcopies = {} + for (sourcepath, rev), copies in svncopies.iteritems(): + sourcectx = getctx(rev) + if sourcectx is None: + continue + sources = [s[1] for s in copies] + if not hg_editor.aresamefiles(sourcectx, parentctx, sources): + continue + hgcopies.update(copies) + return hgcopies def stupid_svn_server_pull_rev(ui, svn, hg_editor, r): used_diff = True @@ -450,6 +538,10 @@ def stupid_svn_server_pull_rev(ui, svn, # TODO this might not be a required step. if p: files_touched.add(p) + + copies = getcopies(svn, hg_editor, b, branches[b], r, files_touched, + parent_ha) + date = r.date.replace('T', ' ').replace('Z', '').split('.')[0] date += ' -0000' def filectxfn(repo, memctx, path): @@ -462,8 +554,9 @@ def stupid_svn_server_pull_rev(ui, svn, exe = exec_files.get(path, None) if exe is None and path in hg_editor.repo[parent_ha]: exe = 'x' in hg_editor.repo[parent_ha].filectx(path).flags() + copied = copies.get(path) return context.memfilectx(path=path, data=fp.read(), islink=False, - isexec=exe, copied=False) + isexec=exe, copied=copied) extra = {} if b: extra['branch'] = b