# HG changeset patch # User Brendan Cully # Date 1159373421 25200 # Node ID 5c93dd0ae41328c99833d0d5cee0735714be641f # Parent 706277866251eb3c9f68bbce45f9f3837496b5db Refactor annotate copy support. diff -r 706277866251 -r 5c93dd0ae413 mercurial/commands.py --- a/mercurial/commands.py Wed Sep 27 09:35:53 2006 +0200 +++ b/mercurial/commands.py Wed Sep 27 09:10:21 2006 -0700 @@ -612,8 +612,9 @@ opmap = [['user', lambda x: ui.shortuser(x.user())], ['number', lambda x: str(x.rev())], ['changeset', lambda x: short(x.node())], - ['date', getdate]] - if not opts['user'] and not opts['changeset'] and not opts['date']: + ['date', getdate], ['follow', lambda x: x.path()]] + if (not opts['user'] and not opts['changeset'] and not opts['date'] + and not opts['follow']): opts['number'] = 1 ctx = repo.changectx(opts['rev']) @@ -625,7 +626,7 @@ ui.write(_("%s: binary file\n") % ((pats and rel) or abs)) continue - lines = fctx.annotate() + lines = fctx.annotate(follow=opts.get('follow')) pieces = [] for o, f in opmap: @@ -2671,6 +2672,7 @@ "^annotate": (annotate, [('r', 'rev', '', _('annotate the specified revision')), + ('f', 'follow', None, _('follow file copies and renames')), ('a', 'text', None, _('treat all files as text')), ('u', 'user', None, _('list the author')), ('d', 'date', None, _('list the date')), diff -r 706277866251 -r 5c93dd0ae413 mercurial/context.py --- a/mercurial/context.py Wed Sep 27 09:35:53 2006 +0200 +++ b/mercurial/context.py Wed Sep 27 09:10:21 2006 -0700 @@ -5,6 +5,10 @@ # This software may be used and distributed according to the terms # of the GNU General Public License, incorporated herein by reference. +from demandload import * +from node import * +demandload(globals(), 'bdiff') + from node import * from demandload import demandload demandload(globals(), "ancestor util") @@ -165,13 +169,74 @@ return [ filectx(self._repo, self._path, fileid=x, filelog=self._filelog) for x in c ] - def annotate(self): - getctx = util.cachefunc(lambda x: filectx(self._repo, self._path, - changeid=x, - filelog=self._filelog)) - hist = self._filelog.annotate(self._filenode) + def annotate(self, follow=False): + '''returns a list of tuples of (ctx, line) for each line + in the file, where ctx is the filectx of the node where + that line was last changed''' + + def decorate(text, rev): + return ([rev] * len(text.splitlines()), text) + + def pair(parent, child): + for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]): + child[0][b1:b2] = parent[0][a1:a2] + return child + + getlog = util.cachefunc(lambda x: self._repo.file(x)) + def getctx(path, fileid): + log = path == self._path and self._filelog or getlog(path) + return filectx(self._repo, path, fileid=fileid, filelog=log) + getctx = util.cachefunc(getctx) + + def parents(f): + # we want to reuse filectx objects as much as possible + p = f._path + pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ] + + if follow: + r = f.renamed() + if r: + pl[0] = (r[0], getlog(r[0]).rev(r[1])) - return [(getctx(rev), line) for rev, line in hist] + return [ getctx(p, n) for p, n in pl if n != -1 ] + + # find all ancestors + needed = {self: 1} + visit = [self] + files = [self._path] + while visit: + f = visit.pop(0) + for p in parents(f): + if p not in needed: + needed[p] = 1 + visit.append(p) + if p._path not in files: + files.append(p._path) + else: + # count how many times we'll use this + needed[p] += 1 + + # sort by revision (per file) which is a topological order + visit = [] + files.reverse() + for f in files: + fn = [(n._filerev, n) for n in needed.keys() if n._path == f] + fn.sort() + visit.extend(fn) + hist = {} + + for r, f in visit: + curr = decorate(f.data(), f) + for p in parents(f): + if p != nullid: + curr = pair(hist[p], curr) + # trim the history of unneeded revs + needed[p] -= 1 + if not needed[p]: + del hist[p] + hist[f] = curr + + return zip(hist[f][0], hist[f][1].splitlines(1)) def ancestor(self, fc2): """ diff -r 706277866251 -r 5c93dd0ae413 tests/test-annotate --- a/tests/test-annotate Wed Sep 27 09:35:53 2006 +0200 +++ b/tests/test-annotate Wed Sep 27 09:10:21 2006 -0700 @@ -1,5 +1,7 @@ #!/bin/sh +export HGMERGE=true + echo % init hg init @@ -21,3 +23,53 @@ echo % annotate -cdnu hg annotate -cdnu a + +cat <>a +a +a +EOF +hg ci -ma1 -d '1 0' +hg cp a b +hg ci -mb -d '1 0' +cat <> b +b +b +b +EOF +hg ci -mb2 -d '2 0' + +echo % annotate b +hg annotate b +echo % annotate -nf b +hg annotate -nf b + +hg up -C 2 +cat <> b +b +c +b +EOF +hg ci -mb2.1 -d '2 0' +hg merge +hg ci -mmergeb -d '3 0' +echo % annotate after merge +hg annotate -nf b + +hg up -C 1 +hg cp a b +cat < b +a +z +a +EOF +hg ci -mc -d '3 0' +hg merge +cat <> b +b +c +b +EOF +echo d >> b +hg ci -mmerge2 -d '4 0' +echo % annotate after rename merge +hg annotate -nf b diff -r 706277866251 -r 5c93dd0ae413 tests/test-annotate.out --- a/tests/test-annotate.out Wed Sep 27 09:35:53 2006 +0200 +++ b/tests/test-annotate.out Wed Sep 27 09:10:21 2006 -0700 @@ -11,3 +11,40 @@ nobody: a % annotate -cdnu nobody 0 8435f90966e4 Thu Jan 01 00:00:01 1970 +0000: a +% annotate b +2: a +2: a +2: a +3: b +3: b +3: b +% annotate -nf b +0 a: a +1 a: a +1 a: a +3 b: b +3 b: b +3 b: b +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +merging b +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% annotate after merge +0 a: a +1 a: a +1 a: a +3 b: b +4 b: c +3 b: b +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +merging b +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +% annotate after rename merge +0 a: a +6 b: z +1 a: a +3 b: b +4 b: c +3 b: b +7 b: d