Bug 1393242 - Hook up hglib to HgRepository; r?mshal
Because hglib spawns a persistent process, we introduce a context
manager for Repository. It no-ops by default. On HgRepository it
controls the lifetime of the persistent hg process.
A helper method for running an hg command via hglib has been added.
We can't transition existing methods to hglib because hglib
requires a context manager, which no consumer is using yet.
MozReview-Commit-ID: 8z0fcGFeAm5
--- a/python/mozversioncontrol/mozversioncontrol/__init__.py
+++ b/python/mozversioncontrol/mozversioncontrol/__init__.py
@@ -37,25 +37,39 @@ def get_tool_path(tool):
pass
raise MissingVCSTool('Unable to obtain %s path. Try running '
'|mach bootstrap| to ensure your environment is up to '
'date.' % tool)
class Repository(object):
+ """A class wrapping utility methods around version control repositories.
+
+ This class is abstract and never instantiated. Obtain an instance by
+ calling a ``get_repository_*()`` helper function.
+
+ Clients are recommended to use the object as a context manager. But not
+ all methods require this.
+ """
+
__metaclass__ = abc.ABCMeta
- '''A class wrapping utility methods around version control repositories.'''
def __init__(self, path, tool):
self.path = os.path.abspath(path)
self._tool = get_tool_path(tool)
self._env = os.environ.copy()
self._version = None
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ pass
+
def _run(self, *args):
return subprocess.check_output((self._tool, ) + args,
cwd=self.path,
env=self._env)
@property
def tool_version(self):
'''Return the version of the VCS tool in use as a `LooseVersion`.'''
@@ -106,23 +120,58 @@ class Repository(object):
@abc.abstractmethod
def get_files_in_working_directory(self):
"""Obtain a list of managed files in the working directory."""
class HgRepository(Repository):
'''An implementation of `Repository` for Mercurial repositories.'''
def __init__(self, path, hg='hg'):
+ import hglib.client
+
super(HgRepository, self).__init__(path, tool=hg)
self._env[b'HGPLAIN'] = b'1'
+ # Setting this modifies a global variable and makes all future hglib
+ # instances use this binary. Since the tool path was validated, this
+ # should be OK. But ideally hglib would offer an API that defines
+ # per-instance binaries.
+ hglib.HGPATH = self._tool
+
+ # Without connect=False this spawns a persistent process. We want
+ # the process lifetime tied to a context manager.
+ self._client = hglib.client.hgclient(self.path, encoding=b'UTF-8',
+ configs=None, connect=False)
+
@property
def name(self):
return 'hg'
+ def __enter__(self):
+ if self._client.server is None:
+ # The cwd if the spawned process should be the repo root to ensure
+ # relative paths are normalized to it.
+ old_cwd = os.getcwd()
+ try:
+ os.chdir(self.path)
+ self._client.open()
+ finally:
+ os.chdir(old_cwd)
+
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self._client.close()
+
+ def _run_in_client(self, args):
+ if not self._client.server:
+ raise Exception('active HgRepository context manager required')
+
+ return self._client.rawcommand(args)
+
def sparse_checkout_present(self):
# We assume a sparse checkout is enabled if the .hg/sparse file
# has data. Strictly speaking, we should look for a requirement in
# .hg/requires. But since the requirement is still experimental
# as of Mercurial 4.3, it's probably more trouble than its worth
# to verify it.
sparse = os.path.join(self.path, '.hg', 'sparse')