diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -24,22 +24,22 @@ # along with this program. If not, see . import logging -import binascii import datetime import re from pylons.i18n.translation import _ from rhodecode.model.meta import Session -from rhodecode.lib import helpers as h +from rhodecode.lib import helpers as h, unionrepo from rhodecode.model import BaseModel from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\ ChangesetStatus from rhodecode.model.notification import NotificationModel from rhodecode.lib.utils2 import safe_unicode -from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil, \ - findcommonoutgoing +from rhodecode.lib.vcs.utils.hgcompat import scmutil +from rhodecode.lib.vcs.utils import safe_str +from rhodecode.lib.vcs.backends.base import EmptyChangeset log = logging.getLogger(__name__) @@ -53,7 +53,10 @@ class PullRequestModel(BaseModel): def get_all(self, repo): repo = self._get_repo(repo) - return PullRequest.query().filter(PullRequest.other_repo == repo).all() + return PullRequest.query()\ + .filter(PullRequest.other_repo == repo)\ + .order_by(PullRequest.created_on.desc())\ + .all() def create(self, created_by, org_repo, org_ref, other_repo, other_ref, revisions, reviewers, title, description=None): @@ -72,13 +75,13 @@ class PullRequestModel(BaseModel): new.title = title new.description = description new.author = created_by_user - self.sa.add(new) + Session().add(new) Session().flush() #members - for member in reviewers: + for member in set(reviewers): _usr = self._get_user(member) reviewer = PullRequestReviewers(_usr, new) - self.sa.add(reviewer) + Session().add(reviewer) #reset state to under-review ChangesetStatusModel().set_status( @@ -87,7 +90,8 @@ class PullRequestModel(BaseModel): user=created_by_user, pull_request=new ) - + revision_data = [(x.raw_id, x.message) + for x in map(org_repo.get_changeset, revisions)] #notification to reviewers notif = NotificationModel() @@ -97,8 +101,9 @@ class PullRequestModel(BaseModel): ) subject = safe_unicode( h.link_to( - _('%(user)s wants you to review pull request #%(pr_id)s') % \ + _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \ {'user': created_by_user.username, + 'pr_title': new.title, 'pr_id': new.pull_request_id}, pr_url ) @@ -110,8 +115,9 @@ class PullRequestModel(BaseModel): 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name, qualified=True,), 'pr_url': pr_url, - 'pr_revisions': revisions + 'pr_revisions': revision_data } + notif.create(created_by=created_by_user, subject=subject, body=body, recipients=reviewers, type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs) @@ -135,7 +141,7 @@ class PullRequestModel(BaseModel): for uid in to_add: _usr = self._get_user(uid) reviewer = PullRequestReviewers(_usr, pull_request) - self.sa.add(reviewer) + Session().add(reviewer) for uid in to_remove: reviewer = PullRequestReviewers.query()\ @@ -143,7 +149,7 @@ class PullRequestModel(BaseModel): PullRequestReviewers.pull_request==pull_request)\ .scalar() if reviewer: - self.sa.delete(reviewer) + Session().delete(reviewer) def delete(self, pull_request): pull_request = self.__get_pull_request(pull_request) @@ -153,13 +159,12 @@ class PullRequestModel(BaseModel): pull_request = self.__get_pull_request(pull_request) pull_request.status = PullRequest.STATUS_CLOSED pull_request.updated_on = datetime.datetime.now() - self.sa.add(pull_request) + Session().add(pull_request) - def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, - discovery_data): + def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge): """ - Returns a list of changesets that are incoming from org_repo@org_ref - to other_repo@other_ref + Returns a list of changesets that can be merged from org_repo@org_ref + to other_repo@other_ref ... and the ancestor that would be used for merge :param org_repo: :param org_ref: @@ -168,110 +173,79 @@ class PullRequestModel(BaseModel): :param tmp: """ - changesets = [] - #case two independent repos - common, incoming, rheads = discovery_data - if org_repo != other_repo: - revs = [ - org_repo._repo.lookup(org_ref[1]), - org_repo._repo.lookup(other_ref[1]), - ] + ancestor = None + + if alias == 'hg': + # lookup up the exact node id + _revset_predicates = { + 'branch': 'branch', + 'book': 'bookmark', + 'tag': 'tag', + 'rev': 'id', + } - obj = findcommonoutgoing(org_repo._repo, - localrepo.locallegacypeer(other_repo._repo.local()), - revs, - force=True) - revs = obj.missing + org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]], + safe_str(org_ref[1])) + if org_ref[1] == EmptyChangeset().raw_id: + org_rev = org_ref[1] + else: + org_rev = org_repo._repo[scmutil.revrange(org_repo._repo, + [org_rev_spec])[-1]] + other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]], + safe_str(other_ref[1])) + if other_ref[1] == EmptyChangeset().raw_id: + other_rev = other_ref[1] + else: + other_rev = other_repo._repo[scmutil.revrange(other_repo._repo, + [other_rev_spec])[-1]] - for cs in map(binascii.hexlify, revs): - _cs = org_repo.get_changeset(cs) - changesets.append(_cs) - # in case we have revisions filter out the ones not in given range - if org_ref[0] == 'rev' and other_ref[0] == 'rev': - revs = [x.raw_id for x in changesets] - start = org_ref[1] - stop = other_ref[1] - changesets = changesets[revs.index(start):revs.index(stop) + 1] - else: + #case two independent repos + if org_repo != other_repo: + hgrepo = unionrepo.unionrepository(other_repo.baseui, + other_repo.path, + org_repo.path) + # all the changesets we are looking for will be in other_repo, + # so rev numbers from hgrepo can be used in other_repo + #no remote compare do it on the same repository - if alias == 'hg': - _revset_predicates = { - 'branch': 'branch', - 'book': 'bookmark', - 'tag': 'tag', - 'rev': 'id', - } + else: + hgrepo = other_repo._repo + + if merge: + revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" % + (other_rev, org_rev, org_rev)] - revs = [ - "ancestors(%s('%s')) and not ancestors(%s('%s'))" % ( - _revset_predicates[other_ref[0]], other_ref[1], - _revset_predicates[org_ref[0]], org_ref[1], - ) - ] + ancestors = scmutil.revrange(hgrepo, + ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)]) + if len(ancestors) == 1: + ancestor = hgrepo[ancestors[0]].hex() + else: + # TODO: have both + and - changesets + revs = ["id('%s') :: id('%s') - id('%s')" % + (org_rev, other_rev, org_rev)] - out = scmutil.revrange(org_repo._repo, revs) - for cs in (out): - changesets.append(org_repo.get_changeset(cs)) - elif alias == 'git': - so, se = org_repo.run_git_command( - 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1], - other_ref[1]) - ) - ids = re.findall(r'[0-9a-fA-F]{40}', so) - for cs in (ids): - changesets.append(org_repo.get_changeset(cs)) + changesets = [other_repo.get_changeset(cs) + for cs in scmutil.revrange(hgrepo, revs)] - return changesets + elif alias == 'git': + assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos + so, se = org_repo.run_git_command( + 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1], + other_ref[1]) + ) + changesets = [org_repo.get_changeset(cs) + for cs in re.findall(r'[0-9a-fA-F]{40}', so)] - def _get_discovery(self, org_repo, org_ref, other_repo, other_ref): + return changesets, ancestor + + def get_compare_data(self, org_repo, org_ref, other_repo, other_ref, merge): """ - Get's mercurial discovery data used to calculate difference between - repos and refs + Returns incoming changesets for mercurial repositories :param org_repo: - :type org_repo: :param org_ref: - :type org_ref: :param other_repo: - :type other_repo: :param other_ref: - :type other_ref: - """ - - _org_repo = org_repo._repo - org_rev_type, org_rev = org_ref - - _other_repo = other_repo._repo - other_rev_type, other_rev = other_ref - - log.debug('Doing discovery for %s@%s vs %s@%s' % ( - org_repo, org_ref, other_repo, other_ref) - ) - - #log.debug('Filter heads are %s[%s]' % ('', org_ref[1])) - org_peer = localrepo.locallegacypeer(_org_repo.local()) - tmp = discovery.findcommonincoming( - repo=_other_repo, # other_repo we check for incoming - remote=org_peer, # org_repo source for incoming -# heads=[_other_repo[other_rev].node(), -# _org_repo[org_rev].node()], - force=True - ) - return tmp - - def get_compare_data(self, org_repo, org_ref, other_repo, other_ref): - """ - Returns a tuple of incomming changesets, and discoverydata cache for - mercurial repositories - - :param org_repo: - :type org_repo: - :param org_ref: - :type org_ref: - :param other_repo: - :type other_repo: - :param other_ref: - :type other_ref: """ if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)): @@ -280,16 +254,8 @@ class PullRequestModel(BaseModel): if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)): raise Exception('other_ref must be a two element list/tuple') - org_repo_scm = org_repo.scm_instance - other_repo_scm = other_repo.scm_instance - - alias = org_repo.scm_instance.alias - discovery_data = [None, None, None] - if alias == 'hg': - discovery_data = self._get_discovery(org_repo_scm, org_ref, - other_repo_scm, other_ref) - cs_ranges = self._get_changesets(alias, - org_repo_scm, org_ref, - other_repo_scm, other_ref, - discovery_data) - return cs_ranges, discovery_data + cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias, + org_repo.scm_instance, org_ref, + other_repo.scm_instance, other_ref, + merge) + return cs_ranges, ancestor