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