##// END OF EJS Templates
verify: allow the storage to signal when renames can be tested on `skipread`...
verify: allow the storage to signal when renames can be tested on `skipread` This applies the new marker in the lfs handler to show it in action, and adds the test mentioned at the beginning of the series to show that fulltext isn't necessary in the LFS case. The existing `skipread` isn't enough, because it is also set if an error occurs reading the revlog data, or the data is censored. It could probably be cleared, but then it technically violates the interface contract. That wouldn't matter for the existing verify algorithm, but it isn't clear how that will change as alternate storage support is added. The flag is probably pretty revlog specific, given the comments in verify.py. But there's already filelog specific stuff in there and I'm not sure what future storage will bring, so I don't want to over-engineer this. Likewise, I'm not sure that we want the verify method for each storage type to completely drive the bus when it comes to detecting renames, so I don't want to go down the rabbithole of having verifyintegrity() return metadata hints at this point. Differential Revision: https://phab.mercurial-scm.org/D7713

File last commit:

r43812:2fe6121c default
r44530:b9e174d4 default
Show More
journal.py
604 lines | 19.9 KiB | text/x-python | PythonLexer
Martijn Pieters
journal: new experimental extension...
r29443 # journal.py
#
# Copyright 2014-2016 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Jun Wu
journal: use lowercase for docstring title...
r31600 """track previous positions of bookmarks (EXPERIMENTAL)
Martijn Pieters
journal: new experimental extension...
r29443
This extension adds a new command: `hg journal`, which shows you where
bookmarks were previously located.
"""
from __future__ import absolute_import
import collections
Martijn Pieters
journal: add share extension support...
r29503 import errno
Martijn Pieters
journal: new experimental extension...
r29443 import os
Martijn Pieters
journal: add dirstate tracking...
r29502 import weakref
Martijn Pieters
journal: new experimental extension...
r29443
from mercurial.i18n import _
from mercurial import (
bookmarks,
cmdutil,
dispatch,
Pulkit Goyal
py3: replace __str__ to __bytes__ in hgext/journal.py...
r36684 encoding,
Martijn Pieters
journal: new experimental extension...
r29443 error,
extensions,
Martijn Pieters
journal: add share extension support...
r29503 hg,
Martijn Pieters
journal: add dirstate tracking...
r29502 localrepo,
lock,
Yuya Nishihara
cmdutil: drop aliases for logcmdutil functions (API)...
r35906 logcmdutil,
Martijn Pieters
journal: new experimental extension...
r29443 node,
Pulkit Goyal
py3: handle keyword arguments in hgext/journal.py...
r35001 pycompat,
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 registrar,
Martijn Pieters
journal: new experimental extension...
r29443 util,
)
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 from mercurial.utils import (
dateutil,
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 procutil,
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 stringutil,
)
Martijn Pieters
journal: new experimental extension...
r29443
cmdtable = {}
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 command = registrar.command(cmdtable)
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Martijn Pieters
journal: new experimental extension...
r29443 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 testedwith = b'ships-with-hg-core'
Martijn Pieters
journal: new experimental extension...
r29443
# storage format version; increment when the format changes
storageversion = 0
# namespaces
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bookmarktype = b'bookmark'
wdirparenttype = b'wdirparent'
Martijn Pieters
journal: add share extension support...
r29503 # In a shared repository, what shared feature name is used
# to indicate this namespace is shared with the source?
sharednamespaces = {
bookmarktype: hg.sharedbookmarks,
}
Martijn Pieters
journal: new experimental extension...
r29443
# Journal recording, register hooks and storage object
def extsetup(ui):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 extensions.wrapfunction(dispatch, b'runcommand', runcommand)
extensions.wrapfunction(bookmarks.bmstore, b'_write', recordbookmarks)
FUJIWARA Katsunori
journal: use wrapfilecache instead of wrapfunction on func of filecache...
r33384 extensions.wrapfilecache(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 localrepo.localrepository, b'dirstate', wrapdirstate
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 extensions.wrapfunction(hg, b'postshare', wrappostshare)
extensions.wrapfunction(hg, b'copystore', unsharejournal)
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 def reposetup(ui, repo):
if repo.local():
repo.journal = journalstorage(repo)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 repo._wlockfreeprefix.add(b'namejournal')
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 dirstate, cached = localrepo.isfilecached(repo, b'dirstate')
FUJIWARA Katsunori
journal: execute setup procedures for already instantiated dirstate...
r33383 if cached:
# already instantiated dirstate isn't yet marked as
# "journal"-ing, even though repo.dirstate() was already
# wrapped by own wrapdirstate()
_setupdirstate(repo, dirstate)
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 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)
Augie Fackler
formatting: blacken the codebase...
r43346
FUJIWARA Katsunori
journal: execute setup procedures for already instantiated dirstate...
r33383 def _setupdirstate(repo, dirstate):
dirstate.journalstorage = repo.journal
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 dirstate.addparentchangecallback(b'journal', recorddirstateparents)
FUJIWARA Katsunori
journal: execute setup procedures for already instantiated dirstate...
r33383
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: add dirstate tracking...
r29502 # hooks to record dirstate changes
def wrapdirstate(orig, repo):
"""Make journal storage available to the dirstate object"""
dirstate = orig(repo)
Martin von Zweigbergk
py3: delete b'' prefix from safehasattr arguments...
r43385 if util.safehasattr(repo, 'journal'):
FUJIWARA Katsunori
journal: execute setup procedures for already instantiated dirstate...
r33383 _setupdirstate(repo, dirstate)
Martijn Pieters
journal: add dirstate tracking...
r29502 return dirstate
Augie Fackler
formatting: blacken the codebase...
r43346
Mateusz Kwapich
journal: use the dirstate parentchange callbacks...
r29773 def recorddirstateparents(dirstate, old, new):
Martijn Pieters
journal: add dirstate tracking...
r29502 """Records all dirstate parent changes in the journal."""
Mateusz Kwapich
journal: use the dirstate parentchange callbacks...
r29773 old = list(old)
new = list(new)
Martin von Zweigbergk
py3: delete b'' prefix from safehasattr arguments...
r43385 if util.safehasattr(dirstate, 'journalstorage'):
Mateusz Kwapich
journal: use the dirstate parentchange callbacks...
r29773 # 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(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 wdirparenttype, b'.', oldhashes, newhashes
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: add dirstate tracking...
r29502
# hooks to record bookmark changes (both local and remote)
Martijn Pieters
journal: new experimental extension...
r29443 def recordbookmarks(orig, store, fp):
"""Records all bookmark changes in the journal."""
repo = store._repo
Martin von Zweigbergk
py3: delete b'' prefix from safehasattr arguments...
r43385 if util.safehasattr(repo, 'journal'):
Martijn Pieters
journal: new experimental extension...
r29443 oldmarks = bookmarks.bmstore(repo)
Gregory Szorc
py3: define and use pycompat.iteritems() for hgext/...
r43375 for mark, value in pycompat.iteritems(store):
Martijn Pieters
journal: new experimental extension...
r29443 oldvalue = oldmarks.get(mark, node.nullid)
if value != oldvalue:
repo.journal.record(bookmarktype, mark, oldvalue, value)
return orig(store, fp)
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: add share extension support...
r29503 # shared repository support
def _readsharedfeatures(repo):
"""A set of shared features for this repository"""
try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return set(repo.vfs.read(b'shared').splitlines())
Martijn Pieters
journal: add share extension support...
r29503 except IOError as inst:
if inst.errno != errno.ENOENT:
raise
return set()
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: add share extension support...
r29503 def _mergeentriesiter(*iterables, **kwargs):
"""Given a set of sorted iterables, yield the next entry in merged order
Note that by default entries go from most recent to oldest.
"""
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 order = kwargs.pop('order', max)
Martijn Pieters
journal: add share extension support...
r29503 iterables = [iter(it) for it in iterables]
# this tracks still active iterables; iterables are deleted as they are
# exhausted, which is why this is a dictionary and why each entry also
# stores the key. Entries are mutable so we can store the next value each
# time.
iterable_map = {}
for key, it in enumerate(iterables):
try:
iterable_map[key] = [next(it), key, it]
except StopIteration:
# empty entry, can be ignored
pass
while iterable_map:
Gregory Szorc
py3: define and use pycompat.itervalues()...
r43374 value, key, it = order(pycompat.itervalues(iterable_map))
Martijn Pieters
journal: add share extension support...
r29503 yield value
try:
iterable_map[key][0] = next(it)
except StopIteration:
# this iterable is empty, remove it from consideration
del iterable_map[key]
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: add share extension support...
r29503 def wrappostshare(orig, sourcerepo, destrepo, **kwargs):
"""Mark this shared working copy as sharing journal information"""
Pierre-Yves David
journal: take wlock for writting the 'shared' file...
r29756 with destrepo.wlock():
orig(sourcerepo, destrepo, **kwargs)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with destrepo.vfs(b'shared', b'a') as fp:
fp.write(b'journal\n')
Martijn Pieters
journal: add share extension support...
r29503
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: add share extension support...
r29503 def unsharejournal(orig, ui, repo, repopath):
"""Copy shared journal entries into this repo when unsharing"""
Augie Fackler
formatting: blacken the codebase...
r43346 if (
repo.path == repopath
and repo.shared()
Martin von Zweigbergk
py3: delete b'' prefix from safehasattr arguments...
r43385 and util.safehasattr(repo, 'journal')
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Gregory Szorc
hg: move share._getsrcrepo into core...
r36177 sharedrepo = hg.sharedreposource(repo)
Martijn Pieters
journal: add share extension support...
r29503 sharedfeatures = _readsharedfeatures(repo)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if sharedrepo and sharedfeatures > {b'journal'}:
Martijn Pieters
journal: add share extension support...
r29503 # there is a shared repository and there are shared journal entries
# to copy. move shared date over from source to destination but
# move the local file first
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if repo.vfs.exists(b'namejournal'):
journalpath = repo.vfs.join(b'namejournal')
util.rename(journalpath, journalpath + b'.bak')
Martijn Pieters
journal: add share extension support...
r29503 storage = repo.journal
local = storage._open(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 repo.vfs, filename=b'namejournal.bak', _newestfirst=False
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: add share extension support...
r29503 shared = (
Augie Fackler
formatting: blacken the codebase...
r43346 e
for e in storage._open(sharedrepo.vfs, _newestfirst=False)
if sharednamespaces.get(e.namespace) in sharedfeatures
)
Martijn Pieters
journal: add share extension support...
r29503 for entry in _mergeentriesiter(local, shared, order=min):
storage._write(repo.vfs, entry)
return orig(ui, repo, repopath)
Augie Fackler
formatting: blacken the codebase...
r43346
class journalentry(
collections.namedtuple(
Augie Fackler
cleanup: remove pointless r-prefixes on single-quoted strings...
r43906 'journalentry',
'timestamp user command namespace name oldhashes newhashes',
Augie Fackler
formatting: blacken the codebase...
r43346 )
):
Martijn Pieters
journal: new experimental extension...
r29443 """Individual journal entry
* timestamp: a mercurial (time, timezone) tuple
* user: the username that ran the command
* namespace: the entry namespace, an opaque string
* name: the name of the changed item, opaque string with meaning in the
namespace
* command: the hg command that triggered this record
* oldhashes: a tuple of one or more binary hashes for the old location
* newhashes: a tuple of one or more binary hashes for the new location
Handles serialisation from and to the storage format. Fields are
separated by newlines, hashes are written out in hex separated by commas,
timestamp and timezone are separated by a space.
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 @classmethod
def fromstorage(cls, line):
Augie Fackler
formatting: blacken the codebase...
r43346 (
time,
user,
command,
namespace,
name,
oldhashes,
newhashes,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ) = line.split(b'\n')
Martijn Pieters
journal: new experimental extension...
r29443 timestamp, tz = time.split()
timestamp, tz = float(timestamp), int(tz)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 oldhashes = tuple(node.bin(hash) for hash in oldhashes.split(b','))
newhashes = tuple(node.bin(hash) for hash in newhashes.split(b','))
Martijn Pieters
journal: new experimental extension...
r29443 return cls(
Augie Fackler
formatting: blacken the codebase...
r43346 (timestamp, tz),
user,
command,
namespace,
name,
oldhashes,
newhashes,
)
Martijn Pieters
journal: new experimental extension...
r29443
Pulkit Goyal
py3: replace __str__ to __bytes__ in hgext/journal.py...
r36684 def __bytes__(self):
"""bytes representation for storage"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 time = b' '.join(map(pycompat.bytestr, self.timestamp))
oldhashes = b','.join([node.hex(hash) for hash in self.oldhashes])
newhashes = b','.join([node.hex(hash) for hash in self.newhashes])
return b'\n'.join(
Augie Fackler
formatting: blacken the codebase...
r43346 (
time,
self.user,
self.command,
self.namespace,
self.name,
oldhashes,
newhashes,
)
)
Martijn Pieters
journal: new experimental extension...
r29443
Pulkit Goyal
py3: replace __str__ to __bytes__ in hgext/journal.py...
r36684 __str__ = encoding.strmethod(__bytes__)
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 class journalstorage(object):
"""Storage for journal entries
Martijn Pieters
journal: add share extension support...
r29503 Entries are divided over two files; one with entries that pertain to the
local working copy *only*, and one with entries that are shared across
multiple working copies when shared using the share extension.
Martijn Pieters
journal: new experimental extension...
r29443 Entries are stored with NUL bytes as separators. See the journalentry
class for the per-entry structure.
The file format starts with an integer version, delimited by a NUL.
Martijn Pieters
journal: add dirstate tracking...
r29502 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).
Martijn Pieters
journal: new experimental extension...
r29443 """
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 _currentcommand = ()
Martijn Pieters
journal: add dirstate tracking...
r29502 _lockref = None
Martijn Pieters
journal: new experimental extension...
r29443
def __init__(self, repo):
Yuya Nishihara
procutil: bulk-replace function calls to point to new module
r37138 self.user = procutil.getuser()
Martijn Pieters
journal: add dirstate tracking...
r29502 self.ui = repo.ui
Martijn Pieters
journal: new experimental extension...
r29443 self.vfs = repo.vfs
Martijn Pieters
journal: add share extension support...
r29503 # is this working copy using a shared storage?
self.sharedfeatures = self.sharedvfs = None
if repo.shared():
features = _readsharedfeatures(repo)
Gregory Szorc
hg: move share._getsrcrepo into core...
r36177 sharedrepo = hg.sharedreposource(repo)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if sharedrepo is not None and b'journal' in features:
Martijn Pieters
journal: add share extension support...
r29503 self.sharedvfs = sharedrepo.vfs
self.sharedfeatures = features
Martijn Pieters
journal: new experimental extension...
r29443 # track the current command for recording in journal entries
@property
def command(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 commandstr = b' '.join(
Augie Fackler
formatting: blacken the codebase...
r43346 map(procutil.shellquote, journalstorage._currentcommand)
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'\n' in commandstr:
Martijn Pieters
journal: new experimental extension...
r29443 # truncate multi-line commands
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 commandstr = commandstr.partition(b'\n')[0] + b' ...'
Martijn Pieters
journal: new experimental extension...
r29443 return commandstr
@classmethod
def recordcommand(cls, *fullargs):
"""Set the current hg arguments, stored with recorded entries"""
# Set the current command on the class because we may have started
# with a non-local repo (cloning for example).
cls._currentcommand = fullargs
Pierre-Yves David
journal: properly check for held lock (issue5349)...
r29928 def _currentlock(self, lockref):
"""Returns the lock if it's held, or None if it's not.
(This is copied from the localrepo class)
"""
if lockref is None:
return None
l = lockref()
if l is None or not l.held:
return None
return l
Martijn Pieters
journal: add share extension support...
r29503 def jlock(self, vfs):
Martijn Pieters
journal: add dirstate tracking...
r29502 """Create a lock for the journal file"""
Pierre-Yves David
journal: properly check for held lock (issue5349)...
r29928 if self._currentlock(self._lockref) is not None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'journal lock does not support nesting'))
desc = _(b'journal of %s') % vfs.base
Martijn Pieters
journal: add dirstate tracking...
r29502 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 l = lock.lock(vfs, b'namejournal.lock', 0, desc=desc)
Martijn Pieters
journal: add dirstate tracking...
r29502 except error.LockHeld as inst:
self.ui.warn(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"waiting for lock on %s held by %r\n") % (desc, inst.locker)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: add dirstate tracking...
r29502 # default to 600 seconds timeout
l = lock.lock(
Augie Fackler
formatting: blacken the codebase...
r43346 vfs,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'namejournal.lock',
self.ui.configint(b"ui", b"timeout"),
Augie Fackler
formatting: blacken the codebase...
r43346 desc=desc,
)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.ui.warn(_(b"got lock after %s seconds\n") % l.delay)
Martijn Pieters
journal: add dirstate tracking...
r29502 self._lockref = weakref.ref(l)
return l
Martijn Pieters
journal: new experimental extension...
r29443 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(
Augie Fackler
formatting: blacken the codebase...
r43346 dateutil.makedate(),
self.user,
self.command,
namespace,
name,
oldhashes,
newhashes,
)
Martijn Pieters
journal: new experimental extension...
r29443
Martijn Pieters
journal: add share extension support...
r29503 vfs = self.vfs
if self.sharedvfs is not None:
# write to the shared repository if this feature is being
# shared between working copies.
if sharednamespaces.get(namespace) in self.sharedfeatures:
vfs = self.sharedvfs
self._write(vfs, entry)
def _write(self, vfs, entry):
with self.jlock(vfs):
Martijn Pieters
journal: new experimental extension...
r29443 # open file in amend mode to ensure it is created if missing
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with vfs(b'namejournal', mode=b'a+b') as f:
Martijn Pieters
journal: new experimental extension...
r29443 f.seek(0, os.SEEK_SET)
# Read just enough bytes to get a version number (up to 2
# digits plus separator)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 version = f.read(3).partition(b'\0')[0]
if version and version != b"%d" % storageversion:
Martijn Pieters
journal: new experimental extension...
r29443 # 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?
Martijn Pieters
journal: add dirstate tracking...
r29502 self.ui.warn(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"unsupported journal file version '%s'\n") % version
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: new experimental extension...
r29443 return
if not version:
# empty file, write version first
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write((b"%d" % storageversion) + b'\0')
Martijn Pieters
journal: new experimental extension...
r29443 f.seek(0, os.SEEK_END)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 f.write(bytes(entry) + b'\0')
Martijn Pieters
journal: new experimental extension...
r29443
def filtered(self, namespace=None, name=None):
"""Yield all journal entries with the given namespace or name
Both the namespace and the name are optional; if neither is given all
entries in the journal are produced.
Martijn Pieters
journal: add support for seaching by pattern...
r29504 Matching supports regular expressions by using the `re:` prefix
(use `literal:` to match names or namespaces that start with `re:`)
Martijn Pieters
journal: new experimental extension...
r29443 """
Martijn Pieters
journal: add support for seaching by pattern...
r29504 if namespace is not None:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 namespace = stringutil.stringmatcher(namespace)[-1]
Martijn Pieters
journal: add support for seaching by pattern...
r29504 if name is not None:
Yuya Nishihara
stringutil: bulk-replace call sites to point to new module...
r37102 name = stringutil.stringmatcher(name)[-1]
Martijn Pieters
journal: new experimental extension...
r29443 for entry in self:
Martijn Pieters
journal: add support for seaching by pattern...
r29504 if namespace is not None and not namespace(entry.namespace):
Martijn Pieters
journal: new experimental extension...
r29443 continue
Martijn Pieters
journal: add support for seaching by pattern...
r29504 if name is not None and not name(entry.name):
Martijn Pieters
journal: new experimental extension...
r29443 continue
yield entry
def __iter__(self):
"""Iterate over the storage
Yields journalentry instances for each contained journal record.
"""
Martijn Pieters
journal: add share extension support...
r29503 local = self._open(self.vfs)
if self.sharedvfs is None:
return local
# iterate over both local and shared entries, but only those
# shared entries that are among the currently shared features
shared = (
Augie Fackler
formatting: blacken the codebase...
r43346 e
for e in self._open(self.sharedvfs)
if sharednamespaces.get(e.namespace) in self.sharedfeatures
)
Martijn Pieters
journal: add share extension support...
r29503 return _mergeentriesiter(local, shared)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def _open(self, vfs, filename=b'namejournal', _newestfirst=True):
Martijn Pieters
journal: add share extension support...
r29503 if not vfs.exists(filename):
Martijn Pieters
journal: new experimental extension...
r29443 return
Martijn Pieters
journal: add share extension support...
r29503 with vfs(filename) as f:
Martijn Pieters
journal: new experimental extension...
r29443 raw = f.read()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 lines = raw.split(b'\0')
Martijn Pieters
journal: new experimental extension...
r29443 version = lines and lines[0]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if version != b"%d" % storageversion:
version = version or _(b'not available')
raise error.Abort(_(b"unknown journal file version '%s'") % version)
Martijn Pieters
journal: new experimental extension...
r29443
Martijn Pieters
journal: add share extension support...
r29503 # Skip the first line, it's a version number. Normally we iterate over
# these in reverse order to list newest first; only when copying across
# a shared storage do we forgo reversing.
lines = lines[1:]
if _newestfirst:
lines = reversed(lines)
Martijn Pieters
journal: new experimental extension...
r29443 for line in lines:
if not line:
continue
yield journalentry.fromstorage(line)
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 # journal reading
# log options that don't make sense for journal
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _ignoreopts = (b'no-merges', b'graph')
Augie Fackler
formatting: blacken the codebase...
r43346
Martijn Pieters
journal: new experimental extension...
r29443 @command(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'journal',
Augie Fackler
formatting: blacken the codebase...
r43346 [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b'', b'all', None, b'show history for all names'),
(b'c', b'commits', None, b'show commit metadata'),
Augie Fackler
formatting: blacken the codebase...
r43346 ]
+ [opt for opt in cmdutil.logopts if opt[1] not in _ignoreopts],
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'[OPTION]... [BOOKMARKNAME]',
Augie Fackler
formatting: blacken the codebase...
r43346 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
)
Martijn Pieters
journal: new experimental extension...
r29443 def journal(ui, repo, *args, **opts):
Martijn Pieters
journal: add dirstate tracking...
r29502 """show the previous position of bookmarks and the working copy
Martijn Pieters
journal: new experimental extension...
r29443
Martijn Pieters
journal: add dirstate tracking...
r29502 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.
Martijn Pieters
journal: new experimental extension...
r29443
Martijn Pieters
journal: add support for seaching by pattern...
r29504 If `name` starts with `re:`, the remainder of the name is treated as
a regular expression. To match a name that actually starts with `re:`,
use the prefix `literal:`.
Martijn Pieters
journal: new experimental extension...
r29443 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.
"""
Pulkit Goyal
py3: handle keyword arguments in hgext/journal.py...
r35001 opts = pycompat.byteskwargs(opts)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 name = b'.'
if opts.get(b'all'):
Martijn Pieters
journal: add dirstate tracking...
r29502 if args:
raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"You can't combine --all and filtering on a name")
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: add dirstate tracking...
r29502 name = None
Martijn Pieters
journal: new experimental extension...
r29443 if args:
Martijn Pieters
journal: add dirstate tracking...
r29502 name = args[0]
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fm = ui.formatter(b'journal', opts)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
journal: inline formatted nodes and date into expression...
r39738 def formatnodes(nodes):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return fm.formatlist(map(fm.hexfunc, nodes), name=b'node', sep=b',')
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if opts.get(b"template") != b"json":
Martijn Pieters
journal: add dirstate tracking...
r29502 if name is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 displayname = _(b'the working copy and bookmarks')
Martijn Pieters
journal: new experimental extension...
r29443 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 displayname = b"'%s'" % name
ui.status(_(b"previous locations of %s:\n") % displayname)
Martijn Pieters
journal: new experimental extension...
r29443
Yuya Nishihara
cmdutil: drop aliases for logcmdutil functions (API)...
r35906 limit = logcmdutil.getlimit(opts)
Martijn Pieters
journal: new experimental extension...
r29443 entry = None
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.pager(b'journal')
Martijn Pieters
journal: add dirstate tracking...
r29502 for count, entry in enumerate(repo.journal.filtered(name=name)):
Martijn Pieters
journal: new experimental extension...
r29443 if count == limit:
break
fm.startitem()
Augie Fackler
formatting: blacken the codebase...
r43346 fm.condwrite(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.verbose, b'oldnodes', b'%s -> ', formatnodes(entry.oldhashes)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fm.write(b'newnodes', b'%s', formatnodes(entry.newhashes))
fm.condwrite(ui.verbose, b'user', b' %-8s', entry.user)
Martijn Pieters
journal: add support for seaching by pattern...
r29504 fm.condwrite(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 opts.get(b'all') or name.startswith(b're:'),
b'name',
b' %-8s',
Augie Fackler
formatting: blacken the codebase...
r43346 entry.name,
)
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: blacken the codebase...
r43346 fm.condwrite(
ui.verbose,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'date',
b' %s',
fm.formatdate(entry.timestamp, b'%Y-%m-%d %H:%M %1%2'),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fm.write(b'command', b' %s\n', entry.command)
Martijn Pieters
journal: new experimental extension...
r29443
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if opts.get(b"commits"):
Yuya Nishihara
journal: use changesetformatter to properly nest list of commits in JSON...
r39740 if fm.isplain():
displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
else:
displayer = logcmdutil.changesetformatter(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, repo, fm.nested(b'changesets'), diffopts=opts
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martijn Pieters
journal: new experimental extension...
r29443 for hash in entry.newhashes:
try:
ctx = repo[hash]
displayer.show(ctx)
except error.RepoLookupError as e:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fm.plain(b"%s\n\n" % pycompat.bytestr(e))
Martijn Pieters
journal: new experimental extension...
r29443 displayer.close()
fm.end()
if entry is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.status(_(b"no recorded locations\n"))