2022-01-16 08:24:05 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2015-05-08 05:04:15 +08:00
|
|
|
import os
|
2016-05-27 02:20:32 +08:00
|
|
|
import shutil
|
2015-03-14 07:30:14 +08:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2019-02-01 11:19:10 +08:00
|
|
|
from pre_commit_hooks.check_merge_conflict import main
|
2015-03-21 07:15:09 +08:00
|
|
|
from pre_commit_hooks.util import cmd_output
|
2016-05-27 02:20:32 +08:00
|
|
|
from testing.util import get_resource_path
|
2021-06-23 08:10:13 +08:00
|
|
|
from testing.util import git_commit
|
2015-03-21 07:15:09 +08:00
|
|
|
|
|
|
|
|
2018-01-22 07:31:23 +08:00
|
|
|
@pytest.fixture
|
2016-05-28 05:09:50 +08:00
|
|
|
def f1_is_a_conflict_file(tmpdir):
|
2015-03-21 07:15:09 +08:00
|
|
|
# Make a merge conflict
|
2016-05-28 05:09:50 +08:00
|
|
|
repo1 = tmpdir.join('repo1')
|
|
|
|
repo1_f1 = repo1.join('f1')
|
|
|
|
repo2 = tmpdir.join('repo2')
|
|
|
|
repo2_f1 = repo2.join('f1')
|
|
|
|
|
2020-05-21 00:07:45 +08:00
|
|
|
cmd_output('git', 'init', '--', str(repo1))
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo1.as_cwd():
|
|
|
|
repo1_f1.ensure()
|
2019-03-11 04:42:05 +08:00
|
|
|
cmd_output('git', 'add', '.')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-m', 'commit1')
|
2015-03-14 07:30:14 +08:00
|
|
|
|
2020-05-21 00:07:45 +08:00
|
|
|
cmd_output('git', 'clone', str(repo1), str(repo2))
|
2015-03-21 07:15:09 +08:00
|
|
|
|
|
|
|
# Commit in master
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo1.as_cwd():
|
|
|
|
repo1_f1.write('parent\n')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-am', 'master commit2')
|
2015-03-21 07:15:09 +08:00
|
|
|
|
|
|
|
# Commit in clone and pull
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo2.as_cwd():
|
|
|
|
repo2_f1.write('child\n')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-am', 'clone commit2')
|
2018-03-26 23:33:11 +08:00
|
|
|
cmd_output('git', 'pull', '--no-rebase', retcode=None)
|
2015-03-21 07:15:09 +08:00
|
|
|
# We should end up in a merge conflict!
|
2016-05-28 05:09:50 +08:00
|
|
|
f1 = repo2_f1.read()
|
2016-05-01 11:35:33 +08:00
|
|
|
assert f1.startswith(
|
2015-03-21 07:15:09 +08:00
|
|
|
'<<<<<<< HEAD\n'
|
|
|
|
'child\n'
|
|
|
|
'=======\n'
|
|
|
|
'parent\n'
|
2017-07-13 09:35:24 +08:00
|
|
|
'>>>>>>>',
|
2016-05-01 11:35:33 +08:00
|
|
|
) or f1.startswith(
|
|
|
|
'<<<<<<< HEAD\n'
|
|
|
|
'child\n'
|
|
|
|
# diff3 conflict style git merges add this line:
|
|
|
|
'||||||| merged common ancestors\n'
|
|
|
|
'=======\n'
|
|
|
|
'parent\n'
|
2017-07-13 09:35:24 +08:00
|
|
|
'>>>>>>>',
|
2017-06-13 01:39:07 +08:00
|
|
|
) or f1.startswith(
|
|
|
|
# .gitconfig with [pull] rebase = preserve causes a rebase which
|
|
|
|
# flips parent / child
|
|
|
|
'<<<<<<< HEAD\n'
|
|
|
|
'parent\n'
|
|
|
|
'=======\n'
|
|
|
|
'child\n'
|
2017-07-13 09:35:24 +08:00
|
|
|
'>>>>>>>',
|
2015-03-21 07:15:09 +08:00
|
|
|
)
|
2015-05-08 05:04:15 +08:00
|
|
|
assert os.path.exists(os.path.join('.git', 'MERGE_MSG'))
|
2018-03-26 23:44:14 +08:00
|
|
|
yield repo2
|
2015-03-21 07:15:09 +08:00
|
|
|
|
|
|
|
|
2018-01-22 07:31:23 +08:00
|
|
|
@pytest.fixture
|
2018-03-26 23:44:14 +08:00
|
|
|
def repository_pending_merge(tmpdir):
|
2015-05-08 05:04:15 +08:00
|
|
|
# Make a (non-conflicting) merge
|
2016-05-28 05:09:50 +08:00
|
|
|
repo1 = tmpdir.join('repo1')
|
|
|
|
repo1_f1 = repo1.join('f1')
|
|
|
|
repo2 = tmpdir.join('repo2')
|
|
|
|
repo2_f1 = repo2.join('f1')
|
|
|
|
repo2_f2 = repo2.join('f2')
|
2020-05-21 00:07:45 +08:00
|
|
|
cmd_output('git', 'init', str(repo1))
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo1.as_cwd():
|
|
|
|
repo1_f1.ensure()
|
2019-03-11 04:42:05 +08:00
|
|
|
cmd_output('git', 'add', '.')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-m', 'commit1')
|
2015-05-08 05:04:15 +08:00
|
|
|
|
2020-05-21 00:07:45 +08:00
|
|
|
cmd_output('git', 'clone', str(repo1), str(repo2))
|
2015-05-08 05:04:15 +08:00
|
|
|
|
|
|
|
# Commit in master
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo1.as_cwd():
|
|
|
|
repo1_f1.write('parent\n')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-am', 'master commit2')
|
2015-05-08 05:04:15 +08:00
|
|
|
|
|
|
|
# Commit in clone and pull without committing
|
2016-05-28 05:09:50 +08:00
|
|
|
with repo2.as_cwd():
|
|
|
|
repo2_f2.write('child\n')
|
2019-03-11 04:42:05 +08:00
|
|
|
cmd_output('git', 'add', '.')
|
2021-06-23 08:10:13 +08:00
|
|
|
git_commit('-m', 'clone commit2')
|
2017-06-13 01:39:07 +08:00
|
|
|
cmd_output('git', 'pull', '--no-commit', '--no-rebase')
|
2015-05-08 05:04:15 +08:00
|
|
|
# We should end up in a pending merge
|
2016-05-28 05:09:50 +08:00
|
|
|
assert repo2_f1.read() == 'parent\n'
|
|
|
|
assert repo2_f2.read() == 'child\n'
|
2015-05-08 05:04:15 +08:00
|
|
|
assert os.path.exists(os.path.join('.git', 'MERGE_HEAD'))
|
2018-03-26 23:44:14 +08:00
|
|
|
yield repo2
|
2015-05-08 05:04:15 +08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.usefixtures('f1_is_a_conflict_file')
|
2022-04-07 04:55:26 +08:00
|
|
|
def test_merge_conflicts_git(capsys):
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main(['f1']) == 1
|
2022-04-07 04:55:26 +08:00
|
|
|
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"
|
|
|
|
)
|
2015-05-08 05:04:15 +08:00
|
|
|
|
|
|
|
|
2015-03-21 07:15:09 +08:00
|
|
|
@pytest.mark.parametrize(
|
2018-03-26 23:44:14 +08:00
|
|
|
'contents', (b'<<<<<<< HEAD\n', b'=======\n', b'>>>>>>> master\n'),
|
2015-03-14 07:30:14 +08:00
|
|
|
)
|
2018-03-26 23:44:14 +08:00
|
|
|
def test_merge_conflicts_failing(contents, repository_pending_merge):
|
|
|
|
repository_pending_merge.join('f2').write_binary(contents)
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main(['f2']) == 1
|
2015-03-14 07:30:14 +08:00
|
|
|
|
|
|
|
|
2015-03-21 07:15:09 +08:00
|
|
|
@pytest.mark.parametrize(
|
2018-03-26 23:44:14 +08:00
|
|
|
'contents', (b'# <<<<<<< HEAD\n', b'# =======\n', b'import mod', b''),
|
2015-03-21 07:15:09 +08:00
|
|
|
)
|
2018-03-26 23:44:14 +08:00
|
|
|
def test_merge_conflicts_ok(contents, f1_is_a_conflict_file):
|
|
|
|
f1_is_a_conflict_file.join('f1').write_binary(contents)
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main(['f1']) == 0
|
2015-03-14 07:30:14 +08:00
|
|
|
|
|
|
|
|
2016-05-27 02:20:32 +08:00
|
|
|
@pytest.mark.usefixtures('f1_is_a_conflict_file')
|
|
|
|
def test_ignores_binary_files():
|
|
|
|
shutil.copy(get_resource_path('img1.jpg'), 'f1')
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main(['f1']) == 0
|
2016-05-27 02:20:32 +08:00
|
|
|
|
|
|
|
|
2016-05-28 05:09:50 +08:00
|
|
|
def test_does_not_care_when_not_in_a_merge(tmpdir):
|
2018-06-27 02:04:04 +08:00
|
|
|
f = tmpdir.join('README.md')
|
|
|
|
f.write_binary(b'problem\n=======\n')
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main([str(f.realpath())]) == 0
|
2018-06-27 02:04:04 +08:00
|
|
|
|
|
|
|
|
|
|
|
def test_care_when_assumed_merge(tmpdir):
|
|
|
|
f = tmpdir.join('README.md')
|
|
|
|
f.write_binary(b'problem\n=======\n')
|
2019-02-01 11:19:10 +08:00
|
|
|
assert main([str(f.realpath()), '--assume-in-merge']) == 1
|
2021-10-05 03:50:38 +08:00
|
|
|
|
|
|
|
|
2022-04-07 04:55:26 +08:00
|
|
|
def test_worktree_merge_conflicts(f1_is_a_conflict_file, tmpdir, capsys):
|
2021-10-05 03:50:38 +08:00
|
|
|
worktree = tmpdir.join('worktree')
|
|
|
|
cmd_output('git', 'worktree', 'add', str(worktree))
|
|
|
|
with worktree.as_cwd():
|
|
|
|
cmd_output(
|
|
|
|
'git', 'pull', '--no-rebase', 'origin', 'master', retcode=None,
|
|
|
|
)
|
|
|
|
msg = f1_is_a_conflict_file.join('.git/worktrees/worktree/MERGE_MSG')
|
|
|
|
assert msg.exists()
|
2022-04-07 04:55:26 +08:00
|
|
|
test_merge_conflicts_git(capsys)
|