##// END OF EJS Templates
pull-requests: fixed case for GIT repositories when a merge check failed due to merge conflicts the pull request wrongly reported missing commits....
marcink -
r4299:04e45b92 default
parent child Browse files
Show More
@@ -0,0 +1,52 b''
1 # -*- coding: utf-8 -*-
2
3 import logging
4 from sqlalchemy import *
5
6 from alembic.migration import MigrationContext
7 from alembic.operations import Operations
8 from sqlalchemy import BigInteger
9
10 from rhodecode.lib.dbmigrate.versions import _reset_base
11 from rhodecode.model import init_model_encryption
12
13
14 log = logging.getLogger(__name__)
15
16
17 def upgrade(migrate_engine):
18 """
19 Upgrade operations go here.
20 Don't create your own engine; bind migrate_engine to your metadata
21 """
22 _reset_base(migrate_engine)
23 from rhodecode.lib.dbmigrate.schema import db_4_18_0_1 as db
24
25 init_model_encryption(db)
26
27 context = MigrationContext.configure(migrate_engine.connect())
28 op = Operations(context)
29
30 pull_requests = db.PullRequest.__table__
31
32 with op.batch_alter_table(pull_requests.name) as batch_op:
33 new_column = Column(
34 'last_merge_metadata',
35 db.JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
36 batch_op.add_column(new_column)
37
38 pull_request_version = db.PullRequestVersion.__table__
39 with op.batch_alter_table(pull_request_version.name) as batch_op:
40 new_column = Column(
41 'last_merge_metadata',
42 db.JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
43 batch_op.add_column(new_column)
44
45
46 def downgrade(migrate_engine):
47 meta = MetaData()
48 meta.bind = migrate_engine
49
50
51 def fixups(models, _SESSION):
52 pass
@@ -45,7 +45,7 b' PYRAMID_SETTINGS = {}'
45 EXTENSIONS = {}
45 EXTENSIONS = {}
46
46
47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
47 __version__ = ('.'.join((str(each) for each in VERSION[:3])))
48 __dbversion__ = 103 # defines current db version for migrations
48 __dbversion__ = 104 # defines current db version for migrations
49 __platform__ = platform.system()
49 __platform__ = platform.system()
50 __license__ = 'AGPLv3, and Commercial License'
50 __license__ = 'AGPLv3, and Commercial License'
51 __author__ = 'RhodeCode GmbH'
51 __author__ = 'RhodeCode GmbH'
@@ -629,7 +629,7 b' class TestPullrequestsView(object):'
629 model_patcher = mock.patch.multiple(
629 model_patcher = mock.patch.multiple(
630 PullRequestModel,
630 PullRequestModel,
631 merge_repo=mock.Mock(return_value=merge_resp),
631 merge_repo=mock.Mock(return_value=merge_resp),
632 merge_status=mock.Mock(return_value=(True, 'WRONG_MESSAGE')))
632 merge_status=mock.Mock(return_value=(None, True, 'WRONG_MESSAGE')))
633
633
634 with model_patcher:
634 with model_patcher:
635 response = self.app.post(
635 response = self.app.post(
@@ -891,6 +891,8 b' class TestPullrequestsView(object):'
891
891
892 vcs = repo.scm_instance()
892 vcs = repo.scm_instance()
893 vcs.remove_ref('refs/heads/{}'.format(branch_name))
893 vcs.remove_ref('refs/heads/{}'.format(branch_name))
894 # NOTE(marcink): run GC to ensure the commits are gone
895 vcs.run_gc()
894
896
895 response = self.app.get(route_path(
897 response = self.app.get(route_path(
896 'pullrequest_show',
898 'pullrequest_show',
@@ -396,6 +396,7 b' class RepoPullRequestsView(RepoAppView, '
396 pull_request_latest, auth_user=self._rhodecode_user,
396 pull_request_latest, auth_user=self._rhodecode_user,
397 translator=self.request.translate,
397 translator=self.request.translate,
398 force_shadow_repo_refresh=force_refresh)
398 force_shadow_repo_refresh=force_refresh)
399
399 c.pr_merge_errors = _merge_check.error_details
400 c.pr_merge_errors = _merge_check.error_details
400 c.pr_merge_possible = not _merge_check.failed
401 c.pr_merge_possible = not _merge_check.failed
401 c.pr_merge_message = _merge_check.merge_msg
402 c.pr_merge_message = _merge_check.merge_msg
@@ -537,6 +538,13 b' class RepoPullRequestsView(RepoAppView, '
537 (ancestor_commit, commit_cache, missing_requirements,
538 (ancestor_commit, commit_cache, missing_requirements,
538 source_commit, target_commit) = cached_diff['commits']
539 source_commit, target_commit) = cached_diff['commits']
539 else:
540 else:
541 # NOTE(marcink): we reach potentially unreachable errors when a PR has
542 # merge errors resulting in potentially hidden commits in the shadow repo.
543 maybe_unreachable = _merge_check.MERGE_CHECK in _merge_check.error_details \
544 and _merge_check.merge_response
545 maybe_unreachable = maybe_unreachable \
546 and _merge_check.merge_response.metadata.get('unresolved_files')
547 log.debug("Using unreachable commits due to MERGE_CHECK in merge simulation")
540 diff_commit_cache = \
548 diff_commit_cache = \
541 (ancestor_commit, commit_cache, missing_requirements,
549 (ancestor_commit, commit_cache, missing_requirements,
542 source_commit, target_commit) = self.get_commits(
550 source_commit, target_commit) = self.get_commits(
@@ -547,7 +555,7 b' class RepoPullRequestsView(RepoAppView, '
547 source_scm,
555 source_scm,
548 target_commit,
556 target_commit,
549 target_ref_id,
557 target_ref_id,
550 target_scm)
558 target_scm, maybe_unreachable=maybe_unreachable)
551
559
552 # register our commit range
560 # register our commit range
553 for comm in commit_cache.values():
561 for comm in commit_cache.values():
@@ -698,15 +706,22 b' class RepoPullRequestsView(RepoAppView, '
698
706
699 def get_commits(
707 def get_commits(
700 self, commits_source_repo, pull_request_at_ver, source_commit,
708 self, commits_source_repo, pull_request_at_ver, source_commit,
701 source_ref_id, source_scm, target_commit, target_ref_id, target_scm):
709 source_ref_id, source_scm, target_commit, target_ref_id, target_scm,
710 maybe_unreachable=False):
711
702 commit_cache = collections.OrderedDict()
712 commit_cache = collections.OrderedDict()
703 missing_requirements = False
713 missing_requirements = False
714
704 try:
715 try:
705 pre_load = ["author", "date", "message", "branch", "parents"]
716 pre_load = ["author", "date", "message", "branch", "parents"]
706 show_revs = pull_request_at_ver.revisions
717
707 for rev in show_revs:
718 pull_request_commits = pull_request_at_ver.revisions
708 comm = commits_source_repo.get_commit(
719 log.debug('Loading %s commits from %s',
709 commit_id=rev, pre_load=pre_load)
720 len(pull_request_commits), commits_source_repo)
721
722 for rev in pull_request_commits:
723 comm = commits_source_repo.get_commit(commit_id=rev, pre_load=pre_load,
724 maybe_unreachable=maybe_unreachable)
710 commit_cache[comm.raw_id] = comm
725 commit_cache[comm.raw_id] = comm
711
726
712 # Order here matters, we first need to get target, and then
727 # Order here matters, we first need to get target, and then
@@ -715,14 +730,12 b' class RepoPullRequestsView(RepoAppView, '
715 commit_id=safe_str(target_ref_id))
730 commit_id=safe_str(target_ref_id))
716
731
717 source_commit = commits_source_repo.get_commit(
732 source_commit = commits_source_repo.get_commit(
718 commit_id=safe_str(source_ref_id))
733 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
719 except CommitDoesNotExistError:
734 except CommitDoesNotExistError:
720 log.warning(
735 log.warning('Failed to get commit from `{}` repo'.format(
721 'Failed to get commit from `{}` repo'.format(
736 commits_source_repo), exc_info=True)
722 commits_source_repo), exc_info=True)
723 except RepositoryRequirementError:
737 except RepositoryRequirementError:
724 log.warning(
738 log.warning('Failed to get all required data from repo', exc_info=True)
725 'Failed to get all required data from repo', exc_info=True)
726 missing_requirements = True
739 missing_requirements = True
727 ancestor_commit = None
740 ancestor_commit = None
728 try:
741 try:
@@ -688,14 +688,17 b' def get_clone_url(request, uri_tmpl, rep'
688 return safe_unicode(url)
688 return safe_unicode(url)
689
689
690
690
691 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
691 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None,
692 maybe_unreachable=False):
692 """
693 """
693 Safe version of get_commit if this commit doesn't exists for a
694 Safe version of get_commit if this commit doesn't exists for a
694 repository it returns a Dummy one instead
695 repository it returns a Dummy one instead
695
696
696 :param repo: repository instance
697 :param repo: repository instance
697 :param commit_id: commit id as str
698 :param commit_id: commit id as str
699 :param commit_idx: numeric commit index
698 :param pre_load: optional list of commit attributes to load
700 :param pre_load: optional list of commit attributes to load
701 :param maybe_unreachable: translate unreachable commits on git repos
699 """
702 """
700 # TODO(skreft): remove these circular imports
703 # TODO(skreft): remove these circular imports
701 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
704 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
@@ -706,7 +709,8 b' def get_commit_safe(repo, commit_id=None'
706
709
707 try:
710 try:
708 commit = repo.get_commit(
711 commit = repo.get_commit(
709 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
712 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load,
713 maybe_unreachable=maybe_unreachable)
710 except (RepositoryError, LookupError):
714 except (RepositoryError, LookupError):
711 commit = EmptyCommit()
715 commit = EmptyCommit()
712 return commit
716 return commit
@@ -216,6 +216,7 b' class MergeResponse(object):'
216 Return a human friendly error message for the given merge status code.
216 Return a human friendly error message for the given merge status code.
217 """
217 """
218 msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason])
218 msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason])
219
219 try:
220 try:
220 return msg.format(**self.metadata)
221 return msg.format(**self.metadata)
221 except Exception:
222 except Exception:
@@ -437,7 +438,8 b' class BaseRepository(object):'
437 self._invalidate_prop_cache('commit_ids')
438 self._invalidate_prop_cache('commit_ids')
438 self._is_empty = False
439 self._is_empty = False
439
440
440 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
441 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
442 translate_tag=None, maybe_unreachable=False):
441 """
443 """
442 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
444 Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx`
443 are both None, most recent commit is returned.
445 are both None, most recent commit is returned.
@@ -228,12 +228,13 b' class GitRepository(BaseRepository):'
228 return []
228 return []
229 return output.splitlines()
229 return output.splitlines()
230
230
231 def _lookup_commit(self, commit_id_or_idx, translate_tag=True):
231 def _lookup_commit(self, commit_id_or_idx, translate_tag=True, maybe_unreachable=False):
232 def is_null(value):
232 def is_null(value):
233 return len(value) == commit_id_or_idx.count('0')
233 return len(value) == commit_id_or_idx.count('0')
234
234
235 if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
235 if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
236 return self.commit_ids[-1]
236 return self.commit_ids[-1]
237
237 commit_missing_err = "Commit {} does not exist for `{}`".format(
238 commit_missing_err = "Commit {} does not exist for `{}`".format(
238 *map(safe_str, [commit_id_or_idx, self.name]))
239 *map(safe_str, [commit_id_or_idx, self.name]))
239
240
@@ -248,7 +249,8 b' class GitRepository(BaseRepository):'
248 elif is_bstr:
249 elif is_bstr:
249 # Need to call remote to translate id for tagging scenario
250 # Need to call remote to translate id for tagging scenario
250 try:
251 try:
251 remote_data = self._remote.get_object(commit_id_or_idx)
252 remote_data = self._remote.get_object(commit_id_or_idx,
253 maybe_unreachable=maybe_unreachable)
252 commit_id_or_idx = remote_data["commit_id"]
254 commit_id_or_idx = remote_data["commit_id"]
253 except (CommitDoesNotExistError,):
255 except (CommitDoesNotExistError,):
254 raise CommitDoesNotExistError(commit_missing_err)
256 raise CommitDoesNotExistError(commit_missing_err)
@@ -410,7 +412,8 b' class GitRepository(BaseRepository):'
410 except Exception:
412 except Exception:
411 return
413 return
412
414
413 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=True):
415 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
416 translate_tag=True, maybe_unreachable=False):
414 """
417 """
415 Returns `GitCommit` object representing commit from git repository
418 Returns `GitCommit` object representing commit from git repository
416 at the given `commit_id` or head (most recent commit) if None given.
419 at the given `commit_id` or head (most recent commit) if None given.
@@ -440,7 +443,7 b' class GitRepository(BaseRepository):'
440 commit_id = "tip"
443 commit_id = "tip"
441
444
442 if translate_tag:
445 if translate_tag:
443 commit_id = self._lookup_commit(commit_id)
446 commit_id = self._lookup_commit(commit_id, maybe_unreachable=maybe_unreachable)
444
447
445 try:
448 try:
446 idx = self._commit_ids[commit_id]
449 idx = self._commit_ids[commit_id]
@@ -662,6 +665,13 b' class GitRepository(BaseRepository):'
662 self._remote.remove_ref(ref_name)
665 self._remote.remove_ref(ref_name)
663 self._invalidate_prop_cache('_refs')
666 self._invalidate_prop_cache('_refs')
664
667
668 def run_gc(self, prune=True):
669 cmd = ['gc', '--aggressive']
670 if prune:
671 cmd += ['--prune=now']
672 _stdout, stderr = self.run_git_command(cmd, fail_on_stderr=False)
673 return stderr
674
665 def _update_server_info(self):
675 def _update_server_info(self):
666 """
676 """
667 runs gits update-server-info command in this repo instance
677 runs gits update-server-info command in this repo instance
@@ -827,8 +837,7 b' class GitRepository(BaseRepository):'
827
837
828 if self.is_empty():
838 if self.is_empty():
829 # TODO(skreft): do something more robust in this case.
839 # TODO(skreft): do something more robust in this case.
830 raise RepositoryError(
840 raise RepositoryError('Do not know how to merge into empty repositories yet')
831 'Do not know how to merge into empty repositories yet')
832 unresolved = None
841 unresolved = None
833
842
834 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
843 # N.B.(skreft): the --no-ff option is used to enforce the creation of a
@@ -836,9 +845,11 b' class GitRepository(BaseRepository):'
836 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
845 cmd = ['-c', 'user.name="%s"' % safe_str(user_name),
837 '-c', 'user.email=%s' % safe_str(user_email),
846 '-c', 'user.email=%s' % safe_str(user_email),
838 'merge', '--no-ff', '-m', safe_str(merge_message)]
847 'merge', '--no-ff', '-m', safe_str(merge_message)]
839 cmd.extend(heads)
848
849 merge_cmd = cmd + heads
850
840 try:
851 try:
841 output = self.run_git_command(cmd, fail_on_stderr=False)
852 self.run_git_command(merge_cmd, fail_on_stderr=False)
842 except RepositoryError:
853 except RepositoryError:
843 files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'],
854 files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'],
844 fail_on_stderr=False)[0].splitlines()
855 fail_on_stderr=False)[0].splitlines()
@@ -846,6 +857,7 b' class GitRepository(BaseRepository):'
846 unresolved = ['U {}'.format(f) for f in files]
857 unresolved = ['U {}'.format(f) for f in files]
847
858
848 # Cleanup any merge leftovers
859 # Cleanup any merge leftovers
860 self._remote.invalidate_vcs_cache()
849 self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
861 self.run_git_command(['merge', '--abort'], fail_on_stderr=False)
850
862
851 if unresolved:
863 if unresolved:
@@ -429,7 +429,8 b' class MercurialRepository(BaseRepository'
429 """
429 """
430 return os.path.join(self.path, '.hg', '.hgrc')
430 return os.path.join(self.path, '.hg', '.hgrc')
431
431
432 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
432 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
433 translate_tag=None, maybe_unreachable=False):
433 """
434 """
434 Returns ``MercurialCommit`` object representing repository's
435 Returns ``MercurialCommit`` object representing repository's
435 commit at the given `commit_id` or `commit_idx`.
436 commit at the given `commit_id` or `commit_idx`.
@@ -276,7 +276,8 b' class SubversionRepository(base.BaseRepo'
276 """
276 """
277 return os.path.join(self.path, 'hooks')
277 return os.path.join(self.path, 'hooks')
278
278
279 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, translate_tag=None):
279 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None,
280 translate_tag=None, maybe_unreachable=False):
280 if self.is_empty():
281 if self.is_empty():
281 raise EmptyRepositoryError("There are no commits yet")
282 raise EmptyRepositoryError("There are no commits yet")
282 if commit_id is not None:
283 if commit_id is not None:
@@ -2339,9 +2339,10 b' class Repository(Base, BaseModel):'
2339 # SCM PROPERTIES
2339 # SCM PROPERTIES
2340 #==========================================================================
2340 #==========================================================================
2341
2341
2342 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
2342 def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, maybe_unreachable=False):
2343 return get_commit_safe(
2343 return get_commit_safe(
2344 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load)
2344 self.scm_instance(), commit_id, commit_idx, pre_load=pre_load,
2345 maybe_unreachable=maybe_unreachable)
2345
2346
2346 def get_changeset(self, rev=None, pre_load=None):
2347 def get_changeset(self, rev=None, pre_load=None):
2347 warnings.warn("Use get_commit", DeprecationWarning)
2348 warnings.warn("Use get_commit", DeprecationWarning)
@@ -4024,6 +4025,10 b' class _PullRequestBase(BaseModel):'
4024 _last_merge_target_rev = Column(
4025 _last_merge_target_rev = Column(
4025 'last_merge_other_rev', String(40), nullable=True)
4026 'last_merge_other_rev', String(40), nullable=True)
4026 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4027 _last_merge_status = Column('merge_status', Integer(), nullable=True)
4028 last_merge_metadata = Column(
4029 'last_merge_metadata', MutationObj.as_mutable(
4030 JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
4031
4027 merge_rev = Column('merge_rev', String(40), nullable=True)
4032 merge_rev = Column('merge_rev', String(40), nullable=True)
4028
4033
4029 reviewer_data = Column(
4034 reviewer_data = Column(
@@ -4123,10 +4128,11 b' class _PullRequestBase(BaseModel):'
4123
4128
4124 pull_request = self
4129 pull_request = self
4125 if with_merge_state:
4130 if with_merge_state:
4126 merge_status = PullRequestModel().merge_status(pull_request)
4131 merge_response, merge_status, msg = \
4132 PullRequestModel().merge_status(pull_request)
4127 merge_state = {
4133 merge_state = {
4128 'status': merge_status[0],
4134 'status': merge_status,
4129 'message': safe_unicode(merge_status[1]),
4135 'message': safe_unicode(msg),
4130 }
4136 }
4131 else:
4137 else:
4132 merge_state = {'status': 'not_available',
4138 merge_state = {'status': 'not_available',
@@ -885,6 +885,7 b' class PullRequestModel(BaseModel):'
885 version._last_merge_source_rev = pull_request._last_merge_source_rev
885 version._last_merge_source_rev = pull_request._last_merge_source_rev
886 version._last_merge_target_rev = pull_request._last_merge_target_rev
886 version._last_merge_target_rev = pull_request._last_merge_target_rev
887 version.last_merge_status = pull_request.last_merge_status
887 version.last_merge_status = pull_request.last_merge_status
888 version.last_merge_metadata = pull_request.last_merge_metadata
888 version.shadow_merge_ref = pull_request.shadow_merge_ref
889 version.shadow_merge_ref = pull_request.shadow_merge_ref
889 version.merge_rev = pull_request.merge_rev
890 version.merge_rev = pull_request.merge_rev
890 version.reviewer_data = pull_request.reviewer_data
891 version.reviewer_data = pull_request.reviewer_data
@@ -1349,30 +1350,28 b' class PullRequestModel(BaseModel):'
1349
1350
1350 return comment, status
1351 return comment, status
1351
1352
1352 def merge_status(self, pull_request, translator=None,
1353 def merge_status(self, pull_request, translator=None, force_shadow_repo_refresh=False):
1353 force_shadow_repo_refresh=False):
1354 _ = translator or get_current_request().translate
1354 _ = translator or get_current_request().translate
1355
1355
1356 if not self._is_merge_enabled(pull_request):
1356 if not self._is_merge_enabled(pull_request):
1357 return False, _('Server-side pull request merging is disabled.')
1357 return None, False, _('Server-side pull request merging is disabled.')
1358
1358 if pull_request.is_closed():
1359 if pull_request.is_closed():
1359 return False, _('This pull request is closed.')
1360 return None, False, _('This pull request is closed.')
1361
1360 merge_possible, msg = self._check_repo_requirements(
1362 merge_possible, msg = self._check_repo_requirements(
1361 target=pull_request.target_repo, source=pull_request.source_repo,
1363 target=pull_request.target_repo, source=pull_request.source_repo,
1362 translator=_)
1364 translator=_)
1363 if not merge_possible:
1365 if not merge_possible:
1364 return merge_possible, msg
1366 return None, merge_possible, msg
1365
1367
1366 try:
1368 try:
1367 resp = self._try_merge(
1369 merge_response = self._try_merge(
1368 pull_request,
1370 pull_request, force_shadow_repo_refresh=force_shadow_repo_refresh)
1369 force_shadow_repo_refresh=force_shadow_repo_refresh)
1371 log.debug("Merge response: %s", merge_response)
1370 log.debug("Merge response: %s", resp)
1372 return merge_response, merge_response.possible, merge_response.merge_status_message
1371 status = resp.possible, resp.merge_status_message
1372 except NotImplementedError:
1373 except NotImplementedError:
1373 status = False, _('Pull request merging is not supported.')
1374 return None, False, _('Pull request merging is not supported.')
1374
1375 return status
1376
1375
1377 def _check_repo_requirements(self, target, source, translator):
1376 def _check_repo_requirements(self, target, source, translator):
1378 """
1377 """
@@ -1439,6 +1438,9 b' class PullRequestModel(BaseModel):'
1439 'target_ref': pull_request.target_ref_parts,
1438 'target_ref': pull_request.target_ref_parts,
1440 'source_ref': pull_request.source_ref_parts,
1439 'source_ref': pull_request.source_ref_parts,
1441 }
1440 }
1441 if pull_request.last_merge_metadata:
1442 metadata.update(pull_request.last_merge_metadata)
1443
1442 if not possible and target_ref.type == 'branch':
1444 if not possible and target_ref.type == 'branch':
1443 # NOTE(marcink): case for mercurial multiple heads on branch
1445 # NOTE(marcink): case for mercurial multiple heads on branch
1444 heads = target_vcs._heads(target_ref.name)
1446 heads = target_vcs._heads(target_ref.name)
@@ -1447,6 +1449,7 b' class PullRequestModel(BaseModel):'
1447 metadata.update({
1449 metadata.update({
1448 'heads': heads
1450 'heads': heads
1449 })
1451 })
1452
1450 merge_state = MergeResponse(
1453 merge_state = MergeResponse(
1451 possible, False, None, pull_request.last_merge_status, metadata=metadata)
1454 possible, False, None, pull_request.last_merge_status, metadata=metadata)
1452
1455
@@ -1487,6 +1490,8 b' class PullRequestModel(BaseModel):'
1487 pull_request.source_ref_parts.commit_id
1490 pull_request.source_ref_parts.commit_id
1488 pull_request._last_merge_target_rev = target_reference.commit_id
1491 pull_request._last_merge_target_rev = target_reference.commit_id
1489 pull_request.last_merge_status = merge_state.failure_reason
1492 pull_request.last_merge_status = merge_state.failure_reason
1493 pull_request.last_merge_metadata = merge_state.metadata
1494
1490 pull_request.shadow_merge_ref = merge_state.merge_ref
1495 pull_request.shadow_merge_ref = merge_state.merge_ref
1491 Session().add(pull_request)
1496 Session().add(pull_request)
1492 Session().commit()
1497 Session().commit()
@@ -1627,7 +1632,7 b' class PullRequestModel(BaseModel):'
1627 target_commit = source_repo.get_commit(
1632 target_commit = source_repo.get_commit(
1628 commit_id=safe_str(target_ref_id))
1633 commit_id=safe_str(target_ref_id))
1629 source_commit = source_repo.get_commit(
1634 source_commit = source_repo.get_commit(
1630 commit_id=safe_str(source_ref_id))
1635 commit_id=safe_str(source_ref_id), maybe_unreachable=True)
1631 if isinstance(source_repo, Repository):
1636 if isinstance(source_repo, Repository):
1632 vcs_repo = source_repo.scm_instance()
1637 vcs_repo = source_repo.scm_instance()
1633 else:
1638 else:
@@ -1730,10 +1735,15 b' class MergeCheck(object):'
1730 self.review_status = None
1735 self.review_status = None
1731 self.merge_possible = None
1736 self.merge_possible = None
1732 self.merge_msg = ''
1737 self.merge_msg = ''
1738 self.merge_response = None
1733 self.failed = None
1739 self.failed = None
1734 self.errors = []
1740 self.errors = []
1735 self.error_details = OrderedDict()
1741 self.error_details = OrderedDict()
1736
1742
1743 def __repr__(self):
1744 return '<MergeCheck(possible:{}, failed:{}, errors:{})>'.format(
1745 self.merge_possible, self.failed, self.errors)
1746
1737 def push_error(self, error_type, message, error_key, details):
1747 def push_error(self, error_type, message, error_key, details):
1738 self.failed = True
1748 self.failed = True
1739 self.errors.append([error_type, message])
1749 self.errors.append([error_type, message])
@@ -1822,11 +1832,14 b' class MergeCheck(object):'
1822 return merge_check
1832 return merge_check
1823
1833
1824 # merge possible, here is the filesystem simulation + shadow repo
1834 # merge possible, here is the filesystem simulation + shadow repo
1825 merge_status, msg = PullRequestModel().merge_status(
1835 merge_response, merge_status, msg = PullRequestModel().merge_status(
1826 pull_request, translator=translator,
1836 pull_request, translator=translator,
1827 force_shadow_repo_refresh=force_shadow_repo_refresh)
1837 force_shadow_repo_refresh=force_shadow_repo_refresh)
1838
1828 merge_check.merge_possible = merge_status
1839 merge_check.merge_possible = merge_status
1829 merge_check.merge_msg = msg
1840 merge_check.merge_msg = msg
1841 merge_check.merge_response = merge_response
1842
1830 if not merge_status:
1843 if not merge_status:
1831 log.debug("MergeCheck: cannot merge, pull request merge not possible.")
1844 log.debug("MergeCheck: cannot merge, pull request merge not possible.")
1832 merge_check.push_error('warning', msg, cls.MERGE_CHECK, None)
1845 merge_check.push_error('warning', msg, cls.MERGE_CHECK, None)
@@ -169,7 +169,7 b' class TestPullRequestModel(object):'
169 assert pull_request._last_merge_target_rev is None
169 assert pull_request._last_merge_target_rev is None
170 assert pull_request.last_merge_status is None
170 assert pull_request.last_merge_status is None
171
171
172 status, msg = PullRequestModel().merge_status(pull_request)
172 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
173 assert status is True
173 assert status is True
174 assert msg == 'This pull request can be automatically merged.'
174 assert msg == 'This pull request can be automatically merged.'
175 self.merge_mock.assert_called_with(
175 self.merge_mock.assert_called_with(
@@ -184,7 +184,7 b' class TestPullRequestModel(object):'
184 assert pull_request.last_merge_status is MergeFailureReason.NONE
184 assert pull_request.last_merge_status is MergeFailureReason.NONE
185
185
186 self.merge_mock.reset_mock()
186 self.merge_mock.reset_mock()
187 status, msg = PullRequestModel().merge_status(pull_request)
187 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
188 assert status is True
188 assert status is True
189 assert msg == 'This pull request can be automatically merged.'
189 assert msg == 'This pull request can be automatically merged.'
190 assert self.merge_mock.called is False
190 assert self.merge_mock.called is False
@@ -198,7 +198,7 b' class TestPullRequestModel(object):'
198 assert pull_request._last_merge_target_rev is None
198 assert pull_request._last_merge_target_rev is None
199 assert pull_request.last_merge_status is None
199 assert pull_request.last_merge_status is None
200
200
201 status, msg = PullRequestModel().merge_status(pull_request)
201 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
202 assert status is False
202 assert status is False
203 assert msg == 'This pull request cannot be merged because of merge conflicts. file1'
203 assert msg == 'This pull request cannot be merged because of merge conflicts. file1'
204 self.merge_mock.assert_called_with(
204 self.merge_mock.assert_called_with(
@@ -213,9 +213,9 b' class TestPullRequestModel(object):'
213 assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED
213 assert 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 merge_response, 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. file1'
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):
@@ -227,7 +227,7 b' class TestPullRequestModel(object):'
227 assert pull_request._last_merge_target_rev is None
227 assert pull_request._last_merge_target_rev is None
228 assert pull_request.last_merge_status is None
228 assert pull_request.last_merge_status is None
229
229
230 status, msg = PullRequestModel().merge_status(pull_request)
230 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
231 assert status is False
231 assert status is False
232 assert msg == (
232 assert msg == (
233 'This pull request cannot be merged because of an unhandled exception. '
233 'This pull request cannot be merged because of an unhandled exception. '
@@ -244,7 +244,7 b' class TestPullRequestModel(object):'
244 assert pull_request.last_merge_status is None
244 assert pull_request.last_merge_status is None
245
245
246 self.merge_mock.reset_mock()
246 self.merge_mock.reset_mock()
247 status, msg = PullRequestModel().merge_status(pull_request)
247 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
248 assert status is False
248 assert status is False
249 assert msg == (
249 assert msg == (
250 'This pull request cannot be merged because of an unhandled exception. '
250 'This pull request cannot be merged because of an unhandled exception. '
@@ -253,7 +253,7 b' class TestPullRequestModel(object):'
253
253
254 def test_merge_status_when_target_is_locked(self, pull_request):
254 def test_merge_status_when_target_is_locked(self, pull_request):
255 pull_request.target_repo.locked = [1, u'12345.50', 'lock_web']
255 pull_request.target_repo.locked = [1, u'12345.50', 'lock_web']
256 status, msg = PullRequestModel().merge_status(pull_request)
256 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
257 assert status is False
257 assert status is False
258 assert msg == (
258 assert msg == (
259 'This pull request cannot be merged because the target repository '
259 'This pull request cannot be merged because the target repository '
@@ -266,7 +266,7 b' class TestPullRequestModel(object):'
266
266
267 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
267 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
268 with patcher:
268 with patcher:
269 status, msg = PullRequestModel().merge_status(pull_request)
269 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
270
270
271 assert status is False
271 assert status is False
272 assert msg == 'Target repository large files support is disabled.'
272 assert msg == 'Target repository large files support is disabled.'
@@ -278,7 +278,7 b' class TestPullRequestModel(object):'
278
278
279 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
279 patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles)
280 with patcher:
280 with patcher:
281 status, msg = PullRequestModel().merge_status(pull_request)
281 merge_response, status, msg = PullRequestModel().merge_status(pull_request)
282
282
283 assert status is False
283 assert status is False
284 assert msg == 'Source repository large files support is disabled.'
284 assert msg == 'Source repository large files support is disabled.'
General Comments 0
You need to be logged in to leave comments. Login now