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