[PATCH V3] journal: new experimental extension
Martijn Pieters
mj at zopatista.com
Wed Jun 29 14:02:24 EDT 2016
On 29 June 2016 at 14:33, Yuya Nishihara <yuya at tcha.org> wrote:
> On Fri, 24 Jun 2016 16:30:20 +0100, Martijn Pieters wrote:
>> # HG changeset patch
>> # User Martijn Pieters <mjpieters at fb.com>
>> # Date 1466781125 -3600
>> # Fri Jun 24 16:12:05 2016 +0100
>> # Node ID 4653159c0dc01e75ea4f9a1825fa6e511e5bce89
>> # Parent d0ae5b8f80dc115064e66e4ed1dfd848c4f7d1b0
>> journal: new experimental extension
>
> I agree the name "journal" is somewhat confusing, but I couldn't think of
> better name. So I'm going to queue this.
>
> I found a few nits. I'll fix them inflight if you agree.
That's fine, thanks!
> And, can you update the wiki page?
>
> https://www.mercurial-scm.org/wiki/ExperimentalExtensionsPlan
Done
>> +# storage format version; increment when the format changes
>> +storage_version = 0
>
> s/storage_version/storageversion/ per our coding style.
>
>> +def runcommand(orig, lui, repo, cmd, fullargs, *args):
>> + """Track the command line options for recording in the journal"""
>> + journalstorage.recordcommand(*fullargs)
>> + return orig(lui, repo, cmd, fullargs, *args)
>
> Maybe we'll need ui.fullargs or something, but it's beyond the scope of
> this patch.
>
>> + def record(self, namespace, name, oldhashes, newhashes):
>> + """Record a new journal entry
>> +
>> + * namespace: an opaque string; this can be used to filter on the type
>> + of recorded entries.
>> + * name: the name defining this entry; for bookmarks, this is the
>> + bookmark name. Can be filtered on when retrieving entries.
>> + * oldhashes and newhashes: each a single binary hash, or a list of
>> + binary hashes. These represent the old and new position of the named
>> + item.
>> +
>> + """
>> + if not isinstance(oldhashes, list):
>> + oldhashes = [oldhashes]
>> + if not isinstance(newhashes, list):
>> + newhashes = [newhashes]
>> +
>> + entry = journalentry(
>> + util.makedate(), self.user, self.command, namespace, name,
>> + oldhashes, newhashes)
>> +
>> + with self.repo.wlock():
>> + 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:
>> + f.seek(0, os.SEEK_SET)
>> + # Read just enough bytes to get a version number (up to 2
>> + # digits plus separator)
>> + version = f.read(3).partition('\0')[0]
>> + if version and version != str(storage_version):
>> + # different version of the storage. Exit early (and not
>> + # 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(
>> + _("unsupported journal file version '%s'\n") % version)
>> + return
>> + if not version:
>> + # empty file, write version first
>> + f.write(str(storage_version) + '\0')
>> + f.seek(0, os.SEEK_END)
>> + f.write(str(entry) + '\0')
>
> [snip]
>
>> + def __iter__(self):
>> + """Iterate over the storage
>> +
>> + Yields journalentry instances for each contained journal record.
>> +
>> + """
>> + if not self.vfs.exists('journal'):
>> + return
>> +
>> + with self.repo.wlock():
>> + with self.vfs('journal') as f:
>> + raw = f.read()
>
> No need of wlock for reading because the journal file is atomically updated.
> I'll remove it.
Ah, better without if not needed.
>> +# journal reading
>> +# log options that don't make sense for journal
>> +_ignore_opts = ('no-merges', 'graph')
>
> s/_ignore_opts/_ignoreopts/
>
>> + at command(
>> + 'journal', [
>> + ('c', 'commits', None, 'show commit metadata'),
>> + ] + [opt for opt in commands.logopts if opt[1] not in _ignore_opts],
>> + '[OPTION]... [BOOKMARKNAME]')
>> +def journal(ui, repo, *args, **opts):
>> + """show the previous position of bookmarks
>> +
>> + 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.
>> +
>> + 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
>> + the time at which it happened.
>> +
>> + Use -c/--commits to output log information on each commit hash; at this
>> + point you can use the usual `--patch`, `--git`, `--stat` and `--template`
>> + switches to alter the log output for these.
>> +
>> + `hg journal -T json` can be used to produce machine readable output.
>> +
>> + """
>> + bookmarkname = None
>> + if args:
>> + bookmarkname = args[0]
>> +
>> + fm = ui.formatter('journal', opts)
>> +
>> + if opts.get("template") != "json":
>> + if bookmarkname is None:
>> + name = _('all bookmarks')
>> + else:
>> + name = "'%s'" % bookmarkname
>> + ui.status(_("Previous locations of %s:\n") % name)
>
> s/Previous/previous/ for consistency.
>
>> + limit = cmdutil.loglimit(opts)
>> + entry = None
>> + for count, entry in enumerate(repo.journal.filtered(name=bookmarkname)):
>> + if count == limit:
>> + break
>> + newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes])
>> + oldhashesstr = ','.join([node.short(hash) for hash in entry.oldhashes])
>> +
>> + fm.startitem()
>> + fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr)
>> + fm.write('newhashes', '%s', newhashesstr)
>> + fm.condwrite(ui.verbose, 'user', ' %s', entry.user.ljust(8))
>> +
>> + timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2')
>> + fm.condwrite(ui.verbose, 'date', ' %s', timestring)
>> + fm.write('command', ' %s\n', entry.command)
>> +
>> + if opts.get("commits"):
>> + displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False)
>> + for hash in entry.newhashes:
>> + try:
>> + ctx = repo[hash]
>> + displayer.show(ctx)
>> + except error.RepoLookupError as e:
>> + fm.write('repolookuperror', "%s\n\n", str(e))
>> + displayer.close()
>> +
>> + fm.end()
>> +
>> + if entry is None:
>> + ui.status(_("no recorded locations\n"))
>
> Formatter and templater stuffs will need rework when we settle the output
> format of this command. But that won't be easy, and this is an experimental
> extension, so I think it's okay to revisit the issue later.
I've included that in the experimental extension notes on the wiki.
Thanks!
--
Martijn Pieters
More information about the Mercurial-devel
mailing list