# HG changeset patch# Parent aae1dd12fce6067cf6b7db8140c705789bcb5ea4# User Nicolas Dumazet <nicdumz.commits@gmail.com># Date 1278148845 -32400log: do not --follow file that is deleted and recreated later (issue732)== What ==issue732 is only one example of a buggy behaviour, but there are in fact manyintricated cases. For example:( "o" contains an alive version of the tracked file, "x" does not)tip - o - o - x - o - o - x ... \ o - o - o - o - x ... \ / o - oThis repository contains at least two instances of the tracked file, butwhen calling "hg log -f file" only the latest one (the one alive in tip)matters to us.== How ==We must extract from the filelog the history of the file instance we'reinterested in and discard changes related to other instances of that file.Naively, we see that we're only interested in ancestors(node), and that allother nodes in the filelog should not be considered.Sadly, the filelog alone does not allow us to track the full history of a file,and we need to read the changelog to fill history gaps when the filelog linkrevis not the "correct" changelog revision.== QUESTIONS ==* Please triple-check what I'm doing when reading the changelog. I am pretty sure of what I'm checking in the patch, _that_ part just is kind of hard to grasp for me.* I do not really like the generation of fllinkrevs. Is it really necessary? (is there is a fast way to find if a linkrev is linked to from a filelog?)* when reading the changelog, I first check all ancestors, to see what revisions we need, and then sort them before reading them, to make sure that any reads we do is in the changelog order. Is it the proper way to do this?diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py--- a/mercurial/cmdutil.py+++ b/mercurial/cmdutil.py@@ -1059,9 +1059,18 @@ # We only have to read through the filelog to find wanted revisions minrev, maxrev = min(revs), max(revs)- # Only files, no patterns. Check the history of each file. def filerevgen(filelog, last):+ """+ Only files, no patterns. Check the history of each file.++ Examines filelog entries within minrev, maxrev linkrev range+ Returns a 2-tuple:+ - an iterator yielding (linkrev, parentlinkrevs, copied) tuples in+ backwards order+ - set(linkrevs in filelog)+ """ cl_count = len(repo)+ fllinkrevs = set() revs = [] for j in xrange(0, last + 1): linkrev = filelog.linkrev(j)@@ -1071,12 +1080,21 @@ # happen while doing "hg log" during a pull or commit if linkrev > maxrev or linkrev >= cl_count: break+ if not follow:+ revs.append((linkrev, None, None))+ continue++ fllinkrevs.add(linkrev)++ parentlinkrevs = []+ for p in filelog.parentrevs(j):+ if p != nullrev:+ parentlinkrevs.append(filelog.linkrev(p)) n = filelog.node(j)- revs.append((filelog.linkrev(j),- follow and filelog.renamed(n)))+ revs.append((linkrev, parentlinkrevs, filelog.renamed(n)))- for rev in reversed(revs):- yield rev+ return reversed(revs), fllinkrevs+ def iterfiles(): for filename in match.files(): yield filename, None@@ -1101,12 +1119,60 @@ else: last = filelog.rev(node)- for rev, copied in filerevgen(filelog, last):- fncache.setdefault(rev, [])- fncache[rev].append(file_)+ def add(rev):+ fncache.setdefault(rev, []).append(file_) wanted.add(rev)+ def clparents(linkrev):+ ret = []+ for p in repo.changelog.parentrevs(linkrev):+ if p != nullrev:+ ret.append(p)+ return ret++ # keep track of all ancestors of the file+ ancestors = set([filelog.linkrev(last)])++ reviterator, fllinkrevs = filerevgen(filelog, last)+ # iterate from latest to oldest revision+ for linkrev, flparentlinkrevs, copied in reviterator:+ if not follow:+ add(linkrev)+ continue+ if linkrev not in ancestors:+ continue+ add(linkrev)+ if copied: copies.append(copied)+ if not flparentlinkrevs:+ # no parents in filelog: the file has just been created+ continue++ clparentrevs = clparents(linkrev)++ if clparentrevs != flparentlinkrevs:+ # Jump in the filelog detected+ # Need to read the changelog to fill the gaps+ toread = set()+ # first read the index+ while clparentrevs:+ rev = clparentrevs.pop()+ if minrev <= rev <= maxrev:+ if rev in fllinkrevs:+ # exists in filelog+ ancestors.add(rev)+ else:+ toread.add(rev)+ clparentrevs.extend(clparents(rev))+ # then read changelog forward+ for rev in sorted(toread):+ n = repo.changelog.node(rev)+ files = repo.changelog.read(n)[3]+ if file_ in files:+ add(rev)+ else:+ # usual case+ ancestors.update(flparentlinkrevs) if slowpath: # We have to read the changelog to match filenames against # changed filesdiff --git a/tests/test-log b/tests/test-log--- a/tests/test-log+++ b/tests/test-log@@ -200,4 +200,53 @@ hg log -p -R .. ../a+cd ..+hg init follow2+cd follow2++# Build the following history:+# tip - o - x - o - x - x+# \ /+# o - o - o - x+# \ /+# o+#+# Where "o" is a revision containing "foo" and+# "x" is a revision without "foo"+touch init+hg ci -A -m "init, unrelated"+echo 'foo' > init+hg ci -m "change, unrelated"+touch foo+hg ci -A -m "add unrelated old foo"+hg rm foo+hg ci -m "delete foo, unrelated"+echo 'related' > foo+hg ci -A -m "add foo, related"++hg up 0+touch branch+hg ci -A -m "first branch, unrelated"+touch foo+hg ci -A -m "create foo, related"+echo 'change' > foo+hg ci -m "change foo, related"++hg up 6+echo 'change foo in branch' > foo+hg ci -m "change foo in branch, related"+hg merge 7+echo 'merge 1' > foo+hg resolve -m foo+hg ci -m "First merge, related"++hg merge 4+echo 'merge 2' > foo+hg resolve -m foo+hg ci -m "Last merge, related"++hg --config "extensions.graphlog=" glog++hg --traceback log -f foo+ exit 0diff --git a/tests/test-log.out b/tests/test-log.out--- a/tests/test-log.out+++ b/tests/test-log.out@@ -593,6 +593,123 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: commit on default+adding init+adding foo+adding foo+1 files updated, 0 files merged, 1 files removed, 0 files unresolved+adding branch+created new head+adding foo+1 files updated, 0 files merged, 0 files removed, 0 files unresolved+created new head+merging foo+warning: conflicts during merge.+merging foo failed!+0 files updated, 0 files merged, 0 files removed, 1 files unresolved+use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon+merging foo+warning: conflicts during merge.+merging foo failed!+1 files updated, 0 files merged, 0 files removed, 1 files unresolved+use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon+@ changeset: 10:7e8598c9f012+|\ tag: tip+| | parent: 9:7b35701b003e+| | parent: 4:e628d99bf1b8+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: Last merge, related+| |+| o changeset: 9:7b35701b003e+| |\ parent: 8:e5416ad8a855+| | | parent: 7:87fe3144dcfa+| | | user: test+| | | date: Thu Jan 01 00:00:00 1970 +0000+| | | summary: First merge, related+| | |+| | o changeset: 8:e5416ad8a855+| | | parent: 6:dc6c325fe5ee+| | | user: test+| | | date: Thu Jan 01 00:00:00 1970 +0000+| | | summary: change foo in branch, related+| | |+| o | changeset: 7:87fe3144dcfa+| |/ user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: change foo, related+| |+| o changeset: 6:dc6c325fe5ee+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: create foo, related+| |+| o changeset: 5:73db34516eb9+| | parent: 0:e87515fd044a+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: first branch, unrelated+| |+o | changeset: 4:e628d99bf1b8+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: add foo, related+| |+o | changeset: 3:8152bb95f03d+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: delete foo, unrelated+| |+o | changeset: 2:8f8ca6577b17+| | user: test+| | date: Thu Jan 01 00:00:00 1970 +0000+| | summary: add unrelated old foo+| |+o | changeset: 1:e5faa7440653+|/ user: test+| date: Thu Jan 01 00:00:00 1970 +0000+| summary: change, unrelated+|+o changeset: 0:e87515fd044a+ user: test+ date: Thu Jan 01 00:00:00 1970 +0000+ summary: init, unrelated++changeset: 10:7e8598c9f012+tag: tip+parent: 9:7b35701b003e+parent: 4:e628d99bf1b8+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: Last merge, related++changeset: 9:7b35701b003e+parent: 8:e5416ad8a855+parent: 7:87fe3144dcfa+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: First merge, related++changeset: 8:e5416ad8a855+parent: 6:dc6c325fe5ee+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: change foo in branch, related++changeset: 7:87fe3144dcfa+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: change foo, related++changeset: 6:dc6c325fe5ee+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: create foo, related++changeset: 4:e628d99bf1b8+user: test+date: Thu Jan 01 00:00:00 1970 +0000+summary: add foo, related+ diff -r 000000000000 -r 24427303d56f a --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/a Thu Jan 01 00:00:00 1970 +0000