Bug 1397406 - Add Repository method to determine if working directory clean; r=dustin
authorGregory Szorc <gps@mozilla.com>
Wed, 06 Sep 2017 12:15:12 -0700
changeset 379270 da68a5ff28d0cd132e031b1a67577a19274c01df
parent 379269 caccc7951f5bd8f0d4b840da0a7b9ea4a5d4df84
child 379271 e9416a307987f144f994dfeb6ed00b3277068279
push id50609
push usergszorc@mozilla.com
push dateWed, 06 Sep 2017 21:58:25 +0000
treeherderautoland@56d353e23439 [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 1397406 - Add Repository method to determine if working directory clean; r=dustin This is generally useful functionality to have. A consume will be introduced in an upcoming commit. MozReview-Commit-ID: 4arTMfJSiEC
--- a/python/mozversioncontrol/mozversioncontrol/__init__.py
+++ b/python/mozversioncontrol/mozversioncontrol/__init__.py
@@ -116,16 +116,28 @@ class Repository(object):
     def forget_add_remove_files(self, path):
         '''Undo the effects of a previous add_remove_files call for `path`.
     def get_files_in_working_directory(self):
         """Obtain a list of managed files in the working directory."""
+    @abc.abstractmethod
+    def working_directory_clean(self, untracked=False, ignored=False):
+        """Determine if the working directory is free of modifications.
+        Returns True if the working directory does not have any file
+        modifications. False otherwise.
+        By default, untracked and ignored files are not considered. If
+        ``untracked`` or ``ignored`` are set, they influence the clean check
+        to factor these file classes into consideration.
+        """
 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'
@@ -202,16 +214,28 @@ class HgRepository(Repository):
         self._run('forget', path)
     def get_files_in_working_directory(self):
         # Can return backslashes on Windows. Normalize to forward slashes.
         return list(p.replace('\\', '/') for p in
                     self._run_in_client([b'files', b'-0']).split(b'\0')
                     if p)
+    def working_directory_clean(self, untracked=False, ignored=False):
+        args = [b'status', b'\0', b'--modified', b'--added', b'--removed',
+                b'--deleted']
+        if untracked:
+            args.append(b'--unknown')
+        if ignored:
+            args.append(b'--ignored')
+        # If output is empty, there are no entries of requested status, which
+        # means we are clean.
+        return not len(self._run_in_client(args).strip())
 class GitRepository(Repository):
     '''An implementation of `Repository` for Git repositories.'''
     def __init__(self, path, git='git'):
         super(GitRepository, self).__init__(path, tool=git)
     def name(self):
@@ -231,16 +255,25 @@ class GitRepository(Repository):
         self._run('add', path)
     def forget_add_remove_files(self, path):
         self._run('reset', path)
     def get_files_in_working_directory(self):
         return self._run('ls-files', '-z').split(b'\0')
+    def working_directory_clean(self, untracked=False, ignored=False):
+        args = ['status', '--porcelain']
+        if untracked:
+            args.append('--untracked-files')
+        if ignored:
+            args.append('--ignored')
+        return not len(self._run(*args).strip())
 class InvalidRepoPath(Exception):
     """Represents a failure to find a VCS repo at a specified path."""
 def get_repository_object(path, hg='hg', git='git'):
     '''Get a repository object for the repository at `path`.
     If `path` is not a known VCS repository, raise an exception.