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 | 45 | EXTENSIONS = {} |
|
46 | 46 | |
|
47 | 47 | __version__ = ('.'.join((str(each) for each in VERSION[:3]))) |
|
48 |
__dbversion__ = 10 |
|
|
48 | __dbversion__ = 104 # defines current db version for migrations | |
|
49 | 49 | __platform__ = platform.system() |
|
50 | 50 | __license__ = 'AGPLv3, and Commercial License' |
|
51 | 51 | __author__ = 'RhodeCode GmbH' |
@@ -629,7 +629,7 b' class TestPullrequestsView(object):' | |||
|
629 | 629 | model_patcher = mock.patch.multiple( |
|
630 | 630 | PullRequestModel, |
|
631 | 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 | 634 | with model_patcher: |
|
635 | 635 | response = self.app.post( |
@@ -891,6 +891,8 b' class TestPullrequestsView(object):' | |||
|
891 | 891 | |
|
892 | 892 | vcs = repo.scm_instance() |
|
893 | 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 | 897 | response = self.app.get(route_path( |
|
896 | 898 | 'pullrequest_show', |
@@ -396,6 +396,7 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
396 | 396 | pull_request_latest, auth_user=self._rhodecode_user, |
|
397 | 397 | translator=self.request.translate, |
|
398 | 398 | force_shadow_repo_refresh=force_refresh) |
|
399 | ||
|
399 | 400 | c.pr_merge_errors = _merge_check.error_details |
|
400 | 401 | c.pr_merge_possible = not _merge_check.failed |
|
401 | 402 | c.pr_merge_message = _merge_check.merge_msg |
@@ -537,6 +538,13 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
537 | 538 | (ancestor_commit, commit_cache, missing_requirements, |
|
538 | 539 | source_commit, target_commit) = cached_diff['commits'] |
|
539 | 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 | 548 | diff_commit_cache = \ |
|
541 | 549 | (ancestor_commit, commit_cache, missing_requirements, |
|
542 | 550 | source_commit, target_commit) = self.get_commits( |
@@ -547,7 +555,7 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
547 | 555 | source_scm, |
|
548 | 556 | target_commit, |
|
549 | 557 | target_ref_id, |
|
550 | target_scm) | |
|
558 | target_scm, maybe_unreachable=maybe_unreachable) | |
|
551 | 559 | |
|
552 | 560 | # register our commit range |
|
553 | 561 | for comm in commit_cache.values(): |
@@ -698,15 +706,22 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
698 | 706 | |
|
699 | 707 | def get_commits( |
|
700 | 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 | 712 | commit_cache = collections.OrderedDict() |
|
703 | 713 | missing_requirements = False |
|
714 | ||
|
704 | 715 | try: |
|
705 | 716 | pre_load = ["author", "date", "message", "branch", "parents"] |
|
706 | show_revs = pull_request_at_ver.revisions | |
|
707 | for rev in show_revs: | |
|
708 | comm = commits_source_repo.get_commit( | |
|
709 | commit_id=rev, pre_load=pre_load) | |
|
717 | ||
|
718 | pull_request_commits = pull_request_at_ver.revisions | |
|
719 | log.debug('Loading %s commits from %s', | |
|
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 | 725 | commit_cache[comm.raw_id] = comm |
|
711 | 726 | |
|
712 | 727 | # Order here matters, we first need to get target, and then |
@@ -715,14 +730,12 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
715 | 730 | commit_id=safe_str(target_ref_id)) |
|
716 | 731 | |
|
717 | 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 | 734 | except CommitDoesNotExistError: |
|
720 | log.warning( | |
|
721 | 'Failed to get commit from `{}` repo'.format( | |
|
722 | commits_source_repo), exc_info=True) | |
|
735 | log.warning('Failed to get commit from `{}` repo'.format( | |
|
736 | commits_source_repo), exc_info=True) | |
|
723 | 737 | except RepositoryRequirementError: |
|
724 | log.warning( | |
|
725 | 'Failed to get all required data from repo', exc_info=True) | |
|
738 | log.warning('Failed to get all required data from repo', exc_info=True) | |
|
726 | 739 | missing_requirements = True |
|
727 | 740 | ancestor_commit = None |
|
728 | 741 | try: |
@@ -688,14 +688,17 b' def get_clone_url(request, uri_tmpl, rep' | |||
|
688 | 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 | 694 | Safe version of get_commit if this commit doesn't exists for a |
|
694 | 695 | repository it returns a Dummy one instead |
|
695 | 696 | |
|
696 | 697 | :param repo: repository instance |
|
697 | 698 | :param commit_id: commit id as str |
|
699 | :param commit_idx: numeric commit index | |
|
698 | 700 | :param pre_load: optional list of commit attributes to load |
|
701 | :param maybe_unreachable: translate unreachable commits on git repos | |
|
699 | 702 | """ |
|
700 | 703 | # TODO(skreft): remove these circular imports |
|
701 | 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 | 710 | try: |
|
708 | 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 | 714 | except (RepositoryError, LookupError): |
|
711 | 715 | commit = EmptyCommit() |
|
712 | 716 | return commit |
@@ -216,6 +216,7 b' class MergeResponse(object):' | |||
|
216 | 216 | Return a human friendly error message for the given merge status code. |
|
217 | 217 | """ |
|
218 | 218 | msg = safe_unicode(self.MERGE_STATUS_MESSAGES[self.failure_reason]) |
|
219 | ||
|
219 | 220 | try: |
|
220 | 221 | return msg.format(**self.metadata) |
|
221 | 222 | except Exception: |
@@ -437,7 +438,8 b' class BaseRepository(object):' | |||
|
437 | 438 | self._invalidate_prop_cache('commit_ids') |
|
438 | 439 | self._is_empty = False |
|
439 | 440 | |
|
440 |
def get_commit(self, commit_id=None, commit_idx=None, pre_load=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 | 444 | Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` |
|
443 | 445 | are both None, most recent commit is returned. |
@@ -228,12 +228,13 b' class GitRepository(BaseRepository):' | |||
|
228 | 228 | return [] |
|
229 | 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 | 232 | def is_null(value): |
|
233 | 233 | return len(value) == commit_id_or_idx.count('0') |
|
234 | 234 | |
|
235 | 235 | if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1): |
|
236 | 236 | return self.commit_ids[-1] |
|
237 | ||
|
237 | 238 | commit_missing_err = "Commit {} does not exist for `{}`".format( |
|
238 | 239 | *map(safe_str, [commit_id_or_idx, self.name])) |
|
239 | 240 | |
@@ -248,7 +249,8 b' class GitRepository(BaseRepository):' | |||
|
248 | 249 | elif is_bstr: |
|
249 | 250 | # Need to call remote to translate id for tagging scenario |
|
250 | 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 | 254 | commit_id_or_idx = remote_data["commit_id"] |
|
253 | 255 | except (CommitDoesNotExistError,): |
|
254 | 256 | raise CommitDoesNotExistError(commit_missing_err) |
@@ -410,7 +412,8 b' class GitRepository(BaseRepository):' | |||
|
410 | 412 | except Exception: |
|
411 | 413 | return |
|
412 | 414 | |
|
413 |
def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, |
|
|
415 | def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, | |
|
416 | translate_tag=True, maybe_unreachable=False): | |
|
414 | 417 | """ |
|
415 | 418 | Returns `GitCommit` object representing commit from git repository |
|
416 | 419 | at the given `commit_id` or head (most recent commit) if None given. |
@@ -440,7 +443,7 b' class GitRepository(BaseRepository):' | |||
|
440 | 443 | commit_id = "tip" |
|
441 | 444 | |
|
442 | 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 | 448 | try: |
|
446 | 449 | idx = self._commit_ids[commit_id] |
@@ -662,6 +665,13 b' class GitRepository(BaseRepository):' | |||
|
662 | 665 | self._remote.remove_ref(ref_name) |
|
663 | 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 | 675 | def _update_server_info(self): |
|
666 | 676 | """ |
|
667 | 677 | runs gits update-server-info command in this repo instance |
@@ -827,8 +837,7 b' class GitRepository(BaseRepository):' | |||
|
827 | 837 | |
|
828 | 838 | if self.is_empty(): |
|
829 | 839 | # TODO(skreft): do something more robust in this case. |
|
830 | raise RepositoryError( | |
|
831 | 'Do not know how to merge into empty repositories yet') | |
|
840 | raise RepositoryError('Do not know how to merge into empty repositories yet') | |
|
832 | 841 | unresolved = None |
|
833 | 842 | |
|
834 | 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 | 845 | cmd = ['-c', 'user.name="%s"' % safe_str(user_name), |
|
837 | 846 | '-c', 'user.email=%s' % safe_str(user_email), |
|
838 | 847 | 'merge', '--no-ff', '-m', safe_str(merge_message)] |
|
839 | cmd.extend(heads) | |
|
848 | ||
|
849 | merge_cmd = cmd + heads | |
|
850 | ||
|
840 | 851 | try: |
|
841 |
|
|
|
852 | self.run_git_command(merge_cmd, fail_on_stderr=False) | |
|
842 | 853 | except RepositoryError: |
|
843 | 854 | files = self.run_git_command(['diff', '--name-only', '--diff-filter', 'U'], |
|
844 | 855 | fail_on_stderr=False)[0].splitlines() |
@@ -846,6 +857,7 b' class GitRepository(BaseRepository):' | |||
|
846 | 857 | unresolved = ['U {}'.format(f) for f in files] |
|
847 | 858 | |
|
848 | 859 | # Cleanup any merge leftovers |
|
860 | self._remote.invalidate_vcs_cache() | |
|
849 | 861 | self.run_git_command(['merge', '--abort'], fail_on_stderr=False) |
|
850 | 862 | |
|
851 | 863 | if unresolved: |
@@ -429,7 +429,8 b' class MercurialRepository(BaseRepository' | |||
|
429 | 429 | """ |
|
430 | 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, |
|
|
432 | def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, | |
|
433 | translate_tag=None, maybe_unreachable=False): | |
|
433 | 434 | """ |
|
434 | 435 | Returns ``MercurialCommit`` object representing repository's |
|
435 | 436 | commit at the given `commit_id` or `commit_idx`. |
@@ -276,7 +276,8 b' class SubversionRepository(base.BaseRepo' | |||
|
276 | 276 | """ |
|
277 | 277 | return os.path.join(self.path, 'hooks') |
|
278 | 278 | |
|
279 |
def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, |
|
|
279 | def get_commit(self, commit_id=None, commit_idx=None, pre_load=None, | |
|
280 | translate_tag=None, maybe_unreachable=False): | |
|
280 | 281 | if self.is_empty(): |
|
281 | 282 | raise EmptyRepositoryError("There are no commits yet") |
|
282 | 283 | if commit_id is not None: |
@@ -2339,9 +2339,10 b' class Repository(Base, BaseModel):' | |||
|
2339 | 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 | 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 | 2347 | def get_changeset(self, rev=None, pre_load=None): |
|
2347 | 2348 | warnings.warn("Use get_commit", DeprecationWarning) |
@@ -4024,6 +4025,10 b' class _PullRequestBase(BaseModel):' | |||
|
4024 | 4025 | _last_merge_target_rev = Column( |
|
4025 | 4026 | 'last_merge_other_rev', String(40), nullable=True) |
|
4026 | 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 | 4032 | merge_rev = Column('merge_rev', String(40), nullable=True) |
|
4028 | 4033 | |
|
4029 | 4034 | reviewer_data = Column( |
@@ -4123,10 +4128,11 b' class _PullRequestBase(BaseModel):' | |||
|
4123 | 4128 | |
|
4124 | 4129 | pull_request = self |
|
4125 | 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 | 4133 | merge_state = { |
|
4128 |
'status': merge_status |
|
|
4129 |
'message': safe_unicode(m |
|
|
4134 | 'status': merge_status, | |
|
4135 | 'message': safe_unicode(msg), | |
|
4130 | 4136 | } |
|
4131 | 4137 | else: |
|
4132 | 4138 | merge_state = {'status': 'not_available', |
@@ -885,6 +885,7 b' class PullRequestModel(BaseModel):' | |||
|
885 | 885 | version._last_merge_source_rev = pull_request._last_merge_source_rev |
|
886 | 886 | version._last_merge_target_rev = pull_request._last_merge_target_rev |
|
887 | 887 | version.last_merge_status = pull_request.last_merge_status |
|
888 | version.last_merge_metadata = pull_request.last_merge_metadata | |
|
888 | 889 | version.shadow_merge_ref = pull_request.shadow_merge_ref |
|
889 | 890 | version.merge_rev = pull_request.merge_rev |
|
890 | 891 | version.reviewer_data = pull_request.reviewer_data |
@@ -1349,30 +1350,28 b' class PullRequestModel(BaseModel):' | |||
|
1349 | 1350 | |
|
1350 | 1351 | return comment, status |
|
1351 | 1352 | |
|
1352 | def merge_status(self, pull_request, translator=None, | |
|
1353 | force_shadow_repo_refresh=False): | |
|
1353 | def merge_status(self, pull_request, translator=None, force_shadow_repo_refresh=False): | |
|
1354 | 1354 | _ = translator or get_current_request().translate |
|
1355 | 1355 | |
|
1356 | 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 | 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 | 1362 | merge_possible, msg = self._check_repo_requirements( |
|
1361 | 1363 | target=pull_request.target_repo, source=pull_request.source_repo, |
|
1362 | 1364 | translator=_) |
|
1363 | 1365 | if not merge_possible: |
|
1364 | return merge_possible, msg | |
|
1366 | return None, merge_possible, msg | |
|
1365 | 1367 | |
|
1366 | 1368 | try: |
|
1367 | resp = self._try_merge( | |
|
1368 | pull_request, | |
|
1369 | force_shadow_repo_refresh=force_shadow_repo_refresh) | |
|
1370 | log.debug("Merge response: %s", resp) | |
|
1371 | status = resp.possible, resp.merge_status_message | |
|
1369 | merge_response = self._try_merge( | |
|
1370 | pull_request, force_shadow_repo_refresh=force_shadow_repo_refresh) | |
|
1371 | log.debug("Merge response: %s", merge_response) | |
|
1372 | return merge_response, merge_response.possible, merge_response.merge_status_message | |
|
1372 | 1373 | except NotImplementedError: |
|
1373 |
|
|
|
1374 | ||
|
1375 | return status | |
|
1374 | return None, False, _('Pull request merging is not supported.') | |
|
1376 | 1375 | |
|
1377 | 1376 | def _check_repo_requirements(self, target, source, translator): |
|
1378 | 1377 | """ |
@@ -1439,6 +1438,9 b' class PullRequestModel(BaseModel):' | |||
|
1439 | 1438 | 'target_ref': pull_request.target_ref_parts, |
|
1440 | 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 | 1444 | if not possible and target_ref.type == 'branch': |
|
1443 | 1445 | # NOTE(marcink): case for mercurial multiple heads on branch |
|
1444 | 1446 | heads = target_vcs._heads(target_ref.name) |
@@ -1447,6 +1449,7 b' class PullRequestModel(BaseModel):' | |||
|
1447 | 1449 | metadata.update({ |
|
1448 | 1450 | 'heads': heads |
|
1449 | 1451 | }) |
|
1452 | ||
|
1450 | 1453 | merge_state = MergeResponse( |
|
1451 | 1454 | possible, False, None, pull_request.last_merge_status, metadata=metadata) |
|
1452 | 1455 | |
@@ -1487,6 +1490,8 b' class PullRequestModel(BaseModel):' | |||
|
1487 | 1490 | pull_request.source_ref_parts.commit_id |
|
1488 | 1491 | pull_request._last_merge_target_rev = target_reference.commit_id |
|
1489 | 1492 | pull_request.last_merge_status = merge_state.failure_reason |
|
1493 | pull_request.last_merge_metadata = merge_state.metadata | |
|
1494 | ||
|
1490 | 1495 | pull_request.shadow_merge_ref = merge_state.merge_ref |
|
1491 | 1496 | Session().add(pull_request) |
|
1492 | 1497 | Session().commit() |
@@ -1627,7 +1632,7 b' class PullRequestModel(BaseModel):' | |||
|
1627 | 1632 | target_commit = source_repo.get_commit( |
|
1628 | 1633 | commit_id=safe_str(target_ref_id)) |
|
1629 | 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 | 1636 | if isinstance(source_repo, Repository): |
|
1632 | 1637 | vcs_repo = source_repo.scm_instance() |
|
1633 | 1638 | else: |
@@ -1730,10 +1735,15 b' class MergeCheck(object):' | |||
|
1730 | 1735 | self.review_status = None |
|
1731 | 1736 | self.merge_possible = None |
|
1732 | 1737 | self.merge_msg = '' |
|
1738 | self.merge_response = None | |
|
1733 | 1739 | self.failed = None |
|
1734 | 1740 | self.errors = [] |
|
1735 | 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 | 1747 | def push_error(self, error_type, message, error_key, details): |
|
1738 | 1748 | self.failed = True |
|
1739 | 1749 | self.errors.append([error_type, message]) |
@@ -1822,11 +1832,14 b' class MergeCheck(object):' | |||
|
1822 | 1832 | return merge_check |
|
1823 | 1833 | |
|
1824 | 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 | 1836 | pull_request, translator=translator, |
|
1827 | 1837 | force_shadow_repo_refresh=force_shadow_repo_refresh) |
|
1838 | ||
|
1828 | 1839 | merge_check.merge_possible = merge_status |
|
1829 | 1840 | merge_check.merge_msg = msg |
|
1841 | merge_check.merge_response = merge_response | |
|
1842 | ||
|
1830 | 1843 | if not merge_status: |
|
1831 | 1844 | log.debug("MergeCheck: cannot merge, pull request merge not possible.") |
|
1832 | 1845 | merge_check.push_error('warning', msg, cls.MERGE_CHECK, None) |
@@ -169,7 +169,7 b' class TestPullRequestModel(object):' | |||
|
169 | 169 | assert pull_request._last_merge_target_rev is None |
|
170 | 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 | 173 | assert status is True |
|
174 | 174 | assert msg == 'This pull request can be automatically merged.' |
|
175 | 175 | self.merge_mock.assert_called_with( |
@@ -184,7 +184,7 b' class TestPullRequestModel(object):' | |||
|
184 | 184 | assert pull_request.last_merge_status is MergeFailureReason.NONE |
|
185 | 185 | |
|
186 | 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 | 188 | assert status is True |
|
189 | 189 | assert msg == 'This pull request can be automatically merged.' |
|
190 | 190 | assert self.merge_mock.called is False |
@@ -198,7 +198,7 b' class TestPullRequestModel(object):' | |||
|
198 | 198 | assert pull_request._last_merge_target_rev is None |
|
199 | 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 | 202 | assert status is False |
|
203 | 203 | assert msg == 'This pull request cannot be merged because of merge conflicts. file1' |
|
204 | 204 | self.merge_mock.assert_called_with( |
@@ -213,9 +213,9 b' class TestPullRequestModel(object):' | |||
|
213 | 213 | assert pull_request.last_merge_status is MergeFailureReason.MERGE_FAILED |
|
214 | 214 | |
|
215 | 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 | 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 | 219 | assert self.merge_mock.called is False |
|
220 | 220 | |
|
221 | 221 | def test_merge_status_unknown_failure(self, pull_request): |
@@ -227,7 +227,7 b' class TestPullRequestModel(object):' | |||
|
227 | 227 | assert pull_request._last_merge_target_rev is None |
|
228 | 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 | 231 | assert status is False |
|
232 | 232 | assert msg == ( |
|
233 | 233 | 'This pull request cannot be merged because of an unhandled exception. ' |
@@ -244,7 +244,7 b' class TestPullRequestModel(object):' | |||
|
244 | 244 | assert pull_request.last_merge_status is None |
|
245 | 245 | |
|
246 | 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 | 248 | assert status is False |
|
249 | 249 | assert msg == ( |
|
250 | 250 | 'This pull request cannot be merged because of an unhandled exception. ' |
@@ -253,7 +253,7 b' class TestPullRequestModel(object):' | |||
|
253 | 253 | |
|
254 | 254 | def test_merge_status_when_target_is_locked(self, pull_request): |
|
255 | 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 | 257 | assert status is False |
|
258 | 258 | assert msg == ( |
|
259 | 259 | 'This pull request cannot be merged because the target repository ' |
@@ -266,7 +266,7 b' class TestPullRequestModel(object):' | |||
|
266 | 266 | |
|
267 | 267 | patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) |
|
268 | 268 | with patcher: |
|
269 | status, msg = PullRequestModel().merge_status(pull_request) | |
|
269 | merge_response, status, msg = PullRequestModel().merge_status(pull_request) | |
|
270 | 270 | |
|
271 | 271 | assert status is False |
|
272 | 272 | assert msg == 'Target repository large files support is disabled.' |
@@ -278,7 +278,7 b' class TestPullRequestModel(object):' | |||
|
278 | 278 | |
|
279 | 279 | patcher = mock.patch.object(PullRequestModel, '_has_largefiles', has_largefiles) |
|
280 | 280 | with patcher: |
|
281 | status, msg = PullRequestModel().merge_status(pull_request) | |
|
281 | merge_response, status, msg = PullRequestModel().merge_status(pull_request) | |
|
282 | 282 | |
|
283 | 283 | assert status is False |
|
284 | 284 | assert msg == 'Source repository large files support is disabled.' |
General Comments 0
You need to be logged in to leave comments.
Login now