A thought for dealing with linkrevs

Augie Fackler raf at durin42.com
Wed Sep 24 10:25:20 CDT 2014


On Wed, Sep 17, 2014 at 11:08:30PM -0500, Matt Mackall wrote:
> So I've been thinking about the linkrev problem a bit again. I've
> occasionally suggested we can deduce the possible positions of the
> missing linkrevs from the shape of the changelog graph as compared to
> the filelog graph. This turned out to be complicated so it never really
> went anywhere.
>
> I'm now tinkering with the idea of treating the linkrev as a simple
> hint: "this is one possibility, and any aliases are numerically after
> it". So if we're interested in following the history of file F from
> changeset C, we can say:
>
> - is F.linkrev an ancestor of C? great, done
> - otherwise, let's look in that vicinity for changesets touching F
>
> Since the usual case for caring about this is log -f or annotate, which
> incrementally walk back through file history, we can imagine a
> "corrector" object that knows where we're following from and can correct
> related linkrevs. We can even imagine this state being automatically
> shared as we traverse contexts, thus making fctx.linkrev() magically do
> the right thing.

Per-file log also cares about this. martinvonz and I recently had some
surprising 'hg log' output in the presence of evolution when looking
at the log for a single file.

(That is: -f doesn't really matter - vanilla log on a single file will
also give wrong answers AFAICT.)

>
> I tried hacking this up, and the below code will incrementally correct
> all the linkrevs in the 2639 ancestors of mercurial/commands.py in
> 0.13s, including fixing up a ton of obsolete linkrevs, which seems like
> a pretty good start. There are probably a bunch of ways it could be
> faster/smarter.
>
> (One can also imagine an alternate corrector that uses a persistent
> cache.)
>
> diff -r 48791c2bea1c mercurial/context.py
> --- a/mercurial/context.py	Tue Sep 16 14:49:56 2014 -0500
> +++ b/mercurial/context.py	Wed Sep 17 22:32:49 2014 -0500
> @@ -17,6 +17,75 @@
>
>  propertycache = util.propertycache
>
> +class linkcorrector(object):
> +    """report (an) appropriate linkrev for file revisions in ancestors of
> +    ctx
> +    """
> +    def __init__(self, ctx, filelog):
> +        self._start = ctx
> +        self._filelog = filelog
> +        self._linkcache = {} # filerev -> rev
> +        self._visit = []
> +        self._scanpos = ctx.rev() + 1
> +        self._ancestors = set([ctx.rev()]) # known ancestors
> +
> +    def correct(self, fctx):
> +        fr = fctx._filerev
> +        linkcache = self._linkcache
> +
> +        # fastest path: already cached
> +        if fr in linkcache:
> +            return linkcache[fr]
> +
> +        # get the actual linkrev
> +        flog = self._filelog
> +        lr = flog.linkrev(fr)
> +
> +        # if linkrev is a known ancestor, return immediately
> +        ancestors = self._ancestors
> +        if lr in ancestors:
> +            linkcache[fr] = lr
> +            return lr
> +
> +        # find new revs visit (without unpacking)
> +        # if we get lucky, we'll hit linkrev and be done
> +        repo = self._start._repo
> +        parentrevs = repo.changelog.parentrevs
> +        vadd = self._visit.append
> +        while True:
> +            r = self._scanpos = self._scanpos - 1
> +            if r in ancestors:
> +                for p in parentrevs(r):
> +                    ancestors.add(p)
> +                if r == lr:
> +                    # good, cache and return
> +                    linkcache[fr] = lr
> +                    return lr
> +                # examine later
> +                vadd(r)
> +            if r <= lr or r == 0:
> +                break
> +
> +        # check possible revs
> +        path = fctx._path
> +        fnode = fctx._filenode
> +        visit = self._visit
> +        while visit:
> +            # these are added breadfirst, take ones from the end,
> +            # hopefully closest to linkrev
> +            r = visit.pop()
> +            ctx = repo[r]
> +            if path in ctx.files():
> +                # maybe fast-pathed
> +                newfnode = ctx.filenode(path)
> +                if fnode == newfnode:
> +                    # hit!
> +                    linkcache[fr] = r
> +                    return r
> +                else:
> +                    # miss, cache for later
> +                    linkcache[flog.rev(newfnode)] = r
> +
>  class basectx(object):
>      """A basectx object represents the common logic for its children:
>      changectx: read-only context that is already present in the repo,
>
> --
> Mathematics is the supreme nostalgia of our time.
>
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


More information about the Mercurial-devel mailing list