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`.
'''
@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.