changeset 42927:d459cd8ea42d

interfaces: introduce an interface for dirstate implementations As usual with adding interface definitions, this describes the way things are, not the way we'd like things to be. There are some clear problems in the interface right now (eg ._map leaks in a few places), but I have plans to clean those up. There are also many missing docstrings, but again, we'll make a second pass to clean that up. Differential Revision: https://phab.mercurial-scm.org/D6836
author Augie Fackler <augie@google.com>
date Tue, 10 Sep 2019 09:21:38 -0400
parents 34ed651ba7e4
children bbd154627a72
files mercurial/dirstate.py mercurial/interfaces/dirstate.py tests/test-check-interfaces.py
diffstat 3 files changed, 267 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/dirstate.py	Tue Sep 10 09:41:58 2019 -0400
+++ b/mercurial/dirstate.py	Tue Sep 10 09:21:38 2019 -0400
@@ -27,6 +27,11 @@
     util,
 )
 
+from .interfaces import (
+    dirstate as intdirstate,
+    util as interfaceutil,
+)
+
 parsers = policy.importmod(r'parsers')
 rustmod = policy.importrust(r'dirstate')
 
@@ -55,6 +60,7 @@
         os.close(tmpfd)
         vfs.unlink(tmpname)
 
+@interfaceutil.implementer(intdirstate.idirstate)
 class dirstate(object):
 
     def __init__(self, opener, ui, root, validate, sparsematchfn):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/interfaces/dirstate.py	Tue Sep 10 09:21:38 2019 -0400
