##// END OF EJS Templates
It's better to use Exception here than assertion. It plays better with the exception handling software like sentry or errormator
marcink -
r3783:f533c054 beta
parent child Browse files
Show More
@@ -1,273 +1,275
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
27 27 import logging
28 28 import traceback
29 29 import re
30 30
31 31 from webob.exc import HTTPNotFound
32 32 from pylons import request, response, session, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError, RepositoryError
37 37 from rhodecode.lib.vcs.utils import safe_str
38 38 from rhodecode.lib.vcs.utils.hgcompat import scmutil
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib import diffs, unionrepo
43 43
44 44 from rhodecode.model.db import Repository
45 45 from webob.exc import HTTPBadRequest
46 46 from rhodecode.lib.diffs import LimitedDiffContainer
47 47
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class CompareController(BaseRepoController):
53 53
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 _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
86 86 """
87 87 Returns a list of changesets that can be merged from org_repo@org_ref
88 88 to other_repo@other_ref ... and the ancestor that would be used for merge
89 89
90 90 :param org_repo:
91 91 :param org_ref:
92 92 :param other_repo:
93 93 :param other_ref:
94 94 :param tmp:
95 95 """
96 96
97 97 ancestor = None
98 98
99 99 if alias == 'hg':
100 100 # lookup up the exact node id
101 101 _revset_predicates = {
102 102 'branch': 'branch',
103 103 'book': 'bookmark',
104 104 'tag': 'tag',
105 105 'rev': 'id',
106 106 }
107 107
108 108 org_rev_spec = "max(%s('%s'))" % (_revset_predicates[org_ref[0]],
109 109 safe_str(org_ref[1]))
110 110 org_revs = scmutil.revrange(org_repo._repo, [org_rev_spec])
111 111 org_rev = org_repo._repo[org_revs[-1] if org_revs else -1].hex()
112 112
113 113 other_rev_spec = "max(%s('%s'))" % (_revset_predicates[other_ref[0]],
114 114 safe_str(other_ref[1]))
115 115 other_revs = scmutil.revrange(other_repo._repo, [other_rev_spec])
116 116 other_rev = other_repo._repo[other_revs[-1] if other_revs else -1].hex()
117 117
118 118 #case two independent repos
119 119 if org_repo != other_repo:
120 120 hgrepo = unionrepo.unionrepository(other_repo.baseui,
121 121 other_repo.path,
122 122 org_repo.path)
123 123 # all the changesets we are looking for will be in other_repo,
124 124 # so rev numbers from hgrepo can be used in other_repo
125 125
126 126 #no remote compare do it on the same repository
127 127 else:
128 128 hgrepo = other_repo._repo
129 129
130 130 if merge:
131 131 revs = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" %
132 132 (other_rev, org_rev, org_rev)]
133 133
134 134 ancestors = scmutil.revrange(hgrepo,
135 135 ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
136 136 if ancestors:
137 137 # pick arbitrary ancestor - but there is usually only one
138 138 ancestor = hgrepo[ancestors[0]].hex()
139 139 else:
140 140 # TODO: have both + and - changesets
141 141 revs = ["id('%s') :: id('%s') - id('%s')" %
142 142 (org_rev, other_rev, org_rev)]
143 143
144 144 changesets = [other_repo.get_changeset(cs)
145 145 for cs in scmutil.revrange(hgrepo, revs)]
146 146
147 147 elif alias == 'git':
148 assert org_repo == other_repo, ('no support for compare for two '
149 'different repositories in git')
148 if org_repo != other_repo:
149 raise Exception('Comparing of different GIT repositories is not'
150 'allowed. Got %s != %s' % (org_repo, other_repo))
151
150 152 so, se = org_repo.run_git_command(
151 153 'log --reverse --pretty="format: %%H" -s -p %s..%s'
152 154 % (org_ref[1], other_ref[1])
153 155 )
154 156 changesets = [org_repo.get_changeset(cs)
155 157 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
156 158
157 159 return changesets, ancestor
158 160
159 161 @LoginRequired()
160 162 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
161 163 'repository.admin')
162 164 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
163 165 # org_ref will be evaluated in org_repo
164 166 org_repo = c.rhodecode_db_repo.repo_name
165 167 org_ref = (org_ref_type, org_ref)
166 168 # other_ref will be evaluated in other_repo
167 169 other_ref = (other_ref_type, other_ref)
168 170 other_repo = request.GET.get('other_repo', org_repo)
169 171 # If merge is True:
170 172 # Show what org would get if merged with other:
171 173 # List changesets that are ancestors of other but not of org.
172 174 # New changesets in org is thus ignored.
173 175 # Diff will be from common ancestor, and merges of org to other will thus be ignored.
174 176 # If merge is False:
175 177 # Make a raw diff from org to other, no matter if related or not.
176 178 # Changesets in one and not in the other will be ignored
177 179 merge = bool(request.GET.get('merge'))
178 180 # fulldiff disables cut_off_limit
179 181 c.fulldiff = request.GET.get('fulldiff')
180 182 # partial uses compare_cs.html template directly
181 183 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
182 184 # as_form puts hidden input field with changeset revisions
183 185 c.as_form = partial and request.GET.get('as_form')
184 186 # swap url for compare_diff page - never partial and never as_form
185 187 c.swap_url = h.url('compare_url',
186 188 repo_name=other_repo,
187 189 org_ref_type=other_ref[0], org_ref=other_ref[1],
188 190 other_repo=org_repo,
189 191 other_ref_type=org_ref[0], other_ref=org_ref[1],
190 192 merge=merge or '')
191 193
192 194 org_repo = Repository.get_by_repo_name(org_repo)
193 195 other_repo = Repository.get_by_repo_name(other_repo)
194 196
195 197 if org_repo is None:
196 198 log.error('Could not find org repo %s' % org_repo)
197 199 raise HTTPNotFound
198 200 if other_repo is None:
199 201 log.error('Could not find other repo %s' % other_repo)
200 202 raise HTTPNotFound
201 203
202 204 if org_repo != other_repo and h.is_git(org_repo):
203 205 log.error('compare of two remote repos not available for GIT REPOS')
204 206 raise HTTPNotFound
205 207
206 208 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
207 209 log.error('compare of two different kind of remote repos not available')
208 210 raise HTTPNotFound
209 211
210 212 self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
211 213 self.__get_cs_or_redirect(rev=other_ref, repo=other_repo, partial=partial)
212 214
213 215 c.org_repo = org_repo
214 216 c.other_repo = other_repo
215 217 c.org_ref = org_ref[1]
216 218 c.other_ref = other_ref[1]
217 219 c.org_ref_type = org_ref[0]
218 220 c.other_ref_type = other_ref[0]
219 221
220 222 c.cs_ranges, c.ancestor = self._get_changesets(org_repo.scm_instance.alias,
221 223 org_repo.scm_instance, org_ref,
222 224 other_repo.scm_instance, other_ref,
223 225 merge)
224 226
225 227 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
226 228 c.cs_ranges])
227 229 if not c.ancestor:
228 230 log.warning('Unable to find ancestor revision')
229 231
230 232 if partial:
231 233 return render('compare/compare_cs.html')
232 234
233 235 if c.ancestor:
234 236 assert merge
235 237 # case we want a simple diff without incoming changesets,
236 238 # previewing what will be merged.
237 239 # Make the diff on the other repo (which is known to have other_ref)
238 240 log.debug('Using ancestor %s as org_ref instead of %s'
239 241 % (c.ancestor, org_ref))
240 242 org_ref = ('rev', c.ancestor)
241 243 org_repo = other_repo
242 244
243 245 diff_limit = self.cut_off_limit if not c.fulldiff else None
244 246
245 247 log.debug('running diff between %s@%s and %s@%s'
246 248 % (org_repo.scm_instance.path, org_ref,
247 249 other_repo.scm_instance.path, other_ref))
248 250 _diff = org_repo.scm_instance.get_diff(rev1=safe_str(org_ref[1]),
249 251 rev2=safe_str(other_ref[1]))
250 252
251 253 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
252 254 diff_limit=diff_limit)
253 255 _parsed = diff_processor.prepare()
254 256
255 257 c.limited_diff = False
256 258 if isinstance(_parsed, LimitedDiffContainer):
257 259 c.limited_diff = True
258 260
259 261 c.files = []
260 262 c.changes = {}
261 263 c.lines_added = 0
262 264 c.lines_deleted = 0
263 265 for f in _parsed:
264 266 st = f['stats']
265 267 if st[0] != 'b':
266 268 c.lines_added += st[0]
267 269 c.lines_deleted += st[1]
268 270 fid = h.FID('', f['filename'])
269 271 c.files.append([fid, f['operation'], f['filename'], f['stats']])
270 272 diff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
271 273 c.changes[fid] = [f['operation'], f['filename'], diff]
272 274
273 275 return render('compare/compare_diff.html')
General Comments 0
You need to be logged in to leave comments. Login now