Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions pre_commit_hooks/check_merge_conflict.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

CONFLICT_PATTERNS = [
b'<<<<<<< ',
b'======= ',
b'=======\r\n',
b'=======\n',
b'=======',
b'>>>>>>> ',
]
N = len(CONFLICT_PATTERNS)


def is_in_merge() -> bool:
Expand All @@ -40,15 +39,21 @@ def main(argv: Sequence[str] | None = None) -> int:
retcode = 0
for filename in args.filenames:
with open(filename, 'rb') as inputfile:
expected_conflict_pattern_index = 0
for i, line in enumerate(inputfile, start=1):
for pattern in CONFLICT_PATTERNS:
if line.startswith(pattern):
# Look for conflict patterns in order
if line.startswith(
CONFLICT_PATTERNS[expected_conflict_pattern_index],
):
expected_conflict_pattern_index += 1
if expected_conflict_pattern_index == N:
print(
f'{filename}:{i}: Merge conflict string '
f'{pattern.strip().decode()!r} found',
f"{filename}:{i}: Merge conflict string "
f"{CONFLICT_PATTERNS[-1].strip().decode()!r} "
f"found",
)
retcode = 1

break
return retcode


Expand Down
39 changes: 30 additions & 9 deletions tests/check_merge_conflict_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,30 @@ def test_merge_conflicts_git(capsys):
assert main(['f1']) == 1
out, _ = capsys.readouterr()
assert out == (
"f1:1: Merge conflict string '<<<<<<<' found\n"
"f1:3: Merge conflict string '=======' found\n"
"f1:5: Merge conflict string '>>>>>>>' found\n"
)


@pytest.mark.parametrize(
'contents', (b'<<<<<<< HEAD\n', b'=======\n', b'>>>>>>> main\n'),
# Individual markers are not actually merge conflicts, need 3 markers
# to mark a real conflict
'contents, expected_retcode',
(
(b'<<<<<<< ', 0),
(b'=======', 0),
(b'>>>>>>> ', 0),
# Real conflict marker
(b'<<<<<<< HEAD\n=======\n>>>>>>> branch\n', 1),
# Allow for the possibility of an .rst file with a =======
# inside a conflict marker
(b'<<<<<<< HEAD\n=======\n=======\n>>>>>>> branch\n', 1),
),
)
def test_merge_conflicts_failing(contents, repository_pending_merge):
repository_pending_merge.join('f2').write_binary(contents)
assert main(['f2']) == 1
def test_merge_conflicts_with_rst(
contents, expected_retcode, repository_pending_merge,
):
repository_pending_merge.join('f2.rst').write_binary(contents)
assert main(['f2.rst']) == expected_retcode


@pytest.mark.parametrize(
Expand All @@ -139,10 +151,19 @@ def test_does_not_care_when_not_in_a_merge(tmpdir):
assert main([str(f.realpath())]) == 0


def test_care_when_assumed_merge(tmpdir):
@pytest.mark.parametrize(
'contents, expected_retcode',
(
# Not a complete conflict marker
(b'=======', 0),
# Complete conflict marker
(b'<<<<<<< HEAD\nproblem\n=======\n>>>>>>> branch\n', 1),
),
)
def test_care_when_assumed_merge(contents, expected_retcode, tmpdir):
f = tmpdir.join('README.md')
f.write_binary(b'problem\n=======\n')
assert main([str(f.realpath()), '--assume-in-merge']) == 1
f.write_binary(contents)
assert main([str(f.realpath()), '--assume-in-merge']) == expected_retcode


def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir, capsys):
Expand Down