hg log 'filename' not showing merges?

Matt Mackall mpm at selenic.com
Mon May 21 17:11:08 CDT 2007


On Mon, May 21, 2007 at 11:35:38PM +0200, Lars Marowsky-Bree wrote:
> On 2007-05-21T11:18:36, Matt Mackall <mpm at selenic.com> wrote:
> 
> > Actually, that won't work reliably either and it's important to
> > understand why. If, when you merge, you take the file version from the
> > first parent, it will appear unchanged in the diff. This is because
> > diff (and the larger notion of "changed"!) is only meaningfully
> > defined for two versions, while merges involve three versions.
> 
> Right. Thanks for this very extensive explanation. I thought I had
> gotten how changesets and merges work, but apparently I was missing one
> bit - in particular that hg log always follows the first parent.
> 
> > for rev in `hg log -f --template "{rev}\n"`; do
> >   echo -n "$rev:"
> >   hg manifest --debug | grep myfile
> > done
> 
> That proved to be a good starting point, but I also needed to make sure
> that I only follow one path down memory lane.
> 
> I hacked up a script to follow and serialize a file's history. It takes
> a filename, a starting revision and a number of maximum revisions to
> follow as arguments and performs absolutely no error checking on those
> values. ;-)
> 
> It prints the changelog + diffs for each different revision on the
> stream. (It'll print when it had several choices to follow.) That's the
> functionality I'm interested in, and indeed, it picks up the changes I
> was looking for.
> 
> Does the functionality make sense to anyone else? I assume it could be
> done much faster within python and mercurial proper, but my python is
> rather weak.

Here's a quick Python hack to show how easy this sort of thing is:

#!/usr/bin/python               
import sys
from mercurial import hg, ui

fname = sys.argv[1]
repo = hg.repository(ui.ui(), ".")
v = repo.workingctx().parents()
seen = {}
fseen = {}

while v:
    c = v.pop(0)
    if c.rev() not in seen and c.rev() != -1:
        seen[c.rev()] = 1
        v += c.parents()
        try:
            f = c.filectx(fname)
        except:
            continue
        if f.rev() not in fseen:
            fseen[f.rev()] = 1
            print c.rev(), f.rev(), f.description().splitlines()[0]

That gives output that looks like this:

$ python traverse mercurial/dirstate.py
4450 4446 Merge with crew-stable.
4441 4440 Fix issue 562: .hgignore requires newline at end.
4439 4330 use atomictemp files to write the dirstate
4445 4375 When reloading the dirstate, recompute ignore information if
needed.
4329 4230 Fix handling of paths when run outside the repo.
4374 4374 dirstate: speed up write by 50%.
4373 4373 dirstate: make parents() faster.
4372 4335 Merge with stable
4325 4256 Merge additional fixes for my matcher changes
4308 4254 Pass normalized directory names to the ignore function
4253 4193 dirstate.statwalk: explicitly test for ignored directories
4229 4229 pass repo.root to util.pathto() in preparation for the next
patch
4228 4179 Move branch read/write to dirstate where it belongs
4192 4172 statwalk: don't put self.root in the files list
4171 4075 Fix dirstate fail at drive root on Windows
4251 4232 Merge with crew-stable
4224 4209 Merge with -stable, fix small test failure
4208 4200 Merge a bunch of matcher and locate fixes.
4182 4182 Merge with crew-stable
...

This walks backwards in the history (breadth-first search) and notes
every time we see a new file revision in the manifest, and what
revision it was introduced in.

That's the obvious way, and takes about 22 seconds to run. Replacing:

        v += c.parents()

with:

        p = c.parents()
        v += p
        if p[1].rev() == -1 and fname not in c.files():
            continue

so that it skips checking revisions which weren't merges and didn't
explicitly change the file makes it take 6 seconds.

-- 
Mathematics is the supreme nostalgia of our time.


More information about the Mercurial mailing list