Bug 1361972 - [mozlint] Add ability to only lint staged changes to --workdir with git
MozReview-Commit-ID: DUxCKN2fiag
--- a/python/mozlint/mozlint/cli.py
+++ b/python/mozlint/mozlint/cli.py
@@ -44,34 +44,44 @@ class MozlintParser(ArgumentParser):
{'const': 'default',
'nargs': '?',
'help': "Lint files touched by commits that are not on the remote repository. "
"Without arguments, finds the default remote that would be pushed to. "
"The remote branch can also be specified manually. Works with "
"mercurial or git."
}],
[['-w', '--workdir'],
- {'default': False,
- 'action': 'store_true',
+ {'const': 'all',
+ 'nargs': '?',
+ 'choices': ['staged', 'all'],
'help': "Lint files touched by changes in the working directory "
- "(i.e haven't been committed yet). Works with mercurial or git.",
+ "(i.e haven't been committed yet). On git, --workdir=staged "
+ "can be used to only consider staged files. Works with "
+ "mercurial or git.",
}],
[['extra_args'],
{'nargs': REMAINDER,
'help': "Extra arguments that will be forwarded to the underlying linter.",
}],
]
def __init__(self, **kwargs):
ArgumentParser.__init__(self, usage=self.__doc__, **kwargs)
for cli, args in self.arguments:
self.add_argument(*cli, **args)
def parse_known_args(self, *args, **kwargs):
+ # Allow '-wo' or '-ow' as shorthand for both --workdir and --outgoing.
+ for token in ('-wo', '-ow'):
+ if token in args[0]:
+ i = args[0].index(token)
+ args[0].pop(i)
+ args[0][i:i] = [token[:2], '-' + token[2]]
+
# This is here so the eslint mach command doesn't lose 'extra_args'
# when using mach's dispatch functionality.
args, extra = ArgumentParser.parse_known_args(self, *args, **kwargs)
args.extra_args = extra
return args, extra
def find_linters(linters=None):
--- a/python/mozlint/mozlint/roller.py
+++ b/python/mozlint/mozlint/roller.py
@@ -106,17 +106,17 @@ class LintRoller(object):
elif isinstance(paths, (list, tuple)):
paths = set(paths)
if not self.linters:
raise LintersNotConfigured
# Calculate files from VCS
if workdir:
- paths.update(self.vcs.by_workdir())
+ paths.update(self.vcs.by_workdir(workdir))
if outgoing:
paths.update(self.vcs.by_outgoing(outgoing))
if not paths and (workdir or outgoing):
print("warning: no files linted")
return {}
paths = paths or ['.']
--- a/python/mozlint/mozlint/vcs.py
+++ b/python/mozlint/mozlint/vcs.py
@@ -39,31 +39,31 @@ class VCSHelper(object):
files = subprocess.check_output(cmd, stderr=subprocess.STDOUT).split()
except subprocess.CalledProcessError as e:
if e.output:
print(' '.join(cmd))
print(e.output)
return []
return [os.path.join(self.root, f) for f in files if f]
- def by_workdir(self, workdir):
+ def by_workdir(self, mode):
return []
def by_outgoing(self, dest='default'):
return []
class HgHelper(VCSHelper):
"""A helper to find files to lint from Mercurial."""
def by_outgoing(self, dest='default'):
return self.run(['hg', 'outgoing', '--quiet', '--template',
"{file_mods % '\\n{file}'}{file_adds % '\\n{file}'}", '-r', '.', dest])
- def by_workdir(self):
+ def by_workdir(self, mode):
return self.run(['hg', 'status', '-amn'])
class GitHelper(VCSHelper):
"""A helper to find files to lint from Git."""
_default = None
@property
@@ -90,17 +90,22 @@ class GitHelper(VCSHelper):
if not self.default:
print("warning: could not find default push, specify a remote for --outgoing")
return []
dest = self.default
comparing = '{}..HEAD'.format(self.default)
return self.run(['git', 'log', '--name-only', '--diff-filter=AM',
'--oneline', '--pretty=format:', comparing])
- def by_workdir(self):
- return self.run(['git', 'diff', '--name-only', '--diff-filter=AM', 'HEAD'])
+ def by_workdir(self, mode):
+ cmd = ['git', 'diff', '--name-only', '--diff-filter=AM']
+ if mode == 'staged':
+ cmd.append('--cached')
+ else:
+ cmd.append('HEAD')
+ return self.run(cmd)
vcs_class = {
'git': GitHelper,
'hg': HgHelper,
'none': VCSHelper,
}
--- a/python/mozlint/test/test_vcs.py
+++ b/python/mozlint/test/test_vcs.py
@@ -97,21 +97,26 @@ def test_vcs_helper(repo):
vcs = VCSHelper.create()
assert vcs.__class__ == vcs_class[repo.vcs]
assert vcs.root == repo.strpath
remotepath = '../remoterepo' if repo.vcs == 'hg' else 'upstream/master'
next(repo.setup)
- assert_files(vcs.by_workdir(), ['bar', 'baz'])
+ assert_files(vcs.by_workdir('all'), ['bar', 'baz'])
+ if repo.vcs == 'git':
+ assert_files(vcs.by_workdir('staged'), ['baz'])
+ elif repo.vcs == 'hg':
+ assert_files(vcs.by_workdir('staged'), ['bar', 'baz'])
assert_files(vcs.by_outgoing(), [])
assert_files(vcs.by_outgoing(remotepath), [])
next(repo.setup)
- assert_files(vcs.by_workdir(), [])
+ assert_files(vcs.by_workdir('all'), [])
+ assert_files(vcs.by_workdir('staged'), [])
assert_files(vcs.by_outgoing(), ['bar', 'baz'])
assert_files(vcs.by_outgoing(remotepath), ['bar', 'baz'])
if __name__ == '__main__':
sys.exit(pytest.main(['--verbose', __file__]))