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