Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 1 | from __future__ import absolute_import |
| 2 | from __future__ import unicode_literals |
| 3 | |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 4 | import os |
Anthony Sottile | 4a01f64 | 2016-05-26 11:20:32 -0700 | [diff] [blame] | 5 | import shutil |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 6 | |
| 7 | import pytest |
| 8 | |
| 9 | from pre_commit_hooks.check_merge_conflict import detect_merge_conflict |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 10 | from pre_commit_hooks.util import cmd_output |
Anthony Sottile | 4a01f64 | 2016-05-26 11:20:32 -0700 | [diff] [blame] | 11 | from testing.util import get_resource_path |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 12 | |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 13 | |
Anthony Sottile | 4ab7914 | 2018-01-21 15:31:23 -0800 | [diff] [blame] | 14 | @pytest.fixture |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 15 | def f1_is_a_conflict_file(tmpdir): |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 16 | # Make a merge conflict |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 17 | repo1 = tmpdir.join('repo1') |
| 18 | repo1_f1 = repo1.join('f1') |
| 19 | repo2 = tmpdir.join('repo2') |
| 20 | repo2_f1 = repo2.join('f1') |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 21 | |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 22 | cmd_output('git', 'init', '--', repo1.strpath) |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 23 | with repo1.as_cwd(): |
| 24 | repo1_f1.ensure() |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 25 | cmd_output('git', 'add', '--', repo1_f1.strpath) |
| 26 | cmd_output('git', 'commit', '--no-gpg-sign', '-m', 'commit1') |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 27 | |
| 28 | cmd_output('git', 'clone', repo1.strpath, repo2.strpath) |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 29 | |
| 30 | # Commit in master |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 31 | with repo1.as_cwd(): |
| 32 | repo1_f1.write('parent\n') |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 33 | cmd_output('git', 'commit', '--no-gpg-sign', '-am', 'master commit2') |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 34 | |
| 35 | # Commit in clone and pull |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 36 | with repo2.as_cwd(): |
| 37 | repo2_f1.write('child\n') |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 38 | cmd_output('git', 'commit', '--no-gpg-sign', '-am', 'clone commit2') |
Anthony Sottile | da8ab1f | 2018-03-26 08:33:11 -0700 | [diff] [blame] | 39 | cmd_output('git', 'pull', '--no-rebase', retcode=None) |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 40 | # We should end up in a merge conflict! |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 41 | f1 = repo2_f1.read() |
William Ting | c92b465 | 2016-04-30 20:35:33 -0700 | [diff] [blame] | 42 | assert f1.startswith( |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 43 | '<<<<<<< HEAD\n' |
| 44 | 'child\n' |
| 45 | '=======\n' |
| 46 | 'parent\n' |
Anthony Sottile | 2a902e0 | 2017-07-12 18:35:24 -0700 | [diff] [blame] | 47 | '>>>>>>>', |
William Ting | c92b465 | 2016-04-30 20:35:33 -0700 | [diff] [blame] | 48 | ) or f1.startswith( |
| 49 | '<<<<<<< HEAD\n' |
| 50 | 'child\n' |
| 51 | # diff3 conflict style git merges add this line: |
| 52 | '||||||| merged common ancestors\n' |
| 53 | '=======\n' |
| 54 | 'parent\n' |
Anthony Sottile | 2a902e0 | 2017-07-12 18:35:24 -0700 | [diff] [blame] | 55 | '>>>>>>>', |
Anthony Sottile | 6076fd1 | 2017-06-12 10:39:07 -0700 | [diff] [blame] | 56 | ) or f1.startswith( |
| 57 | # .gitconfig with [pull] rebase = preserve causes a rebase which |
| 58 | # flips parent / child |
| 59 | '<<<<<<< HEAD\n' |
| 60 | 'parent\n' |
| 61 | '=======\n' |
| 62 | 'child\n' |
Anthony Sottile | 2a902e0 | 2017-07-12 18:35:24 -0700 | [diff] [blame] | 63 | '>>>>>>>', |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 64 | ) |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 65 | assert os.path.exists(os.path.join('.git', 'MERGE_MSG')) |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 66 | yield repo2 |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 67 | |
| 68 | |
Anthony Sottile | 4ab7914 | 2018-01-21 15:31:23 -0800 | [diff] [blame] | 69 | @pytest.fixture |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 70 | def repository_pending_merge(tmpdir): |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 71 | # Make a (non-conflicting) merge |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 72 | repo1 = tmpdir.join('repo1') |
| 73 | repo1_f1 = repo1.join('f1') |
| 74 | repo2 = tmpdir.join('repo2') |
| 75 | repo2_f1 = repo2.join('f1') |
| 76 | repo2_f2 = repo2.join('f2') |
| 77 | cmd_output('git', 'init', repo1.strpath) |
| 78 | with repo1.as_cwd(): |
| 79 | repo1_f1.ensure() |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 80 | cmd_output('git', 'add', '--', repo1_f1.strpath) |
| 81 | cmd_output('git', 'commit', '--no-gpg-sign', '-m', 'commit1') |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 82 | |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 83 | cmd_output('git', 'clone', repo1.strpath, repo2.strpath) |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 84 | |
| 85 | # Commit in master |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 86 | with repo1.as_cwd(): |
| 87 | repo1_f1.write('parent\n') |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 88 | cmd_output('git', 'commit', '--no-gpg-sign', '-am', 'master commit2') |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 89 | |
| 90 | # Commit in clone and pull without committing |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 91 | with repo2.as_cwd(): |
| 92 | repo2_f2.write('child\n') |
Sander Maijers | 9e89b76 | 2016-06-12 18:49:44 +0200 | [diff] [blame] | 93 | cmd_output('git', 'add', '--', repo2_f2.strpath) |
| 94 | cmd_output('git', 'commit', '--no-gpg-sign', '-m', 'clone commit2') |
Anthony Sottile | 6076fd1 | 2017-06-12 10:39:07 -0700 | [diff] [blame] | 95 | cmd_output('git', 'pull', '--no-commit', '--no-rebase') |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 96 | # We should end up in a pending merge |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 97 | assert repo2_f1.read() == 'parent\n' |
| 98 | assert repo2_f2.read() == 'child\n' |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 99 | assert os.path.exists(os.path.join('.git', 'MERGE_HEAD')) |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 100 | yield repo2 |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 101 | |
| 102 | |
| 103 | @pytest.mark.usefixtures('f1_is_a_conflict_file') |
| 104 | def test_merge_conflicts_git(): |
| 105 | assert detect_merge_conflict(['f1']) == 1 |
| 106 | |
| 107 | |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 108 | @pytest.mark.parametrize( |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 109 | 'contents', (b'<<<<<<< HEAD\n', b'=======\n', b'>>>>>>> master\n'), |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 110 | ) |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 111 | def test_merge_conflicts_failing(contents, repository_pending_merge): |
| 112 | repository_pending_merge.join('f2').write_binary(contents) |
Alexander Dupuy | 5c75293 | 2015-05-07 23:04:15 +0200 | [diff] [blame] | 113 | assert detect_merge_conflict(['f2']) == 1 |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 114 | |
| 115 | |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 116 | @pytest.mark.parametrize( |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 117 | 'contents', (b'# <<<<<<< HEAD\n', b'# =======\n', b'import mod', b''), |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 118 | ) |
Anthony Sottile | 9b1b44e | 2018-03-26 08:44:14 -0700 | [diff] [blame] | 119 | def test_merge_conflicts_ok(contents, f1_is_a_conflict_file): |
| 120 | f1_is_a_conflict_file.join('f1').write_binary(contents) |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 121 | assert detect_merge_conflict(['f1']) == 0 |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 122 | |
Guy Kisel | 779a429 | 2015-03-13 16:30:14 -0700 | [diff] [blame] | 123 | |
Anthony Sottile | 4a01f64 | 2016-05-26 11:20:32 -0700 | [diff] [blame] | 124 | @pytest.mark.usefixtures('f1_is_a_conflict_file') |
| 125 | def test_ignores_binary_files(): |
| 126 | shutil.copy(get_resource_path('img1.jpg'), 'f1') |
| 127 | assert detect_merge_conflict(['f1']) == 0 |
| 128 | |
| 129 | |
Anthony Sottile | a99475a | 2016-05-27 14:09:50 -0700 | [diff] [blame] | 130 | def test_does_not_care_when_not_in_a_merge(tmpdir): |
| 131 | tmpdir.join('README.md').write('problem\n=======\n') |
Anthony Sottile | 635fa7d | 2015-03-20 16:15:09 -0700 | [diff] [blame] | 132 | assert detect_merge_conflict(['README.md']) == 0 |