From fdc2a889deb1550cfca7b85b3f4d9fa6ee9901e9 Mon Sep 17 00:00:00 2001 From: 8geese Date: Sun, 3 May 2015 22:09:51 -0700 Subject: [PATCH] fix for #157 --- pre_commit/commands/run.py | 22 ++++++++++++++ pre_commit/main.py | 5 ++++ tests/commands/run_test.py | 59 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py index a9e59d55..d0b092fe 100644 --- a/pre_commit/commands/run.py +++ b/pre_commit/commands/run.py @@ -139,6 +139,18 @@ def _has_unmerged_paths(runner): return bool(stdout.strip()) +def _has_unstaged_config(runner): + retcode, stdout, stderr = runner.cmd_runner.run( + [ + 'git', 'diff', '--exit-code', runner.config_file_path + ], + retcode=None, + encoding=None, + ) + # be explicit, other git errors don't mean it has an unstaged config. + return retcode == 1 + + def run(runner, args, write=sys_stdout_write_wrapper, environ=os.environ): # Set up our logging handler logger.addHandler(LoggingHandler(args.color, write=write)) @@ -151,6 +163,16 @@ def run(runner, args, write=sys_stdout_write_wrapper, environ=os.environ): if bool(args.source) != bool(args.origin): logger.error('Specify both --origin and --source.') return 1 + if _has_unstaged_config(runner) and not args.no_stash: + if args.allow_unstaged_config: + logger.warn('You have an unstaged config file and have ' + 'specified the --allow-unstaged-config option.\n' + 'Note that your config will be stashed before the ' + 'config is parsed unless --no-stash is specified.') + else: + logger.error('You have an unstaged config file and have not ' + 'specified the --allow-unstaged-config option.\n') + return 1 # Don't stash if specified or files are specified if args.no_stash or args.all_files or args.files: diff --git a/pre_commit/main.py b/pre_commit/main.py index 7ad7c0e8..e0b86b30 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -94,6 +94,11 @@ def main(argv=None): '--source', '-s', help='The remote branch"s commit_id when using `git push`', ) + run_parser.add_argument( + '--allow-unstaged-config', default=False, action='store_true', + help='Allow an unstaged config to be present. Note that this will' + 'be stashed before parsing unless --no-stash is specified' + ) run_mutex_group = run_parser.add_mutually_exclusive_group(required=False) run_mutex_group.add_argument( '--all-files', '-a', action='store_true', default=False, diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py index 9cca610a..2ffd2be7 100644 --- a/tests/commands/run_test.py +++ b/tests/commands/run_test.py @@ -53,6 +53,7 @@ def _get_opts( no_stash=False, origin='', source='', + allow_unstaged_config=False, ): # These are mutually exclusive assert not (all_files and files) @@ -65,6 +66,7 @@ def _get_opts( no_stash=no_stash, origin=origin, source=source, + allow_unstaged_config=allow_unstaged_config, ) @@ -334,3 +336,60 @@ def test_lots_of_files(mock_out_store_directory, tmpdir_factory): stderr=subprocess.STDOUT, env=env, ) + + +def test_allow_unstaged_config_option(repo_with_passing_hook, + mock_out_store_directory): + + with cwd(repo_with_passing_hook): + with io.open( + '.pre-commit-config.yaml', 'a+', + ) as config_file: + # writing a newline should be relatively harmless to get a change + config_file.write('\n') + + args = _get_opts(allow_unstaged_config=True) + ret, printed = _do_run(repo_with_passing_hook, args) + common_msg = 'You have an unstaged config file' + warning_msg = 'have specified the --allow-unstaged-config option.' + + assert common_msg in printed + assert warning_msg in printed + assert ret == 0 + + +def test_no_allow_unstaged_config_option(repo_with_passing_hook, + mock_out_store_directory): + + with cwd(repo_with_passing_hook): + with io.open( + '.pre-commit-config.yaml', 'a+', + ) as config_file: + # writing a newline should be relatively harmless to get a change + config_file.write('\n') + + args = _get_opts(allow_unstaged_config=False) + ret, printed = _do_run(repo_with_passing_hook, args) + common_msg = 'You have an unstaged config file' + error_msg = 'have not specified the --allow-unstaged-config option.\n' + + assert common_msg in printed + assert error_msg in printed + assert ret == 1 + + +def test_no_stash_suppresses_allow_unstaged_config_option( + repo_with_passing_hook, mock_out_store_directory): + + with cwd(repo_with_passing_hook): + with io.open( + '.pre-commit-config.yaml', 'a+', + ) as config_file: + # writing a newline should be relatively harmless to get a change + config_file.write('\n') + + args = _get_opts(allow_unstaged_config=False, no_stash=True) + ret, printed = _do_run(repo_with_passing_hook, args) + common_msg = 'You have an unstaged config file' + + assert common_msg not in printed