Bug 1393242 - Hook up hglib to HgRepository; r=mshal
☠☠ backed out by 299de9e2cdab ☠ ☠
authorGregory Szorc <gps@mozilla.com>
Wed, 23 Aug 2017 15:09:27 -0700
changeset 376729 ffe8bef74be0f16e129f00e13f62d4ae16b2aea7
parent 376728 159028ef12d252e4cfaec0eae04e238cea90c00b
child 376730 18d89d8ab860835f3a380658d2606b68a19ce94c
push id32389
push userarchaeopteryx@coole-files.de
push dateFri, 25 Aug 2017 11:08:14 +0000
treeherdermozilla-central@3bbd8e25df3d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -33,25 +33,39 @@ def get_tool_path(tool):
     raise Exception('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,
     def tool_version(self):
         '''Return the version of the VCS tool in use as a `LooseVersion`.'''
@@ -102,23 +116,51 @@ class Repository(object):
     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='UTF-8',
+                                             configs=None, connect=False)
     def name(self):
         return 'hg'
+    def __enter__(self):
+        if self._client.server is None:
+            self._client.open()
+        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')