Merge pull request #47 from guykisel/merge-conflict-hook

Add check-merge-conflict hook
diff --git a/README.md b/README.md
index e980a98..b4c7ee8 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,7 @@
 - `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..ac405b2
--- /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 + 1))
+                        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 @@
             '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