[PATCH 1 of 3 V2] journal: add dirstate tracking

Martijn Pieters mj at zopatista.com
Tue Jul 5 04:48:37 EDT 2016


On 4 July 2016 at 22:28, FUJIWARA Katsunori <foozy at lares.dti.ne.jp> wrote:
> At Mon, 04 Jul 2016 15:35:56 +0100,
> Martijn Pieters wrote:
>>  # Journal recording, register hooks and storage object
>>  def extsetup(ui):
>>      extensions.wrapfunction(dispatch, 'runcommand', runcommand)
>>      extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks)
>> +    extensions.wrapfunction(
>> +        dirstate.dirstate, '_writedirstate', recorddirstateparents)
>> +    extensions.wrapfunction(
>> +        localrepo.localrepository.dirstate, 'func', wrapdirstate)
>>
>
> Wrapping "dirstate._writedirstate()" might track intermediate changing
> parents while automated commit like backout, graft, import, rebase and
> so on.
>
> Recording such intermediate status depends on:
>
>   - whether changing parents while automated commit occurs inside
>     transaction or not
>
>     - if so (e.g. import):
>
>       - whether external hook is invoked or not inside transaction scope
>
>         - if so, intermediate commit writes changes (= changing
>           parents) out at external hook invocation
>
>         - otherwise, writing changes out is delayed until transaction
>           closing
>
>     - otherwise (e.g. graft):
>
>       actions below immediately write intermediate changes out.
>
>       - intermediate commit
>       - external hook invocation after internal updating
>
> (see also https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
> for detail about dirstate and transaction)
>
> What would you think about appearance of such intermediate change in
> journal records ?
>
> IMHO, from the point of view of end user, comparison between "parents"
> before and after each command invocation seems enough resolution for
> recording changes.

I'd rather record a dirstate change too many than too few here; they
may want to recover to an earlier state *within* a set of steps driven
by automation.

>> +def recorddirstateparents(orig, dirstate, dirstatefp):
>> +    """Records all dirstate parent changes in the journal."""
>> +    if util.safehasattr(dirstate, 'journalstorage'):
>> +        old = [node.nullid, node.nullid]
>> +        nodesize = len(node.nullid)
>> +        try:
>> +            # The only source for the old state is in the dirstate file still
>> +            # on disk; the in-memory dirstate object only contains the new
>> +            # state. dirstate._filename is always the actual dirstate file;
>> +            # backups for a transactions, shelving or the repo journal use a
>> +            # suffix or prefix, _filename never changes.
>> +            with dirstate._opener(dirstate._filename) as fp:
>> +                state = fp.read(2 * nodesize)
>> +            if len(state) == 2 * nodesize:
>> +                old = [state[:nodesize], state[nodesize:]]
>> +        except IOError:
>> +            pass
>> +
>> +        new = dirstate.parents()
>> +        if old != new:
>
> Comparison between "parents" in memory and in ".hg/dirstate" might
> imply unexpected record for automated commit inside transaction,
> because changes inside transaction are written into ".hg/dirstate.pending".
>
>   - this comparison always concludes that "parents" is changed, even
>     if it isn't changed after previous recording
>
>   - "parents" stored in ".hg/dirstate" is always used as the source of
>     changing parents
>
>     e.g. if automated commit implies A -> I1 -> I2 -> I3 -> B commits,
>     this records (A -> I1), (A -> I2), (A -> I3), (A -> B).
>
>     (if external hook is invoked)

Right; so would reading `.hg/dirstate.pending` if present be a better
idea here then? We can use `with dirstate._opendirstatefile() as fp:`
in that case, which will select between `.hg/dirstate` and
`.hg/dirstate.pending` as needed.

-- 
Martijn Pieters


More information about the Mercurial-devel mailing list