diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -343,10 +343,27 @@ def diff(repo, node1=None, node2=None, f if not node1: node1 = repo.dirstate.parents()[0] + + clcache = {} + def getchangelog(n): + if n not in clcache: + clcache[n] = repo.changelog.read(n) + return clcache[n] + mcache = {} + def getmanifest(n): + if n not in mcache: + mcache[n] = repo.manifest.read(n) + return mcache[n] + fcache = {} + def getfile(f): + if f not in fcache: + fcache[f] = repo.file(f) + return fcache[f] + # reading the data for node1 early allows it to play nicely # with repo.status and the revlog cache. - change = repo.changelog.read(node1) - mmap = repo.manifest.read(change[0]) + change = getchangelog(node1) + mmap = getmanifest(change[0]) date1 = util.datestr(change[2]) if not changes: @@ -367,17 +384,32 @@ def diff(repo, node1=None, node2=None, f if not modified and not added and not removed: return + def renamedbetween(f, n1, n2): + r1, r2 = map(repo.changelog.rev, (n1, n2)) + src = None + while r2 > r1: + cl = getchangelog(n2)[0] + m = getmanifest(cl) + try: + src = getfile(f).renamed(m[f]) + except KeyError: + return None + if src: + f = src[0] + n2 = repo.changelog.parents(n2)[0] + r2 = repo.changelog.rev(n2) + return src + if node2: - change = repo.changelog.read(node2) - mmap2 = repo.manifest.read(change[0]) + change = getchangelog(node2) + mmap2 = getmanifest(change[0]) _date2 = util.datestr(change[2]) def date2(f): return _date2 def read(f): - return repo.file(f).read(mmap2[f]) + return getfile(f).read(mmap2[f]) def renamed(f): - src = repo.file(f).renamed(mmap2[f]) - return src and src[0] or None + return renamedbetween(f, node1, node2) else: tz = util.makedate()[1] _date2 = util.datestr() @@ -390,7 +422,18 @@ def diff(repo, node1=None, node2=None, f def read(f): return repo.wread(f) def renamed(f): - return repo.dirstate.copies.get(f) + src = repo.dirstate.copies.get(f) + parent = repo.dirstate.parents()[0] + if src: + f = src[0] + of = renamedbetween(f, node1, parent) + if of: + return of + elif src: + cl = getchangelog(parent)[0] + return (src, getmanifest(cl)[src]) + else: + return None if repo.ui.quiet: r = None @@ -404,7 +447,7 @@ def diff(repo, node1=None, node2=None, f src = renamed(f) if src: copied[f] = src - srcs = [x[1] for x in copied.items()] + srcs = [x[1][0] for x in copied.items()] all = modified + added + removed all.sort() @@ -413,7 +456,7 @@ def diff(repo, node1=None, node2=None, f tn = None dodiff = True if f in mmap: - to = repo.file(f).read(mmap[f]) + to = getfile(f).read(mmap[f]) if f not in removed: tn = read(f) if opts.git: @@ -432,13 +475,13 @@ def diff(repo, node1=None, node2=None, f else: mode = gitmode(util.is_exec(repo.wjoin(f), None)) if f in copied: - a = copied[f] + a, arev = copied[f] omode = gitmode(mmap.execf(a)) addmodehdr(header, omode, mode) op = a in removed and 'rename' or 'copy' header.append('%s from %s\n' % (op, a)) header.append('%s to %s\n' % (op, f)) - to = repo.file(a).read(mmap[a]) + to = getfile(a).read(arev) else: header.append('new file mode %s\n' % mode) elif f in removed: