diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -881,7 +881,8 @@ def debugrename(ui, repo, file1, *pats, ctx = repo.changectx(opts.get('rev', 'tip')) for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts, ctx.node()): - m = ctx.filectx(abs).renamed() + fctx = ctx.filectx(abs) + m = fctx.filelog().renamed(fctx.filenode()) if m: ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1]))) else: @@ -1701,8 +1702,7 @@ def log(ui, repo, *pats, **opts): endrev = repo.changelog.count() rcache = {} ncache = {} - dcache = [] - def getrenamed(fn, rev, man): + def getrenamed(fn, rev): '''looks up all renames for a file (up to endrev) the first time the file is given. It indexes on the changerev and only parses the manifest if linkrev != changerev. @@ -1722,13 +1722,14 @@ def log(ui, repo, *pats, **opts): break if rev in rcache[fn]: return rcache[fn][rev] - mr = repo.manifest.rev(man) - if repo.manifest.parentrevs(mr) != (mr - 1, nullrev): - return ncache[fn].get(repo.manifest.find(man, fn)[0]) - if not dcache or dcache[0] != man: - dcache[:] = [man, repo.manifest.readdelta(man)] - if fn in dcache[1]: - return ncache[fn].get(dcache[1][fn]) + + # If linkrev != rev (i.e. rev not found in rcache) fallback to + # filectx logic. + + try: + return repo.changectx(rev).filectx(fn).renamed() + except revlog.LookupError: + pass return None df = False @@ -1765,9 +1766,8 @@ def log(ui, repo, *pats, **opts): copies = [] if opts.get('copies') and rev: - mf = get(rev)[0] for fn in get(rev)[3]: - rename = getrenamed(fn, rev, mf) + rename = getrenamed(fn, rev) if rename: copies.append((fn, rename[0])) displayer.show(rev, changenode, copies=copies) diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -235,6 +235,7 @@ class filectx(object): return self._changectx.rev() return self._filelog.linkrev(self._filenode) + def linkrev(self): return self._filelog.linkrev(self._filenode) def node(self): return self._changectx.node() def user(self): return self._changectx.user() def date(self): return self._changectx.date() @@ -245,12 +246,36 @@ class filectx(object): def changectx(self): return self._changectx def data(self): return self._filelog.read(self._filenode) - def renamed(self): return self._filelog.renamed(self._filenode) def path(self): return self._path def size(self): return self._filelog.size(self._filerev) def cmp(self, text): return self._filelog.cmp(self._filenode, text) + def renamed(self): + """check if file was actually renamed in this changeset revision + + If rename logged in file revision, we report copy for changeset only + if file revisions linkrev points back to the changeset in question + or both changeset parents contain different file revisions. + """ + + renamed = self._filelog.renamed(self._filenode) + if not renamed: + return renamed + + if self.rev() == self.linkrev(): + return renamed + + name = self.path() + fnode = self._filenode + for p in self._changectx.parents(): + try: + if fnode == p.filenode(name): + return None + except revlog.LookupError: + pass + return renamed + def parents(self): p = self._path fl = self._filelog @@ -322,7 +347,7 @@ class filectx(object): return [getctx(p, n) for p, n in pl if n != nullrev] # use linkrev to find the first changeset where self appeared - if self.rev() != self._filelog.linkrev(self._filenode): + if self.rev() != self.linkrev(): base = self.filectx(self.filerev()) else: base = self diff --git a/tests/test-convert-hg-source b/tests/test-convert-hg-source --- a/tests/test-convert-hg-source +++ b/tests/test-convert-hg-source @@ -29,6 +29,9 @@ hg up -C 1 hg merge 2 hg ci -m 'merge remote copy' -d '4 0' +chmod +x baz +hg ci -m 'mark baz executable' -d '5 0' + cd .. hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded' cd new diff --git a/tests/test-convert-hg-source.out b/tests/test-convert-hg-source.out --- a/tests/test-convert-hg-source.out +++ b/tests/test-convert-hg-source.out @@ -9,11 +9,12 @@ initializing destination new repository scanning source... sorting... converting... -4 add foo bar -3 change foo -2 make bar and baz copies of foo -1 merge local copy -0 merge remote copy +5 add foo bar +4 change foo +3 make bar and baz copies of foo +2 merge local copy +1 merge remote copy +0 mark baz executable comparing with ../orig searching for changes no changes found diff --git a/tests/test-convert-svn-sink.out b/tests/test-convert-svn-sink.out --- a/tests/test-convert-svn-sink.out +++ b/tests/test-convert-svn-sink.out @@ -167,31 +167,29 @@ c d1 % executable 5:f205b3636d77 -svn: Path 'b' does not exist assuming destination a-hg initializing svn wc 'a-hg-wc' scanning source... sorting... converting... 0 make a file executable -abort: svn exited with status 1 -At revision 5. - 5 5 test . - M 5 4 test c - 5 1 test d1 - 5 1 test d1/d2 - 5 1 test d1/d2/b +At revision 6. + 6 6 test . + 6 6 test c + 6 1 test d1 + 6 1 test d1/d2 + 6 1 test d1/d2/b + revision="6"> test /b + action="M">/c -remove a file +make a file executable executable diff --git a/tests/test-log b/tests/test-log --- a/tests/test-log +++ b/tests/test-log @@ -41,6 +41,11 @@ echo foo > foo hg ci -Ame2 -d '6 0' hg log -vC --template '{rev} {file_copies%filecopy}\n' -r 5 +echo % log copies, execute bit set +chmod +x e +hg ci -me3 -d '7 0' +hg log -vC --template '{rev} {file_copies%filecopy}\n' -r 6 + echo '% log -p d' hg log -pv d diff --git a/tests/test-log.out b/tests/test-log.out --- a/tests/test-log.out +++ b/tests/test-log.out @@ -86,6 +86,8 @@ 0 1 files updated, 0 files merged, 1 files removed, 0 files unresolved adding foo 5 e (dir/b) +% log copies, execute bit set +6 % log -p d changeset: 3:16b60bf3f99a user: test