Show More
@@ -153,7 +153,7 b' class MergeResponse(object):' | |||
|
153 | 153 | u'This pull request cannot be merged because of an unhandled exception. ' |
|
154 | 154 | u'{exception}'), |
|
155 | 155 | MergeFailureReason.MERGE_FAILED: lazy_ugettext( |
|
156 | u'This pull request cannot be merged because of merge conflicts.'), | |
|
156 | u'This pull request cannot be merged because of merge conflicts. {unresolved_files}'), | |
|
157 | 157 | MergeFailureReason.PUSH_FAILED: lazy_ugettext( |
|
158 | 158 | u'This pull request could not be merged because push to ' |
|
159 | 159 | u'target:`{target}@{merge_commit}` failed.'), |
@@ -42,7 +42,7 b' from rhodecode.lib.vcs.backends.git.diff' | |||
|
42 | 42 | from rhodecode.lib.vcs.backends.git.inmemory import GitInMemoryCommit |
|
43 | 43 | from rhodecode.lib.vcs.exceptions import ( |
|
44 | 44 | CommitDoesNotExistError, EmptyRepositoryError, |
|
45 | RepositoryError, TagAlreadyExistError, TagDoesNotExistError, VCSError) | |
|
45 | RepositoryError, TagAlreadyExistError, TagDoesNotExistError, VCSError, UnresolvedFilesInRepo) | |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | SHA_PATTERN = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$') |
@@ -826,9 +826,10 b' class GitRepository(BaseRepository):' | |||
|
826 | 826 | return |
|
827 | 827 | |
|
828 | 828 | if self.is_empty(): |
|
829 |
# TODO(skreft): do some |
|
|
829 | # TODO(skreft): do something more robust in this case. | |
|
830 | 830 | raise RepositoryError( |
|
831 | 831 | 'Do not know how to merge into empty repositories yet') |
|
832 | unresolved = None | |
|
832 | 833 | |
|
833 | 834 | # N.B.(skreft): the --no-ff option is used to enforce the creation of a |
|
834 | 835 | # commit message. We also specify the user who is doing the merge. |
@@ -839,9 +840,18 b' class GitRepository(BaseRepository):' | |||
|
839 | 840 | try: |
|
840 | 841 | output = self.run_git_command(cmd, fail_on_stderr=False) |
|
841 | 842 | except RepositoryError: |
|
843 | files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'], | |
|
844 | fail_on_stderr=False)[0].splitlines() | |
|
845 | # NOTE(marcink): we add U notation for consistent with HG backend output | |
|
846 | unresolved = ['U {}'.format(f) for f in files] | |
|
847 | ||
|
842 | 848 | # Cleanup any merge leftovers |
|
843 | 849 | self.run_git_command(['merge', '--abort'], fail_on_stderr=False) |
|
844 | raise | |
|
850 | ||
|
851 | if unresolved: | |
|
852 | raise UnresolvedFilesInRepo(unresolved) | |
|
853 | else: | |
|
854 | raise | |
|
845 | 855 | |
|
846 | 856 | def _local_push( |
|
847 | 857 | self, source_branch, repository_path, target_branch, |
@@ -977,8 +987,11 b' class GitRepository(BaseRepository):' | |||
|
977 | 987 | # the shadow repository. |
|
978 | 988 | shadow_repo.set_refs('refs/heads/pr-merge', merge_commit_id) |
|
979 | 989 | merge_ref = Reference('branch', 'pr-merge', merge_commit_id) |
|
980 | except RepositoryError: | |
|
990 | except RepositoryError as e: | |
|
981 | 991 | log.exception('Failure when doing local merge on git shadow repo') |
|
992 | if isinstance(e, UnresolvedFilesInRepo): | |
|
993 | metadata['unresolved_files'] = 'file: ' + (', file: '.join(e.args[0])) | |
|
994 | ||
|
982 | 995 | merge_possible = False |
|
983 | 996 | merge_failure_reason = MergeFailureReason.MERGE_FAILED |
|
984 | 997 |
@@ -42,7 +42,7 b' from rhodecode.lib.vcs.backends.hg.diff ' | |||
|
42 | 42 | from rhodecode.lib.vcs.backends.hg.inmemory import MercurialInMemoryCommit |
|
43 | 43 | from rhodecode.lib.vcs.exceptions import ( |
|
44 | 44 | EmptyRepositoryError, RepositoryError, TagAlreadyExistError, |
|
45 | TagDoesNotExistError, CommitDoesNotExistError, SubrepoMergeError) | |
|
45 | TagDoesNotExistError, CommitDoesNotExistError, SubrepoMergeError, UnresolvedFilesInRepo) | |
|
46 | 46 | from rhodecode.lib.vcs.compat import configparser |
|
47 | 47 | |
|
48 | 48 | hexlify = binascii.hexlify |
@@ -634,6 +634,7 b' class MercurialRepository(BaseRepository' | |||
|
634 | 634 | # In this case we should force a commit message |
|
635 | 635 | return source_ref.commit_id, True |
|
636 | 636 | |
|
637 | unresolved = None | |
|
637 | 638 | if use_rebase: |
|
638 | 639 | try: |
|
639 | 640 | bookmark_name = 'rcbook%s%s' % (source_ref.commit_id, |
@@ -644,17 +645,23 b' class MercurialRepository(BaseRepository' | |||
|
644 | 645 | self._remote.invalidate_vcs_cache() |
|
645 | 646 | self._update(bookmark_name, clean=True) |
|
646 | 647 | return self._identify(), True |
|
647 | except RepositoryError: | |
|
648 | except RepositoryError as e: | |
|
648 | 649 | # The rebase-abort may raise another exception which 'hides' |
|
649 | 650 | # the original one, therefore we log it here. |
|
650 | 651 | log.exception('Error while rebasing shadow repo during merge.') |
|
652 | if 'unresolved conflicts' in e.message: | |
|
653 | unresolved = self._remote.get_unresolved_files() | |
|
654 | log.debug('unresolved files: %s', unresolved) | |
|
651 | 655 | |
|
652 | 656 | # Cleanup any rebase leftovers |
|
653 | 657 | self._remote.invalidate_vcs_cache() |
|
654 | 658 | self._remote.rebase(abort=True) |
|
655 | 659 | self._remote.invalidate_vcs_cache() |
|
656 | 660 | self._remote.update(clean=True) |
|
657 |
|
|
|
661 | if unresolved: | |
|
662 | raise UnresolvedFilesInRepo(unresolved) | |
|
663 | else: | |
|
664 | raise | |
|
658 | 665 | else: |
|
659 | 666 | try: |
|
660 | 667 | self._remote.merge(source_ref.commit_id) |
@@ -664,10 +671,20 b' class MercurialRepository(BaseRepository' | |||
|
664 | 671 | username=safe_str('%s <%s>' % (user_name, user_email))) |
|
665 | 672 | self._remote.invalidate_vcs_cache() |
|
666 | 673 | return self._identify(), True |
|
667 | except RepositoryError: | |
|
674 | except RepositoryError as e: | |
|
675 | # The merge-abort may raise another exception which 'hides' | |
|
676 | # the original one, therefore we log it here. | |
|
677 | log.exception('Error while merging shadow repo during merge.') | |
|
678 | if 'unresolved merge conflicts' in e.message: | |
|
679 | unresolved = self._remote.get_unresolved_files() | |
|
680 | log.debug('unresolved files: %s', unresolved) | |
|
681 | ||
|
668 | 682 | # Cleanup any merge leftovers |
|
669 | 683 | self._remote.update(clean=True) |
|
670 |
|
|
|
684 | if unresolved: | |
|
685 | raise UnresolvedFilesInRepo(unresolved) | |
|
686 | else: | |
|
687 | raise | |
|
671 | 688 | |
|
672 | 689 | def _local_close(self, target_ref, user_name, user_email, |
|
673 | 690 | source_ref, close_message=''): |
@@ -810,8 +827,11 b' class MercurialRepository(BaseRepository' | |||
|
810 | 827 | merge_possible = False |
|
811 | 828 | merge_failure_reason = MergeFailureReason.SUBREPO_MERGE_FAILED |
|
812 | 829 | needs_push = False |
|
813 | except RepositoryError: | |
|
830 | except RepositoryError as e: | |
|
814 | 831 | log.exception('Failure when doing local merge on hg shadow repo') |
|
832 | if isinstance(e, UnresolvedFilesInRepo): | |
|
833 | metadata['unresolved_files'] = 'file: ' + (', file: '.join(e.args[0])) | |
|
834 | ||
|
815 | 835 | merge_possible = False |
|
816 | 836 | merge_failure_reason = MergeFailureReason.MERGE_FAILED |
|
817 | 837 | needs_push = False |
@@ -50,6 +50,10 b' class RepositoryRequirementError(Reposit' | |||
|
50 | 50 | pass |
|
51 | 51 | |
|
52 | 52 | |
|
53 | class UnresolvedFilesInRepo(RepositoryError): | |
|
54 | pass | |
|
55 | ||
|
56 | ||
|
53 | 57 | class VCSBackendNotSupportedError(VCSError): |
|
54 | 58 | """ |
|
55 | 59 | Exception raised when VCSServer does not support requested backend |
@@ -1335,6 +1335,7 b' class PullRequestModel(BaseModel):' | |||
|
1335 | 1335 | else: |
|
1336 | 1336 | possible = pull_request.last_merge_status == MergeFailureReason.NONE |
|
1337 | 1337 | metadata = { |
|
1338 | 'unresolved_files': '', | |
|
1338 | 1339 | 'target_ref': pull_request.target_ref_parts, |
|
1339 | 1340 | 'source_ref': pull_request.source_ref_parts, |
|
1340 | 1341 | } |
@@ -191,7 +191,8 b' class TestPullRequestModel(object):' | |||
|
191 | 191 | |
|
192 | 192 | def test_merge_status_known_failure(self, pull_request): |
|
193 | 193 | self.merge_mock.return_value = MergeResponse( |
|
194 |
False, False, None, MergeFailureReason.MERGE_FAILED |
|
|
194 | False, False, None, MergeFailureReason.MERGE_FAILED, | |
|
195 | metadata={'unresolved_files': 'file1'}) | |
|
195 | 196 | |
|
196 | 197 | assert pull_request._last_merge_source_rev is None |
|
197 | 198 | assert pull_request._last_merge_target_rev is None |
@@ -199,7 +200,7 b' class TestPullRequestModel(object):' | |||
|
199 | 200 | |
|
200 | 201 | status, msg = PullRequestModel().merge_status(pull_request) |
|
201 | 202 | assert status is False |
|
202 | assert msg == 'This pull request cannot be merged because of merge conflicts.' | |
|
203 | assert msg == 'This pull request cannot be merged because of merge conflicts. file1' | |
|
203 | 204 | self.merge_mock.assert_called_with( |
|
204 | 205 | self.repo_id, self.workspace_id, |
|
205 | 206 | pull_request.target_ref_parts, |
@@ -209,13 +210,12 b' class TestPullRequestModel(object):' | |||
|
209 | 210 | |
|
210 | 211 | assert pull_request._last_merge_source_rev == self.source_commit |
|
211 | 212 | assert pull_request._last_merge_target_rev == self.target_commit |
|
212 | assert ( | |
|
213 | pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED) | |
|
213 | assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED | |
|
214 | 214 | |
|
215 | 215 | self.merge_mock.reset_mock() |
|
216 | 216 | status, msg = PullRequestModel().merge_status(pull_request) |
|
217 | 217 | assert status is False |
|
218 | assert msg == 'This pull request cannot be merged because of merge conflicts.' | |
|
218 | assert msg == 'This pull request cannot be merged because of merge conflicts. ' | |
|
219 | 219 | assert self.merge_mock.called is False |
|
220 | 220 | |
|
221 | 221 | def test_merge_status_unknown_failure(self, pull_request): |
@@ -518,7 +518,7 b' def test_outdated_comments(' | |||
|
518 | 518 | (MergeFailureReason.UNKNOWN, |
|
519 | 519 | 'This pull request cannot be merged because of an unhandled exception. CRASH'), |
|
520 | 520 | (MergeFailureReason.MERGE_FAILED, |
|
521 | 'This pull request cannot be merged because of merge conflicts.'), | |
|
521 | 'This pull request cannot be merged because of merge conflicts. CONFLICT_FILE'), | |
|
522 | 522 | (MergeFailureReason.PUSH_FAILED, |
|
523 | 523 | 'This pull request could not be merged because push to target:`some-repo@merge_commit` failed.'), |
|
524 | 524 | (MergeFailureReason.TARGET_IS_NOT_HEAD, |
@@ -540,13 +540,15 b' def test_outdated_comments(' | |||
|
540 | 540 | def test_merge_response_message(mr_type, expected_msg): |
|
541 | 541 | merge_ref = Reference('type', 'ref_name', '6126b7bfcc82ad2d3deaee22af926b082ce54cc6') |
|
542 | 542 | metadata = { |
|
543 | 'unresolved_files': 'CONFLICT_FILE', | |
|
543 | 544 | 'exception': "CRASH", |
|
544 | 545 | 'target': 'some-repo', |
|
545 | 546 | 'merge_commit': 'merge_commit', |
|
546 | 547 | 'target_ref': merge_ref, |
|
547 | 548 | 'source_ref': merge_ref, |
|
548 | 549 | 'heads': ','.join(['a', 'b', 'c']), |
|
549 |
'locked_by': 'user:123' |
|
|
550 | 'locked_by': 'user:123' | |
|
551 | } | |
|
550 | 552 | |
|
551 | 553 | merge_response = MergeResponse(True, True, merge_ref, mr_type, metadata=metadata) |
|
552 | 554 | assert merge_response.merge_status_message == expected_msg |
General Comments 0
You need to be logged in to leave comments.
Login now