Look for all 3 git conflict markers in check_merge_conflict.

This commit is contained in:
Anton I. Sipos 2025-12-19 13:16:42 -08:00
parent 5dcc56558c
commit 19609da705
No known key found for this signature in database
GPG key ID: 327E0FD283F0D9AD
2 changed files with 42 additions and 18 deletions

View file

@ -9,11 +9,10 @@ from pre_commit_hooks.util import cmd_output
CONFLICT_PATTERNS = [
b'<<<<<<< ',
b'======= ',
b'=======\r\n',
b'=======\n',
b'=======',
b'>>>>>>> ',
]
N = len(CONFLICT_PATTERNS)
def is_in_merge() -> bool:
@ -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

View file

@ -105,18 +105,29 @@ 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(
@ -138,11 +149,19 @@ def test_does_not_care_when_not_in_a_merge(tmpdir):
f.write_binary(b'problem\n=======\n')
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):