@@ -0,0 +1,257 @@
+from __future__ import absolute_import, print_function
+
+import contextlib
+
+from .. import (
+    node as nodemod,
+)
+
+from . import (
+    util as interfaceutil,
+)
+
+class idirstate(interfaceutil.Interface):
+
+    def __init__(opener, ui, root, validate, sparsematchfn):
+        '''Create a new dirstate object.
+
+        opener is an open()-like callable that can be used to open the
+        dirstate file; root is the root of the directory tracked by
+        the dirstate.
+        '''
+
+    @contextlib.contextmanager
+    def parentchange():
+        '''Context manager for handling dirstate parents.
+
+        If an exception occurs in the scope of the context manager,
+        the incoherent dirstate won't be written when wlock is
+        released.
+        '''
+
+    def pendingparentchange():
+        '''Returns true if the dirstate is in the middle of a set of changes
+        that modify the dirstate parent.
+        '''
+
+    _map = interfaceutil.Attribute(
+        """Return the dirstate contents (see documentation for dirstatemap).
+
+        TODO this should not be exposed.
+        """
+        )
+
+    def hasdir(d):
+        pass
+
+    _ignore = interfaceutil.Attribute('Matcher for ignored files.')
+
+    _checklink = interfaceutil.Attribute('Callable for checking symlinks.')
+    _checkexec = interfaceutil.Attribute('Callable for checking exec bits.')
+
+    def flagfunc(buildfallback):
+        pass
+
+    def getcwd():
+        '''Return the path from which a canonical path is calculated.
+
+        This path should be used to resolve file patterns or to convert
+        canonical paths back to file paths for display. It shouldn't be
+        used to get real file paths. Use vfs functions instead.
+        '''
+
+    def pathto(f, cwd=None):
+        pass
+
+    def __getitem__(key):
+        '''Return the current state of key (a filename) in the dirstate.
+
+        States are:
+          n  normal
+          m  needs merging
+          r  marked for removal
+          a  marked for addition
+          ?  not tracked
+        '''
+
+    def __contains__(key):
+        """Check if bytestring `key` is known to the dirstate."""
+
+    def __iter__():
+        """Iterate the dirstate's contained filenames as bytestrings."""
+
+    def items():
+        """Iterate the dirstate's entries as (filename, dirstatetuple).
+
+        As usual, filename is a bytestring.
+        """
+
+    iteritems = items
+
+    def parents():
+        pass
+
+    def p1():
+        pass
+
+    def p2():
+        pass
+
+    def branch():
+        pass
+
+    def setparents(p1, p2=nodemod.nullid):
+        """Set dirstate parents to p1 and p2.
+
+        When moving from two parents to one, 'm' merged entries a
+        adjusted to normal and previous copy records discarded and
+        returned by the call.
+
+        See localrepo.setparents()
+        """
+
+    def setbranch(branch):
+        pass
+
+    def invalidate():
+        '''Causes the next access to reread the dirstate.
+
+        This is different from localrepo.invalidatedirstate() because it always
+        rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
+        check whether the dirstate has changed before rereading it.'''
+
+    def copy(source, dest):
+        """Mark dest as a copy of source. Unmark dest if source is None."""
+
+    def copied(file):
+        pass
+
+    def copies():
+        pass
+
+    def normal(f, parentfiledata=None):
+        '''Mark a file normal and clean.
+
+        parentfiledata: (mode, size, mtime) of the clean file
+
+        parentfiledata should be computed from memory (for mode,
+        size), as or close as possible from the point where we
+        determined the file was clean, to limit the risk of the
+        file having been changed by an external process between the
+        moment where the file was determined to be clean and now.'''
+        pass
+
+    def normallookup(f):
+        '''Mark a file normal, but possibly dirty.'''
+
+    def otherparent(f):
+        '''Mark as coming from the other parent, always dirty.'''
+
+    def add(f):
+        '''Mark a file added.'''
+
+    def remove(f):
+        '''Mark a file removed.'''
+
+    def merge(f):
+        '''Mark a file merged.'''
+
+    def drop(f):
+        '''Drop a file from the dirstate'''
+
+    def normalize(path, isknown=False, ignoremissing=False):
+        '''
+        normalize the case of a pathname when on a casefolding filesystem
+
+        isknown specifies whether the filename came from walking the
+        disk, to avoid extra filesystem access.
+
+        If ignoremissing is True, missing path are returned
+        unchanged. Otherwise, we try harder to normalize possibly
+        existing path components.
+
+        The normalized case is determined based on the following precedence:
+
+        - version of name already stored in the dirstate
+        - version of name stored on disk
+        - version provided via command arguments
+        '''
+
+    def clear():
+        pass
+
+    def rebuild(parent, allfiles, changedfiles=None):
+        pass
+
+    def identity():
+        '''Return identity of dirstate it to detect changing in storage
+
+        If identity of previous dirstate is equal to this, writing
+        changes based on the former dirstate out can keep consistency.
+        '''
+
+    def write(tr):
+        pass
+
+    def addparentchangecallback(category, callback):
+        """add a callback to be called when the wd parents are changed
+
+        Callback will be called with the following arguments:
+            dirstate, (oldp1, oldp2), (newp1, newp2)
+
+        Category is a unique identifier to allow overwriting an old callback
+        with a newer callback.
+        """
+
+    def _ignorefiles():
+        """Return a list of files containing patterns to ignore.
+
+        TODO this should not be exposed."""
+
+    def _ignorefileandline(f):
+        """Given a file `f`, return the ignore file and line that ignores it.
+
+        TODO this should not be exposed."""
+
+    def walk(match, subrepos, unknown, ignored, full=True):
+        '''
+        Walk recursively through the directory tree, finding all files
+        matched by match.
+
+        If full is False, maybe skip some known-clean files.
+
+        Return a dict mapping filename to stat-like object (either
+        mercurial.osutil.stat instance or return value of os.stat()).
+
+        '''
+
+    def status(match, subrepos, ignored, clean, unknown):
+        '''Determine the status of the working copy relative to the
+        dirstate and return a pair of (unsure, status), where status is of type
+        scmutil.status and:
+
+          unsure:
+            files that might have been modified since the dirstate was
+            written, but need to be read to be sure (size is the same
+            but mtime differs)
+          status.modified:
+            files that have definitely been modified since the dirstate
+            was written (different size or mode)
+          status.clean:
+            files that have definitely not been modified since the
+            dirstate was written
+        '''
+
+    def matches(match):
+        '''
+        return files in the dirstate (in whatever state) filtered by match
+        '''
+
+    def savebackup(tr, backupname):
+        '''Save current dirstate into backup file'''
+
+    def restorebackup(tr, backupname):
+        '''Restore dirstate by backup file'''
+
+    def clearbackup(tr, backupname):
+        '''Clear backup file'''
--- a/tests/test-check-interfaces.py	Tue Sep 10 09:41:58 2019 -0400
+++ b/tests/test-check-interfaces.py	Tue Sep 10 09:21:38 2019 -0400
@@ -15,6 +15,7 @@
     sys.exit(80)
 
 from mercurial.interfaces import (
+    dirstate as intdirstate,
     repository,
 )
 from mercurial.thirdparty.zope import (
@@ -25,6 +26,7 @@
 )
 from mercurial import (
     bundlerepo,
+    dirstate,
     filelog,
     httppeer,
     localrepo,
@@ -189,6 +191,8 @@
     ziverify.verifyClass(repository.iverifyproblem,
                          simplestorerepo.simplefilestoreproblem)
 
+    ziverify.verifyClass(intdirstate.idirstate, dirstate.dirstate)
+
     vfs = vfsmod.vfs(b'.')
     fl = filelog.filelog(vfs, b'dummy.i')
     checkzobject(fl, allowextra=True)