##// END OF EJS Templates
pullrequests: optimize locating changeset to compare...
Mads Kiilerich -
r3725:f73a6383 beta
parent child Browse files
Show More
@@ -1,255 +1,255 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.pull_request
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull request model for RhodeCode
7 7
8 8 :created_on: Jun 6, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import datetime
28 28 import re
29 29
30 30 from pylons.i18n.translation import _
31 31
32 32 from rhodecode.model.meta import Session
33 33 from rhodecode.lib import helpers as h, unionrepo
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
36 36 ChangesetStatus
37 37 from rhodecode.model.notification import NotificationModel
38 38 from rhodecode.lib.utils2 import safe_unicode
39 39
40 40 from rhodecode.lib.vcs.utils.hgcompat import scmutil
41 41 from rhodecode.lib.vcs.utils import safe_str
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class PullRequestModel(BaseModel):
47 47
48 48 cls = PullRequest
49 49
50 50 def __get_pull_request(self, pull_request):
51 51 return self._get_instance(PullRequest, pull_request)
52 52
53 53 def get_all(self, repo):
54 54 repo = self._get_repo(repo)
55 55 return PullRequest.query()\
56 56 .filter(PullRequest.other_repo == repo)\
57 57 .order_by(PullRequest.created_on.desc())\
58 58 .all()
59 59
60 60 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
61 61 revisions, reviewers, title, description=None):
62 62 from rhodecode.model.changeset_status import ChangesetStatusModel
63 63
64 64 created_by_user = self._get_user(created_by)
65 65 org_repo = self._get_repo(org_repo)
66 66 other_repo = self._get_repo(other_repo)
67 67
68 68 new = PullRequest()
69 69 new.org_repo = org_repo
70 70 new.org_ref = org_ref
71 71 new.other_repo = other_repo
72 72 new.other_ref = other_ref
73 73 new.revisions = revisions
74 74 new.title = title
75 75 new.description = description
76 76 new.author = created_by_user
77 77 Session().add(new)
78 78 Session().flush()
79 79 #members
80 80 for member in set(reviewers):
81 81 _usr = self._get_user(member)
82 82 reviewer = PullRequestReviewers(_usr, new)
83 83 Session().add(reviewer)
84 84
85 85 #reset state to under-review
86 86 ChangesetStatusModel().set_status(
87 87 repo=org_repo,
88 88 status=ChangesetStatus.STATUS_UNDER_REVIEW,
89 89 user=created_by_user,
90 90 pull_request=new
91 91 )
92 92 revision_data = [(x.raw_id, x.message)
93 93 for x in map(org_repo.get_changeset, revisions)]
94 94 #notification to reviewers
95 95 notif = NotificationModel()
96 96
97 97 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
98 98 pull_request_id=new.pull_request_id,
99 99 qualified=True,
100 100 )
101 101 subject = safe_unicode(
102 102 h.link_to(
103 103 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
104 104 {'user': created_by_user.username,
105 105 'pr_title': new.title,
106 106 'pr_id': new.pull_request_id},
107 107 pr_url
108 108 )
109 109 )
110 110 body = description
111 111 kwargs = {
112 112 'pr_title': title,
113 113 'pr_user_created': h.person(created_by_user.email),
114 114 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
115 115 qualified=True,),
116 116 'pr_url': pr_url,
117 117 'pr_revisions': revision_data
118 118 }
119 119
120 120 notif.create(created_by=created_by_user, subject=subject, body=body,
121 121 recipients=reviewers,
122 122 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
123 123 return new
124 124
125 125 def update_reviewers(self, pull_request, reviewers_ids):
126 126 reviewers_ids = set(reviewers_ids)
127 127 pull_request = self.__get_pull_request(pull_request)
128 128 current_reviewers = PullRequestReviewers.query()\
129 129 .filter(PullRequestReviewers.pull_request==
130 130 pull_request)\
131 131 .all()
132 132 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
133 133
134 134 to_add = reviewers_ids.difference(current_reviewers_ids)
135 135 to_remove = current_reviewers_ids.difference(reviewers_ids)
136 136
137 137 log.debug("Adding %s reviewers" % to_add)
138 138 log.debug("Removing %s reviewers" % to_remove)
139 139
140 140 for uid in to_add:
141 141 _usr = self._get_user(uid)
142 142 reviewer = PullRequestReviewers(_usr, pull_request)
143 143 Session().add(reviewer)
144 144
145 145 for uid in to_remove:
146 146 reviewer = PullRequestReviewers.query()\
147 147 .filter(PullRequestReviewers.user_id==uid,
148 148 PullRequestReviewers.pull_request==pull_request)\
149 149 .scalar()
150 150 if reviewer:
151 151 Session().delete(reviewer)
152 152
153 153 def delete(self, pull_request):
154 154 pull_request = self.__get_pull_request(pull_request)
155 155 Session().delete(pull_request)
156 156
157 157 def close_pull_request(self, pull_request):
158 158 pull_request = self.__get_pull_request(pull_request)
159 159 pull_request.status = PullRequest.STATUS_CLOSED
160 160 pull_request.updated_on = datetime.datetime.now()
161 161 Session().add(pull_request)
162 162
163 163 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
164 164 """
165 165 Returns a list of changesets that can be merged from org_repo@org_ref
166 166 to other_repo@other_ref ... and the ancestor that would be used for merge
167 167
168 168 :param org_repo:
169 169 :param org_ref:
170 170 :param other_repo:
171 171 :param other_ref:
172 172 :param tmp:
173 173 """
174 174
175 175 ancestor = None
176 176
177 177 if alias == 'hg':
178 178 # lookup up the exact node id
179 179 _revset_predicates = {
180 180 'branch': 'branch',
181 181 'book': 'bookmark',
182 182 'tag': 'tag',
183 183 'rev': 'id',
184 184 }
185 185
186 org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
187 safe_str(org_ref[1]))
186 org_rev_spec = "max(%s('%s'))" % (_revset_predicates[org_ref[0]],
187 safe_str(org_ref[1]))
188 188 org_revs = scmutil.revrange(org_repo._repo, [org_rev_spec])
189 189 org_rev = org_repo._repo[org_revs[-1] if org_revs else -1].hex()
190 190
191 other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
192 safe_str(other_ref[1]))
191 other_rev_spec = "max(%s('%s'))" % (_revset_predicates[other_ref[0]],
192 safe_str(other_ref[1]))
193 193 other_revs = scmutil.revrange(other_repo._repo, [other_rev_spec])
194 194 other_rev = other_repo._repo[other_revs[-1] if other_revs else -1].hex()
195 195
196 196 #case two independent repos
197 197 if org_repo != other_repo:
198 198 hgrepo = unionrepo.unionrepository(other_repo.baseui,
199 199 other_repo.path,
200 200 org_repo.path)
201 201 # all the changesets we are looking for will be in other_repo,
202 202 # so rev numbers from hgrepo can be used in other_repo
203 203
204 204 #no remote compare do it on the same repository
205 205 else:
206 206 hgrepo = other_repo._repo
207 207
208 208 if merge:
209 209 revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" %
210 210 (other_rev, org_rev, org_rev)]
211 211
212 212 ancestors = scmutil.revrange(hgrepo,
213 213 ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
214 214 if len(ancestors) == 1:
215 215 ancestor = hgrepo[ancestors[0]].hex()
216 216 else:
217 217 # TODO: have both + and - changesets
218 218 revs = ["id('%s') :: id('%s') - id('%s')" %
219 219 (org_rev, other_rev, org_rev)]
220 220
221 221 changesets = [other_repo.get_changeset(cs)
222 222 for cs in scmutil.revrange(hgrepo, revs)]
223 223
224 224 elif alias == 'git':
225 225 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
226 226 so, se = org_repo.run_git_command(
227 227 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
228 228 other_ref[1])
229 229 )
230 230 changesets = [org_repo.get_changeset(cs)
231 231 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
232 232
233 233 return changesets, ancestor
234 234
235 235 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref, merge):
236 236 """
237 237 Returns incoming changesets for mercurial repositories
238 238
239 239 :param org_repo:
240 240 :param org_ref:
241 241 :param other_repo:
242 242 :param other_ref:
243 243 """
244 244
245 245 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
246 246 raise Exception('org_ref must be a two element list/tuple')
247 247
248 248 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
249 249 raise Exception('other_ref must be a two element list/tuple')
250 250
251 251 cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias,
252 252 org_repo.scm_instance, org_ref,
253 253 other_repo.scm_instance, other_ref,
254 254 merge)
255 255 return cs_ranges, ancestor
General Comments 0
You need to be logged in to leave comments. Login now