##// END OF EJS Templates
compare: drop unused rev_start and rev_end
Mads Kiilerich -
r3484:75e56353 beta
parent child Browse files
Show More
@@ -1,195 +1,177 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.compare
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 compare controller for pylons showing differences between two
7 7 repos, branches, bookmarks or tips
8 8
9 9 :created_on: May 6, 2012
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26 import logging
27 27 import traceback
28 28
29 29 from webob.exc import HTTPNotFound
30 30 from pylons import request, response, session, tmpl_context as c, url
31 31 from pylons.controllers.util import abort, redirect
32 32 from pylons.i18n.translation import _
33 33
34 34 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.base import BaseRepoController, render
37 37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 38 from rhodecode.lib import diffs
39 39
40 40 from rhodecode.model.db import Repository
41 41 from rhodecode.model.pull_request import PullRequestModel
42 42 from webob.exc import HTTPBadRequest
43 43 from rhodecode.lib.diffs import LimitedDiffContainer
44 44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class CompareController(BaseRepoController):
50 50
51 51 @LoginRequired()
52 52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 53 'repository.admin')
54 54 def __before__(self):
55 55 super(CompareController, self).__before__()
56 56
57 57 def __get_cs_or_redirect(self, rev, repo, redirect_after=True,
58 58 partial=False):
59 59 """
60 60 Safe way to get changeset if error occur it redirects to changeset with
61 61 proper message. If partial is set then don't do redirect raise Exception
62 62 instead
63 63
64 64 :param rev: revision to fetch
65 65 :param repo: repo instance
66 66 """
67 67
68 68 try:
69 69 type_, rev = rev
70 70 return repo.scm_instance.get_changeset(rev)
71 71 except EmptyRepositoryError, e:
72 72 if not redirect_after:
73 73 return None
74 74 h.flash(h.literal(_('There are no changesets yet')),
75 75 category='warning')
76 76 redirect(url('summary_home', repo_name=repo.repo_name))
77 77
78 78 except RepositoryError, e:
79 79 log.error(traceback.format_exc())
80 80 h.flash(str(e), category='warning')
81 81 if not partial:
82 82 redirect(h.url('summary_home', repo_name=repo.repo_name))
83 83 raise HTTPBadRequest()
84 84
85 85 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
86 86 # org_ref will be evaluated in org_repo
87 87 org_repo = c.rhodecode_db_repo.repo_name
88 88 org_ref = (org_ref_type, org_ref)
89 89 # other_ref will be evaluated in other_repo
90 90 other_ref = (other_ref_type, other_ref)
91 91 other_repo = request.GET.get('other_repo', org_repo)
92 92 # fulldiff disables cut_off_limit
93 93 c.fulldiff = request.GET.get('fulldiff')
94 # only consider this range of changesets
95 rev_start = request.GET.get('rev_start')
96 rev_end = request.GET.get('rev_end')
97 94 # partial uses compare_cs.html template directly
98 95 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
99 96 # as_form puts hidden input field with changeset revisions
100 97 c.as_form = partial and request.GET.get('as_form')
101 98 # swap url for compare_diff page - never partial and never as_form
102 99 c.swap_url = h.url('compare_url',
103 100 repo_name=other_repo,
104 101 org_ref_type=other_ref[0], org_ref=other_ref[1],
105 102 other_repo=org_repo,
106 103 other_ref_type=org_ref[0], other_ref=org_ref[1])
107 104
108 105 org_repo = Repository.get_by_repo_name(org_repo)
109 106 other_repo = Repository.get_by_repo_name(other_repo)
110 107
111 108 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
112 109 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
113 110
114 111 if org_repo is None:
115 112 log.error('Could not find org repo %s' % org_repo)
116 113 raise HTTPNotFound
117 114 if other_repo is None:
118 115 log.error('Could not find other repo %s' % other_repo)
119 116 raise HTTPNotFound
120 117
121 118 if org_repo != other_repo and h.is_git(org_repo):
122 119 log.error('compare of two remote repos not available for GIT REPOS')
123 120 raise HTTPNotFound
124 121
125 122 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
126 123 log.error('compare of two different kind of remote repos not available')
127 124 raise HTTPNotFound
128 125
129 126 c.org_repo = org_repo
130 127 c.other_repo = other_repo
131 128 c.org_ref = org_ref[1]
132 129 c.other_ref = other_ref[1]
133 130 c.org_ref_type = org_ref[0]
134 131 c.other_ref_type = other_ref[0]
135 132
136 if rev_start and rev_end:
137 # swap revs with cherry picked ones, save them for display
138 #org_ref = ('rev', rev_start)
139 #other_ref = ('rev', rev_end)
140 c.org_ref = rev_start[:12]
141 c.other_ref = rev_end[:12]
142 # get parent of
143 # rev start to include it in the diff
144 _cs = other_repo.scm_instance.get_changeset(rev_start)
145 rev_start = _cs.parents[0].raw_id if _cs.parents else EmptyChangeset().raw_id
146 org_ref = ('rev', rev_start)
147 other_ref = ('rev', rev_end)
148 #if we cherry pick it's not remote, make the other_repo org_repo
149 org_repo = other_repo
150
151 133 c.cs_ranges, ancestor = PullRequestModel().get_compare_data(
152 134 org_repo, org_ref, other_repo, other_ref)
153 135
154 136 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
155 137 c.cs_ranges])
156 138 if partial:
157 139 return render('compare/compare_cs.html')
158 140
159 141 if ancestor and org_repo != other_repo:
160 142 # case we want a simple diff without incoming changesets,
161 143 # previewing what will be merged.
162 144 # Make the diff on the forked repo, with
163 145 # revision that is common ancestor
164 146 log.debug('Using ancestor %s as org_ref instead of %s'
165 147 % (ancestor, org_ref))
166 148 org_ref = ('rev', ancestor)
167 149 org_repo = other_repo
168 150
169 151 diff_limit = self.cut_off_limit if not c.fulldiff else None
170 152
171 153 _diff = diffs.differ(org_repo, org_ref, other_repo, other_ref)
172 154
173 155 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
174 156 diff_limit=diff_limit)
175 157 _parsed = diff_processor.prepare()
176 158
177 159 c.limited_diff = False
178 160 if isinstance(_parsed, LimitedDiffContainer):
179 161 c.limited_diff = True
180 162
181 163 c.files = []
182 164 c.changes = {}
183 165 c.lines_added = 0
184 166 c.lines_deleted = 0
185 167 for f in _parsed:
186 168 st = f['stats']
187 169 if st[0] != 'b':
188 170 c.lines_added += st[0]
189 171 c.lines_deleted += st[1]
190 172 fid = h.FID('', f['filename'])
191 173 c.files.append([fid, f['operation'], f['filename'], f['stats']])
192 174 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
193 175 c.changes[fid] = [f['operation'], f['filename'], diff]
194 176
195 177 return render('compare/compare_diff.html')
@@ -1,488 +1,485 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2010-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 import logging
26 26 import traceback
27 27 import formencode
28 28
29 29 from webob.exc import HTTPNotFound, HTTPForbidden
30 30 from collections import defaultdict
31 31 from itertools import groupby
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.base import BaseRepoController, render
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 40 NotAnonymous
41 41 from rhodecode.lib import helpers as h
42 42 from rhodecode.lib import diffs
43 43 from rhodecode.lib.utils import action_logger, jsonify
44 44 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
45 45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 46 from rhodecode.lib.diffs import LimitedDiffContainer
47 47 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
48 48 ChangesetComment
49 49 from rhodecode.model.pull_request import PullRequestModel
50 50 from rhodecode.model.meta import Session
51 51 from rhodecode.model.repo import RepoModel
52 52 from rhodecode.model.comment import ChangesetCommentsModel
53 53 from rhodecode.model.changeset_status import ChangesetStatusModel
54 54 from rhodecode.model.forms import PullRequestForm
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 class PullrequestsController(BaseRepoController):
60 60
61 61 @LoginRequired()
62 62 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
63 63 'repository.admin')
64 64 def __before__(self):
65 65 super(PullrequestsController, self).__before__()
66 66 repo_model = RepoModel()
67 67 c.users_array = repo_model.get_users_js()
68 68 c.users_groups_array = repo_model.get_users_groups_js()
69 69
70 70 def _get_repo_refs(self, repo, rev=None):
71 71 """return a structure with repo's interesting changesets, suitable for
72 72 the selectors in pullrequest.html"""
73 73 branches = [('branch:%s:%s' % (k, v), k)
74 74 for k, v in repo.branches.iteritems()]
75 75 bookmarks = [('book:%s:%s' % (k, v), k)
76 76 for k, v in repo.bookmarks.iteritems()]
77 77 tags = [('tag:%s:%s' % (k, v), k)
78 78 for k, v in repo.tags.iteritems()
79 79 if k != 'tip']
80 80
81 81 tip = repo.tags['tip']
82 82 colontip = ':' + tip
83 83 tips = [x[1] for x in branches + bookmarks + tags
84 84 if x[0].endswith(colontip)]
85 85 selected = 'tag:tip:%s' % tip
86 86 special = [(selected, 'tip (%s)' % ', '.join(tips))]
87 87
88 88 if rev:
89 89 selected = 'rev:%s:%s' % (rev, rev)
90 90 special.append((selected, rev))
91 91
92 92 return [(special, _("Special")),
93 93 (bookmarks, _("Bookmarks")),
94 94 (branches, _("Branches")),
95 95 (tags, _("Tags")),
96 96 ], selected
97 97
98 98 def _get_is_allowed_change_status(self, pull_request):
99 99 owner = self.rhodecode_user.user_id == pull_request.user_id
100 100 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
101 101 pull_request.reviewers]
102 102 return (self.rhodecode_user.admin or owner or reviewer)
103 103
104 104 def show_all(self, repo_name):
105 105 c.pull_requests = PullRequestModel().get_all(repo_name)
106 106 c.repo_name = repo_name
107 107 return render('/pullrequests/pullrequest_show_all.html')
108 108
109 109 @NotAnonymous()
110 110 def index(self):
111 111 org_repo = c.rhodecode_db_repo
112 112
113 113 if org_repo.scm_instance.alias != 'hg':
114 114 log.error('Review not available for GIT REPOS')
115 115 raise HTTPNotFound
116 116
117 117 try:
118 118 org_repo.scm_instance.get_changeset()
119 119 except EmptyRepositoryError, e:
120 120 h.flash(h.literal(_('There are no changesets yet')),
121 121 category='warning')
122 122 redirect(url('summary_home', repo_name=org_repo.repo_name))
123 123
124 124 other_repos_info = {}
125 125
126 126 c.org_repos = []
127 127 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
128 128 c.default_org_repo = org_repo.repo_name
129 129 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance)
130 130
131 131 c.other_repos = []
132 132 # add org repo to other so we can open pull request against itself
133 133 c.other_repos.extend(c.org_repos)
134 134 c.default_other_repo = org_repo.repo_name
135 135 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.scm_instance)
136 136 usr_data = lambda usr: dict(user_id=usr.user_id,
137 137 username=usr.username,
138 138 firstname=usr.firstname,
139 139 lastname=usr.lastname,
140 140 gravatar_link=h.gravatar_url(usr.email, 14))
141 141 other_repos_info[org_repo.repo_name] = {
142 142 'user': usr_data(org_repo.user),
143 143 'description': org_repo.description,
144 144 'revs': h.select('other_ref', c.default_other_ref,
145 145 c.default_other_refs, class_='refs')
146 146 }
147 147
148 148 # gather forks and add to this list ... even though it is rare to
149 149 # request forks to pull their parent
150 150 for fork in org_repo.forks:
151 151 c.other_repos.append((fork.repo_name, fork.repo_name))
152 152 refs, default_ref = self._get_repo_refs(fork.scm_instance)
153 153 other_repos_info[fork.repo_name] = {
154 154 'user': usr_data(fork.user),
155 155 'description': fork.description,
156 156 'revs': h.select('other_ref', default_ref, refs, class_='refs')
157 157 }
158 158
159 159 # add parents of this fork also, but only if it's not empty
160 160 if org_repo.parent and org_repo.parent.scm_instance.revisions:
161 161 c.default_other_repo = org_repo.parent.repo_name
162 162 c.default_other_refs, c.default_other_ref = self._get_repo_refs(org_repo.parent.scm_instance)
163 163 c.other_repos.append((org_repo.parent.repo_name, org_repo.parent.repo_name))
164 164 other_repos_info[org_repo.parent.repo_name] = {
165 165 'user': usr_data(org_repo.parent.user),
166 166 'description': org_repo.parent.description,
167 167 'revs': h.select('other_ref', c.default_other_ref,
168 168 c.default_other_refs, class_='refs')
169 169 }
170 170
171 171 c.other_repos_info = json.dumps(other_repos_info)
172 172 # other repo owner
173 173 c.review_members = []
174 174 return render('/pullrequests/pullrequest.html')
175 175
176 176 @NotAnonymous()
177 177 def create(self, repo_name):
178 178 repo = RepoModel()._get_repo(repo_name)
179 179 try:
180 180 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
181 181 except formencode.Invalid, errors:
182 182 log.error(traceback.format_exc())
183 183 if errors.error_dict.get('revisions'):
184 184 msg = 'Revisions: %s' % errors.error_dict['revisions']
185 185 elif errors.error_dict.get('pullrequest_title'):
186 186 msg = _('Pull request requires a title with min. 3 chars')
187 187 else:
188 188 msg = _('error during creation of pull request')
189 189
190 190 h.flash(msg, 'error')
191 191 return redirect(url('pullrequest_home', repo_name=repo_name))
192 192
193 193 org_repo = _form['org_repo']
194 194 org_ref = _form['org_ref']
195 195 other_repo = _form['other_repo']
196 196 other_ref = _form['other_ref']
197 197 revisions = _form['revisions']
198 198 reviewers = _form['review_members']
199 199
200 200 # if we have cherry picked pull request we don't care what is in
201 201 # org_ref/other_ref
202 202 rev_start = request.POST.get('rev_start')
203 203 rev_end = request.POST.get('rev_end')
204 204
205 205 if rev_start and rev_end:
206 206 # this is swapped to simulate that rev_end is a revision from
207 207 # parent of the fork
208 208 org_ref = 'rev:%s:%s' % (rev_end, rev_end)
209 209 other_ref = 'rev:%s:%s' % (rev_start, rev_start)
210 210
211 211 title = _form['pullrequest_title']
212 212 description = _form['pullrequest_desc']
213 213
214 214 try:
215 215 pull_request = PullRequestModel().create(
216 216 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
217 217 other_ref, revisions, reviewers, title, description
218 218 )
219 219 Session().commit()
220 220 h.flash(_('Successfully opened new pull request'),
221 221 category='success')
222 222 except Exception:
223 223 h.flash(_('Error occurred during sending pull request'),
224 224 category='error')
225 225 log.error(traceback.format_exc())
226 226 return redirect(url('pullrequest_home', repo_name=repo_name))
227 227
228 228 return redirect(url('pullrequest_show', repo_name=other_repo,
229 229 pull_request_id=pull_request.pull_request_id))
230 230
231 231 @NotAnonymous()
232 232 @jsonify
233 233 def update(self, repo_name, pull_request_id):
234 234 pull_request = PullRequest.get_or_404(pull_request_id)
235 235 if pull_request.is_closed():
236 236 raise HTTPForbidden()
237 237 #only owner or admin can update it
238 238 owner = pull_request.author.user_id == c.rhodecode_user.user_id
239 239 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
240 240 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
241 241 request.POST.get('reviewers_ids', '').split(',')))
242 242
243 243 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
244 244 Session().commit()
245 245 return True
246 246 raise HTTPForbidden()
247 247
248 248 @NotAnonymous()
249 249 @jsonify
250 250 def delete(self, repo_name, pull_request_id):
251 251 pull_request = PullRequest.get_or_404(pull_request_id)
252 252 #only owner can delete it !
253 253 if pull_request.author.user_id == c.rhodecode_user.user_id:
254 254 PullRequestModel().delete(pull_request)
255 255 Session().commit()
256 256 h.flash(_('Successfully deleted pull request'),
257 257 category='success')
258 258 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
259 259 raise HTTPForbidden()
260 260
261 261 def _load_compare_data(self, pull_request, enable_comments=True):
262 262 """
263 263 Load context data needed for generating compare diff
264 264
265 265 :param pull_request:
266 266 :type pull_request:
267 267 """
268 rev_start = request.GET.get('rev_start')
269 rev_end = request.GET.get('rev_end')
270
271 268 org_repo = pull_request.org_repo
272 269 (org_ref_type,
273 270 org_ref_name,
274 271 org_ref_rev) = pull_request.org_ref.split(':')
275 272
276 273 other_repo = org_repo
277 274 (other_ref_type,
278 275 other_ref_name,
279 276 other_ref_rev) = pull_request.other_ref.split(':')
280 277
281 278 # despite opening revisions for bookmarks/branches/tags, we always
282 # convert this to rev to prevent changes after book or branch change
279 # convert this to rev to prevent changes after bookmark or branch change
283 280 org_ref = ('rev', org_ref_rev)
284 281 other_ref = ('rev', other_ref_rev)
285 282
286 283 c.org_repo = org_repo
287 284 c.other_repo = other_repo
288 285
289 286 c.fulldiff = fulldiff = request.GET.get('fulldiff')
290 287
291 288 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
292 289
293 290 other_ref = ('rev', getattr(c.cs_ranges[0].parents[0]
294 291 if c.cs_ranges[0].parents
295 292 else EmptyChangeset(), 'raw_id'))
296 293
297 294 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
298 295
299 296 c.org_ref = org_ref[1]
300 297 c.org_ref_type = org_ref[0]
301 298 c.other_ref = other_ref[1]
302 299 c.other_ref_type = other_ref[0]
303 300
304 301 diff_limit = self.cut_off_limit if not fulldiff else None
305 302
306 303 #we swap org/other ref since we run a simple diff on one repo
307 304 _diff = diffs.differ(org_repo, other_ref, other_repo, org_ref)
308 305
309 306 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
310 307 diff_limit=diff_limit)
311 308 _parsed = diff_processor.prepare()
312 309
313 310 c.limited_diff = False
314 311 if isinstance(_parsed, LimitedDiffContainer):
315 312 c.limited_diff = True
316 313
317 314 c.files = []
318 315 c.changes = {}
319 316 c.lines_added = 0
320 317 c.lines_deleted = 0
321 318 for f in _parsed:
322 319 st = f['stats']
323 320 if st[0] != 'b':
324 321 c.lines_added += st[0]
325 322 c.lines_deleted += st[1]
326 323 fid = h.FID('', f['filename'])
327 324 c.files.append([fid, f['operation'], f['filename'], f['stats']])
328 325 diff = diff_processor.as_html(enable_comments=enable_comments,
329 326 parsed_lines=[f])
330 327 c.changes[fid] = [f['operation'], f['filename'], diff]
331 328
332 329 def show(self, repo_name, pull_request_id):
333 330 repo_model = RepoModel()
334 331 c.users_array = repo_model.get_users_js()
335 332 c.users_groups_array = repo_model.get_users_groups_js()
336 333 c.pull_request = PullRequest.get_or_404(pull_request_id)
337 334 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
338 335 cc_model = ChangesetCommentsModel()
339 336 cs_model = ChangesetStatusModel()
340 337 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
341 338 pull_request=c.pull_request,
342 339 with_revisions=True)
343 340
344 341 cs_statuses = defaultdict(list)
345 342 for st in _cs_statuses:
346 343 cs_statuses[st.author.username] += [st]
347 344
348 345 c.pull_request_reviewers = []
349 346 c.pull_request_pending_reviewers = []
350 347 for o in c.pull_request.reviewers:
351 348 st = cs_statuses.get(o.user.username, None)
352 349 if st:
353 350 sorter = lambda k: k.version
354 351 st = [(x, list(y)[0])
355 352 for x, y in (groupby(sorted(st, key=sorter), sorter))]
356 353 else:
357 354 c.pull_request_pending_reviewers.append(o.user)
358 355 c.pull_request_reviewers.append([o.user, st])
359 356
360 357 # pull_requests repo_name we opened it against
361 358 # ie. other_repo must match
362 359 if repo_name != c.pull_request.other_repo.repo_name:
363 360 raise HTTPNotFound
364 361
365 362 # load compare data into template context
366 363 enable_comments = not c.pull_request.is_closed()
367 364 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
368 365
369 366 # inline comments
370 367 c.inline_cnt = 0
371 368 c.inline_comments = cc_model.get_inline_comments(
372 369 c.rhodecode_db_repo.repo_id,
373 370 pull_request=pull_request_id)
374 371 # count inline comments
375 372 for __, lines in c.inline_comments:
376 373 for comments in lines.values():
377 374 c.inline_cnt += len(comments)
378 375 # comments
379 376 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
380 377 pull_request=pull_request_id)
381 378
382 379 try:
383 380 cur_status = c.statuses[c.pull_request.revisions[0]][0]
384 381 except:
385 382 log.error(traceback.format_exc())
386 383 cur_status = 'undefined'
387 384 if c.pull_request.is_closed() and 0:
388 385 c.current_changeset_status = cur_status
389 386 else:
390 387 # changeset(pull-request) status calulation based on reviewers
391 388 c.current_changeset_status = cs_model.calculate_status(
392 389 c.pull_request_reviewers,
393 390 )
394 391 c.changeset_statuses = ChangesetStatus.STATUSES
395 392
396 393 c.as_form = False
397 394 return render('/pullrequests/pullrequest_show.html')
398 395
399 396 @NotAnonymous()
400 397 @jsonify
401 398 def comment(self, repo_name, pull_request_id):
402 399 pull_request = PullRequest.get_or_404(pull_request_id)
403 400 if pull_request.is_closed():
404 401 raise HTTPForbidden()
405 402
406 403 status = request.POST.get('changeset_status')
407 404 change_status = request.POST.get('change_changeset_status')
408 405 text = request.POST.get('text')
409 406 close_pr = request.POST.get('save_close')
410 407
411 408 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
412 409 if status and change_status and allowed_to_change_status:
413 410 _def = (_('status change -> %s')
414 411 % ChangesetStatus.get_status_lbl(status))
415 412 if close_pr:
416 413 _def = _('Closing with') + ' ' + _def
417 414 text = text or _def
418 415 comm = ChangesetCommentsModel().create(
419 416 text=text,
420 417 repo=c.rhodecode_db_repo.repo_id,
421 418 user=c.rhodecode_user.user_id,
422 419 pull_request=pull_request_id,
423 420 f_path=request.POST.get('f_path'),
424 421 line_no=request.POST.get('line'),
425 422 status_change=(ChangesetStatus.get_status_lbl(status)
426 423 if status and change_status
427 424 and allowed_to_change_status else None),
428 425 closing_pr=close_pr
429 426 )
430 427
431 428 action_logger(self.rhodecode_user,
432 429 'user_commented_pull_request:%s' % pull_request_id,
433 430 c.rhodecode_db_repo, self.ip_addr, self.sa)
434 431
435 432 if allowed_to_change_status:
436 433 # get status if set !
437 434 if status and change_status:
438 435 ChangesetStatusModel().set_status(
439 436 c.rhodecode_db_repo.repo_id,
440 437 status,
441 438 c.rhodecode_user.user_id,
442 439 comm,
443 440 pull_request=pull_request_id
444 441 )
445 442
446 443 if close_pr:
447 444 if status in ['rejected', 'approved']:
448 445 PullRequestModel().close_pull_request(pull_request_id)
449 446 action_logger(self.rhodecode_user,
450 447 'user_closed_pull_request:%s' % pull_request_id,
451 448 c.rhodecode_db_repo, self.ip_addr, self.sa)
452 449 else:
453 450 h.flash(_('Closing pull request on other statuses than '
454 451 'rejected or approved forbidden'),
455 452 category='warning')
456 453
457 454 Session().commit()
458 455
459 456 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
460 457 return redirect(h.url('pullrequest_show', repo_name=repo_name,
461 458 pull_request_id=pull_request_id))
462 459
463 460 data = {
464 461 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
465 462 }
466 463 if comm:
467 464 c.co = comm
468 465 data.update(comm.get_dict())
469 466 data.update({'rendered_text':
470 467 render('changeset/changeset_comment_block.html')})
471 468
472 469 return data
473 470
474 471 @NotAnonymous()
475 472 @jsonify
476 473 def delete_comment(self, repo_name, comment_id):
477 474 co = ChangesetComment.get(comment_id)
478 475 if co.pull_request.is_closed():
479 476 #don't allow deleting comments on closed pull request
480 477 raise HTTPForbidden()
481 478
482 479 owner = co.author.user_id == c.rhodecode_user.user_id
483 480 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
484 481 ChangesetCommentsModel().delete(comment=co)
485 482 Session().commit()
486 483 return True
487 484 else:
488 485 raise HTTPForbidden()
@@ -1,202 +1,198 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)}
11 11 &raquo;
12 12 ${_('new pull request')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 23 <div style="float:left;padding:0px 30px 30px 30px">
24 <input type="hidden" name="rev_start" value="${request.GET.get('rev_start')}" />
25 <input type="hidden" name="rev_end" value="${request.GET.get('rev_end')}" />
26
27 24 ##ORG
28 25 <div style="float:left">
29 26 <div>
30 27 <span style="font-size: 20px">
31 28 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref',c.default_org_ref,c.org_refs,class_='refs')}
32 29 </span>
33 30 <div style="padding:5px 3px 3px 20px;">${c.rhodecode_db_repo.description}</div>
34 31 </div>
35 32 <div style="clear:both;padding-top: 10px"></div>
36 33 </div>
37 34 <div style="float:left;font-size:24px;padding:0px 20px">
38 35 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
39 36 </div>
40 37
41 38 ##OTHER, most Probably the PARENT OF THIS FORK
42 39 <div style="float:left">
43 40 <div>
44 41 <span style="font-size: 20px">
45 42 ${h.select('other_repo',c.default_other_repo,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_other_ref,c.default_other_refs,class_='refs')}
46 43 </span>
47 44 <div id="other_repo_desc" style="padding:5px 3px 3px 20px;"></div>
48 45 </div>
49 46 <div style="clear:both;padding-top: 10px"></div>
50 47 </div>
51 48 <div style="clear:both;padding-top: 10px"></div>
52 49 ## overview pulled by ajax
53 50 <div style="float:left" id="pull_request_overview"></div>
54 51 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
55 52 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
56 53 </div>
57 54 </div>
58 55 <div style="float:left; border-left:1px dashed #eee">
59 56 <h4>${_('Pull request reviewers')}</h4>
60 57 <div id="reviewers" style="padding:0px 0px 0px 15px">
61 58 ## members goes here !
62 59 <div class="group_members_wrap">
63 60 <ul id="review_members" class="group_members">
64 61 %for member in c.review_members:
65 62 <li id="reviewer_${member.user_id}">
66 63 <div class="reviewers_member">
67 64 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
68 65 <div style="float:left">${member.full_name} (${_('owner')})</div>
69 66 <input type="hidden" value="${member.user_id}" name="review_members" />
70 67 <span class="delete_icon action_button" onclick="removeReviewMember(${member.user_id})"></span>
71 68 </div>
72 69 </li>
73 70 %endfor
74 71 </ul>
75 72 </div>
76 73
77 74 <div class='ac'>
78 75 <div class="reviewer_ac">
79 76 ${h.text('user', class_='yui-ac-input')}
80 77 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
81 78 <div id="reviewers_container"></div>
82 79 </div>
83 80 </div>
84 81 </div>
85 82 </div>
86 83 <h3>${_('Create new pull request')}</h3>
87 84
88 85 <div class="form">
89 86 <!-- fields -->
90 87
91 88 <div class="fields">
92 89
93 90 <div class="field">
94 91 <div class="label">
95 92 <label for="pullrequest_title">${_('Title')}:</label>
96 93 </div>
97 94 <div class="input">
98 95 ${h.text('pullrequest_title',size=30)}
99 96 </div>
100 97 </div>
101 98
102 99 <div class="field">
103 100 <div class="label label-textarea">
104 101 <label for="pullrequest_desc">${_('description')}:</label>
105 102 </div>
106 103 <div class="textarea text-area editor">
107 104 ${h.textarea('pullrequest_desc',size=30)}
108 105 </div>
109 106 </div>
110 107
111 108 <div class="buttons">
112 109 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
113 110 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
114 111 </div>
115 112 </div>
116 113 </div>
117 114 ${h.end_form()}
118 115
119 116 </div>
120 117
121 118 <script type="text/javascript">
122 119 var _USERS_AC_DATA = ${c.users_array|n};
123 120 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
124 121 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
125 122
126 123 var other_repos_info = ${c.other_repos_info|n};
127 124
128 125 var loadPreview = function(){
129 126 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
130 127 //url template
131 128 var url = "${h.url('compare_url',
132 129 repo_name='__other_repo__',
133 130 org_ref_type='__other_ref_type__',
134 131 org_ref='__other_ref__',
135 132 other_repo='__org_repo__',
136 133 other_ref_type='__org_ref_type__',
137 134 other_ref='__org_ref__',
138 135 as_form=True,
139 rev_start=request.GET.get('rev_start',''),
140 rev_end=request.GET.get('rev_end',''))}";
136 )}";
141 137 var org_repo = YUQ('#pull_request_form #org_repo')[0].value;
142 138 var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':');
143 139
144 140 var other_repo = YUQ('#pull_request_form #other_repo')[0].value;
145 141 var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':');
146 142
147 143 var select_refs = YUQ('#pull_request_form select.refs')
148 144 var rev_data = {
149 145 'org_repo': org_repo,
150 146 'org_ref': org_ref[2],
151 147 'org_ref_type': 'rev',
152 148 'other_repo': other_repo,
153 149 'other_ref': other_ref[2],
154 150 'other_ref_type': 'rev',
155 151 }; // gather the org/other ref and repo here
156 152
157 153 for (k in rev_data){
158 154 url = url.replace('__'+k+'__',rev_data[k]);
159 155 }
160 156
161 157 YUD.get('pull_request_overview').innerHTML = "${_('Loading ...')}";
162 158 YUD.get('pull_request_overview_url').href = url; // shouldn't have as_form ... but ...
163 159 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
164 160 ypjax(url,'pull_request_overview', function(data){
165 161 var sel_box = YUQ('#pull_request_form #other_repo')[0];
166 162 var repo_name = sel_box.options[sel_box.selectedIndex].value;
167 163 var _data = other_repos_info[repo_name];
168 164 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
169 165 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
170 166 // select back the revision that was just compared
171 167 setSelectValue(YUD.get('other_ref'), rev_data['other_ref']);
172 168 // reset && add the reviewer based on selected repo
173 169 YUD.get('review_members').innerHTML = '';
174 170 addReviewMember(_data.user.user_id, _data.user.firstname,
175 171 _data.user.lastname, _data.user.username,
176 172 _data.user.gravatar_link);
177 173 })
178 174 }
179 175
180 176 ## refresh automatically when something changes (org_repo can't change)
181 177
182 178 YUE.on('org_ref', 'change', function(e){
183 179 loadPreview();
184 180 });
185 181
186 182 YUE.on('other_repo', 'change', function(e){
187 183 var repo_name = e.currentTarget.value;
188 184 // replace the <select> of changed repo
189 185 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
190 186 loadPreview();
191 187 });
192 188
193 189 YUE.on('other_ref', 'change', function(e){
194 190 loadPreview();
195 191 });
196 192
197 193 //lazy load overview after 0.5s
198 194 setTimeout(loadPreview, 500)
199 195
200 196 </script>
201 197
202 198 </%def>
@@ -1,445 +1,434 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.model.repo import RepoModel
3 3 from rhodecode.model.meta import Session
4 4 from rhodecode.model.db import Repository
5 5 from rhodecode.model.scm import ScmModel
6 6 from rhodecode.lib.vcs.backends.base import EmptyChangeset
7 7
8 8
9 9 def _fork_repo(fork_name, vcs_type, parent=None):
10 10 if vcs_type =='hg':
11 11 _REPO = HG_REPO
12 12 elif vcs_type == 'git':
13 13 _REPO = GIT_REPO
14 14
15 15 if parent:
16 16 _REPO = parent
17 17
18 18 form_data = dict(
19 19 repo_name=fork_name,
20 20 repo_name_full=fork_name,
21 21 repo_group=None,
22 22 repo_type=vcs_type,
23 23 description='',
24 24 private=False,
25 25 copy_permissions=False,
26 26 landing_rev='tip',
27 27 update_after_clone=False,
28 28 fork_parent_id=Repository.get_by_repo_name(_REPO),
29 29 )
30 30 RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN)
31 31
32 32 Session().commit()
33 33 return Repository.get_by_repo_name(fork_name)
34 34
35 35
36 36 def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False):
37 37 repo = Repository.get_by_repo_name(repo)
38 38 _cs = parent
39 39 if not parent:
40 40 _cs = EmptyChangeset(alias=vcs_type)
41 41
42 42 if newfile:
43 43 cs = ScmModel().create_node(
44 44 repo=repo.scm_instance, repo_name=repo.repo_name,
45 45 cs=_cs, user=TEST_USER_ADMIN_LOGIN,
46 46 author=TEST_USER_ADMIN_LOGIN,
47 47 message=message,
48 48 content=content,
49 49 f_path=filename
50 50 )
51 51 else:
52 52 cs = ScmModel().commit_change(
53 53 repo=repo.scm_instance, repo_name=repo.repo_name,
54 54 cs=parent, user=TEST_USER_ADMIN_LOGIN,
55 55 author=TEST_USER_ADMIN_LOGIN,
56 56 message=message,
57 57 content=content,
58 58 f_path=filename
59 59 )
60 60 return cs
61 61
62 62
63 63 class TestCompareController(TestController):
64 64
65 65 def setUp(self):
66 66 self.r1_id = None
67 67 self.r2_id = None
68 68
69 69 def tearDown(self):
70 70 if self.r2_id:
71 71 RepoModel().delete(self.r2_id)
72 72 if self.r1_id:
73 73 RepoModel().delete(self.r1_id)
74 74 Session().commit()
75 75 Session.remove()
76 76
77 77 def test_compare_forks_on_branch_extra_commits_hg(self):
78 78 self.log_user()
79 79 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
80 80 description='diff-test',
81 81 owner=TEST_USER_ADMIN_LOGIN)
82 82 Session().commit()
83 83 self.r1_id = repo1.repo_id
84 84 #commit something !
85 85 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
86 86 message='commit1', vcs_type='hg', parent=None, newfile=True)
87 87
88 88 #fork this repo
89 89 repo2 = _fork_repo('one-fork', 'hg', parent='one')
90 90 self.r2_id = repo2.repo_id
91 91
92 92 #add two extra commit into fork
93 93 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
94 94 message='commit2', vcs_type='hg', parent=cs0)
95 95
96 96 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
97 97 message='commit3', vcs_type='hg', parent=cs1)
98 98
99 99 rev1 = 'default'
100 100 rev2 = 'default'
101 101
102 102 response = self.app.get(url(controller='compare', action='index',
103 103 repo_name=repo1.repo_name,
104 104 org_ref_type="branch",
105 105 org_ref=rev2,
106 106 other_repo=repo2.repo_name,
107 107 other_ref_type="branch",
108 108 other_ref=rev1,
109 109 ))
110 110
111 111 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
112 112 response.mustcontain("""Showing 2 commits""")
113 113 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
114 114
115 115 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
116 116 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
117 117
118 118 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
119 119 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
120 120 ## files
121 121 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name))
122 122 #swap
123 123 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name))
124 124
125 125 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
126 126 self.log_user()
127 127
128 128 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
129 129 description='diff-test',
130 130 owner=TEST_USER_ADMIN_LOGIN)
131 131 Session().commit()
132 132 self.r1_id = repo1.repo_id
133 133
134 134 #commit something !
135 135 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
136 136 message='commit1', vcs_type='hg', parent=None, newfile=True)
137 137
138 138 #fork this repo
139 139 repo2 = _fork_repo('one-fork', 'hg', parent='one')
140 140 self.r2_id = repo2.repo_id
141 141
142 142 #now commit something to origin repo
143 143 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
144 144 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
145 145
146 146 #add two extra commit into fork
147 147 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
148 148 message='commit2', vcs_type='hg', parent=cs0)
149 149
150 150 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
151 151 message='commit3', vcs_type='hg', parent=cs1)
152 152
153 153 rev1 = 'default'
154 154 rev2 = 'default'
155 155
156 156 response = self.app.get(url(controller='compare', action='index',
157 157 repo_name=repo1.repo_name,
158 158 org_ref_type="branch",
159 159 org_ref=rev2,
160 160 other_repo=repo2.repo_name,
161 161 other_ref_type="branch",
162 162 other_ref=rev1,
163 163 ))
164 164 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, rev2, repo2.repo_name, rev1))
165 165 response.mustcontain("""Showing 2 commits""")
166 166 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
167 167
168 168 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
169 169 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
170 170
171 171 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
172 172 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
173 173 ## files
174 174 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s#C--826e8142e6ba">file1</a>""" % (repo1.repo_name, rev2, rev1, repo2.repo_name))
175 175 #swap
176 176 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s?other_repo=%s">[swap]</a>""" % (repo2.repo_name, rev1, rev2, repo1.repo_name))
177 177
178 178 def test_compare_cherry_pick_changesets_from_bottom(self):
179 179
180 180 # repo1:
181 181 # cs0:
182 182 # cs1:
183 183 # repo1-fork- in which we will cherry pick bottom changesets
184 184 # cs0:
185 185 # cs1:
186 186 # cs2: x
187 187 # cs3: x
188 188 # cs4: x
189 189 # cs5:
190 190 #make repo1, and cs1+cs2
191 191 self.log_user()
192 192
193 193 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
194 194 description='diff-test',
195 195 owner=TEST_USER_ADMIN_LOGIN)
196 196 Session().commit()
197 197 self.r1_id = repo1.repo_id
198 198
199 199 #commit something !
200 200 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
201 201 message='commit1', vcs_type='hg', parent=None,
202 202 newfile=True)
203 203 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
204 204 message='commit2', vcs_type='hg', parent=cs0)
205 205 #fork this repo
206 206 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
207 207 self.r2_id = repo2.repo_id
208 208 #now make cs3-6
209 209 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
210 210 message='commit3', vcs_type='hg', parent=cs1)
211 211 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
212 212 message='commit4', vcs_type='hg', parent=cs2)
213 213 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
214 214 message='commit5', vcs_type='hg', parent=cs3)
215 215 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
216 216 message='commit6', vcs_type='hg', parent=cs4)
217 217
218 rev1 = 'tip'
219 rev2 = 'tip'
220
221 218 response = self.app.get(url(controller='compare', action='index',
222 219 repo_name=repo2.repo_name,
223 org_ref_type="tag",
224 org_ref=rev1,
220 org_ref_type="rev",
221 org_ref=cs1.short_id, # parent of cs2, in repo2
225 222 other_repo=repo1.repo_name,
226 other_ref_type="tag",
227 other_ref=rev2,
228 rev_start=cs2.raw_id,
229 rev_end=cs4.raw_id,
223 other_ref_type="rev",
224 other_ref=cs4.short_id,
230 225 ))
231 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs2.short_id, repo1.repo_name, cs4.short_id))
226 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs1.short_id, repo1.repo_name, cs4.short_id))
232 227 response.mustcontain("""Showing 3 commits""")
233 228 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
234 229
235 230 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
236 231 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
237 232 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
238 233
239 234 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo1.repo_name, cs2.raw_id, cs2.short_id))
240 235 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
241 236 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
242 237 ## files
243 238 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
244 239
245 240 def test_compare_cherry_pick_changesets_from_top(self):
246 241 # repo1:
247 242 # cs0:
248 243 # cs1:
249 244 # repo1-fork- in which we will cherry pick bottom changesets
250 245 # cs0:
251 246 # cs1:
252 247 # cs2:
253 248 # cs3: x
254 249 # cs4: x
255 250 # cs5: x
256 251 #
257 252 #make repo1, and cs1+cs2
258 253 self.log_user()
259 254 repo1 = RepoModel().create_repo(repo_name='repo1', repo_type='hg',
260 255 description='diff-test',
261 256 owner=TEST_USER_ADMIN_LOGIN)
262 257 Session().commit()
263 258 self.r1_id = repo1.repo_id
264 259
265 260 #commit something !
266 261 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
267 262 message='commit1', vcs_type='hg', parent=None,
268 263 newfile=True)
269 264 cs1 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\n',
270 265 message='commit2', vcs_type='hg', parent=cs0)
271 266 #fork this repo
272 267 repo2 = _fork_repo('repo1-fork', 'hg', parent='repo1')
273 268 self.r2_id = repo2.repo_id
274 269 #now make cs3-6
275 270 cs2 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
276 271 message='commit3', vcs_type='hg', parent=cs1)
277 272 cs3 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\n',
278 273 message='commit4', vcs_type='hg', parent=cs2)
279 274 cs4 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\n',
280 275 message='commit5', vcs_type='hg', parent=cs3)
281 276 cs5 = _commit_change(repo1.repo_name, filename='file1', content='line1\nline2\nline3\nline4\nline5\nline6\n',
282 277 message='commit6', vcs_type='hg', parent=cs4)
283 rev1 = 'tip'
284 rev2 = 'tip'
285
286 278 response = self.app.get(url(controller='compare', action='index',
287 repo_name=repo2.repo_name,
288 org_ref_type="tag",
289 org_ref=rev1,
290 other_repo=repo1.repo_name,
291 other_ref_type="tag",
292 other_ref=rev2,
293 rev_start=cs3.raw_id,
294 rev_end=cs5.raw_id,
279 repo_name=repo1.repo_name,
280 org_ref_type="rev",
281 org_ref=cs2.short_id, # parent of cs3, not in repo2
282 other_ref_type="rev",
283 other_ref=cs5.short_id,
295 284 ))
296 285
297 response.mustcontain('%s@%s -&gt; %s@%s' % (repo2.repo_name, cs3.short_id, repo1.repo_name, cs5.short_id))
286 response.mustcontain('%s@%s -&gt; %s@%s' % (repo1.repo_name, cs2.short_id, repo1.repo_name, cs5.short_id))
298 287 response.mustcontain("""Showing 3 commits""")
299 288 response.mustcontain("""1 file changed with 3 insertions and 0 deletions""")
300 289
301 290 response.mustcontain("""<div class="message tooltip" title="commit4" style="white-space:normal">commit4</div>""")
302 291 response.mustcontain("""<div class="message tooltip" title="commit5" style="white-space:normal">commit5</div>""")
303 292 response.mustcontain("""<div class="message tooltip" title="commit6" style="white-space:normal">commit6</div>""")
304 293
305 294 response.mustcontain("""<a href="/%s/changeset/%s">r3:%s</a>""" % (repo1.repo_name, cs3.raw_id, cs3.short_id))
306 295 response.mustcontain("""<a href="/%s/changeset/%s">r4:%s</a>""" % (repo1.repo_name, cs4.raw_id, cs4.short_id))
307 296 response.mustcontain("""<a href="/%s/changeset/%s">r5:%s</a>""" % (repo1.repo_name, cs5.raw_id, cs5.short_id))
308 297 ## files
309 298 response.mustcontain("""#C--826e8142e6ba">file1</a>""")
310 299
311 300 def test_compare_cherry_pick_changeset_mixed_branches(self):
312 301 """
313 302
314 303 """
315 304 pass
316 305 #TODO write this tastecase
317 306
318 307 def test_compare_remote_branches_hg(self):
319 308 self.log_user()
320 309
321 310 repo2 = _fork_repo(HG_FORK, 'hg')
322 311 self.r2_id = repo2.repo_id
323 312 rev1 = '56349e29c2af'
324 313 rev2 = '7d4bc8ec6be5'
325 314
326 315 response = self.app.get(url(controller='compare', action='index',
327 316 repo_name=HG_REPO,
328 317 org_ref_type="rev",
329 318 org_ref=rev1,
330 319 other_ref_type="rev",
331 320 other_ref=rev2,
332 321 other_repo=HG_FORK,
333 322 ))
334 323 response.mustcontain('%s@%s -&gt; %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
335 324 ## outgoing changesets between those revisions
336 325
337 326 response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_FORK))
338 327 response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_FORK))
339 328 response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_FORK, rev2))
340 329
341 330 ## files
342 331 response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK))
343 332 response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK))
344 333 response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s?other_repo=%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2, HG_FORK))
345 334
346 335 def test_org_repo_new_commits_after_forking_simple_diff(self):
347 336 self.log_user()
348 337
349 338 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
350 339 description='diff-test',
351 340 owner=TEST_USER_ADMIN_LOGIN)
352 341
353 342 Session().commit()
354 343 self.r1_id = repo1.repo_id
355 344 r1_name = repo1.repo_name
356 345
357 346 #commit something initially !
358 347 cs0 = ScmModel().create_node(
359 348 repo=repo1.scm_instance, repo_name=r1_name,
360 349 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
361 350 author=TEST_USER_ADMIN_LOGIN,
362 351 message='commit1',
363 352 content='line1',
364 353 f_path='file1'
365 354 )
366 355 Session().commit()
367 356 self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
368 357 #fork the repo1
369 358 repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
370 359 description='compare-test',
371 360 clone_uri=repo1.repo_full_path,
372 361 owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
373 362 Session().commit()
374 363 self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
375 364 self.r2_id = repo2.repo_id
376 365 r2_name = repo2.repo_name
377 366
378 367 #make 3 new commits in fork
379 368 cs1 = ScmModel().create_node(
380 369 repo=repo2.scm_instance, repo_name=r2_name,
381 370 cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
382 371 author=TEST_USER_ADMIN_LOGIN,
383 372 message='commit1-fork',
384 373 content='file1-line1-from-fork',
385 374 f_path='file1-fork'
386 375 )
387 376 cs2 = ScmModel().create_node(
388 377 repo=repo2.scm_instance, repo_name=r2_name,
389 378 cs=cs1, user=TEST_USER_ADMIN_LOGIN,
390 379 author=TEST_USER_ADMIN_LOGIN,
391 380 message='commit2-fork',
392 381 content='file2-line1-from-fork',
393 382 f_path='file2-fork'
394 383 )
395 384 cs3 = ScmModel().create_node(
396 385 repo=repo2.scm_instance, repo_name=r2_name,
397 386 cs=cs2, user=TEST_USER_ADMIN_LOGIN,
398 387 author=TEST_USER_ADMIN_LOGIN,
399 388 message='commit3-fork',
400 389 content='file3-line1-from-fork',
401 390 f_path='file3-fork'
402 391 )
403 392
404 393 #compare !
405 394 rev1 = 'default'
406 395 rev2 = 'default'
407 396
408 397 response = self.app.get(url(controller='compare', action='index',
409 398 repo_name=r2_name,
410 399 org_ref_type="branch",
411 400 org_ref=rev1,
412 401 other_ref_type="branch",
413 402 other_ref=rev2,
414 403 other_repo=r1_name,
415 404 ))
416 405 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
417 406 response.mustcontain('No files')
418 407 response.mustcontain('No changesets')
419 408
420 409 #add new commit into parent !
421 410 cs0 = ScmModel().create_node(
422 411 repo=repo1.scm_instance, repo_name=r1_name,
423 412 cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
424 413 author=TEST_USER_ADMIN_LOGIN,
425 414 message='commit2-parent',
426 415 content='line1-added-after-fork',
427 416 f_path='file2'
428 417 )
429 418 #compare !
430 419 rev1 = 'default'
431 420 rev2 = 'default'
432 421 response = self.app.get(url(controller='compare', action='index',
433 422 repo_name=r2_name,
434 423 org_ref_type="branch",
435 424 org_ref=rev1,
436 425 other_ref_type="branch",
437 426 other_ref=rev2,
438 427 other_repo=r1_name,
439 428 ))
440 429
441 430 response.mustcontain('%s@%s -&gt; %s@%s' % (r2_name, rev1, r1_name, rev2))
442 431
443 432 response.mustcontain("""commit2-parent""")
444 433 response.mustcontain("""1 file changed with 1 insertions and 0 deletions""")
445 434 response.mustcontain("""line1-added-after-fork""")
General Comments 0
You need to be logged in to leave comments. Login now