diff -r 57875cf423c9 -r 2372284d9457 hgext/journal.py --- a/hgext/journal.py Sat Oct 05 10:29:34 2019 -0400 +++ b/hgext/journal.py Sun Oct 06 09:45:02 2019 -0400 @@ -68,10 +68,12 @@ extensions.wrapfunction(dispatch, 'runcommand', runcommand) extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) extensions.wrapfilecache( - localrepo.localrepository, 'dirstate', wrapdirstate) + localrepo.localrepository, 'dirstate', wrapdirstate + ) extensions.wrapfunction(hg, 'postshare', wrappostshare) extensions.wrapfunction(hg, 'copystore', unsharejournal) + def reposetup(ui, repo): if repo.local(): repo.journal = journalstorage(repo) @@ -84,15 +86,18 @@ # wrapped by own wrapdirstate() _setupdirstate(repo, dirstate) + 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) + def _setupdirstate(repo, dirstate): dirstate.journalstorage = repo.journal dirstate.addparentchangecallback('journal', recorddirstateparents) + # hooks to record dirstate changes def wrapdirstate(orig, repo): """Make journal storage available to the dirstate object""" @@ -101,6 +106,7 @@ _setupdirstate(repo, dirstate) return dirstate + def recorddirstateparents(dirstate, old, new): """Records all dirstate parent changes in the journal.""" old = list(old) @@ -110,7 +116,9 @@ 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) + wdirparenttype, '.', oldhashes, newhashes + ) + # hooks to record bookmark changes (both local and remote) def recordbookmarks(orig, store, fp): @@ -124,6 +132,7 @@ repo.journal.record(bookmarktype, mark, oldvalue, value) return orig(store, fp) + # shared repository support def _readsharedfeatures(repo): """A set of shared features for this repository""" @@ -134,6 +143,7 @@ raise return set() + def _mergeentriesiter(*iterables, **kwargs): """Given a set of sorted iterables, yield the next entry in merged order @@ -162,6 +172,7 @@ # this iterable is empty, remove it from consideration del iterable_map[key] + def wrappostshare(orig, sourcerepo, destrepo, **kwargs): """Mark this shared working copy as sharing journal information""" with destrepo.wlock(): @@ -169,10 +180,14 @@ with destrepo.vfs('shared', 'a') as fp: fp.write('journal\n') + def unsharejournal(orig, ui, repo, repopath): """Copy shared journal entries into this repo when unsharing""" - if (repo.path == repopath and repo.shared() and - util.safehasattr(repo, 'journal')): + if ( + repo.path == repopath + and repo.shared() + and util.safehasattr(repo, 'journal') + ): sharedrepo = hg.sharedreposource(repo) sharedfeatures = _readsharedfeatures(repo) if sharedrepo and sharedfeatures > {'journal'}: @@ -184,18 +199,25 @@ util.rename(journalpath, journalpath + '.bak') storage = repo.journal local = storage._open( - repo.vfs, filename='namejournal.bak', _newestfirst=False) + repo.vfs, filename='namejournal.bak', _newestfirst=False + ) shared = ( - e for e in storage._open(sharedrepo.vfs, _newestfirst=False) - if sharednamespaces.get(e.namespace) in sharedfeatures) + e + for e in storage._open(sharedrepo.vfs, _newestfirst=False) + if sharednamespaces.get(e.namespace) in sharedfeatures + ) for entry in _mergeentriesiter(local, shared, order=min): storage._write(repo.vfs, entry) return orig(ui, repo, repopath) -class journalentry(collections.namedtuple( + +class journalentry( + collections.namedtuple( r'journalentry', - r'timestamp user command namespace name oldhashes newhashes')): + r'timestamp user command namespace name oldhashes newhashes', + ) +): """Individual journal entry * timestamp: a mercurial (time, timezone) tuple @@ -212,29 +234,52 @@ timestamp and timezone are separated by a space. """ + @classmethod def fromstorage(cls, line): - (time, user, command, namespace, name, - oldhashes, newhashes) = line.split('\n') + ( + time, + user, + command, + namespace, + name, + oldhashes, + newhashes, + ) = line.split('\n') timestamp, tz = time.split() timestamp, tz = float(timestamp), int(tz) oldhashes = tuple(node.bin(hash) for hash in oldhashes.split(',')) newhashes = tuple(node.bin(hash) for hash in newhashes.split(',')) return cls( - (timestamp, tz), user, command, namespace, name, - oldhashes, newhashes) + (timestamp, tz), + user, + command, + namespace, + name, + oldhashes, + newhashes, + ) def __bytes__(self): """bytes representation for storage""" time = ' '.join(map(pycompat.bytestr, self.timestamp)) oldhashes = ','.join([node.hex(hash) for hash in self.oldhashes]) newhashes = ','.join([node.hex(hash) for hash in self.newhashes]) - return '\n'.join(( - time, self.user, self.command, self.namespace, self.name, - oldhashes, newhashes)) + return '\n'.join( + ( + time, + self.user, + self.command, + self.namespace, + self.name, + oldhashes, + newhashes, + ) + ) __str__ = encoding.strmethod(__bytes__) + class journalstorage(object): """Storage for journal entries @@ -252,6 +297,7 @@ the dirstate). """ + _currentcommand = () _lockref = None @@ -273,7 +319,8 @@ @property def command(self): commandstr = ' '.join( - map(procutil.shellquote, journalstorage._currentcommand)) + map(procutil.shellquote, journalstorage._currentcommand) + ) if '\n' in commandstr: # truncate multi-line commands commandstr = commandstr.partition('\n')[0] + ' ...' @@ -307,11 +354,15 @@ l = lock.lock(vfs, 'namejournal.lock', 0, desc=desc) except error.LockHeld as inst: self.ui.warn( - _("waiting for lock on %s held by %r\n") % (desc, inst.locker)) + _("waiting for lock on %s held by %r\n") % (desc, inst.locker) + ) # default to 600 seconds timeout l = lock.lock( - vfs, 'namejournal.lock', - self.ui.configint("ui", "timeout"), desc=desc) + vfs, + 'namejournal.lock', + self.ui.configint("ui", "timeout"), + desc=desc, + ) self.ui.warn(_("got lock after %s seconds\n") % l.delay) self._lockref = weakref.ref(l) return l @@ -334,8 +385,14 @@ newhashes = [newhashes] entry = journalentry( - dateutil.makedate(), self.user, self.command, namespace, name, - oldhashes, newhashes) + dateutil.makedate(), + self.user, + self.command, + namespace, + name, + oldhashes, + newhashes, + ) vfs = self.vfs if self.sharedvfs is not None: @@ -360,7 +417,8 @@ # the file is corrupt. In future, perhaps rotate the file # instead? self.ui.warn( - _("unsupported journal file version '%s'\n") % version) + _("unsupported journal file version '%s'\n") % version + ) return if not version: # empty file, write version first @@ -403,8 +461,10 @@ # iterate over both local and shared entries, but only those # shared entries that are among the currently shared features shared = ( - e for e in self._open(self.sharedvfs) - if sharednamespaces.get(e.namespace) in self.sharedfeatures) + e + for e in self._open(self.sharedvfs) + if sharednamespaces.get(e.namespace) in self.sharedfeatures + ) return _mergeentriesiter(local, shared) def _open(self, vfs, filename='namejournal', _newestfirst=True): @@ -431,16 +491,22 @@ continue yield journalentry.fromstorage(line) + # journal reading # log options that don't make sense for journal _ignoreopts = ('no-merges', 'graph') + + @command( - 'journal', [ + 'journal', + [ ('', 'all', None, 'show history for all names'), ('c', 'commits', None, 'show commit metadata'), - ] + [opt for opt in cmdutil.logopts if opt[1] not in _ignoreopts], + ] + + [opt for opt in cmdutil.logopts if opt[1] not in _ignoreopts], '[OPTION]... [BOOKMARKNAME]', - helpcategory=command.CATEGORY_CHANGE_ORGANIZATION) + helpcategory=command.CATEGORY_CHANGE_ORGANIZATION, +) def journal(ui, repo, *args, **opts): """show the previous position of bookmarks and the working copy @@ -471,12 +537,14 @@ if opts.get('all'): if args: raise error.Abort( - _("You can't combine --all and filtering on a name")) + _("You can't combine --all and filtering on a name") + ) name = None if args: name = args[0] fm = ui.formatter('journal', opts) + def formatnodes(nodes): return fm.formatlist(map(fm.hexfunc, nodes), name='node', sep=',') @@ -495,16 +563,24 @@ break fm.startitem() - fm.condwrite(ui.verbose, 'oldnodes', '%s -> ', - formatnodes(entry.oldhashes)) + fm.condwrite( + ui.verbose, 'oldnodes', '%s -> ', formatnodes(entry.oldhashes) + ) fm.write('newnodes', '%s', formatnodes(entry.newhashes)) fm.condwrite(ui.verbose, 'user', ' %-8s', entry.user) fm.condwrite( opts.get('all') or name.startswith('re:'), - 'name', ' %-8s', entry.name) + 'name', + ' %-8s', + entry.name, + ) - fm.condwrite(ui.verbose, 'date', ' %s', - fm.formatdate(entry.timestamp, '%Y-%m-%d %H:%M %1%2')) + fm.condwrite( + ui.verbose, + 'date', + ' %s', + fm.formatdate(entry.timestamp, '%Y-%m-%d %H:%M %1%2'), + ) fm.write('command', ' %s\n', entry.command) if opts.get("commits"): @@ -512,7 +588,8 @@ displayer = logcmdutil.changesetdisplayer(ui, repo, opts) else: displayer = logcmdutil.changesetformatter( - ui, repo, fm.nested('changesets'), diffopts=opts) + ui, repo, fm.nested('changesets'), diffopts=opts + ) for hash in entry.newhashes: try: ctx = repo[hash]