##// END OF EJS Templates
pullrequests: simplify handling of null revisions...
Mads Kiilerich -
r3722:5dcfa630 beta
parent child Browse files
Show More
@@ -1,261 +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 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43 42
44 43 log = logging.getLogger(__name__)
45 44
46 45
47 46 class PullRequestModel(BaseModel):
48 47
49 48 cls = PullRequest
50 49
51 50 def __get_pull_request(self, pull_request):
52 51 return self._get_instance(PullRequest, pull_request)
53 52
54 53 def get_all(self, repo):
55 54 repo = self._get_repo(repo)
56 55 return PullRequest.query()\
57 56 .filter(PullRequest.other_repo == repo)\
58 57 .order_by(PullRequest.created_on.desc())\
59 58 .all()
60 59
61 60 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
62 61 revisions, reviewers, title, description=None):
63 62 from rhodecode.model.changeset_status import ChangesetStatusModel
64 63
65 64 created_by_user = self._get_user(created_by)
66 65 org_repo = self._get_repo(org_repo)
67 66 other_repo = self._get_repo(other_repo)
68 67
69 68 new = PullRequest()
70 69 new.org_repo = org_repo
71 70 new.org_ref = org_ref
72 71 new.other_repo = other_repo
73 72 new.other_ref = other_ref
74 73 new.revisions = revisions
75 74 new.title = title
76 75 new.description = description
77 76 new.author = created_by_user
78 77 Session().add(new)
79 78 Session().flush()
80 79 #members
81 80 for member in set(reviewers):
82 81 _usr = self._get_user(member)
83 82 reviewer = PullRequestReviewers(_usr, new)
84 83 Session().add(reviewer)
85 84
86 85 #reset state to under-review
87 86 ChangesetStatusModel().set_status(
88 87 repo=org_repo,
89 88 status=ChangesetStatus.STATUS_UNDER_REVIEW,
90 89 user=created_by_user,
91 90 pull_request=new
92 91 )
93 92 revision_data = [(x.raw_id, x.message)
94 93 for x in map(org_repo.get_changeset, revisions)]
95 94 #notification to reviewers
96 95 notif = NotificationModel()
97 96
98 97 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
99 98 pull_request_id=new.pull_request_id,
100 99 qualified=True,
101 100 )
102 101 subject = safe_unicode(
103 102 h.link_to(
104 103 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
105 104 {'user': created_by_user.username,
106 105 'pr_title': new.title,
107 106 'pr_id': new.pull_request_id},
108 107 pr_url
109 108 )
110 109 )
111 110 body = description
112 111 kwargs = {
113 112 'pr_title': title,
114 113 'pr_user_created': h.person(created_by_user.email),
115 114 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
116 115 qualified=True,),
117 116 'pr_url': pr_url,
118 117 'pr_revisions': revision_data
119 118 }
120 119
121 120 notif.create(created_by=created_by_user, subject=subject, body=body,
122 121 recipients=reviewers,
123 122 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
124 123 return new
125 124
126 125 def update_reviewers(self, pull_request, reviewers_ids):
127 126 reviewers_ids = set(reviewers_ids)
128 127 pull_request = self.__get_pull_request(pull_request)
129 128 current_reviewers = PullRequestReviewers.query()\
130 129 .filter(PullRequestReviewers.pull_request==
131 130 pull_request)\
132 131 .all()
133 132 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
134 133
135 134 to_add = reviewers_ids.difference(current_reviewers_ids)
136 135 to_remove = current_reviewers_ids.difference(reviewers_ids)
137 136
138 137 log.debug("Adding %s reviewers" % to_add)
139 138 log.debug("Removing %s reviewers" % to_remove)
140 139
141 140 for uid in to_add:
142 141 _usr = self._get_user(uid)
143 142 reviewer = PullRequestReviewers(_usr, pull_request)
144 143 Session().add(reviewer)
145 144
146 145 for uid in to_remove:
147 146 reviewer = PullRequestReviewers.query()\
148 147 .filter(PullRequestReviewers.user_id==uid,
149 148 PullRequestReviewers.pull_request==pull_request)\
150 149 .scalar()
151 150 if reviewer:
152 151 Session().delete(reviewer)
153 152
154 153 def delete(self, pull_request):
155 154 pull_request = self.__get_pull_request(pull_request)
156 155 Session().delete(pull_request)
157 156
158 157 def close_pull_request(self, pull_request):
159 158 pull_request = self.__get_pull_request(pull_request)
160 159 pull_request.status = PullRequest.STATUS_CLOSED
161 160 pull_request.updated_on = datetime.datetime.now()
162 161 Session().add(pull_request)
163 162
164 163 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
165 164 """
166 165 Returns a list of changesets that can be merged from org_repo@org_ref
167 166 to other_repo@other_ref ... and the ancestor that would be used for merge
168 167
169 168 :param org_repo:
170 169 :param org_ref:
171 170 :param other_repo:
172 171 :param other_ref:
173 172 :param tmp:
174 173 """
175 174
176 175 ancestor = None
177 176
178 177 if alias == 'hg':
179 178 # lookup up the exact node id
180 179 _revset_predicates = {
181 180 'branch': 'branch',
182 181 'book': 'bookmark',
183 182 'tag': 'tag',
184 183 'rev': 'id',
185 184 }
186 185
187 186 org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
188 187 safe_str(org_ref[1]))
189 if org_ref[1] == EmptyChangeset().raw_id:
190 org_rev = org_ref[1]
191 else:
192 org_rev = org_repo._repo[scmutil.revrange(org_repo._repo,
193 [org_rev_spec])[-1]]
188 org_rev = org_repo._repo[scmutil.revrange(org_repo._repo,
189 [org_rev_spec])[-1]]
190
194 191 other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
195 192 safe_str(other_ref[1]))
196 if other_ref[1] == EmptyChangeset().raw_id:
197 other_rev = other_ref[1]
198 else:
199 other_rev = other_repo._repo[scmutil.revrange(other_repo._repo,
200 [other_rev_spec])[-1]]
193 other_rev = other_repo._repo[scmutil.revrange(other_repo._repo,
194 [other_rev_spec])[-1]]
201 195
202 196 #case two independent repos
203 197 if org_repo != other_repo:
204 198 hgrepo = unionrepo.unionrepository(other_repo.baseui,
205 199 other_repo.path,
206 200 org_repo.path)
207 201 # all the changesets we are looking for will be in other_repo,
208 202 # so rev numbers from hgrepo can be used in other_repo
209 203
210 204 #no remote compare do it on the same repository
211 205 else:
212 206 hgrepo = other_repo._repo
213 207
214 208 if merge:
215 209 revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" %
216 210 (other_rev, org_rev, org_rev)]
217 211
218 212 ancestors = scmutil.revrange(hgrepo,
219 213 ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
220 214 if len(ancestors) == 1:
221 215 ancestor = hgrepo[ancestors[0]].hex()
222 216 else:
223 217 # TODO: have both + and - changesets
224 218 revs = ["id('%s') :: id('%s') - id('%s')" %
225 219 (org_rev, other_rev, org_rev)]
226 220
227 221 changesets = [other_repo.get_changeset(cs)
228 222 for cs in scmutil.revrange(hgrepo, revs)]
229 223
230 224 elif alias == 'git':
231 225 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
232 226 so, se = org_repo.run_git_command(
233 227 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
234 228 other_ref[1])
235 229 )
236 230 changesets = [org_repo.get_changeset(cs)
237 231 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
238 232
239 233 return changesets, ancestor
240 234
241 235 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref, merge):
242 236 """
243 237 Returns incoming changesets for mercurial repositories
244 238
245 239 :param org_repo:
246 240 :param org_ref:
247 241 :param other_repo:
248 242 :param other_ref:
249 243 """
250 244
251 245 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
252 246 raise Exception('org_ref must be a two element list/tuple')
253 247
254 248 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
255 249 raise Exception('other_ref must be a two element list/tuple')
256 250
257 251 cs_ranges, ancestor = self._get_changesets(org_repo.scm_instance.alias,
258 252 org_repo.scm_instance, org_ref,
259 253 other_repo.scm_instance, other_ref,
260 254 merge)
261 255 return cs_ranges, ancestor
General Comments 0
You need to be logged in to leave comments. Login now