[PATCH 1 of 3 V2] journal: add dirstate tracking
FUJIWARA Katsunori
foozy at lares.dti.ne.jp
Mon Jul 4 17:28:01 EDT 2016
At Mon, 04 Jul 2016 15:35:56 +0100,
Martijn Pieters wrote:
>
> # HG changeset patch
> # User Martijn Pieters <mjpieters at fb.com>
> # Date 1467642813 -3600
> # Mon Jul 04 15:33:33 2016 +0100
> # Node ID 05a4e9561e5398502acb7d2329a1ad9adff774cf
> # Parent 53b7fc7cc2bbbcd1c344ce50519a868cac7ed9ac
> journal: add dirstate tracking
>
> Note that now the default action for `hg journal` is to list the working copy
> history, not all bookmarks. In its place is the `--all` switch which lists all
> name changes recorded, including the name for which the change was recorded on
> each line.
>
> diff --git a/hgext/journal.py b/hgext/journal.py
> --- a/hgext/journal.py
> +++ b/hgext/journal.py
> @@ -15,6 +15,7 @@
>
> import collections
> import os
> +import weakref
>
> from mercurial.i18n import _
>
> @@ -22,9 +23,12 @@
> bookmarks,
> cmdutil,
> commands,
> + dirstate,
> dispatch,
> error,
> extensions,
> + localrepo,
> + lock,
> node,
> util,
> )
> @@ -43,11 +47,16 @@
>
> # namespaces
> bookmarktype = 'bookmark'
> +wdirparenttype = 'wdirparent'
>
> # 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.
> +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)
> + # only record two hashes if there was a merge
> + oldhashes = old[:1] if old[1] == node.nullid else old
> + newhashes = new[:1] if new[1] == node.nullid else new
> + dirstate.journalstorage.record(
> + wdirparenttype, '.', oldhashes, newhashes)
> +
> + return orig(dirstate, dirstatefp)
> +
> +# hooks to record bookmark changes (both local and remote)
> def recordbookmarks(orig, store, fp):
> """Records all bookmark changes in the journal."""
> repo = store._repo
> @@ -117,12 +163,17 @@
>
> The file format starts with an integer version, delimited by a NUL.
>
> + This storage uses a dedicated lock; this makes it easier to avoid issues
> + with adding entries that added when the regular wlock is unlocked (e.g.
> + the dirstate).
> +
> """
> _currentcommand = ()
> + _lockref = None
>
> def __init__(self, repo):
> - self.repo = repo
> self.user = util.getuser()
> + self.ui = repo.ui
> self.vfs = repo.vfs
>
> # track the current command for recording in journal entries
> @@ -142,6 +193,24 @@
> # with a non-local repo (cloning for example).
> cls._currentcommand = fullargs
>
> + def jlock(self):
> + """Create a lock for the journal file"""
> + if self._lockref and self._lockref():
> + raise error.Abort(_('journal lock does not support nesting'))
> + desc = _('journal of %s') % self.vfs.base
> + try:
> + l = lock.lock(self.vfs, 'journal.lock', 0, desc=desc)
> + except error.LockHeld as inst:
> + self.ui.warn(
> + _("waiting for lock on %s held by %r\n") % (desc, inst.locker))
> + # default to 600 seconds timeout
> + l = lock.lock(
> + self.vfs, 'journal.lock',
> + int(self.ui.config("ui", "timeout", "600")), desc=desc)
> + self.ui.warn(_("got lock after %s seconds\n") % l.delay)
> + self._lockref = weakref.ref(l)
> + return l
> +
> def record(self, namespace, name, oldhashes, newhashes):
> """Record a new journal entry
>
> @@ -163,7 +232,7 @@
> util.makedate(), self.user, self.command, namespace, name,
> oldhashes, newhashes)
>
> - with self.repo.wlock():
> + with self.jlock():
> version = None
> # open file in amend mode to ensure it is created if missing
> with self.vfs('journal', mode='a+b', atomictemp=True) as f:
> @@ -176,7 +245,7 @@
> # write anything) if this is not a version we can handle or
> # the file is corrupt. In future, perhaps rotate the file
> # instead?
> - self.repo.ui.warn(
> + self.ui.warn(
> _("unsupported journal file version '%s'\n") % version)
> return
> if not version:
> @@ -229,15 +298,19 @@
> _ignoreopts = ('no-merges', 'graph')
> @command(
> 'journal', [
> + ('', 'all', None, 'show history for all names'),
> ('c', 'commits', None, 'show commit metadata'),
> ] + [opt for opt in commands.logopts if opt[1] not in _ignoreopts],
> '[OPTION]... [BOOKMARKNAME]')
> def journal(ui, repo, *args, **opts):
> - """show the previous position of bookmarks
> + """show the previous position of bookmarks and the working copy
>
> - The journal is used to see the previous commits of bookmarks. By default
> - the previous locations for all bookmarks are shown. Passing a bookmark
> - name will show all the previous positions of that bookmark.
> + The journal is used to see the previous commits that bookmarks and the
> + working copy pointed to. By default the previous locations for the working
> + copy. Passing a bookmark name will show all the previous positions of
> + that bookmark. Use the --all switch to show previous locations for all
> + bookmarks and the working copy; each line will then include the bookmark
> + name, or '.' for the working copy, as well.
>
> By default hg journal only shows the commit hash and the command that was
> running at that time. -v/--verbose will show the prior hash, the user, and
> @@ -250,22 +323,27 @@
> `hg journal -T json` can be used to produce machine readable output.
>
> """
> - bookmarkname = None
> + name = '.'
> + if opts.get('all'):
> + if args:
> + raise error.Abort(
> + _("You can't combine --all and filtering on a name"))
> + name = None
> if args:
> - bookmarkname = args[0]
> + name = args[0]
>
> fm = ui.formatter('journal', opts)
>
> if opts.get("template") != "json":
> - if bookmarkname is None:
> - name = _('all bookmarks')
> + if name is None:
> + displayname = _('the working copy and bookmarks')
> else:
> - name = "'%s'" % bookmarkname
> - ui.status(_("previous locations of %s:\n") % name)
> + displayname = "'%s'" % name
> + ui.status(_("previous locations of %s:\n") % displayname)
>
> limit = cmdutil.loglimit(opts)
> entry = None
> - for count, entry in enumerate(repo.journal.filtered(name=bookmarkname)):
> + for count, entry in enumerate(repo.journal.filtered(name=name)):
> if count == limit:
> break
> newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes])
> @@ -275,6 +353,7 @@
> fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr)
> fm.write('newhashes', '%s', newhashesstr)
> fm.condwrite(ui.verbose, 'user', ' %s', entry.user.ljust(8))
> + fm.condwrite(opts.get('all'), 'name', ' %s', entry.name.ljust(8))
>
> timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2')
> fm.condwrite(ui.verbose, 'date', ' %s', timestring)
> diff --git a/tests/test-journal.t b/tests/test-journal.t
> --- a/tests/test-journal.t
> +++ b/tests/test-journal.t
> @@ -32,22 +32,37 @@
>
> $ hg init repo
> $ cd repo
> - $ echo a > a
> - $ hg commit -Aqm a
> - $ echo b > a
> - $ hg commit -Aqm b
> - $ hg up 0
> - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
>
> Test empty journal
>
> $ hg journal
> - previous locations of all bookmarks:
> + previous locations of '.':
> no recorded locations
> $ hg journal foo
> previous locations of 'foo':
> no recorded locations
>
> +Test that working copy changes are tracked
> +
> + $ echo a > a
> + $ hg commit -Aqm a
> + $ hg journal
> + previous locations of '.':
> + cb9a9f314b8b commit -Aqm a
> + $ echo b > a
> + $ hg commit -Aqm b
> + $ hg journal
> + previous locations of '.':
> + 1e6c11564562 commit -Aqm b
> + cb9a9f314b8b commit -Aqm a
> + $ hg up 0
> + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
> + $ hg journal
> + previous locations of '.':
> + cb9a9f314b8b up 0
> + 1e6c11564562 commit -Aqm b
> + cb9a9f314b8b commit -Aqm a
> +
> Test that bookmarks are tracked
>
> $ hg book -r tip bar
> @@ -68,22 +83,32 @@
> cb9a9f314b8b book -f bar
> 1e6c11564562 book -r tip bar
>
> -Test that you can list all bookmarks as well as limit the list or filter on them
> +Test that bookmarks and working copy tracking is not mixed
> +
> + $ hg journal
> + previous locations of '.':
> + 1e6c11564562 up
> + cb9a9f314b8b up 0
> + 1e6c11564562 commit -Aqm b
> + cb9a9f314b8b commit -Aqm a
> +
> +Test that you can list all entries as well as limit the list or filter on them
>
> $ hg book -r tip baz
> - $ hg journal
> - previous locations of all bookmarks:
> - 1e6c11564562 book -r tip baz
> + $ hg journal --all
> + previous locations of the working copy and bookmarks:
> + 1e6c11564562 baz book -r tip baz
> + 1e6c11564562 bar up
> + 1e6c11564562 . up
> + cb9a9f314b8b bar book -f bar
> + 1e6c11564562 bar book -r tip bar
> + cb9a9f314b8b . up 0
> + 1e6c11564562 . commit -Aqm b
> + cb9a9f314b8b . commit -Aqm a
> + $ hg journal --limit 2
> + previous locations of '.':
> 1e6c11564562 up
> - cb9a9f314b8b book -f bar
> - 1e6c11564562 book -r tip bar
> - $ hg journal --limit 2
> - previous locations of all bookmarks:
> - 1e6c11564562 book -r tip baz
> - 1e6c11564562 up
> - $ hg journal baz
> - previous locations of 'baz':
> - 1e6c11564562 book -r tip baz
> + cb9a9f314b8b up 0
> $ hg journal bar
> previous locations of 'bar':
> 1e6c11564562 up
> @@ -92,26 +117,27 @@
> $ hg journal foo
> previous locations of 'foo':
> no recorded locations
> + $ hg journal .
> + previous locations of '.':
> + 1e6c11564562 up
> + cb9a9f314b8b up 0
> + 1e6c11564562 commit -Aqm b
> + cb9a9f314b8b commit -Aqm a
>
> Test that verbose and commit output work
>
> - $ hg journal --verbose
> - previous locations of all bookmarks:
> - 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip baz
> - cb9a9f314b8b -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 up
> - 1e6c11564562 -> cb9a9f314b8b foobar 1970-01-01 00:00 +0000 book -f bar
> - 000000000000 -> 1e6c11564562 foobar 1970-01-01 00:00 +0000 book -r tip bar
> + $ hg journal --verbose --all
> + previous locations of the working copy and bookmarks:
> + 000000000000 -> 1e6c11564562 foobar baz 1970-01-01 00:00 +0000 book -r tip baz
> + cb9a9f314b8b -> 1e6c11564562 foobar bar 1970-01-01 00:00 +0000 up
> + cb9a9f314b8b -> 1e6c11564562 foobar . 1970-01-01 00:00 +0000 up
> + 1e6c11564562 -> cb9a9f314b8b foobar bar 1970-01-01 00:00 +0000 book -f bar
> + 000000000000 -> 1e6c11564562 foobar bar 1970-01-01 00:00 +0000 book -r tip bar
> + 1e6c11564562 -> cb9a9f314b8b foobar . 1970-01-01 00:00 +0000 up 0
> + cb9a9f314b8b -> 1e6c11564562 foobar . 1970-01-01 00:00 +0000 commit -Aqm b
> + 000000000000 -> cb9a9f314b8b foobar . 1970-01-01 00:00 +0000 commit -Aqm a
> $ hg journal --commit
> - previous locations of all bookmarks:
> - 1e6c11564562 book -r tip baz
> - changeset: 1:1e6c11564562
> - bookmark: bar
> - bookmark: baz
> - tag: tip
> - user: test
> - date: Thu Jan 01 00:00:00 1970 +0000
> - summary: b
> -
> + previous locations of '.':
> 1e6c11564562 up
> changeset: 1:1e6c11564562
> bookmark: bar
> @@ -121,13 +147,13 @@
> date: Thu Jan 01 00:00:00 1970 +0000
> summary: b
>
> - cb9a9f314b8b book -f bar
> + cb9a9f314b8b up 0
> changeset: 0:cb9a9f314b8b
> user: test
> date: Thu Jan 01 00:00:00 1970 +0000
> summary: a
>
> - 1e6c11564562 book -r tip bar
> + 1e6c11564562 commit -Aqm b
> changeset: 1:1e6c11564562
> bookmark: bar
> bookmark: baz
> @@ -136,12 +162,18 @@
> date: Thu Jan 01 00:00:00 1970 +0000
> summary: b
>
> + cb9a9f314b8b commit -Aqm a
> + changeset: 0:cb9a9f314b8b
> + user: test
> + date: Thu Jan 01 00:00:00 1970 +0000
> + summary: a
> +
>
> Test for behaviour on unexpected storage version information
>
> $ printf '42\0' > .hg/journal
> $ hg journal
> - previous locations of all bookmarks:
> + previous locations of '.':
> abort: unknown journal file version '42'
> [255]
> $ hg book -r tip doomed
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
----------------------------------------------------------------------
[FUJIWARA Katsunori] foozy at lares.dti.ne.jp
More information about the Mercurial-devel
mailing list