diff --git a/README.md b/README.md index e980a98..b4c7ee8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ Add this to your `.pre-commit-config.yaml` - `check-case-conflict` - Check for files that would conflict in case-insensitive filesystems. - `check-docstring-first` - Checks a common error of defining a docstring after code. - `check-json` - Attempts to load all json files to verify syntax. +- `check-merge-conflict` - Check for files that contain merge conflict strings. - `check-xml` - Attempts to load all xml files to verify syntax. - `check-yaml` - Attempts to load all yaml files to verify syntax. - `debug-statements` - Check for pdb / ipdb / pudb statements in code. diff --git a/hooks.yaml b/hooks.yaml index 0d84f03..595637f 100644 --- a/hooks.yaml +++ b/hooks.yaml @@ -31,6 +31,13 @@ entry: check-json language: python files: \.json$ +- id: check-merge-conflict + name: Check for merge conflicts + description: Check for files that contain merge conflict strings. + entry: check-merge-conflict + language: python + # Match all files + files: '' - id: check-xml name: Check Xml description: This hook checks xml files for parseable syntax. diff --git a/pre_commit_hooks/check_merge_conflict.py b/pre_commit_hooks/check_merge_conflict.py new file mode 100644 index 0000000..fc211bc --- /dev/null +++ b/pre_commit_hooks/check_merge_conflict.py @@ -0,0 +1,31 @@ +from __future__ import print_function + +import argparse +import sys + +CONFLICT_PATTERNS = [ + '<<<<<<< ', + '=======', + '>>>>>>> ' +] +WARNING_MSG = 'Merge conflict string "{0}" found in {1}:{2}' + + +def detect_merge_conflict(argv=None): + parser = argparse.ArgumentParser() + parser.add_argument('filenames', nargs='*') + args = parser.parse_args(argv) + + retcode = 0 + for filename in args.filenames: + with open(filename) as inputfile: + for i, line in enumerate(inputfile): + for pattern in CONFLICT_PATTERNS: + if line.startswith(pattern): + print(WARNING_MSG.format(pattern, filename, i)) + retcode = 1 + + return retcode + +if __name__ == '__main__': + sys.exit(detect_merge_conflict()) diff --git a/setup.py b/setup.py index e4f0900..0455b53 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ setup( 'check-added-large-files = pre_commit_hooks.check_added_large_files:main', 'check-case-conflict = pre_commit_hooks.check_case_conflict:main', 'check-docstring-first = pre_commit_hooks.check_docstring_first:main', + 'check-merge-conflict = pre_commit_hooks.check_merge_conflict:detect_merge_conflict', 'check-json = pre_commit_hooks.check_json:check_json', 'check-xml = pre_commit_hooks.check_xml:check_xml', 'check-yaml = pre_commit_hooks.check_yaml:check_yaml', diff --git a/tests/check_merge_conflict_test.py b/tests/check_merge_conflict_test.py new file mode 100644 index 0000000..bcf90eb --- /dev/null +++ b/tests/check_merge_conflict_test.py @@ -0,0 +1,26 @@ +import os.path + +import pytest + +from pre_commit_hooks.check_merge_conflict import detect_merge_conflict + +# Input, expected return value +TESTS = ( + (b'<<<<<<< HEAD', 1), + (b'=======', 1), + (b'>>>>>>> master', 1), + (b'# <<<<<<< HEAD', 0), + (b'# =======', 0), + (b'import my_module', 0), + (b'', 0), +) + + +@pytest.mark.parametrize(('input_s', 'expected_retval'), TESTS) +def test_detect_merge_conflict(input_s, expected_retval, tmpdir): + path = os.path.join(tmpdir.strpath, 'file.txt') + + with open(path, 'wb') as file_obj: + file_obj.write(input_s) + + assert detect_merge_conflict([path]) == expected_retval