Bug 1397406 - Add Repository method to determine if working directory clean; r?dustin draft
authorGregory Szorc <gps@mozilla.com>
Wed, 06 Sep 2017 12:15:12 -0700
changeset 660259 8ca48411de46e2d1d67f93c7a2a0703542e3a4e5
parent 660258 94c02703f07c931bfb9796611403859b07c3a2c0
child 660260 77495b5e90f62d3bb309cc64d6ced8114719eccb
child 660268 30f638f3ca496580e66761645a6144d4885e99ab
push id78338
push usergszorc@mozilla.com
push dateWed, 06 Sep 2017 19:21:56 +0000
reviewersdustin
bugs1397406
milestone57.0a1
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
python/mozversioncontrol/mozversioncontrol/__init__.py
--- 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`.
         '''
 
     @abc.abstractmethod
     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)
 
     @property
     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.