# HG changeset patch # User Pulkit Goyal # Date 1566078333 -10800 # Node ID 268662aac0752cc7ecd2f98ec80d0194ad8050ff # Parent 383fdfa6bba9366abe3aceeddfa65dd4a0b3870d interfaces: create a new folder for interfaces and move repository.py in it I was trying to understand current interfaces and write new ones and I realized we need to improve how current interfaces are organised. This creates a dedicated folder for defining interfaces and move `repository.py` which defines all the current interfaces inside it. Differential Revision: https://phab.mercurial-scm.org/D6741 diff -r 383fdfa6bba9 -r 268662aac075 contrib/import-checker.py --- a/contrib/import-checker.py Thu Aug 22 16:47:31 2019 -0700 +++ b/contrib/import-checker.py Sun Aug 18 00:45:33 2019 +0300 @@ -28,6 +28,7 @@ 'mercurial.hgweb.common', 'mercurial.hgweb.request', 'mercurial.i18n', + 'mercurial.interfaces', 'mercurial.node', # for revlog to re-export constant to extensions 'mercurial.revlogutils.constants', diff -r 383fdfa6bba9 -r 268662aac075 hgext/lfs/__init__.py --- a/hgext/lfs/__init__.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/lfs/__init__.py Sun Aug 18 00:45:33 2019 +0300 @@ -141,13 +141,16 @@ minifileset, node, pycompat, - repository, revlog, scmutil, templateutil, util, ) +from mercurial.interfaces import ( + repository, +) + from . import ( blobstore, wireprotolfsserver, diff -r 383fdfa6bba9 -r 268662aac075 hgext/lfs/wrapper.py --- a/hgext/lfs/wrapper.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/lfs/wrapper.py Sun Aug 18 00:45:33 2019 +0300 @@ -21,7 +21,6 @@ exchange, exthelper, localrepo, - repository, revlog, scmutil, upgrade, @@ -30,6 +29,10 @@ wireprotov1server, ) +from mercurial.interfaces import ( + repository, +) + from mercurial.utils import ( storageutil, stringutil, diff -r 383fdfa6bba9 -r 268662aac075 hgext/narrow/__init__.py --- a/hgext/narrow/__init__.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/narrow/__init__.py Sun Aug 18 00:45:33 2019 +0300 @@ -17,6 +17,9 @@ from mercurial import ( localrepo, registrar, +) + +from mercurial.interfaces import ( repository, ) diff -r 383fdfa6bba9 -r 268662aac075 hgext/narrow/narrowbundle2.py --- a/hgext/narrow/narrowbundle2.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/narrow/narrowbundle2.py Sun Aug 18 00:45:33 2019 +0300 @@ -23,10 +23,12 @@ localrepo, narrowspec, repair, - repository, util, wireprototypes, ) +from mercurial.interfaces import ( + repository, +) from mercurial.utils import ( stringutil, ) diff -r 383fdfa6bba9 -r 268662aac075 hgext/narrow/narrowcommands.py --- a/hgext/narrow/narrowcommands.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/narrow/narrowcommands.py Sun Aug 18 00:45:33 2019 +0300 @@ -25,12 +25,14 @@ pycompat, registrar, repair, - repository, repoview, sparse, util, wireprototypes, ) +from mercurial.interfaces import ( + repository, +) table = {} command = registrar.command(table) diff -r 383fdfa6bba9 -r 268662aac075 hgext/sqlitestore.py --- a/hgext/sqlitestore.py Thu Aug 22 16:47:31 2019 -0700 +++ b/hgext/sqlitestore.py Sun Aug 18 00:45:33 2019 +0300 @@ -70,10 +70,12 @@ mdiff, pycompat, registrar, - repository, util, verify, ) +from mercurial.interfaces import ( + repository, +) from mercurial.utils import ( interfaceutil, storageutil, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/changegroup.py --- a/mercurial/changegroup.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/changegroup.py Sun Aug 18 00:45:33 2019 +0300 @@ -25,8 +25,11 @@ mdiff, phases, pycompat, + util, +) + +from .interfaces import ( repository, - util, ) _CHANGEGROUPV1_DELTA_HEADER = struct.Struct("20s20s20s20s") diff -r 383fdfa6bba9 -r 268662aac075 mercurial/exchange.py --- a/mercurial/exchange.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/exchange.py Sun Aug 18 00:45:33 2019 +0300 @@ -34,7 +34,6 @@ phases, pushkey, pycompat, - repository, scmutil, sslutil, streamclone, @@ -42,6 +41,9 @@ util, wireprototypes, ) +from .interfaces import ( + repository, +) from .utils import ( stringutil, ) diff -r 383fdfa6bba9 -r 268662aac075 mercurial/exchangev2.py --- a/mercurial/exchangev2.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/exchangev2.py Sun Aug 18 00:45:33 2019 +0300 @@ -22,8 +22,10 @@ narrowspec, phases, pycompat, + setdiscovery, +) +from .interfaces import ( repository, - setdiscovery, ) def pull(pullop): diff -r 383fdfa6bba9 -r 268662aac075 mercurial/filelog.py --- a/mercurial/filelog.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/filelog.py Sun Aug 18 00:45:33 2019 +0300 @@ -14,8 +14,10 @@ ) from . import ( error, + revlog, +) +from .interfaces import ( repository, - revlog, ) from .utils import ( interfaceutil, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/hg.py --- a/mercurial/hg.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/hg.py Sun Aug 18 00:45:33 2019 +0300 @@ -39,7 +39,6 @@ node, phases, pycompat, - repository as repositorymod, scmutil, sshpeer, statichttprepo, @@ -51,6 +50,10 @@ vfs as vfsmod, ) +from .interfaces import ( + repository as repositorymod, +) + release = lock.release # shared features diff -r 383fdfa6bba9 -r 268662aac075 mercurial/httppeer.py --- a/mercurial/httppeer.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/httppeer.py Sun Aug 18 00:45:33 2019 +0300 @@ -16,12 +16,14 @@ import weakref from .i18n import _ +from .interfaces import ( + repository, +) from . import ( bundle2, error, httpconnection, pycompat, - repository, statichttprepo, url as urlmod, util, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/interfaces/__init__.py diff -r 383fdfa6bba9 -r 268662aac075 mercurial/interfaces/repository.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/interfaces/repository.py Sun Aug 18 00:45:33 2019 +0300 @@ -0,0 +1,1877 @@ +# repository.py - Interfaces and base classes for repositories and peers. +# +# Copyright 2017 Gregory Szorc +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +from ..i18n import _ +from .. import ( + error, +) +from ..utils import ( + interfaceutil, +) + +# When narrowing is finalized and no longer subject to format changes, +# we should move this to just "narrow" or similar. +NARROW_REQUIREMENT = 'narrowhg-experimental' + +# Local repository feature string. + +# Revlogs are being used for file storage. +REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage' +# The storage part of the repository is shared from an external source. +REPO_FEATURE_SHARED_STORAGE = b'sharedstore' +# LFS supported for backing file storage. +REPO_FEATURE_LFS = b'lfs' +# Repository supports being stream cloned. +REPO_FEATURE_STREAM_CLONE = b'streamclone' +# Files storage may lack data for all ancestors. +REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage' + +REVISION_FLAG_CENSORED = 1 << 15 +REVISION_FLAG_ELLIPSIS = 1 << 14 +REVISION_FLAG_EXTSTORED = 1 << 13 + +REVISION_FLAGS_KNOWN = ( + REVISION_FLAG_CENSORED | REVISION_FLAG_ELLIPSIS | REVISION_FLAG_EXTSTORED) + +CG_DELTAMODE_STD = b'default' +CG_DELTAMODE_PREV = b'previous' +CG_DELTAMODE_FULL = b'fulltext' +CG_DELTAMODE_P1 = b'p1' + +class ipeerconnection(interfaceutil.Interface): + """Represents a "connection" to a repository. + + This is the base interface for representing a connection to a repository. + It holds basic properties and methods applicable to all peer types. + + This is not a complete interface definition and should not be used + outside of this module. + """ + ui = interfaceutil.Attribute("""ui.ui instance""") + + def url(): + """Returns a URL string representing this peer. + + Currently, implementations expose the raw URL used to construct the + instance. It may contain credentials as part of the URL. The + expectations of the value aren't well-defined and this could lead to + data leakage. + + TODO audit/clean consumers and more clearly define the contents of this + value. + """ + + def local(): + """Returns a local repository instance. + + If the peer represents a local repository, returns an object that + can be used to interface with it. Otherwise returns ``None``. + """ + + def peer(): + """Returns an object conforming to this interface. + + Most implementations will ``return self``. + """ + + def canpush(): + """Returns a boolean indicating if this peer can be pushed to.""" + + def close(): + """Close the connection to this peer. + + This is called when the peer will no longer be used. Resources + associated with the peer should be cleaned up. + """ + +class ipeercapabilities(interfaceutil.Interface): + """Peer sub-interface related to capabilities.""" + + def capable(name): + """Determine support for a named capability. + + Returns ``False`` if capability not supported. + + Returns ``True`` if boolean capability is supported. Returns a string + if capability support is non-boolean. + + Capability strings may or may not map to wire protocol capabilities. + """ + + def requirecap(name, purpose): + """Require a capability to be present. + + Raises a ``CapabilityError`` if the capability isn't present. + """ + +class ipeercommands(interfaceutil.Interface): + """Client-side interface for communicating over the wire protocol. + + This interface is used as a gateway to the Mercurial wire protocol. + methods commonly call wire protocol commands of the same name. + """ + + def branchmap(): + """Obtain heads in named branches. + + Returns a dict mapping branch name to an iterable of nodes that are + heads on that branch. + """ + + def capabilities(): + """Obtain capabilities of the peer. + + Returns a set of string capabilities. + """ + + def clonebundles(): + """Obtains the clone bundles manifest for the repo. + + Returns the manifest as unparsed bytes. + """ + + def debugwireargs(one, two, three=None, four=None, five=None): + """Used to facilitate debugging of arguments passed over the wire.""" + + def getbundle(source, **kwargs): + """Obtain remote repository data as a bundle. + + This command is how the bulk of repository data is transferred from + the peer to the local repository + + Returns a generator of bundle data. + """ + + def heads(): + """Determine all known head revisions in the peer. + + Returns an iterable of binary nodes. + """ + + def known(nodes): + """Determine whether multiple nodes are known. + + Accepts an iterable of nodes whose presence to check for. + + Returns an iterable of booleans indicating of the corresponding node + at that index is known to the peer. + """ + + def listkeys(namespace): + """Obtain all keys in a pushkey namespace. + + Returns an iterable of key names. + """ + + def lookup(key): + """Resolve a value to a known revision. + + Returns a binary node of the resolved revision on success. + """ + + def pushkey(namespace, key, old, new): + """Set a value using the ``pushkey`` protocol. + + Arguments correspond to the pushkey namespace and key to operate on and + the old and new values for that key. + + Returns a string with the peer result. The value inside varies by the + namespace. + """ + + def stream_out(): + """Obtain streaming clone data. + + Successful result should be a generator of data chunks. + """ + + def unbundle(bundle, heads, url): + """Transfer repository data to the peer. + + This is how the bulk of data during a push is transferred. + + Returns the integer number of heads added to the peer. + """ + +class ipeerlegacycommands(interfaceutil.Interface): + """Interface for implementing support for legacy wire protocol commands. + + Wire protocol commands transition to legacy status when they are no longer + used by modern clients. To facilitate identifying which commands are + legacy, the interfaces are split. + """ + + def between(pairs): + """Obtain nodes between pairs of nodes. + + ``pairs`` is an iterable of node pairs. + + Returns an iterable of iterables of nodes corresponding to each + requested pair. + """ + + def branches(nodes): + """Obtain ancestor changesets of specific nodes back to a branch point. + + For each requested node, the peer finds the first ancestor node that is + a DAG root or is a merge. + + Returns an iterable of iterables with the resolved values for each node. + """ + + def changegroup(nodes, source): + """Obtain a changegroup with data for descendants of specified nodes.""" + + def changegroupsubset(bases, heads, source): + pass + +class ipeercommandexecutor(interfaceutil.Interface): + """Represents a mechanism to execute remote commands. + + This is the primary interface for requesting that wire protocol commands + be executed. Instances of this interface are active in a context manager + and have a well-defined lifetime. When the context manager exits, all + outstanding requests are waited on. + """ + + def callcommand(name, args): + """Request that a named command be executed. + + Receives the command name and a dictionary of command arguments. + + Returns a ``concurrent.futures.Future`` that will resolve to the + result of that command request. That exact value is left up to + the implementation and possibly varies by command. + + Not all commands can coexist with other commands in an executor + instance: it depends on the underlying wire protocol transport being + used and the command itself. + + Implementations MAY call ``sendcommands()`` automatically if the + requested command can not coexist with other commands in this executor. + + Implementations MAY call ``sendcommands()`` automatically when the + future's ``result()`` is called. So, consumers using multiple + commands with an executor MUST ensure that ``result()`` is not called + until all command requests have been issued. + """ + + def sendcommands(): + """Trigger submission of queued command requests. + + Not all transports submit commands as soon as they are requested to + run. When called, this method forces queued command requests to be + issued. It will no-op if all commands have already been sent. + + When called, no more new commands may be issued with this executor. + """ + + def close(): + """Signal that this command request is finished. + + When called, no more new commands may be issued. All outstanding + commands that have previously been issued are waited on before + returning. This not only includes waiting for the futures to resolve, + but also waiting for all response data to arrive. In other words, + calling this waits for all on-wire state for issued command requests + to finish. + + When used as a context manager, this method is called when exiting the + context manager. + + This method may call ``sendcommands()`` if there are buffered commands. + """ + +class ipeerrequests(interfaceutil.Interface): + """Interface for executing commands on a peer.""" + + limitedarguments = interfaceutil.Attribute( + """True if the peer cannot receive large argument value for commands.""" + ) + + def commandexecutor(): + """A context manager that resolves to an ipeercommandexecutor. + + The object this resolves to can be used to issue command requests + to the peer. + + Callers should call its ``callcommand`` method to issue command + requests. + + A new executor should be obtained for each distinct set of commands + (possibly just a single command) that the consumer wants to execute + as part of a single operation or round trip. This is because some + peers are half-duplex and/or don't support persistent connections. + e.g. in the case of HTTP peers, commands sent to an executor represent + a single HTTP request. While some peers may support multiple command + sends over the wire per executor, consumers need to code to the least + capable peer. So it should be assumed that command executors buffer + called commands until they are told to send them and that each + command executor could result in a new connection or wire-level request + being issued. + """ + +class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests): + """Unified interface for peer repositories. + + All peer instances must conform to this interface. + """ + +class ipeerv2(ipeerconnection, ipeercapabilities, ipeerrequests): + """Unified peer interface for wire protocol version 2 peers.""" + + apidescriptor = interfaceutil.Attribute( + """Data structure holding description of server API.""") + +@interfaceutil.implementer(ipeerbase) +class peer(object): + """Base class for peer repositories.""" + + limitedarguments = False + + def capable(self, name): + caps = self.capabilities() + if name in caps: + return True + + name = '%s=' % name + for cap in caps: + if cap.startswith(name): + return cap[len(name):] + + return False + + def requirecap(self, name, purpose): + if self.capable(name): + return + + raise error.CapabilityError( + _('cannot %s; remote repository does not support the ' + '\'%s\' capability') % (purpose, name)) + +class iverifyproblem(interfaceutil.Interface): + """Represents a problem with the integrity of the repository. + + Instances of this interface are emitted to describe an integrity issue + with a repository (e.g. corrupt storage, missing data, etc). + + Instances are essentially messages associated with severity. + """ + warning = interfaceutil.Attribute( + """Message indicating a non-fatal problem.""") + + error = interfaceutil.Attribute( + """Message indicating a fatal problem.""") + + node = interfaceutil.Attribute( + """Revision encountering the problem. + + ``None`` means the problem doesn't apply to a single revision. + """) + +class irevisiondelta(interfaceutil.Interface): + """Represents a delta between one revision and another. + + Instances convey enough information to allow a revision to be exchanged + with another repository. + + Instances represent the fulltext revision data or a delta against + another revision. Therefore the ``revision`` and ``delta`` attributes + are mutually exclusive. + + Typically used for changegroup generation. + """ + + node = interfaceutil.Attribute( + """20 byte node of this revision.""") + + p1node = interfaceutil.Attribute( + """20 byte node of 1st parent of this revision.""") + + p2node = interfaceutil.Attribute( + """20 byte node of 2nd parent of this revision.""") + + linknode = interfaceutil.Attribute( + """20 byte node of the changelog revision this node is linked to.""") + + flags = interfaceutil.Attribute( + """2 bytes of integer flags that apply to this revision. + + This is a bitwise composition of the ``REVISION_FLAG_*`` constants. + """) + + basenode = interfaceutil.Attribute( + """20 byte node of the revision this data is a delta against. + + ``nullid`` indicates that the revision is a full revision and not + a delta. + """) + + baserevisionsize = interfaceutil.Attribute( + """Size of base revision this delta is against. + + May be ``None`` if ``basenode`` is ``nullid``. + """) + + revision = interfaceutil.Attribute( + """Raw fulltext of revision data for this node.""") + + delta = interfaceutil.Attribute( + """Delta between ``basenode`` and ``node``. + + Stored in the bdiff delta format. + """) + +class ifilerevisionssequence(interfaceutil.Interface): + """Contains index data for all revisions of a file. + + Types implementing this behave like lists of tuples. The index + in the list corresponds to the revision number. The values contain + index metadata. + + The *null* revision (revision number -1) is always the last item + in the index. + """ + + def __len__(): + """The total number of revisions.""" + + def __getitem__(rev): + """Returns the object having a specific revision number. + + Returns an 8-tuple with the following fields: + + offset+flags + Contains the offset and flags for the revision. 64-bit unsigned + integer where first 6 bytes are the offset and the next 2 bytes + are flags. The offset can be 0 if it is not used by the store. + compressed size + Size of the revision data in the store. It can be 0 if it isn't + needed by the store. + uncompressed size + Fulltext size. It can be 0 if it isn't needed by the store. + base revision + Revision number of revision the delta for storage is encoded + against. -1 indicates not encoded against a base revision. + link revision + Revision number of changelog revision this entry is related to. + p1 revision + Revision number of 1st parent. -1 if no 1st parent. + p2 revision + Revision number of 2nd parent. -1 if no 1st parent. + node + Binary node value for this revision number. + + Negative values should index off the end of the sequence. ``-1`` + should return the null revision. ``-2`` should return the most + recent revision. + """ + + def __contains__(rev): + """Whether a revision number exists.""" + + def insert(self, i, entry): + """Add an item to the index at specific revision.""" + +class ifileindex(interfaceutil.Interface): + """Storage interface for index data of a single file. + + File storage data is divided into index metadata and data storage. + This interface defines the index portion of the interface. + + The index logically consists of: + + * A mapping between revision numbers and nodes. + * DAG data (storing and querying the relationship between nodes). + * Metadata to facilitate storage. + """ + def __len__(): + """Obtain the number of revisions stored for this file.""" + + def __iter__(): + """Iterate over revision numbers for this file.""" + + def hasnode(node): + """Returns a bool indicating if a node is known to this store. + + Implementations must only return True for full, binary node values: + hex nodes, revision numbers, and partial node matches must be + rejected. + + The null node is never present. + """ + + def revs(start=0, stop=None): + """Iterate over revision numbers for this file, with control.""" + + def parents(node): + """Returns a 2-tuple of parent nodes for a revision. + + Values will be ``nullid`` if the parent is empty. + """ + + def parentrevs(rev): + """Like parents() but operates on revision numbers.""" + + def rev(node): + """Obtain the revision number given a node. + + Raises ``error.LookupError`` if the node is not known. + """ + + def node(rev): + """Obtain the node value given a revision number. + + Raises ``IndexError`` if the node is not known. + """ + + def lookup(node): + """Attempt to resolve a value to a node. + + Value can be a binary node, hex node, revision number, or a string + that can be converted to an integer. + + Raises ``error.LookupError`` if a node could not be resolved. + """ + + def linkrev(rev): + """Obtain the changeset revision number a revision is linked to.""" + + def iscensored(rev): + """Return whether a revision's content has been censored.""" + + def commonancestorsheads(node1, node2): + """Obtain an iterable of nodes containing heads of common ancestors. + + See ``ancestor.commonancestorsheads()``. + """ + + def descendants(revs): + """Obtain descendant revision numbers for a set of revision numbers. + + If ``nullrev`` is in the set, this is equivalent to ``revs()``. + """ + + def heads(start=None, stop=None): + """Obtain a list of nodes that are DAG heads, with control. + + The set of revisions examined can be limited by specifying + ``start`` and ``stop``. ``start`` is a node. ``stop`` is an + iterable of nodes. DAG traversal starts at earlier revision + ``start`` and iterates forward until any node in ``stop`` is + encountered. + """ + + def children(node): + """Obtain nodes that are children of a node. + + Returns a list of nodes. + """ + +class ifiledata(interfaceutil.Interface): + """Storage interface for data storage of a specific file. + + This complements ``ifileindex`` and provides an interface for accessing + data for a tracked file. + """ + def size(rev): + """Obtain the fulltext size of file data. + + Any metadata is excluded from size measurements. + """ + + def revision(node, raw=False): + """"Obtain fulltext data for a node. + + By default, any storage transformations are applied before the data + is returned. If ``raw`` is True, non-raw storage transformations + are not applied. + + The fulltext data may contain a header containing metadata. Most + consumers should use ``read()`` to obtain the actual file data. + """ + + def rawdata(node): + """Obtain raw data for a node. + """ + + def read(node): + """Resolve file fulltext data. + + This is similar to ``revision()`` except any metadata in the data + headers is stripped. + """ + + def renamed(node): + """Obtain copy metadata for a node. + + Returns ``False`` if no copy metadata is stored or a 2-tuple of + (path, node) from which this revision was copied. + """ + + def cmp(node, fulltext): + """Compare fulltext to another revision. + + Returns True if the fulltext is different from what is stored. + + This takes copy metadata into account. + + TODO better document the copy metadata and censoring logic. + """ + + def emitrevisions(nodes, + nodesorder=None, + revisiondata=False, + assumehaveparentrevisions=False, + deltamode=CG_DELTAMODE_STD): + """Produce ``irevisiondelta`` for revisions. + + Given an iterable of nodes, emits objects conforming to the + ``irevisiondelta`` interface that describe revisions in storage. + + This method is a generator. + + The input nodes may be unordered. Implementations must ensure that a + node's parents are emitted before the node itself. Transitively, this + means that a node may only be emitted once all its ancestors in + ``nodes`` have also been emitted. + + By default, emits "index" data (the ``node``, ``p1node``, and + ``p2node`` attributes). If ``revisiondata`` is set, revision data + will also be present on the emitted objects. + + With default argument values, implementations can choose to emit + either fulltext revision data or a delta. When emitting deltas, + implementations must consider whether the delta's base revision + fulltext is available to the receiver. + + The base revision fulltext is guaranteed to be available if any of + the following are met: + + * Its fulltext revision was emitted by this method call. + * A delta for that revision was emitted by this method call. + * ``assumehaveparentrevisions`` is True and the base revision is a + parent of the node. + + ``nodesorder`` can be used to control the order that revisions are + emitted. By default, revisions can be reordered as long as they are + in DAG topological order (see above). If the value is ``nodes``, + the iteration order from ``nodes`` should be used. If the value is + ``storage``, then the native order from the backing storage layer + is used. (Not all storage layers will have strong ordering and behavior + of this mode is storage-dependent.) ``nodes`` ordering can force + revisions to be emitted before their ancestors, so consumers should + use it with care. + + The ``linknode`` attribute on the returned ``irevisiondelta`` may not + be set and it is the caller's responsibility to resolve it, if needed. + + If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested, + all revision data should be emitted as deltas against the revision + emitted just prior. The initial revision should be a delta against its + 1st parent. + """ + +class ifilemutation(interfaceutil.Interface): + """Storage interface for mutation events of a tracked file.""" + + def add(filedata, meta, transaction, linkrev, p1, p2): + """Add a new revision to the store. + + Takes file data, dictionary of metadata, a transaction, linkrev, + and parent nodes. + + Returns the node that was added. + + May no-op if a revision matching the supplied data is already stored. + """ + + def addrevision(revisiondata, transaction, linkrev, p1, p2, node=None, + flags=0, cachedelta=None): + """Add a new revision to the store. + + This is similar to ``add()`` except it operates at a lower level. + + The data passed in already contains a metadata header, if any. + + ``node`` and ``flags`` can be used to define the expected node and + the flags to use with storage. ``flags`` is a bitwise value composed + of the various ``REVISION_FLAG_*`` constants. + + ``add()`` is usually called when adding files from e.g. the working + directory. ``addrevision()`` is often called by ``add()`` and for + scenarios where revision data has already been computed, such as when + applying raw data from a peer repo. + """ + + def addgroup(deltas, linkmapper, transaction, addrevisioncb=None, + maybemissingparents=False): + """Process a series of deltas for storage. + + ``deltas`` is an iterable of 7-tuples of + (node, p1, p2, linknode, deltabase, delta, flags) defining revisions + to add. + + The ``delta`` field contains ``mpatch`` data to apply to a base + revision, identified by ``deltabase``. The base node can be + ``nullid``, in which case the header from the delta can be ignored + and the delta used as the fulltext. + + ``addrevisioncb`` should be called for each node as it is committed. + + ``maybemissingparents`` is a bool indicating whether the incoming + data may reference parents/ancestor revisions that aren't present. + This flag is set when receiving data into a "shallow" store that + doesn't hold all history. + + Returns a list of nodes that were processed. A node will be in the list + even if it existed in the store previously. + """ + + def censorrevision(tr, node, tombstone=b''): + """Remove the content of a single revision. + + The specified ``node`` will have its content purged from storage. + Future attempts to access the revision data for this node will + result in failure. + + A ``tombstone`` message can optionally be stored. This message may be + displayed to users when they attempt to access the missing revision + data. + + Storage backends may have stored deltas against the previous content + in this revision. As part of censoring a revision, these storage + backends are expected to rewrite any internally stored deltas such + that they no longer reference the deleted content. + """ + + def getstrippoint(minlink): + """Find the minimum revision that must be stripped to strip a linkrev. + + Returns a 2-tuple containing the minimum revision number and a set + of all revisions numbers that would be broken by this strip. + + TODO this is highly revlog centric and should be abstracted into + a higher-level deletion API. ``repair.strip()`` relies on this. + """ + + def strip(minlink, transaction): + """Remove storage of items starting at a linkrev. + + This uses ``getstrippoint()`` to determine the first node to remove. + Then it effectively truncates storage for all revisions after that. + + TODO this is highly revlog centric and should be abstracted into a + higher-level deletion API. + """ + +class ifilestorage(ifileindex, ifiledata, ifilemutation): + """Complete storage interface for a single tracked file.""" + + def files(): + """Obtain paths that are backing storage for this file. + + TODO this is used heavily by verify code and there should probably + be a better API for that. + """ + + def storageinfo(exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + """Obtain information about storage for this file's data. + + Returns a dict describing storage for this tracked path. The keys + in the dict map to arguments of the same. The arguments are bools + indicating whether to calculate and obtain that data. + + exclusivefiles + Iterable of (vfs, path) describing files that are exclusively + used to back storage for this tracked path. + + sharedfiles + Iterable of (vfs, path) describing files that are used to back + storage for this tracked path. Those files may also provide storage + for other stored entities. + + revisionscount + Number of revisions available for retrieval. + + trackedsize + Total size in bytes of all tracked revisions. This is a sum of the + length of the fulltext of all revisions. + + storedsize + Total size in bytes used to store data for all tracked revisions. + This is commonly less than ``trackedsize`` due to internal usage + of deltas rather than fulltext revisions. + + Not all storage backends may support all queries are have a reasonable + value to use. In that case, the value should be set to ``None`` and + callers are expected to handle this special value. + """ + + def verifyintegrity(state): + """Verifies the integrity of file storage. + + ``state`` is a dict holding state of the verifier process. It can be + used to communicate data between invocations of multiple storage + primitives. + + If individual revisions cannot have their revision content resolved, + the method is expected to set the ``skipread`` key to a set of nodes + that encountered problems. + + The method yields objects conforming to the ``iverifyproblem`` + interface. + """ + +class idirs(interfaceutil.Interface): + """Interface representing a collection of directories from paths. + + This interface is essentially a derived data structure representing + directories from a collection of paths. + """ + + def addpath(path): + """Add a path to the collection. + + All directories in the path will be added to the collection. + """ + + def delpath(path): + """Remove a path from the collection. + + If the removal was the last path in a particular directory, the + directory is removed from the collection. + """ + + def __iter__(): + """Iterate over the directories in this collection of paths.""" + + def __contains__(path): + """Whether a specific directory is in this collection.""" + +class imanifestdict(interfaceutil.Interface): + """Interface representing a manifest data structure. + + A manifest is effectively a dict mapping paths to entries. Each entry + consists of a binary node and extra flags affecting that entry. + """ + + def __getitem__(path): + """Returns the binary node value for a path in the manifest. + + Raises ``KeyError`` if the path does not exist in the manifest. + + Equivalent to ``self.find(path)[0]``. + """ + + def find(path): + """Returns the entry for a path in the manifest. + + Returns a 2-tuple of (node, flags). + + Raises ``KeyError`` if the path does not exist in the manifest. + """ + + def __len__(): + """Return the number of entries in the manifest.""" + + def __nonzero__(): + """Returns True if the manifest has entries, False otherwise.""" + + __bool__ = __nonzero__ + + def __setitem__(path, node): + """Define the node value for a path in the manifest. + + If the path is already in the manifest, its flags will be copied to + the new entry. + """ + + def __contains__(path): + """Whether a path exists in the manifest.""" + + def __delitem__(path): + """Remove a path from the manifest. + + Raises ``KeyError`` if the path is not in the manifest. + """ + + def __iter__(): + """Iterate over paths in the manifest.""" + + def iterkeys(): + """Iterate over paths in the manifest.""" + + def keys(): + """Obtain a list of paths in the manifest.""" + + def filesnotin(other, match=None): + """Obtain the set of paths in this manifest but not in another. + + ``match`` is an optional matcher function to be applied to both + manifests. + + Returns a set of paths. + """ + + def dirs(): + """Returns an object implementing the ``idirs`` interface.""" + + def hasdir(dir): + """Returns a bool indicating if a directory is in this manifest.""" + + def matches(match): + """Generate a new manifest filtered through a matcher. + + Returns an object conforming to the ``imanifestdict`` interface. + """ + + def walk(match): + """Generator of paths in manifest satisfying a matcher. + + This is equivalent to ``self.matches(match).iterkeys()`` except a new + manifest object is not created. + + If the matcher has explicit files listed and they don't exist in + the manifest, ``match.bad()`` is called for each missing file. + """ + + def diff(other, match=None, clean=False): + """Find differences between this manifest and another. + + This manifest is compared to ``other``. + + If ``match`` is provided, the two manifests are filtered against this + matcher and only entries satisfying the matcher are compared. + + If ``clean`` is True, unchanged files are included in the returned + object. + + Returns a dict with paths as keys and values of 2-tuples of 2-tuples of + the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)`` + represents the node and flags for this manifest and ``(node2, flag2)`` + are the same for the other manifest. + """ + + def setflag(path, flag): + """Set the flag value for a given path. + + Raises ``KeyError`` if the path is not already in the manifest. + """ + + def get(path, default=None): + """Obtain the node value for a path or a default value if missing.""" + + def flags(path, default=''): + """Return the flags value for a path or a default value if missing.""" + + def copy(): + """Return a copy of this manifest.""" + + def items(): + """Returns an iterable of (path, node) for items in this manifest.""" + + def iteritems(): + """Identical to items().""" + + def iterentries(): + """Returns an iterable of (path, node, flags) for this manifest. + + Similar to ``iteritems()`` except items are a 3-tuple and include + flags. + """ + + def text(): + """Obtain the raw data representation for this manifest. + + Result is used to create a manifest revision. + """ + + def fastdelta(base, changes): + """Obtain a delta between this manifest and another given changes. + + ``base`` in the raw data representation for another manifest. + + ``changes`` is an iterable of ``(path, to_delete)``. + + Returns a 2-tuple containing ``bytearray(self.text())`` and the + delta between ``base`` and this manifest. + """ + +class imanifestrevisionbase(interfaceutil.Interface): + """Base interface representing a single revision of a manifest. + + Should not be used as a primary interface: should always be inherited + as part of a larger interface. + """ + + def new(): + """Obtain a new manifest instance. + + Returns an object conforming to the ``imanifestrevisionwritable`` + interface. The instance will be associated with the same + ``imanifestlog`` collection as this instance. + """ + + def copy(): + """Obtain a copy of this manifest instance. + + Returns an object conforming to the ``imanifestrevisionwritable`` + interface. The instance will be associated with the same + ``imanifestlog`` collection as this instance. + """ + + def read(): + """Obtain the parsed manifest data structure. + + The returned object conforms to the ``imanifestdict`` interface. + """ + +class imanifestrevisionstored(imanifestrevisionbase): + """Interface representing a manifest revision committed to storage.""" + + def node(): + """The binary node for this manifest.""" + + parents = interfaceutil.Attribute( + """List of binary nodes that are parents for this manifest revision.""" + ) + + def readdelta(shallow=False): + """Obtain the manifest data structure representing changes from parent. + + This manifest is compared to its 1st parent. A new manifest representing + those differences is constructed. + + The returned object conforms to the ``imanifestdict`` interface. + """ + + def readfast(shallow=False): + """Calls either ``read()`` or ``readdelta()``. + + The faster of the two options is called. + """ + + def find(key): + """Calls self.read().find(key)``. + + Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``. + """ + +class imanifestrevisionwritable(imanifestrevisionbase): + """Interface representing a manifest revision that can be committed.""" + + def write(transaction, linkrev, p1node, p2node, added, removed, match=None): + """Add this revision to storage. + + Takes a transaction object, the changeset revision number it will + be associated with, its parent nodes, and lists of added and + removed paths. + + If match is provided, storage can choose not to inspect or write out + items that do not match. Storage is still required to be able to provide + the full manifest in the future for any directories written (these + manifests should not be "narrowed on disk"). + + Returns the binary node of the created revision. + """ + +class imanifeststorage(interfaceutil.Interface): + """Storage interface for manifest data.""" + + tree = interfaceutil.Attribute( + """The path to the directory this manifest tracks. + + The empty bytestring represents the root manifest. + """) + + index = interfaceutil.Attribute( + """An ``ifilerevisionssequence`` instance.""") + + indexfile = interfaceutil.Attribute( + """Path of revlog index file. + + TODO this is revlog specific and should not be exposed. + """) + + opener = interfaceutil.Attribute( + """VFS opener to use to access underlying files used for storage. + + TODO this is revlog specific and should not be exposed. + """) + + version = interfaceutil.Attribute( + """Revlog version number. + + TODO this is revlog specific and should not be exposed. + """) + + _generaldelta = interfaceutil.Attribute( + """Whether generaldelta storage is being used. + + TODO this is revlog specific and should not be exposed. + """) + + fulltextcache = interfaceutil.Attribute( + """Dict with cache of fulltexts. + + TODO this doesn't feel appropriate for the storage interface. + """) + + def __len__(): + """Obtain the number of revisions stored for this manifest.""" + + def __iter__(): + """Iterate over revision numbers for this manifest.""" + + def rev(node): + """Obtain the revision number given a binary node. + + Raises ``error.LookupError`` if the node is not known. + """ + + def node(rev): + """Obtain the node value given a revision number. + + Raises ``error.LookupError`` if the revision is not known. + """ + + def lookup(value): + """Attempt to resolve a value to a node. + + Value can be a binary node, hex node, revision number, or a bytes + that can be converted to an integer. + + Raises ``error.LookupError`` if a ndoe could not be resolved. + """ + + def parents(node): + """Returns a 2-tuple of parent nodes for a node. + + Values will be ``nullid`` if the parent is empty. + """ + + def parentrevs(rev): + """Like parents() but operates on revision numbers.""" + + def linkrev(rev): + """Obtain the changeset revision number a revision is linked to.""" + + def revision(node, _df=None, raw=False): + """Obtain fulltext data for a node.""" + + def rawdata(node, _df=None): + """Obtain raw data for a node.""" + + def revdiff(rev1, rev2): + """Obtain a delta between two revision numbers. + + The returned data is the result of ``bdiff.bdiff()`` on the raw + revision data. + """ + + def cmp(node, fulltext): + """Compare fulltext to another revision. + + Returns True if the fulltext is different from what is stored. + """ + + def emitrevisions(nodes, + nodesorder=None, + revisiondata=False, + assumehaveparentrevisions=False): + """Produce ``irevisiondelta`` describing revisions. + + See the documentation for ``ifiledata`` for more. + """ + + def addgroup(deltas, linkmapper, transaction, addrevisioncb=None): + """Process a series of deltas for storage. + + See the documentation in ``ifilemutation`` for more. + """ + + def rawsize(rev): + """Obtain the size of tracked data. + + Is equivalent to ``len(m.rawdata(node))``. + + TODO this method is only used by upgrade code and may be removed. + """ + + def getstrippoint(minlink): + """Find minimum revision that must be stripped to strip a linkrev. + + See the documentation in ``ifilemutation`` for more. + """ + + def strip(minlink, transaction): + """Remove storage of items starting at a linkrev. + + See the documentation in ``ifilemutation`` for more. + """ + + def checksize(): + """Obtain the expected sizes of backing files. + + TODO this is used by verify and it should not be part of the interface. + """ + + def files(): + """Obtain paths that are backing storage for this manifest. + + TODO this is used by verify and there should probably be a better API + for this functionality. + """ + + def deltaparent(rev): + """Obtain the revision that a revision is delta'd against. + + TODO delta encoding is an implementation detail of storage and should + not be exposed to the storage interface. + """ + + def clone(tr, dest, **kwargs): + """Clone this instance to another.""" + + def clearcaches(clear_persisted_data=False): + """Clear any caches associated with this instance.""" + + def dirlog(d): + """Obtain a manifest storage instance for a tree.""" + + def add(m, transaction, link, p1, p2, added, removed, readtree=None, + match=None): + """Add a revision to storage. + + ``m`` is an object conforming to ``imanifestdict``. + + ``link`` is the linkrev revision number. + + ``p1`` and ``p2`` are the parent revision numbers. + + ``added`` and ``removed`` are iterables of added and removed paths, + respectively. + + ``readtree`` is a function that can be used to read the child tree(s) + when recursively writing the full tree structure when using + treemanifets. + + ``match`` is a matcher that can be used to hint to storage that not all + paths must be inspected; this is an optimization and can be safely + ignored. Note that the storage must still be able to reproduce a full + manifest including files that did not match. + """ + + def storageinfo(exclusivefiles=False, sharedfiles=False, + revisionscount=False, trackedsize=False, + storedsize=False): + """Obtain information about storage for this manifest's data. + + See ``ifilestorage.storageinfo()`` for a description of this method. + This one behaves the same way, except for manifest data. + """ + +class imanifestlog(interfaceutil.Interface): + """Interface representing a collection of manifest snapshots. + + Represents the root manifest in a repository. + + Also serves as a means to access nested tree manifests and to cache + tree manifests. + """ + + def __getitem__(node): + """Obtain a manifest instance for a given binary node. + + Equivalent to calling ``self.get('', node)``. + + The returned object conforms to the ``imanifestrevisionstored`` + interface. + """ + + def get(tree, node, verify=True): + """Retrieve the manifest instance for a given directory and binary node. + + ``node`` always refers to the node of the root manifest (which will be + the only manifest if flat manifests are being used). + + If ``tree`` is the empty string, the root manifest is returned. + Otherwise the manifest for the specified directory will be returned + (requires tree manifests). + + If ``verify`` is True, ``LookupError`` is raised if the node is not + known. + + The returned object conforms to the ``imanifestrevisionstored`` + interface. + """ + + def getstorage(tree): + """Retrieve an interface to storage for a particular tree. + + If ``tree`` is the empty bytestring, storage for the root manifest will + be returned. Otherwise storage for a tree manifest is returned. + + TODO formalize interface for returned object. + """ + + def clearcaches(): + """Clear caches associated with this collection.""" + + def rev(node): + """Obtain the revision number for a binary node. + + Raises ``error.LookupError`` if the node is not known. + """ + +class ilocalrepositoryfilestorage(interfaceutil.Interface): + """Local repository sub-interface providing access to tracked file storage. + + This interface defines how a repository accesses storage for a single + tracked file path. + """ + + def file(f): + """Obtain a filelog for a tracked path. + + The returned type conforms to the ``ifilestorage`` interface. + """ + +class ilocalrepositorymain(interfaceutil.Interface): + """Main interface for local repositories. + + This currently captures the reality of things - not how things should be. + """ + + supportedformats = interfaceutil.Attribute( + """Set of requirements that apply to stream clone. + + This is actually a class attribute and is shared among all instances. + """) + + supported = interfaceutil.Attribute( + """Set of requirements that this repo is capable of opening.""") + + requirements = interfaceutil.Attribute( + """Set of requirements this repo uses.""") + + features = interfaceutil.Attribute( + """Set of "features" this repository supports. + + A "feature" is a loosely-defined term. It can refer to a feature + in the classical sense or can describe an implementation detail + of the repository. For example, a ``readonly`` feature may denote + the repository as read-only. Or a ``revlogfilestore`` feature may + denote that the repository is using revlogs for file storage. + + The intent of features is to provide a machine-queryable mechanism + for repo consumers to test for various repository characteristics. + + Features are similar to ``requirements``. The main difference is that + requirements are stored on-disk and represent requirements to open the + repository. Features are more run-time capabilities of the repository + and more granular capabilities (which may be derived from requirements). + """) + + filtername = interfaceutil.Attribute( + """Name of the repoview that is active on this repo.""") + + wvfs = interfaceutil.Attribute( + """VFS used to access the working directory.""") + + vfs = interfaceutil.Attribute( + """VFS rooted at the .hg directory. + + Used to access repository data not in the store. + """) + + svfs = interfaceutil.Attribute( + """VFS rooted at the store. + + Used to access repository data in the store. Typically .hg/store. + But can point elsewhere if the store is shared. + """) + + root = interfaceutil.Attribute( + """Path to the root of the working directory.""") + + path = interfaceutil.Attribute( + """Path to the .hg directory.""") + + origroot = interfaceutil.Attribute( + """The filesystem path that was used to construct the repo.""") + + auditor = interfaceutil.Attribute( + """A pathauditor for the working directory. + + This checks if a path refers to a nested repository. + + Operates on the filesystem. + """) + + nofsauditor = interfaceutil.Attribute( + """A pathauditor for the working directory. + + This is like ``auditor`` except it doesn't do filesystem checks. + """) + + baseui = interfaceutil.Attribute( + """Original ui instance passed into constructor.""") + + ui = interfaceutil.Attribute( + """Main ui instance for this instance.""") + + sharedpath = interfaceutil.Attribute( + """Path to the .hg directory of the repo this repo was shared from.""") + + store = interfaceutil.Attribute( + """A store instance.""") + + spath = interfaceutil.Attribute( + """Path to the store.""") + + sjoin = interfaceutil.Attribute( + """Alias to self.store.join.""") + + cachevfs = interfaceutil.Attribute( + """A VFS used to access the cache directory. + + Typically .hg/cache. + """) + + wcachevfs = interfaceutil.Attribute( + """A VFS used to access the cache directory dedicated to working copy + + Typically .hg/wcache. + """) + + filteredrevcache = interfaceutil.Attribute( + """Holds sets of revisions to be filtered.""") + + names = interfaceutil.Attribute( + """A ``namespaces`` instance.""") + + def close(): + """Close the handle on this repository.""" + + def peer(): + """Obtain an object conforming to the ``peer`` interface.""" + + def unfiltered(): + """Obtain an unfiltered/raw view of this repo.""" + + def filtered(name, visibilityexceptions=None): + """Obtain a named view of this repository.""" + + obsstore = interfaceutil.Attribute( + """A store of obsolescence data.""") + + changelog = interfaceutil.Attribute( + """A handle on the changelog revlog.""") + + manifestlog = interfaceutil.Attribute( + """An instance conforming to the ``imanifestlog`` interface. + + Provides access to manifests for the repository. + """) + + dirstate = interfaceutil.Attribute( + """Working directory state.""") + + narrowpats = interfaceutil.Attribute( + """Matcher patterns for this repository's narrowspec.""") + + def narrowmatch(match=None, includeexact=False): + """Obtain a matcher for the narrowspec.""" + + def setnarrowpats(newincludes, newexcludes): + """Define the narrowspec for this repository.""" + + def __getitem__(changeid): + """Try to resolve a changectx.""" + + def __contains__(changeid): + """Whether a changeset exists.""" + + def __nonzero__(): + """Always returns True.""" + return True + + __bool__ = __nonzero__ + + def __len__(): + """Returns the number of changesets in the repo.""" + + def __iter__(): + """Iterate over revisions in the changelog.""" + + def revs(expr, *args): + """Evaluate a revset. + + Emits revisions. + """ + + def set(expr, *args): + """Evaluate a revset. + + Emits changectx instances. + """ + + def anyrevs(specs, user=False, localalias=None): + """Find revisions matching one of the given revsets.""" + + def url(): + """Returns a string representing the location of this repo.""" + + def hook(name, throw=False, **args): + """Call a hook.""" + + def tags(): + """Return a mapping of tag to node.""" + + def tagtype(tagname): + """Return the type of a given tag.""" + + def tagslist(): + """Return a list of tags ordered by revision.""" + + def nodetags(node): + """Return the tags associated with a node.""" + + def nodebookmarks(node): + """Return the list of bookmarks pointing to the specified node.""" + + def branchmap(): + """Return a mapping of branch to heads in that branch.""" + + def revbranchcache(): + pass + + def branchtip(branchtip, ignoremissing=False): + """Return the tip node for a given branch.""" + + def lookup(key): + """Resolve the node for a revision.""" + + def lookupbranch(key): + """Look up the branch name of the given revision or branch name.""" + + def known(nodes): + """Determine whether a series of nodes is known. + + Returns a list of bools. + """ + + def local(): + """Whether the repository is local.""" + return True + + def publishing(): + """Whether the repository is a publishing repository.""" + + def cancopy(): + pass + + def shared(): + """The type of shared repository or None.""" + + def wjoin(f, *insidef): + """Calls self.vfs.reljoin(self.root, f, *insidef)""" + + def setparents(p1, p2): + """Set the parent nodes of the working directory.""" + + def filectx(path, changeid=None, fileid=None): + """Obtain a filectx for the given file revision.""" + + def getcwd(): + """Obtain the current working directory from the dirstate.""" + + def pathto(f, cwd=None): + """Obtain the relative path to a file.""" + + def adddatafilter(name, fltr): + pass + + def wread(filename): + """Read a file from wvfs, using data filters.""" + + def wwrite(filename, data, flags, backgroundclose=False, **kwargs): + """Write data to a file in the wvfs, using data filters.""" + + def wwritedata(filename, data): + """Resolve data for writing to the wvfs, using data filters.""" + + def currenttransaction(): + """Obtain the current transaction instance or None.""" + + def transaction(desc, report=None): + """Open a new transaction to write to the repository.""" + + def undofiles(): + """Returns a list of (vfs, path) for files to undo transactions.""" + + def recover(): + """Roll back an interrupted transaction.""" + + def rollback(dryrun=False, force=False): + """Undo the last transaction. + + DANGEROUS. + """ + + def updatecaches(tr=None, full=False): + """Warm repo caches.""" + + def invalidatecaches(): + """Invalidate cached data due to the repository mutating.""" + + def invalidatevolatilesets(): + pass + + def invalidatedirstate(): + """Invalidate the dirstate.""" + + def invalidate(clearfilecache=False): + pass + + def invalidateall(): + pass + + def lock(wait=True): + """Lock the repository store and return a lock instance.""" + + def wlock(wait=True): + """Lock the non-store parts of the repository.""" + + def currentwlock(): + """Return the wlock if it's held or None.""" + + def checkcommitpatterns(wctx, vdirs, match, status, fail): + pass + + def commit(text='', user=None, date=None, match=None, force=False, + editor=False, extra=None): + """Add a new revision to the repository.""" + + def commitctx(ctx, error=False, origctx=None): + """Commit a commitctx instance to the repository.""" + + def destroying(): + """Inform the repository that nodes are about to be destroyed.""" + + def destroyed(): + """Inform the repository that nodes have been destroyed.""" + + def status(node1='.', node2=None, match=None, ignored=False, + clean=False, unknown=False, listsubrepos=False): + """Convenience method to call repo[x].status().""" + + def addpostdsstatus(ps): + pass + + def postdsstatus(): + pass + + def clearpostdsstatus(): + pass + + def heads(start=None): + """Obtain list of nodes that are DAG heads.""" + + def branchheads(branch=None, start=None, closed=False): + pass + + def branches(nodes): + pass + + def between(pairs): + pass + + def checkpush(pushop): + pass + + prepushoutgoinghooks = interfaceutil.Attribute( + """util.hooks instance.""") + + def pushkey(namespace, key, old, new): + pass + + def listkeys(namespace): + pass + + def debugwireargs(one, two, three=None, four=None, five=None): + pass + + def savecommitmessage(text): + pass + +class completelocalrepository(ilocalrepositorymain, + ilocalrepositoryfilestorage): + """Complete interface for a local repository.""" + +class iwireprotocolcommandcacher(interfaceutil.Interface): + """Represents a caching backend for wire protocol commands. + + Wire protocol version 2 supports transparent caching of many commands. + To leverage this caching, servers can activate objects that cache + command responses. Objects handle both cache writing and reading. + This interface defines how that response caching mechanism works. + + Wire protocol version 2 commands emit a series of objects that are + serialized and sent to the client. The caching layer exists between + the invocation of the command function and the sending of its output + objects to an output layer. + + Instances of this interface represent a binding to a cache that + can serve a response (in place of calling a command function) and/or + write responses to a cache for subsequent use. + + When a command request arrives, the following happens with regards + to this interface: + + 1. The server determines whether the command request is cacheable. + 2. If it is, an instance of this interface is spawned. + 3. The cacher is activated in a context manager (``__enter__`` is called). + 4. A cache *key* for that request is derived. This will call the + instance's ``adjustcachekeystate()`` method so the derivation + can be influenced. + 5. The cacher is informed of the derived cache key via a call to + ``setcachekey()``. + 6. The cacher's ``lookup()`` method is called to test for presence of + the derived key in the cache. + 7. If ``lookup()`` returns a hit, that cached result is used in place + of invoking the command function. ``__exit__`` is called and the instance + is discarded. + 8. The command function is invoked. + 9. ``onobject()`` is called for each object emitted by the command + function. + 10. After the final object is seen, ``onfinished()`` is called. + 11. ``__exit__`` is called to signal the end of use of the instance. + + Cache *key* derivation can be influenced by the instance. + + Cache keys are initially derived by a deterministic representation of + the command request. This includes the command name, arguments, protocol + version, etc. This initial key derivation is performed by CBOR-encoding a + data structure and feeding that output into a hasher. + + Instances of this interface can influence this initial key derivation + via ``adjustcachekeystate()``. + + The instance is informed of the derived cache key via a call to + ``setcachekey()``. The instance must store the key locally so it can + be consulted on subsequent operations that may require it. + + When constructed, the instance has access to a callable that can be used + for encoding response objects. This callable receives as its single + argument an object emitted by a command function. It returns an iterable + of bytes chunks representing the encoded object. Unless the cacher is + caching native Python objects in memory or has a way of reconstructing + the original Python objects, implementations typically call this function + to produce bytes from the output objects and then store those bytes in + the cache. When it comes time to re-emit those bytes, they are wrapped + in a ``wireprototypes.encodedresponse`` instance to tell the output + layer that they are pre-encoded. + + When receiving the objects emitted by the command function, instances + can choose what to do with those objects. The simplest thing to do is + re-emit the original objects. They will be forwarded to the output + layer and will be processed as if the cacher did not exist. + + Implementations could also choose to not emit objects - instead locally + buffering objects or their encoded representation. They could then emit + a single "coalesced" object when ``onfinished()`` is called. In + this way, the implementation would function as a filtering layer of + sorts. + + When caching objects, typically the encoded form of the object will + be stored. Keep in mind that if the original object is forwarded to + the output layer, it will need to be encoded there as well. For large + output, this redundant encoding could add overhead. Implementations + could wrap the encoded object data in ``wireprototypes.encodedresponse`` + instances to avoid this overhead. + """ + def __enter__(): + """Marks the instance as active. + + Should return self. + """ + + def __exit__(exctype, excvalue, exctb): + """Called when cacher is no longer used. + + This can be used by implementations to perform cleanup actions (e.g. + disconnecting network sockets, aborting a partially cached response. + """ + + def adjustcachekeystate(state): + """Influences cache key derivation by adjusting state to derive key. + + A dict defining the state used to derive the cache key is passed. + + Implementations can modify this dict to record additional state that + is wanted to influence key derivation. + + Implementations are *highly* encouraged to not modify or delete + existing keys. + """ + + def setcachekey(key): + """Record the derived cache key for this request. + + Instances may mutate the key for internal usage, as desired. e.g. + instances may wish to prepend the repo name, introduce path + components for filesystem or URL addressing, etc. Behavior is up to + the cache. + + Returns a bool indicating if the request is cacheable by this + instance. + """ + + def lookup(): + """Attempt to resolve an entry in the cache. + + The instance is instructed to look for the cache key that it was + informed about via the call to ``setcachekey()``. + + If there's no cache hit or the cacher doesn't wish to use the cached + entry, ``None`` should be returned. + + Else, a dict defining the cached result should be returned. The + dict may have the following keys: + + objs + An iterable of objects that should be sent to the client. That + iterable of objects is expected to be what the command function + would return if invoked or an equivalent representation thereof. + """ + + def onobject(obj): + """Called when a new object is emitted from the command function. + + Receives as its argument the object that was emitted from the + command function. + + This method returns an iterator of objects to forward to the output + layer. The easiest implementation is a generator that just + ``yield obj``. + """ + + def onfinished(): + """Called after all objects have been emitted from the command function. + + Implementations should return an iterator of objects to forward to + the output layer. + + This method can be a generator. + """ diff -r 383fdfa6bba9 -r 268662aac075 mercurial/localrepo.py --- a/mercurial/localrepo.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/localrepo.py Sun Aug 18 00:45:33 2019 +0300 @@ -52,7 +52,6 @@ phases, pushkey, pycompat, - repository, repoview, revset, revsetlang, @@ -66,6 +65,11 @@ util, vfs as vfsmod, ) + +from .interfaces import ( + repository, +) + from .utils import ( interfaceutil, procutil, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/manifest.py --- a/mercurial/manifest.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/manifest.py Sun Aug 18 00:45:33 2019 +0300 @@ -24,10 +24,12 @@ mdiff, policy, pycompat, - repository, revlog, util, ) +from .interfaces import ( + repository, +) from .utils import ( interfaceutil, ) diff -r 383fdfa6bba9 -r 268662aac075 mercurial/narrowspec.py --- a/mercurial/narrowspec.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/narrowspec.py Sun Aug 18 00:45:33 2019 +0300 @@ -8,11 +8,13 @@ from __future__ import absolute_import from .i18n import _ +from .interfaces import ( + repository, +) from . import ( error, match as matchmod, merge, - repository, scmutil, sparse, util, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/repository.py --- a/mercurial/repository.py Thu Aug 22 16:47:31 2019 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1877 +0,0 @@ -# repository.py - Interfaces and base classes for repositories and peers. -# -# Copyright 2017 Gregory Szorc -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -from __future__ import absolute_import - -from .i18n import _ -from . import ( - error, -) -from .utils import ( - interfaceutil, -) - -# When narrowing is finalized and no longer subject to format changes, -# we should move this to just "narrow" or similar. -NARROW_REQUIREMENT = 'narrowhg-experimental' - -# Local repository feature string. - -# Revlogs are being used for file storage. -REPO_FEATURE_REVLOG_FILE_STORAGE = b'revlogfilestorage' -# The storage part of the repository is shared from an external source. -REPO_FEATURE_SHARED_STORAGE = b'sharedstore' -# LFS supported for backing file storage. -REPO_FEATURE_LFS = b'lfs' -# Repository supports being stream cloned. -REPO_FEATURE_STREAM_CLONE = b'streamclone' -# Files storage may lack data for all ancestors. -REPO_FEATURE_SHALLOW_FILE_STORAGE = b'shallowfilestorage' - -REVISION_FLAG_CENSORED = 1 << 15 -REVISION_FLAG_ELLIPSIS = 1 << 14 -REVISION_FLAG_EXTSTORED = 1 << 13 - -REVISION_FLAGS_KNOWN = ( - REVISION_FLAG_CENSORED | REVISION_FLAG_ELLIPSIS | REVISION_FLAG_EXTSTORED) - -CG_DELTAMODE_STD = b'default' -CG_DELTAMODE_PREV = b'previous' -CG_DELTAMODE_FULL = b'fulltext' -CG_DELTAMODE_P1 = b'p1' - -class ipeerconnection(interfaceutil.Interface): - """Represents a "connection" to a repository. - - This is the base interface for representing a connection to a repository. - It holds basic properties and methods applicable to all peer types. - - This is not a complete interface definition and should not be used - outside of this module. - """ - ui = interfaceutil.Attribute("""ui.ui instance""") - - def url(): - """Returns a URL string representing this peer. - - Currently, implementations expose the raw URL used to construct the - instance. It may contain credentials as part of the URL. The - expectations of the value aren't well-defined and this could lead to - data leakage. - - TODO audit/clean consumers and more clearly define the contents of this - value. - """ - - def local(): - """Returns a local repository instance. - - If the peer represents a local repository, returns an object that - can be used to interface with it. Otherwise returns ``None``. - """ - - def peer(): - """Returns an object conforming to this interface. - - Most implementations will ``return self``. - """ - - def canpush(): - """Returns a boolean indicating if this peer can be pushed to.""" - - def close(): - """Close the connection to this peer. - - This is called when the peer will no longer be used. Resources - associated with the peer should be cleaned up. - """ - -class ipeercapabilities(interfaceutil.Interface): - """Peer sub-interface related to capabilities.""" - - def capable(name): - """Determine support for a named capability. - - Returns ``False`` if capability not supported. - - Returns ``True`` if boolean capability is supported. Returns a string - if capability support is non-boolean. - - Capability strings may or may not map to wire protocol capabilities. - """ - - def requirecap(name, purpose): - """Require a capability to be present. - - Raises a ``CapabilityError`` if the capability isn't present. - """ - -class ipeercommands(interfaceutil.Interface): - """Client-side interface for communicating over the wire protocol. - - This interface is used as a gateway to the Mercurial wire protocol. - methods commonly call wire protocol commands of the same name. - """ - - def branchmap(): - """Obtain heads in named branches. - - Returns a dict mapping branch name to an iterable of nodes that are - heads on that branch. - """ - - def capabilities(): - """Obtain capabilities of the peer. - - Returns a set of string capabilities. - """ - - def clonebundles(): - """Obtains the clone bundles manifest for the repo. - - Returns the manifest as unparsed bytes. - """ - - def debugwireargs(one, two, three=None, four=None, five=None): - """Used to facilitate debugging of arguments passed over the wire.""" - - def getbundle(source, **kwargs): - """Obtain remote repository data as a bundle. - - This command is how the bulk of repository data is transferred from - the peer to the local repository - - Returns a generator of bundle data. - """ - - def heads(): - """Determine all known head revisions in the peer. - - Returns an iterable of binary nodes. - """ - - def known(nodes): - """Determine whether multiple nodes are known. - - Accepts an iterable of nodes whose presence to check for. - - Returns an iterable of booleans indicating of the corresponding node - at that index is known to the peer. - """ - - def listkeys(namespace): - """Obtain all keys in a pushkey namespace. - - Returns an iterable of key names. - """ - - def lookup(key): - """Resolve a value to a known revision. - - Returns a binary node of the resolved revision on success. - """ - - def pushkey(namespace, key, old, new): - """Set a value using the ``pushkey`` protocol. - - Arguments correspond to the pushkey namespace and key to operate on and - the old and new values for that key. - - Returns a string with the peer result. The value inside varies by the - namespace. - """ - - def stream_out(): - """Obtain streaming clone data. - - Successful result should be a generator of data chunks. - """ - - def unbundle(bundle, heads, url): - """Transfer repository data to the peer. - - This is how the bulk of data during a push is transferred. - - Returns the integer number of heads added to the peer. - """ - -class ipeerlegacycommands(interfaceutil.Interface): - """Interface for implementing support for legacy wire protocol commands. - - Wire protocol commands transition to legacy status when they are no longer - used by modern clients. To facilitate identifying which commands are - legacy, the interfaces are split. - """ - - def between(pairs): - """Obtain nodes between pairs of nodes. - - ``pairs`` is an iterable of node pairs. - - Returns an iterable of iterables of nodes corresponding to each - requested pair. - """ - - def branches(nodes): - """Obtain ancestor changesets of specific nodes back to a branch point. - - For each requested node, the peer finds the first ancestor node that is - a DAG root or is a merge. - - Returns an iterable of iterables with the resolved values for each node. - """ - - def changegroup(nodes, source): - """Obtain a changegroup with data for descendants of specified nodes.""" - - def changegroupsubset(bases, heads, source): - pass - -class ipeercommandexecutor(interfaceutil.Interface): - """Represents a mechanism to execute remote commands. - - This is the primary interface for requesting that wire protocol commands - be executed. Instances of this interface are active in a context manager - and have a well-defined lifetime. When the context manager exits, all - outstanding requests are waited on. - """ - - def callcommand(name, args): - """Request that a named command be executed. - - Receives the command name and a dictionary of command arguments. - - Returns a ``concurrent.futures.Future`` that will resolve to the - result of that command request. That exact value is left up to - the implementation and possibly varies by command. - - Not all commands can coexist with other commands in an executor - instance: it depends on the underlying wire protocol transport being - used and the command itself. - - Implementations MAY call ``sendcommands()`` automatically if the - requested command can not coexist with other commands in this executor. - - Implementations MAY call ``sendcommands()`` automatically when the - future's ``result()`` is called. So, consumers using multiple - commands with an executor MUST ensure that ``result()`` is not called - until all command requests have been issued. - """ - - def sendcommands(): - """Trigger submission of queued command requests. - - Not all transports submit commands as soon as they are requested to - run. When called, this method forces queued command requests to be - issued. It will no-op if all commands have already been sent. - - When called, no more new commands may be issued with this executor. - """ - - def close(): - """Signal that this command request is finished. - - When called, no more new commands may be issued. All outstanding - commands that have previously been issued are waited on before - returning. This not only includes waiting for the futures to resolve, - but also waiting for all response data to arrive. In other words, - calling this waits for all on-wire state for issued command requests - to finish. - - When used as a context manager, this method is called when exiting the - context manager. - - This method may call ``sendcommands()`` if there are buffered commands. - """ - -class ipeerrequests(interfaceutil.Interface): - """Interface for executing commands on a peer.""" - - limitedarguments = interfaceutil.Attribute( - """True if the peer cannot receive large argument value for commands.""" - ) - - def commandexecutor(): - """A context manager that resolves to an ipeercommandexecutor. - - The object this resolves to can be used to issue command requests - to the peer. - - Callers should call its ``callcommand`` method to issue command - requests. - - A new executor should be obtained for each distinct set of commands - (possibly just a single command) that the consumer wants to execute - as part of a single operation or round trip. This is because some - peers are half-duplex and/or don't support persistent connections. - e.g. in the case of HTTP peers, commands sent to an executor represent - a single HTTP request. While some peers may support multiple command - sends over the wire per executor, consumers need to code to the least - capable peer. So it should be assumed that command executors buffer - called commands until they are told to send them and that each - command executor could result in a new connection or wire-level request - being issued. - """ - -class ipeerbase(ipeerconnection, ipeercapabilities, ipeerrequests): - """Unified interface for peer repositories. - - All peer instances must conform to this interface. - """ - -class ipeerv2(ipeerconnection, ipeercapabilities, ipeerrequests): - """Unified peer interface for wire protocol version 2 peers.""" - - apidescriptor = interfaceutil.Attribute( - """Data structure holding description of server API.""") - -@interfaceutil.implementer(ipeerbase) -class peer(object): - """Base class for peer repositories.""" - - limitedarguments = False - - def capable(self, name): - caps = self.capabilities() - if name in caps: - return True - - name = '%s=' % name - for cap in caps: - if cap.startswith(name): - return cap[len(name):] - - return False - - def requirecap(self, name, purpose): - if self.capable(name): - return - - raise error.CapabilityError( - _('cannot %s; remote repository does not support the ' - '\'%s\' capability') % (purpose, name)) - -class iverifyproblem(interfaceutil.Interface): - """Represents a problem with the integrity of the repository. - - Instances of this interface are emitted to describe an integrity issue - with a repository (e.g. corrupt storage, missing data, etc). - - Instances are essentially messages associated with severity. - """ - warning = interfaceutil.Attribute( - """Message indicating a non-fatal problem.""") - - error = interfaceutil.Attribute( - """Message indicating a fatal problem.""") - - node = interfaceutil.Attribute( - """Revision encountering the problem. - - ``None`` means the problem doesn't apply to a single revision. - """) - -class irevisiondelta(interfaceutil.Interface): - """Represents a delta between one revision and another. - - Instances convey enough information to allow a revision to be exchanged - with another repository. - - Instances represent the fulltext revision data or a delta against - another revision. Therefore the ``revision`` and ``delta`` attributes - are mutually exclusive. - - Typically used for changegroup generation. - """ - - node = interfaceutil.Attribute( - """20 byte node of this revision.""") - - p1node = interfaceutil.Attribute( - """20 byte node of 1st parent of this revision.""") - - p2node = interfaceutil.Attribute( - """20 byte node of 2nd parent of this revision.""") - - linknode = interfaceutil.Attribute( - """20 byte node of the changelog revision this node is linked to.""") - - flags = interfaceutil.Attribute( - """2 bytes of integer flags that apply to this revision. - - This is a bitwise composition of the ``REVISION_FLAG_*`` constants. - """) - - basenode = interfaceutil.Attribute( - """20 byte node of the revision this data is a delta against. - - ``nullid`` indicates that the revision is a full revision and not - a delta. - """) - - baserevisionsize = interfaceutil.Attribute( - """Size of base revision this delta is against. - - May be ``None`` if ``basenode`` is ``nullid``. - """) - - revision = interfaceutil.Attribute( - """Raw fulltext of revision data for this node.""") - - delta = interfaceutil.Attribute( - """Delta between ``basenode`` and ``node``. - - Stored in the bdiff delta format. - """) - -class ifilerevisionssequence(interfaceutil.Interface): - """Contains index data for all revisions of a file. - - Types implementing this behave like lists of tuples. The index - in the list corresponds to the revision number. The values contain - index metadata. - - The *null* revision (revision number -1) is always the last item - in the index. - """ - - def __len__(): - """The total number of revisions.""" - - def __getitem__(rev): - """Returns the object having a specific revision number. - - Returns an 8-tuple with the following fields: - - offset+flags - Contains the offset and flags for the revision. 64-bit unsigned - integer where first 6 bytes are the offset and the next 2 bytes - are flags. The offset can be 0 if it is not used by the store. - compressed size - Size of the revision data in the store. It can be 0 if it isn't - needed by the store. - uncompressed size - Fulltext size. It can be 0 if it isn't needed by the store. - base revision - Revision number of revision the delta for storage is encoded - against. -1 indicates not encoded against a base revision. - link revision - Revision number of changelog revision this entry is related to. - p1 revision - Revision number of 1st parent. -1 if no 1st parent. - p2 revision - Revision number of 2nd parent. -1 if no 1st parent. - node - Binary node value for this revision number. - - Negative values should index off the end of the sequence. ``-1`` - should return the null revision. ``-2`` should return the most - recent revision. - """ - - def __contains__(rev): - """Whether a revision number exists.""" - - def insert(self, i, entry): - """Add an item to the index at specific revision.""" - -class ifileindex(interfaceutil.Interface): - """Storage interface for index data of a single file. - - File storage data is divided into index metadata and data storage. - This interface defines the index portion of the interface. - - The index logically consists of: - - * A mapping between revision numbers and nodes. - * DAG data (storing and querying the relationship between nodes). - * Metadata to facilitate storage. - """ - def __len__(): - """Obtain the number of revisions stored for this file.""" - - def __iter__(): - """Iterate over revision numbers for this file.""" - - def hasnode(node): - """Returns a bool indicating if a node is known to this store. - - Implementations must only return True for full, binary node values: - hex nodes, revision numbers, and partial node matches must be - rejected. - - The null node is never present. - """ - - def revs(start=0, stop=None): - """Iterate over revision numbers for this file, with control.""" - - def parents(node): - """Returns a 2-tuple of parent nodes for a revision. - - Values will be ``nullid`` if the parent is empty. - """ - - def parentrevs(rev): - """Like parents() but operates on revision numbers.""" - - def rev(node): - """Obtain the revision number given a node. - - Raises ``error.LookupError`` if the node is not known. - """ - - def node(rev): - """Obtain the node value given a revision number. - - Raises ``IndexError`` if the node is not known. - """ - - def lookup(node): - """Attempt to resolve a value to a node. - - Value can be a binary node, hex node, revision number, or a string - that can be converted to an integer. - - Raises ``error.LookupError`` if a node could not be resolved. - """ - - def linkrev(rev): - """Obtain the changeset revision number a revision is linked to.""" - - def iscensored(rev): - """Return whether a revision's content has been censored.""" - - def commonancestorsheads(node1, node2): - """Obtain an iterable of nodes containing heads of common ancestors. - - See ``ancestor.commonancestorsheads()``. - """ - - def descendants(revs): - """Obtain descendant revision numbers for a set of revision numbers. - - If ``nullrev`` is in the set, this is equivalent to ``revs()``. - """ - - def heads(start=None, stop=None): - """Obtain a list of nodes that are DAG heads, with control. - - The set of revisions examined can be limited by specifying - ``start`` and ``stop``. ``start`` is a node. ``stop`` is an - iterable of nodes. DAG traversal starts at earlier revision - ``start`` and iterates forward until any node in ``stop`` is - encountered. - """ - - def children(node): - """Obtain nodes that are children of a node. - - Returns a list of nodes. - """ - -class ifiledata(interfaceutil.Interface): - """Storage interface for data storage of a specific file. - - This complements ``ifileindex`` and provides an interface for accessing - data for a tracked file. - """ - def size(rev): - """Obtain the fulltext size of file data. - - Any metadata is excluded from size measurements. - """ - - def revision(node, raw=False): - """"Obtain fulltext data for a node. - - By default, any storage transformations are applied before the data - is returned. If ``raw`` is True, non-raw storage transformations - are not applied. - - The fulltext data may contain a header containing metadata. Most - consumers should use ``read()`` to obtain the actual file data. - """ - - def rawdata(node): - """Obtain raw data for a node. - """ - - def read(node): - """Resolve file fulltext data. - - This is similar to ``revision()`` except any metadata in the data - headers is stripped. - """ - - def renamed(node): - """Obtain copy metadata for a node. - - Returns ``False`` if no copy metadata is stored or a 2-tuple of - (path, node) from which this revision was copied. - """ - - def cmp(node, fulltext): - """Compare fulltext to another revision. - - Returns True if the fulltext is different from what is stored. - - This takes copy metadata into account. - - TODO better document the copy metadata and censoring logic. - """ - - def emitrevisions(nodes, - nodesorder=None, - revisiondata=False, - assumehaveparentrevisions=False, - deltamode=CG_DELTAMODE_STD): - """Produce ``irevisiondelta`` for revisions. - - Given an iterable of nodes, emits objects conforming to the - ``irevisiondelta`` interface that describe revisions in storage. - - This method is a generator. - - The input nodes may be unordered. Implementations must ensure that a - node's parents are emitted before the node itself. Transitively, this - means that a node may only be emitted once all its ancestors in - ``nodes`` have also been emitted. - - By default, emits "index" data (the ``node``, ``p1node``, and - ``p2node`` attributes). If ``revisiondata`` is set, revision data - will also be present on the emitted objects. - - With default argument values, implementations can choose to emit - either fulltext revision data or a delta. When emitting deltas, - implementations must consider whether the delta's base revision - fulltext is available to the receiver. - - The base revision fulltext is guaranteed to be available if any of - the following are met: - - * Its fulltext revision was emitted by this method call. - * A delta for that revision was emitted by this method call. - * ``assumehaveparentrevisions`` is True and the base revision is a - parent of the node. - - ``nodesorder`` can be used to control the order that revisions are - emitted. By default, revisions can be reordered as long as they are - in DAG topological order (see above). If the value is ``nodes``, - the iteration order from ``nodes`` should be used. If the value is - ``storage``, then the native order from the backing storage layer - is used. (Not all storage layers will have strong ordering and behavior - of this mode is storage-dependent.) ``nodes`` ordering can force - revisions to be emitted before their ancestors, so consumers should - use it with care. - - The ``linknode`` attribute on the returned ``irevisiondelta`` may not - be set and it is the caller's responsibility to resolve it, if needed. - - If ``deltamode`` is CG_DELTAMODE_PREV and revision data is requested, - all revision data should be emitted as deltas against the revision - emitted just prior. The initial revision should be a delta against its - 1st parent. - """ - -class ifilemutation(interfaceutil.Interface): - """Storage interface for mutation events of a tracked file.""" - - def add(filedata, meta, transaction, linkrev, p1, p2): - """Add a new revision to the store. - - Takes file data, dictionary of metadata, a transaction, linkrev, - and parent nodes. - - Returns the node that was added. - - May no-op if a revision matching the supplied data is already stored. - """ - - def addrevision(revisiondata, transaction, linkrev, p1, p2, node=None, - flags=0, cachedelta=None): - """Add a new revision to the store. - - This is similar to ``add()`` except it operates at a lower level. - - The data passed in already contains a metadata header, if any. - - ``node`` and ``flags`` can be used to define the expected node and - the flags to use with storage. ``flags`` is a bitwise value composed - of the various ``REVISION_FLAG_*`` constants. - - ``add()`` is usually called when adding files from e.g. the working - directory. ``addrevision()`` is often called by ``add()`` and for - scenarios where revision data has already been computed, such as when - applying raw data from a peer repo. - """ - - def addgroup(deltas, linkmapper, transaction, addrevisioncb=None, - maybemissingparents=False): - """Process a series of deltas for storage. - - ``deltas`` is an iterable of 7-tuples of - (node, p1, p2, linknode, deltabase, delta, flags) defining revisions - to add. - - The ``delta`` field contains ``mpatch`` data to apply to a base - revision, identified by ``deltabase``. The base node can be - ``nullid``, in which case the header from the delta can be ignored - and the delta used as the fulltext. - - ``addrevisioncb`` should be called for each node as it is committed. - - ``maybemissingparents`` is a bool indicating whether the incoming - data may reference parents/ancestor revisions that aren't present. - This flag is set when receiving data into a "shallow" store that - doesn't hold all history. - - Returns a list of nodes that were processed. A node will be in the list - even if it existed in the store previously. - """ - - def censorrevision(tr, node, tombstone=b''): - """Remove the content of a single revision. - - The specified ``node`` will have its content purged from storage. - Future attempts to access the revision data for this node will - result in failure. - - A ``tombstone`` message can optionally be stored. This message may be - displayed to users when they attempt to access the missing revision - data. - - Storage backends may have stored deltas against the previous content - in this revision. As part of censoring a revision, these storage - backends are expected to rewrite any internally stored deltas such - that they no longer reference the deleted content. - """ - - def getstrippoint(minlink): - """Find the minimum revision that must be stripped to strip a linkrev. - - Returns a 2-tuple containing the minimum revision number and a set - of all revisions numbers that would be broken by this strip. - - TODO this is highly revlog centric and should be abstracted into - a higher-level deletion API. ``repair.strip()`` relies on this. - """ - - def strip(minlink, transaction): - """Remove storage of items starting at a linkrev. - - This uses ``getstrippoint()`` to determine the first node to remove. - Then it effectively truncates storage for all revisions after that. - - TODO this is highly revlog centric and should be abstracted into a - higher-level deletion API. - """ - -class ifilestorage(ifileindex, ifiledata, ifilemutation): - """Complete storage interface for a single tracked file.""" - - def files(): - """Obtain paths that are backing storage for this file. - - TODO this is used heavily by verify code and there should probably - be a better API for that. - """ - - def storageinfo(exclusivefiles=False, sharedfiles=False, - revisionscount=False, trackedsize=False, - storedsize=False): - """Obtain information about storage for this file's data. - - Returns a dict describing storage for this tracked path. The keys - in the dict map to arguments of the same. The arguments are bools - indicating whether to calculate and obtain that data. - - exclusivefiles - Iterable of (vfs, path) describing files that are exclusively - used to back storage for this tracked path. - - sharedfiles - Iterable of (vfs, path) describing files that are used to back - storage for this tracked path. Those files may also provide storage - for other stored entities. - - revisionscount - Number of revisions available for retrieval. - - trackedsize - Total size in bytes of all tracked revisions. This is a sum of the - length of the fulltext of all revisions. - - storedsize - Total size in bytes used to store data for all tracked revisions. - This is commonly less than ``trackedsize`` due to internal usage - of deltas rather than fulltext revisions. - - Not all storage backends may support all queries are have a reasonable - value to use. In that case, the value should be set to ``None`` and - callers are expected to handle this special value. - """ - - def verifyintegrity(state): - """Verifies the integrity of file storage. - - ``state`` is a dict holding state of the verifier process. It can be - used to communicate data between invocations of multiple storage - primitives. - - If individual revisions cannot have their revision content resolved, - the method is expected to set the ``skipread`` key to a set of nodes - that encountered problems. - - The method yields objects conforming to the ``iverifyproblem`` - interface. - """ - -class idirs(interfaceutil.Interface): - """Interface representing a collection of directories from paths. - - This interface is essentially a derived data structure representing - directories from a collection of paths. - """ - - def addpath(path): - """Add a path to the collection. - - All directories in the path will be added to the collection. - """ - - def delpath(path): - """Remove a path from the collection. - - If the removal was the last path in a particular directory, the - directory is removed from the collection. - """ - - def __iter__(): - """Iterate over the directories in this collection of paths.""" - - def __contains__(path): - """Whether a specific directory is in this collection.""" - -class imanifestdict(interfaceutil.Interface): - """Interface representing a manifest data structure. - - A manifest is effectively a dict mapping paths to entries. Each entry - consists of a binary node and extra flags affecting that entry. - """ - - def __getitem__(path): - """Returns the binary node value for a path in the manifest. - - Raises ``KeyError`` if the path does not exist in the manifest. - - Equivalent to ``self.find(path)[0]``. - """ - - def find(path): - """Returns the entry for a path in the manifest. - - Returns a 2-tuple of (node, flags). - - Raises ``KeyError`` if the path does not exist in the manifest. - """ - - def __len__(): - """Return the number of entries in the manifest.""" - - def __nonzero__(): - """Returns True if the manifest has entries, False otherwise.""" - - __bool__ = __nonzero__ - - def __setitem__(path, node): - """Define the node value for a path in the manifest. - - If the path is already in the manifest, its flags will be copied to - the new entry. - """ - - def __contains__(path): - """Whether a path exists in the manifest.""" - - def __delitem__(path): - """Remove a path from the manifest. - - Raises ``KeyError`` if the path is not in the manifest. - """ - - def __iter__(): - """Iterate over paths in the manifest.""" - - def iterkeys(): - """Iterate over paths in the manifest.""" - - def keys(): - """Obtain a list of paths in the manifest.""" - - def filesnotin(other, match=None): - """Obtain the set of paths in this manifest but not in another. - - ``match`` is an optional matcher function to be applied to both - manifests. - - Returns a set of paths. - """ - - def dirs(): - """Returns an object implementing the ``idirs`` interface.""" - - def hasdir(dir): - """Returns a bool indicating if a directory is in this manifest.""" - - def matches(match): - """Generate a new manifest filtered through a matcher. - - Returns an object conforming to the ``imanifestdict`` interface. - """ - - def walk(match): - """Generator of paths in manifest satisfying a matcher. - - This is equivalent to ``self.matches(match).iterkeys()`` except a new - manifest object is not created. - - If the matcher has explicit files listed and they don't exist in - the manifest, ``match.bad()`` is called for each missing file. - """ - - def diff(other, match=None, clean=False): - """Find differences between this manifest and another. - - This manifest is compared to ``other``. - - If ``match`` is provided, the two manifests are filtered against this - matcher and only entries satisfying the matcher are compared. - - If ``clean`` is True, unchanged files are included in the returned - object. - - Returns a dict with paths as keys and values of 2-tuples of 2-tuples of - the form ``((node1, flag1), (node2, flag2))`` where ``(node1, flag1)`` - represents the node and flags for this manifest and ``(node2, flag2)`` - are the same for the other manifest. - """ - - def setflag(path, flag): - """Set the flag value for a given path. - - Raises ``KeyError`` if the path is not already in the manifest. - """ - - def get(path, default=None): - """Obtain the node value for a path or a default value if missing.""" - - def flags(path, default=''): - """Return the flags value for a path or a default value if missing.""" - - def copy(): - """Return a copy of this manifest.""" - - def items(): - """Returns an iterable of (path, node) for items in this manifest.""" - - def iteritems(): - """Identical to items().""" - - def iterentries(): - """Returns an iterable of (path, node, flags) for this manifest. - - Similar to ``iteritems()`` except items are a 3-tuple and include - flags. - """ - - def text(): - """Obtain the raw data representation for this manifest. - - Result is used to create a manifest revision. - """ - - def fastdelta(base, changes): - """Obtain a delta between this manifest and another given changes. - - ``base`` in the raw data representation for another manifest. - - ``changes`` is an iterable of ``(path, to_delete)``. - - Returns a 2-tuple containing ``bytearray(self.text())`` and the - delta between ``base`` and this manifest. - """ - -class imanifestrevisionbase(interfaceutil.Interface): - """Base interface representing a single revision of a manifest. - - Should not be used as a primary interface: should always be inherited - as part of a larger interface. - """ - - def new(): - """Obtain a new manifest instance. - - Returns an object conforming to the ``imanifestrevisionwritable`` - interface. The instance will be associated with the same - ``imanifestlog`` collection as this instance. - """ - - def copy(): - """Obtain a copy of this manifest instance. - - Returns an object conforming to the ``imanifestrevisionwritable`` - interface. The instance will be associated with the same - ``imanifestlog`` collection as this instance. - """ - - def read(): - """Obtain the parsed manifest data structure. - - The returned object conforms to the ``imanifestdict`` interface. - """ - -class imanifestrevisionstored(imanifestrevisionbase): - """Interface representing a manifest revision committed to storage.""" - - def node(): - """The binary node for this manifest.""" - - parents = interfaceutil.Attribute( - """List of binary nodes that are parents for this manifest revision.""" - ) - - def readdelta(shallow=False): - """Obtain the manifest data structure representing changes from parent. - - This manifest is compared to its 1st parent. A new manifest representing - those differences is constructed. - - The returned object conforms to the ``imanifestdict`` interface. - """ - - def readfast(shallow=False): - """Calls either ``read()`` or ``readdelta()``. - - The faster of the two options is called. - """ - - def find(key): - """Calls self.read().find(key)``. - - Returns a 2-tuple of ``(node, flags)`` or raises ``KeyError``. - """ - -class imanifestrevisionwritable(imanifestrevisionbase): - """Interface representing a manifest revision that can be committed.""" - - def write(transaction, linkrev, p1node, p2node, added, removed, match=None): - """Add this revision to storage. - - Takes a transaction object, the changeset revision number it will - be associated with, its parent nodes, and lists of added and - removed paths. - - If match is provided, storage can choose not to inspect or write out - items that do not match. Storage is still required to be able to provide - the full manifest in the future for any directories written (these - manifests should not be "narrowed on disk"). - - Returns the binary node of the created revision. - """ - -class imanifeststorage(interfaceutil.Interface): - """Storage interface for manifest data.""" - - tree = interfaceutil.Attribute( - """The path to the directory this manifest tracks. - - The empty bytestring represents the root manifest. - """) - - index = interfaceutil.Attribute( - """An ``ifilerevisionssequence`` instance.""") - - indexfile = interfaceutil.Attribute( - """Path of revlog index file. - - TODO this is revlog specific and should not be exposed. - """) - - opener = interfaceutil.Attribute( - """VFS opener to use to access underlying files used for storage. - - TODO this is revlog specific and should not be exposed. - """) - - version = interfaceutil.Attribute( - """Revlog version number. - - TODO this is revlog specific and should not be exposed. - """) - - _generaldelta = interfaceutil.Attribute( - """Whether generaldelta storage is being used. - - TODO this is revlog specific and should not be exposed. - """) - - fulltextcache = interfaceutil.Attribute( - """Dict with cache of fulltexts. - - TODO this doesn't feel appropriate for the storage interface. - """) - - def __len__(): - """Obtain the number of revisions stored for this manifest.""" - - def __iter__(): - """Iterate over revision numbers for this manifest.""" - - def rev(node): - """Obtain the revision number given a binary node. - - Raises ``error.LookupError`` if the node is not known. - """ - - def node(rev): - """Obtain the node value given a revision number. - - Raises ``error.LookupError`` if the revision is not known. - """ - - def lookup(value): - """Attempt to resolve a value to a node. - - Value can be a binary node, hex node, revision number, or a bytes - that can be converted to an integer. - - Raises ``error.LookupError`` if a ndoe could not be resolved. - """ - - def parents(node): - """Returns a 2-tuple of parent nodes for a node. - - Values will be ``nullid`` if the parent is empty. - """ - - def parentrevs(rev): - """Like parents() but operates on revision numbers.""" - - def linkrev(rev): - """Obtain the changeset revision number a revision is linked to.""" - - def revision(node, _df=None, raw=False): - """Obtain fulltext data for a node.""" - - def rawdata(node, _df=None): - """Obtain raw data for a node.""" - - def revdiff(rev1, rev2): - """Obtain a delta between two revision numbers. - - The returned data is the result of ``bdiff.bdiff()`` on the raw - revision data. - """ - - def cmp(node, fulltext): - """Compare fulltext to another revision. - - Returns True if the fulltext is different from what is stored. - """ - - def emitrevisions(nodes, - nodesorder=None, - revisiondata=False, - assumehaveparentrevisions=False): - """Produce ``irevisiondelta`` describing revisions. - - See the documentation for ``ifiledata`` for more. - """ - - def addgroup(deltas, linkmapper, transaction, addrevisioncb=None): - """Process a series of deltas for storage. - - See the documentation in ``ifilemutation`` for more. - """ - - def rawsize(rev): - """Obtain the size of tracked data. - - Is equivalent to ``len(m.rawdata(node))``. - - TODO this method is only used by upgrade code and may be removed. - """ - - def getstrippoint(minlink): - """Find minimum revision that must be stripped to strip a linkrev. - - See the documentation in ``ifilemutation`` for more. - """ - - def strip(minlink, transaction): - """Remove storage of items starting at a linkrev. - - See the documentation in ``ifilemutation`` for more. - """ - - def checksize(): - """Obtain the expected sizes of backing files. - - TODO this is used by verify and it should not be part of the interface. - """ - - def files(): - """Obtain paths that are backing storage for this manifest. - - TODO this is used by verify and there should probably be a better API - for this functionality. - """ - - def deltaparent(rev): - """Obtain the revision that a revision is delta'd against. - - TODO delta encoding is an implementation detail of storage and should - not be exposed to the storage interface. - """ - - def clone(tr, dest, **kwargs): - """Clone this instance to another.""" - - def clearcaches(clear_persisted_data=False): - """Clear any caches associated with this instance.""" - - def dirlog(d): - """Obtain a manifest storage instance for a tree.""" - - def add(m, transaction, link, p1, p2, added, removed, readtree=None, - match=None): - """Add a revision to storage. - - ``m`` is an object conforming to ``imanifestdict``. - - ``link`` is the linkrev revision number. - - ``p1`` and ``p2`` are the parent revision numbers. - - ``added`` and ``removed`` are iterables of added and removed paths, - respectively. - - ``readtree`` is a function that can be used to read the child tree(s) - when recursively writing the full tree structure when using - treemanifets. - - ``match`` is a matcher that can be used to hint to storage that not all - paths must be inspected; this is an optimization and can be safely - ignored. Note that the storage must still be able to reproduce a full - manifest including files that did not match. - """ - - def storageinfo(exclusivefiles=False, sharedfiles=False, - revisionscount=False, trackedsize=False, - storedsize=False): - """Obtain information about storage for this manifest's data. - - See ``ifilestorage.storageinfo()`` for a description of this method. - This one behaves the same way, except for manifest data. - """ - -class imanifestlog(interfaceutil.Interface): - """Interface representing a collection of manifest snapshots. - - Represents the root manifest in a repository. - - Also serves as a means to access nested tree manifests and to cache - tree manifests. - """ - - def __getitem__(node): - """Obtain a manifest instance for a given binary node. - - Equivalent to calling ``self.get('', node)``. - - The returned object conforms to the ``imanifestrevisionstored`` - interface. - """ - - def get(tree, node, verify=True): - """Retrieve the manifest instance for a given directory and binary node. - - ``node`` always refers to the node of the root manifest (which will be - the only manifest if flat manifests are being used). - - If ``tree`` is the empty string, the root manifest is returned. - Otherwise the manifest for the specified directory will be returned - (requires tree manifests). - - If ``verify`` is True, ``LookupError`` is raised if the node is not - known. - - The returned object conforms to the ``imanifestrevisionstored`` - interface. - """ - - def getstorage(tree): - """Retrieve an interface to storage for a particular tree. - - If ``tree`` is the empty bytestring, storage for the root manifest will - be returned. Otherwise storage for a tree manifest is returned. - - TODO formalize interface for returned object. - """ - - def clearcaches(): - """Clear caches associated with this collection.""" - - def rev(node): - """Obtain the revision number for a binary node. - - Raises ``error.LookupError`` if the node is not known. - """ - -class ilocalrepositoryfilestorage(interfaceutil.Interface): - """Local repository sub-interface providing access to tracked file storage. - - This interface defines how a repository accesses storage for a single - tracked file path. - """ - - def file(f): - """Obtain a filelog for a tracked path. - - The returned type conforms to the ``ifilestorage`` interface. - """ - -class ilocalrepositorymain(interfaceutil.Interface): - """Main interface for local repositories. - - This currently captures the reality of things - not how things should be. - """ - - supportedformats = interfaceutil.Attribute( - """Set of requirements that apply to stream clone. - - This is actually a class attribute and is shared among all instances. - """) - - supported = interfaceutil.Attribute( - """Set of requirements that this repo is capable of opening.""") - - requirements = interfaceutil.Attribute( - """Set of requirements this repo uses.""") - - features = interfaceutil.Attribute( - """Set of "features" this repository supports. - - A "feature" is a loosely-defined term. It can refer to a feature - in the classical sense or can describe an implementation detail - of the repository. For example, a ``readonly`` feature may denote - the repository as read-only. Or a ``revlogfilestore`` feature may - denote that the repository is using revlogs for file storage. - - The intent of features is to provide a machine-queryable mechanism - for repo consumers to test for various repository characteristics. - - Features are similar to ``requirements``. The main difference is that - requirements are stored on-disk and represent requirements to open the - repository. Features are more run-time capabilities of the repository - and more granular capabilities (which may be derived from requirements). - """) - - filtername = interfaceutil.Attribute( - """Name of the repoview that is active on this repo.""") - - wvfs = interfaceutil.Attribute( - """VFS used to access the working directory.""") - - vfs = interfaceutil.Attribute( - """VFS rooted at the .hg directory. - - Used to access repository data not in the store. - """) - - svfs = interfaceutil.Attribute( - """VFS rooted at the store. - - Used to access repository data in the store. Typically .hg/store. - But can point elsewhere if the store is shared. - """) - - root = interfaceutil.Attribute( - """Path to the root of the working directory.""") - - path = interfaceutil.Attribute( - """Path to the .hg directory.""") - - origroot = interfaceutil.Attribute( - """The filesystem path that was used to construct the repo.""") - - auditor = interfaceutil.Attribute( - """A pathauditor for the working directory. - - This checks if a path refers to a nested repository. - - Operates on the filesystem. - """) - - nofsauditor = interfaceutil.Attribute( - """A pathauditor for the working directory. - - This is like ``auditor`` except it doesn't do filesystem checks. - """) - - baseui = interfaceutil.Attribute( - """Original ui instance passed into constructor.""") - - ui = interfaceutil.Attribute( - """Main ui instance for this instance.""") - - sharedpath = interfaceutil.Attribute( - """Path to the .hg directory of the repo this repo was shared from.""") - - store = interfaceutil.Attribute( - """A store instance.""") - - spath = interfaceutil.Attribute( - """Path to the store.""") - - sjoin = interfaceutil.Attribute( - """Alias to self.store.join.""") - - cachevfs = interfaceutil.Attribute( - """A VFS used to access the cache directory. - - Typically .hg/cache. - """) - - wcachevfs = interfaceutil.Attribute( - """A VFS used to access the cache directory dedicated to working copy - - Typically .hg/wcache. - """) - - filteredrevcache = interfaceutil.Attribute( - """Holds sets of revisions to be filtered.""") - - names = interfaceutil.Attribute( - """A ``namespaces`` instance.""") - - def close(): - """Close the handle on this repository.""" - - def peer(): - """Obtain an object conforming to the ``peer`` interface.""" - - def unfiltered(): - """Obtain an unfiltered/raw view of this repo.""" - - def filtered(name, visibilityexceptions=None): - """Obtain a named view of this repository.""" - - obsstore = interfaceutil.Attribute( - """A store of obsolescence data.""") - - changelog = interfaceutil.Attribute( - """A handle on the changelog revlog.""") - - manifestlog = interfaceutil.Attribute( - """An instance conforming to the ``imanifestlog`` interface. - - Provides access to manifests for the repository. - """) - - dirstate = interfaceutil.Attribute( - """Working directory state.""") - - narrowpats = interfaceutil.Attribute( - """Matcher patterns for this repository's narrowspec.""") - - def narrowmatch(match=None, includeexact=False): - """Obtain a matcher for the narrowspec.""" - - def setnarrowpats(newincludes, newexcludes): - """Define the narrowspec for this repository.""" - - def __getitem__(changeid): - """Try to resolve a changectx.""" - - def __contains__(changeid): - """Whether a changeset exists.""" - - def __nonzero__(): - """Always returns True.""" - return True - - __bool__ = __nonzero__ - - def __len__(): - """Returns the number of changesets in the repo.""" - - def __iter__(): - """Iterate over revisions in the changelog.""" - - def revs(expr, *args): - """Evaluate a revset. - - Emits revisions. - """ - - def set(expr, *args): - """Evaluate a revset. - - Emits changectx instances. - """ - - def anyrevs(specs, user=False, localalias=None): - """Find revisions matching one of the given revsets.""" - - def url(): - """Returns a string representing the location of this repo.""" - - def hook(name, throw=False, **args): - """Call a hook.""" - - def tags(): - """Return a mapping of tag to node.""" - - def tagtype(tagname): - """Return the type of a given tag.""" - - def tagslist(): - """Return a list of tags ordered by revision.""" - - def nodetags(node): - """Return the tags associated with a node.""" - - def nodebookmarks(node): - """Return the list of bookmarks pointing to the specified node.""" - - def branchmap(): - """Return a mapping of branch to heads in that branch.""" - - def revbranchcache(): - pass - - def branchtip(branchtip, ignoremissing=False): - """Return the tip node for a given branch.""" - - def lookup(key): - """Resolve the node for a revision.""" - - def lookupbranch(key): - """Look up the branch name of the given revision or branch name.""" - - def known(nodes): - """Determine whether a series of nodes is known. - - Returns a list of bools. - """ - - def local(): - """Whether the repository is local.""" - return True - - def publishing(): - """Whether the repository is a publishing repository.""" - - def cancopy(): - pass - - def shared(): - """The type of shared repository or None.""" - - def wjoin(f, *insidef): - """Calls self.vfs.reljoin(self.root, f, *insidef)""" - - def setparents(p1, p2): - """Set the parent nodes of the working directory.""" - - def filectx(path, changeid=None, fileid=None): - """Obtain a filectx for the given file revision.""" - - def getcwd(): - """Obtain the current working directory from the dirstate.""" - - def pathto(f, cwd=None): - """Obtain the relative path to a file.""" - - def adddatafilter(name, fltr): - pass - - def wread(filename): - """Read a file from wvfs, using data filters.""" - - def wwrite(filename, data, flags, backgroundclose=False, **kwargs): - """Write data to a file in the wvfs, using data filters.""" - - def wwritedata(filename, data): - """Resolve data for writing to the wvfs, using data filters.""" - - def currenttransaction(): - """Obtain the current transaction instance or None.""" - - def transaction(desc, report=None): - """Open a new transaction to write to the repository.""" - - def undofiles(): - """Returns a list of (vfs, path) for files to undo transactions.""" - - def recover(): - """Roll back an interrupted transaction.""" - - def rollback(dryrun=False, force=False): - """Undo the last transaction. - - DANGEROUS. - """ - - def updatecaches(tr=None, full=False): - """Warm repo caches.""" - - def invalidatecaches(): - """Invalidate cached data due to the repository mutating.""" - - def invalidatevolatilesets(): - pass - - def invalidatedirstate(): - """Invalidate the dirstate.""" - - def invalidate(clearfilecache=False): - pass - - def invalidateall(): - pass - - def lock(wait=True): - """Lock the repository store and return a lock instance.""" - - def wlock(wait=True): - """Lock the non-store parts of the repository.""" - - def currentwlock(): - """Return the wlock if it's held or None.""" - - def checkcommitpatterns(wctx, vdirs, match, status, fail): - pass - - def commit(text='', user=None, date=None, match=None, force=False, - editor=False, extra=None): - """Add a new revision to the repository.""" - - def commitctx(ctx, error=False, origctx=None): - """Commit a commitctx instance to the repository.""" - - def destroying(): - """Inform the repository that nodes are about to be destroyed.""" - - def destroyed(): - """Inform the repository that nodes have been destroyed.""" - - def status(node1='.', node2=None, match=None, ignored=False, - clean=False, unknown=False, listsubrepos=False): - """Convenience method to call repo[x].status().""" - - def addpostdsstatus(ps): - pass - - def postdsstatus(): - pass - - def clearpostdsstatus(): - pass - - def heads(start=None): - """Obtain list of nodes that are DAG heads.""" - - def branchheads(branch=None, start=None, closed=False): - pass - - def branches(nodes): - pass - - def between(pairs): - pass - - def checkpush(pushop): - pass - - prepushoutgoinghooks = interfaceutil.Attribute( - """util.hooks instance.""") - - def pushkey(namespace, key, old, new): - pass - - def listkeys(namespace): - pass - - def debugwireargs(one, two, three=None, four=None, five=None): - pass - - def savecommitmessage(text): - pass - -class completelocalrepository(ilocalrepositorymain, - ilocalrepositoryfilestorage): - """Complete interface for a local repository.""" - -class iwireprotocolcommandcacher(interfaceutil.Interface): - """Represents a caching backend for wire protocol commands. - - Wire protocol version 2 supports transparent caching of many commands. - To leverage this caching, servers can activate objects that cache - command responses. Objects handle both cache writing and reading. - This interface defines how that response caching mechanism works. - - Wire protocol version 2 commands emit a series of objects that are - serialized and sent to the client. The caching layer exists between - the invocation of the command function and the sending of its output - objects to an output layer. - - Instances of this interface represent a binding to a cache that - can serve a response (in place of calling a command function) and/or - write responses to a cache for subsequent use. - - When a command request arrives, the following happens with regards - to this interface: - - 1. The server determines whether the command request is cacheable. - 2. If it is, an instance of this interface is spawned. - 3. The cacher is activated in a context manager (``__enter__`` is called). - 4. A cache *key* for that request is derived. This will call the - instance's ``adjustcachekeystate()`` method so the derivation - can be influenced. - 5. The cacher is informed of the derived cache key via a call to - ``setcachekey()``. - 6. The cacher's ``lookup()`` method is called to test for presence of - the derived key in the cache. - 7. If ``lookup()`` returns a hit, that cached result is used in place - of invoking the command function. ``__exit__`` is called and the instance - is discarded. - 8. The command function is invoked. - 9. ``onobject()`` is called for each object emitted by the command - function. - 10. After the final object is seen, ``onfinished()`` is called. - 11. ``__exit__`` is called to signal the end of use of the instance. - - Cache *key* derivation can be influenced by the instance. - - Cache keys are initially derived by a deterministic representation of - the command request. This includes the command name, arguments, protocol - version, etc. This initial key derivation is performed by CBOR-encoding a - data structure and feeding that output into a hasher. - - Instances of this interface can influence this initial key derivation - via ``adjustcachekeystate()``. - - The instance is informed of the derived cache key via a call to - ``setcachekey()``. The instance must store the key locally so it can - be consulted on subsequent operations that may require it. - - When constructed, the instance has access to a callable that can be used - for encoding response objects. This callable receives as its single - argument an object emitted by a command function. It returns an iterable - of bytes chunks representing the encoded object. Unless the cacher is - caching native Python objects in memory or has a way of reconstructing - the original Python objects, implementations typically call this function - to produce bytes from the output objects and then store those bytes in - the cache. When it comes time to re-emit those bytes, they are wrapped - in a ``wireprototypes.encodedresponse`` instance to tell the output - layer that they are pre-encoded. - - When receiving the objects emitted by the command function, instances - can choose what to do with those objects. The simplest thing to do is - re-emit the original objects. They will be forwarded to the output - layer and will be processed as if the cacher did not exist. - - Implementations could also choose to not emit objects - instead locally - buffering objects or their encoded representation. They could then emit - a single "coalesced" object when ``onfinished()`` is called. In - this way, the implementation would function as a filtering layer of - sorts. - - When caching objects, typically the encoded form of the object will - be stored. Keep in mind that if the original object is forwarded to - the output layer, it will need to be encoded there as well. For large - output, this redundant encoding could add overhead. Implementations - could wrap the encoded object data in ``wireprototypes.encodedresponse`` - instances to avoid this overhead. - """ - def __enter__(): - """Marks the instance as active. - - Should return self. - """ - - def __exit__(exctype, excvalue, exctb): - """Called when cacher is no longer used. - - This can be used by implementations to perform cleanup actions (e.g. - disconnecting network sockets, aborting a partially cached response. - """ - - def adjustcachekeystate(state): - """Influences cache key derivation by adjusting state to derive key. - - A dict defining the state used to derive the cache key is passed. - - Implementations can modify this dict to record additional state that - is wanted to influence key derivation. - - Implementations are *highly* encouraged to not modify or delete - existing keys. - """ - - def setcachekey(key): - """Record the derived cache key for this request. - - Instances may mutate the key for internal usage, as desired. e.g. - instances may wish to prepend the repo name, introduce path - components for filesystem or URL addressing, etc. Behavior is up to - the cache. - - Returns a bool indicating if the request is cacheable by this - instance. - """ - - def lookup(): - """Attempt to resolve an entry in the cache. - - The instance is instructed to look for the cache key that it was - informed about via the call to ``setcachekey()``. - - If there's no cache hit or the cacher doesn't wish to use the cached - entry, ``None`` should be returned. - - Else, a dict defining the cached result should be returned. The - dict may have the following keys: - - objs - An iterable of objects that should be sent to the client. That - iterable of objects is expected to be what the command function - would return if invoked or an equivalent representation thereof. - """ - - def onobject(obj): - """Called when a new object is emitted from the command function. - - Receives as its argument the object that was emitted from the - command function. - - This method returns an iterator of objects to forward to the output - layer. The easiest implementation is a generator that just - ``yield obj``. - """ - - def onfinished(): - """Called after all objects have been emitted from the command function. - - Implementations should return an iterator of objects to forward to - the output layer. - - This method can be a generator. - """ diff -r 383fdfa6bba9 -r 268662aac075 mercurial/revlog.py --- a/mercurial/revlog.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/revlog.py Sun Aug 18 00:45:33 2019 +0300 @@ -65,10 +65,12 @@ mdiff, policy, pycompat, - repository, templatefilters, util, ) +from .interfaces import ( + repository, +) from .revlogutils import ( deltas as deltautil, flagutil, diff -r 383fdfa6bba9 -r 268662aac075 mercurial/revlogutils/constants.py --- a/mercurial/revlogutils/constants.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/revlogutils/constants.py Sun Aug 18 00:45:33 2019 +0300 @@ -9,7 +9,7 @@ from __future__ import absolute_import -from .. import ( +from ..interfaces import ( repository, ) diff -r 383fdfa6bba9 -r 268662aac075 mercurial/streamclone.py --- a/mercurial/streamclone.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/streamclone.py Sun Aug 18 00:45:33 2019 +0300 @@ -12,13 +12,15 @@ import struct from .i18n import _ +from .interfaces import ( + repository, +) from . import ( cacheutil, error, narrowspec, phases, pycompat, - repository, store, util, ) diff -r 383fdfa6bba9 -r 268662aac075 mercurial/testing/storage.py --- a/mercurial/testing/storage.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/testing/storage.py Sun Aug 18 00:45:33 2019 +0300 @@ -17,6 +17,8 @@ from .. import ( error, mdiff, +) +from ..interfaces import ( repository, ) from ..utils import ( diff -r 383fdfa6bba9 -r 268662aac075 mercurial/utils/storageutil.py --- a/mercurial/utils/storageutil.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/utils/storageutil.py Sun Aug 18 00:45:33 2019 +0300 @@ -22,8 +22,8 @@ error, mdiff, pycompat, - repository, ) +from ..interfaces import repository _nullhash = hashlib.sha1(nullid) diff -r 383fdfa6bba9 -r 268662aac075 mercurial/wireprotov1peer.py --- a/mercurial/wireprotov1peer.py Thu Aug 22 16:47:31 2019 -0700 +++ b/mercurial/wireprotov1peer.py Sun Aug 18 00:45:33 2019 +0300 @@ -22,10 +22,12 @@ error, pushkey as pushkeymod, pycompat, - repository, util, wireprototypes, ) +from .interfaces import ( + repository, +) from .utils import ( interfaceutil, ) diff -r 383fdfa6bba9 -r 268662aac075 setup.py --- a/setup.py Thu Aug 22 16:47:31 2019 -0700 +++ b/setup.py Sun Aug 18 00:45:33 2019 +0300 @@ -1067,6 +1067,7 @@ 'mercurial.cext', 'mercurial.cffi', 'mercurial.hgweb', + 'mercurial.interfaces', 'mercurial.pure', 'mercurial.thirdparty', 'mercurial.thirdparty.attr', diff -r 383fdfa6bba9 -r 268662aac075 tests/notcapable --- a/tests/notcapable Thu Aug 22 16:47:31 2019 -0700 +++ b/tests/notcapable Sun Aug 18 00:45:33 2019 +0300 @@ -6,7 +6,8 @@ fi cat > notcapable-$CAP.py << EOF -from mercurial import extensions, localrepo, repository +from mercurial import extensions, localrepo +from mercurial.interfaces import repository def extsetup(ui): extensions.wrapfunction(repository.peer, 'capable', wrapcapable) extensions.wrapfunction(localrepo.localrepository, 'peer', wrappeer) diff -r 383fdfa6bba9 -r 268662aac075 tests/pullext.py --- a/tests/pullext.py Thu Aug 22 16:47:31 2019 -0700 +++ b/tests/pullext.py Sun Aug 18 00:45:33 2019 +0300 @@ -13,6 +13,8 @@ error, extensions, localrepo, +) +from mercurial.interfaces import ( repository, ) diff -r 383fdfa6bba9 -r 268662aac075 tests/simplestorerepo.py --- a/tests/simplestorerepo.py Thu Aug 22 16:47:31 2019 -0700 +++ b/tests/simplestorerepo.py Sun Aug 18 00:45:33 2019 +0300 @@ -32,11 +32,13 @@ localrepo, mdiff, pycompat, - repository, revlog, store, verify, ) +from mercurial.interfaces import ( + repository, +) from mercurial.utils import ( cborutil, interfaceutil, diff -r 383fdfa6bba9 -r 268662aac075 tests/test-check-interfaces.py --- a/tests/test-check-interfaces.py Thu Aug 22 16:47:31 2019 -0700 +++ b/tests/test-check-interfaces.py Sun Aug 18 00:45:33 2019 +0300 @@ -14,6 +14,9 @@ 'test-repo']): sys.exit(80) +from mercurial.interfaces import ( + repository, +) from mercurial.thirdparty.zope import ( interface as zi, ) @@ -27,7 +30,6 @@ localrepo, manifest, pycompat, - repository, revlog, sshpeer, statichttprepo, diff -r 383fdfa6bba9 -r 268662aac075 tests/wireprotosimplecache.py --- a/tests/wireprotosimplecache.py Thu Aug 22 16:47:31 2019 -0700 +++ b/tests/wireprotosimplecache.py Sun Aug 18 00:45:33 2019 +0300 @@ -10,12 +10,14 @@ from mercurial import ( extensions, registrar, - repository, util, wireprotoserver, wireprototypes, wireprotov2server, ) +from mercurial.interfaces import ( + repository, +) from mercurial.utils import ( interfaceutil, stringutil,