##// END OF EJS Templates
compare: make sure to not do any redirects for XHR requests types....
marcink -
r2050:e9bc65ad default
parent child Browse files
Show More
@@ -1,323 +1,325 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2017 RhodeCode GmbH
3 # Copyright (C) 2012-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23
23
24 from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPFound
24 from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from pyramid.renderers import render
26 from pyramid.renderers import render
27 from pyramid.response import Response
27 from pyramid.response import Response
28
28
29
29
30 from rhodecode.apps._base import RepoAppView
30 from rhodecode.apps._base import RepoAppView
31 from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name
31 from rhodecode.controllers.utils import parse_path_ref, get_commit_from_ref_name
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib import diffs, codeblocks
33 from rhodecode.lib import diffs, codeblocks
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.utils import safe_str
35 from rhodecode.lib.utils import safe_str
36 from rhodecode.lib.utils2 import safe_unicode, str2bool
36 from rhodecode.lib.utils2 import safe_unicode, str2bool
37 from rhodecode.lib.vcs.exceptions import (
37 from rhodecode.lib.vcs.exceptions import (
38 EmptyRepositoryError, RepositoryError, RepositoryRequirementError,
38 EmptyRepositoryError, RepositoryError, RepositoryRequirementError,
39 NodeDoesNotExistError)
39 NodeDoesNotExistError)
40 from rhodecode.model.db import Repository, ChangesetStatus
40 from rhodecode.model.db import Repository, ChangesetStatus
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class RepoCompareView(RepoAppView):
45 class RepoCompareView(RepoAppView):
46 def load_default_context(self):
46 def load_default_context(self):
47 c = self._get_local_tmpl_context(include_app_defaults=True)
47 c = self._get_local_tmpl_context(include_app_defaults=True)
48
48
49 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
49 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
50 c.repo_info = self.db_repo
50 c.repo_info = self.db_repo
51 c.rhodecode_repo = self.rhodecode_vcs_repo
51 c.rhodecode_repo = self.rhodecode_vcs_repo
52
52
53 self._register_global_c(c)
53 self._register_global_c(c)
54 return c
54 return c
55
55
56 def _get_commit_or_redirect(
56 def _get_commit_or_redirect(
57 self, ref, ref_type, repo, redirect_after=True, partial=False):
57 self, ref, ref_type, repo, redirect_after=True, partial=False):
58 """
58 """
59 This is a safe way to get a commit. If an error occurs it
59 This is a safe way to get a commit. If an error occurs it
60 redirects to a commit with a proper message. If partial is set
60 redirects to a commit with a proper message. If partial is set
61 then it does not do redirect raise and throws an exception instead.
61 then it does not do redirect raise and throws an exception instead.
62 """
62 """
63 _ = self.request.translate
63 _ = self.request.translate
64 try:
64 try:
65 return get_commit_from_ref_name(repo, safe_str(ref), ref_type)
65 return get_commit_from_ref_name(repo, safe_str(ref), ref_type)
66 except EmptyRepositoryError:
66 except EmptyRepositoryError:
67 if not redirect_after:
67 if not redirect_after:
68 return repo.scm_instance().EMPTY_COMMIT
68 return repo.scm_instance().EMPTY_COMMIT
69 h.flash(h.literal(_('There are no commits yet')),
69 h.flash(h.literal(_('There are no commits yet')),
70 category='warning')
70 category='warning')
71 raise HTTPFound(
71 if not partial:
72 h.route_path('repo_summary', repo_name=repo.repo_name))
72 raise HTTPFound(
73 h.route_path('repo_summary', repo_name=repo.repo_name))
74 raise HTTPBadRequest()
73
75
74 except RepositoryError as e:
76 except RepositoryError as e:
75 log.exception(safe_str(e))
77 log.exception(safe_str(e))
76 h.flash(safe_str(h.escape(e)), category='warning')
78 h.flash(safe_str(h.escape(e)), category='warning')
77 if not partial:
79 if not partial:
78 raise HTTPFound(
80 raise HTTPFound(
79 h.route_path('repo_summary', repo_name=repo.repo_name))
81 h.route_path('repo_summary', repo_name=repo.repo_name))
80 raise HTTPBadRequest()
82 raise HTTPBadRequest()
81
83
82 @LoginRequired()
84 @LoginRequired()
83 @HasRepoPermissionAnyDecorator(
85 @HasRepoPermissionAnyDecorator(
84 'repository.read', 'repository.write', 'repository.admin')
86 'repository.read', 'repository.write', 'repository.admin')
85 @view_config(
87 @view_config(
86 route_name='repo_compare_select', request_method='GET',
88 route_name='repo_compare_select', request_method='GET',
87 renderer='rhodecode:templates/compare/compare_diff.mako')
89 renderer='rhodecode:templates/compare/compare_diff.mako')
88 def compare_select(self):
90 def compare_select(self):
89 _ = self.request.translate
91 _ = self.request.translate
90 c = self.load_default_context()
92 c = self.load_default_context()
91
93
92 source_repo = self.db_repo_name
94 source_repo = self.db_repo_name
93 target_repo = self.request.GET.get('target_repo', source_repo)
95 target_repo = self.request.GET.get('target_repo', source_repo)
94 c.source_repo = Repository.get_by_repo_name(source_repo)
96 c.source_repo = Repository.get_by_repo_name(source_repo)
95 c.target_repo = Repository.get_by_repo_name(target_repo)
97 c.target_repo = Repository.get_by_repo_name(target_repo)
96
98
97 if c.source_repo is None or c.target_repo is None:
99 if c.source_repo is None or c.target_repo is None:
98 raise HTTPNotFound()
100 raise HTTPNotFound()
99
101
100 c.compare_home = True
102 c.compare_home = True
101 c.commit_ranges = []
103 c.commit_ranges = []
102 c.collapse_all_commits = False
104 c.collapse_all_commits = False
103 c.diffset = None
105 c.diffset = None
104 c.limited_diff = False
106 c.limited_diff = False
105 c.source_ref = c.target_ref = _('Select commit')
107 c.source_ref = c.target_ref = _('Select commit')
106 c.source_ref_type = ""
108 c.source_ref_type = ""
107 c.target_ref_type = ""
109 c.target_ref_type = ""
108 c.commit_statuses = ChangesetStatus.STATUSES
110 c.commit_statuses = ChangesetStatus.STATUSES
109 c.preview_mode = False
111 c.preview_mode = False
110 c.file_path = None
112 c.file_path = None
111
113
112 return self._get_template_context(c)
114 return self._get_template_context(c)
113
115
114 @LoginRequired()
116 @LoginRequired()
115 @HasRepoPermissionAnyDecorator(
117 @HasRepoPermissionAnyDecorator(
116 'repository.read', 'repository.write', 'repository.admin')
118 'repository.read', 'repository.write', 'repository.admin')
117 @view_config(
119 @view_config(
118 route_name='repo_compare', request_method='GET',
120 route_name='repo_compare', request_method='GET',
119 renderer=None)
121 renderer=None)
120 def compare(self):
122 def compare(self):
121 _ = self.request.translate
123 _ = self.request.translate
122 c = self.load_default_context()
124 c = self.load_default_context()
123
125
124 source_ref_type = self.request.matchdict['source_ref_type']
126 source_ref_type = self.request.matchdict['source_ref_type']
125 source_ref = self.request.matchdict['source_ref']
127 source_ref = self.request.matchdict['source_ref']
126 target_ref_type = self.request.matchdict['target_ref_type']
128 target_ref_type = self.request.matchdict['target_ref_type']
127 target_ref = self.request.matchdict['target_ref']
129 target_ref = self.request.matchdict['target_ref']
128
130
129 # source_ref will be evaluated in source_repo
131 # source_ref will be evaluated in source_repo
130 source_repo_name = self.db_repo_name
132 source_repo_name = self.db_repo_name
131 source_path, source_id = parse_path_ref(source_ref)
133 source_path, source_id = parse_path_ref(source_ref)
132
134
133 # target_ref will be evaluated in target_repo
135 # target_ref will be evaluated in target_repo
134 target_repo_name = self.request.GET.get('target_repo', source_repo_name)
136 target_repo_name = self.request.GET.get('target_repo', source_repo_name)
135 target_path, target_id = parse_path_ref(
137 target_path, target_id = parse_path_ref(
136 target_ref, default_path=self.request.GET.get('f_path', ''))
138 target_ref, default_path=self.request.GET.get('f_path', ''))
137
139
138 # if merge is True
140 # if merge is True
139 # Show what changes since the shared ancestor commit of target/source
141 # Show what changes since the shared ancestor commit of target/source
140 # the source would get if it was merged with target. Only commits
142 # the source would get if it was merged with target. Only commits
141 # which are in target but not in source will be shown.
143 # which are in target but not in source will be shown.
142 merge = str2bool(self.request.GET.get('merge'))
144 merge = str2bool(self.request.GET.get('merge'))
143 # if merge is False
145 # if merge is False
144 # Show a raw diff of source/target refs even if no ancestor exists
146 # Show a raw diff of source/target refs even if no ancestor exists
145
147
146 # c.fulldiff disables cut_off_limit
148 # c.fulldiff disables cut_off_limit
147 c.fulldiff = str2bool(self.request.GET.get('fulldiff'))
149 c.fulldiff = str2bool(self.request.GET.get('fulldiff'))
148
150
149 c.file_path = target_path
151 c.file_path = target_path
150 c.commit_statuses = ChangesetStatus.STATUSES
152 c.commit_statuses = ChangesetStatus.STATUSES
151
153
152 # if partial, returns just compare_commits.html (commits log)
154 # if partial, returns just compare_commits.html (commits log)
153 partial = self.request.is_xhr
155 partial = self.request.is_xhr
154
156
155 # swap url for compare_diff page
157 # swap url for compare_diff page
156 c.swap_url = h.route_path(
158 c.swap_url = h.route_path(
157 'repo_compare',
159 'repo_compare',
158 repo_name=target_repo_name,
160 repo_name=target_repo_name,
159 source_ref_type=target_ref_type,
161 source_ref_type=target_ref_type,
160 source_ref=target_ref,
162 source_ref=target_ref,
161 target_repo=source_repo_name,
163 target_repo=source_repo_name,
162 target_ref_type=source_ref_type,
164 target_ref_type=source_ref_type,
163 target_ref=source_ref,
165 target_ref=source_ref,
164 _query=dict(merge=merge and '1' or '', f_path=target_path))
166 _query=dict(merge=merge and '1' or '', f_path=target_path))
165
167
166 source_repo = Repository.get_by_repo_name(source_repo_name)
168 source_repo = Repository.get_by_repo_name(source_repo_name)
167 target_repo = Repository.get_by_repo_name(target_repo_name)
169 target_repo = Repository.get_by_repo_name(target_repo_name)
168
170
169 if source_repo is None:
171 if source_repo is None:
170 log.error('Could not find the source repo: {}'
172 log.error('Could not find the source repo: {}'
171 .format(source_repo_name))
173 .format(source_repo_name))
172 h.flash(_('Could not find the source repo: `{}`')
174 h.flash(_('Could not find the source repo: `{}`')
173 .format(h.escape(source_repo_name)), category='error')
175 .format(h.escape(source_repo_name)), category='error')
174 raise HTTPFound(
176 raise HTTPFound(
175 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
177 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
176
178
177 if target_repo is None:
179 if target_repo is None:
178 log.error('Could not find the target repo: {}'
180 log.error('Could not find the target repo: {}'
179 .format(source_repo_name))
181 .format(source_repo_name))
180 h.flash(_('Could not find the target repo: `{}`')
182 h.flash(_('Could not find the target repo: `{}`')
181 .format(h.escape(target_repo_name)), category='error')
183 .format(h.escape(target_repo_name)), category='error')
182 raise HTTPFound(
184 raise HTTPFound(
183 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
185 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
184
186
185 source_scm = source_repo.scm_instance()
187 source_scm = source_repo.scm_instance()
186 target_scm = target_repo.scm_instance()
188 target_scm = target_repo.scm_instance()
187
189
188 source_alias = source_scm.alias
190 source_alias = source_scm.alias
189 target_alias = target_scm.alias
191 target_alias = target_scm.alias
190 if source_alias != target_alias:
192 if source_alias != target_alias:
191 msg = _('The comparison of two different kinds of remote repos '
193 msg = _('The comparison of two different kinds of remote repos '
192 'is not available')
194 'is not available')
193 log.error(msg)
195 log.error(msg)
194 h.flash(msg, category='error')
196 h.flash(msg, category='error')
195 raise HTTPFound(
197 raise HTTPFound(
196 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
198 h.route_path('repo_compare_select', repo_name=self.db_repo_name))
197
199
198 source_commit = self._get_commit_or_redirect(
200 source_commit = self._get_commit_or_redirect(
199 ref=source_id, ref_type=source_ref_type, repo=source_repo,
201 ref=source_id, ref_type=source_ref_type, repo=source_repo,
200 partial=partial)
202 partial=partial)
201 target_commit = self._get_commit_or_redirect(
203 target_commit = self._get_commit_or_redirect(
202 ref=target_id, ref_type=target_ref_type, repo=target_repo,
204 ref=target_id, ref_type=target_ref_type, repo=target_repo,
203 partial=partial)
205 partial=partial)
204
206
205 c.compare_home = False
207 c.compare_home = False
206 c.source_repo = source_repo
208 c.source_repo = source_repo
207 c.target_repo = target_repo
209 c.target_repo = target_repo
208 c.source_ref = source_ref
210 c.source_ref = source_ref
209 c.target_ref = target_ref
211 c.target_ref = target_ref
210 c.source_ref_type = source_ref_type
212 c.source_ref_type = source_ref_type
211 c.target_ref_type = target_ref_type
213 c.target_ref_type = target_ref_type
212
214
213 pre_load = ["author", "branch", "date", "message"]
215 pre_load = ["author", "branch", "date", "message"]
214 c.ancestor = None
216 c.ancestor = None
215
217
216 if c.file_path:
218 if c.file_path:
217 if source_commit == target_commit:
219 if source_commit == target_commit:
218 c.commit_ranges = []
220 c.commit_ranges = []
219 else:
221 else:
220 c.commit_ranges = [target_commit]
222 c.commit_ranges = [target_commit]
221 else:
223 else:
222 try:
224 try:
223 c.commit_ranges = source_scm.compare(
225 c.commit_ranges = source_scm.compare(
224 source_commit.raw_id, target_commit.raw_id,
226 source_commit.raw_id, target_commit.raw_id,
225 target_scm, merge, pre_load=pre_load)
227 target_scm, merge, pre_load=pre_load)
226 if merge:
228 if merge:
227 c.ancestor = source_scm.get_common_ancestor(
229 c.ancestor = source_scm.get_common_ancestor(
228 source_commit.raw_id, target_commit.raw_id, target_scm)
230 source_commit.raw_id, target_commit.raw_id, target_scm)
229 except RepositoryRequirementError:
231 except RepositoryRequirementError:
230 msg = _('Could not compare repos with different '
232 msg = _('Could not compare repos with different '
231 'large file settings')
233 'large file settings')
232 log.error(msg)
234 log.error(msg)
233 if partial:
235 if partial:
234 return Response(msg)
236 return Response(msg)
235 h.flash(msg, category='error')
237 h.flash(msg, category='error')
236 raise HTTPFound(
238 raise HTTPFound(
237 h.route_path('repo_compare_select',
239 h.route_path('repo_compare_select',
238 repo_name=self.db_repo_name))
240 repo_name=self.db_repo_name))
239
241
240 c.statuses = self.db_repo.statuses(
242 c.statuses = self.db_repo.statuses(
241 [x.raw_id for x in c.commit_ranges])
243 [x.raw_id for x in c.commit_ranges])
242
244
243 # auto collapse if we have more than limit
245 # auto collapse if we have more than limit
244 collapse_limit = diffs.DiffProcessor._collapse_commits_over
246 collapse_limit = diffs.DiffProcessor._collapse_commits_over
245 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
247 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
246
248
247 if partial: # for PR ajax commits loader
249 if partial: # for PR ajax commits loader
248 if not c.ancestor:
250 if not c.ancestor:
249 return Response('') # cannot merge if there is no ancestor
251 return Response('') # cannot merge if there is no ancestor
250
252
251 html = render(
253 html = render(
252 'rhodecode:templates/compare/compare_commits.mako',
254 'rhodecode:templates/compare/compare_commits.mako',
253 self._get_template_context(c), self.request)
255 self._get_template_context(c), self.request)
254 return Response(html)
256 return Response(html)
255
257
256 if c.ancestor:
258 if c.ancestor:
257 # case we want a simple diff without incoming commits,
259 # case we want a simple diff without incoming commits,
258 # previewing what will be merged.
260 # previewing what will be merged.
259 # Make the diff on target repo (which is known to have target_ref)
261 # Make the diff on target repo (which is known to have target_ref)
260 log.debug('Using ancestor %s as source_ref instead of %s'
262 log.debug('Using ancestor %s as source_ref instead of %s'
261 % (c.ancestor, source_ref))
263 % (c.ancestor, source_ref))
262 source_repo = target_repo
264 source_repo = target_repo
263 source_commit = target_repo.get_commit(commit_id=c.ancestor)
265 source_commit = target_repo.get_commit(commit_id=c.ancestor)
264
266
265 # diff_limit will cut off the whole diff if the limit is applied
267 # diff_limit will cut off the whole diff if the limit is applied
266 # otherwise it will just hide the big files from the front-end
268 # otherwise it will just hide the big files from the front-end
267 diff_limit = c.visual.cut_off_limit_diff
269 diff_limit = c.visual.cut_off_limit_diff
268 file_limit = c.visual.cut_off_limit_file
270 file_limit = c.visual.cut_off_limit_file
269
271
270 log.debug('calculating diff between '
272 log.debug('calculating diff between '
271 'source_ref:%s and target_ref:%s for repo `%s`',
273 'source_ref:%s and target_ref:%s for repo `%s`',
272 source_commit, target_commit,
274 source_commit, target_commit,
273 safe_unicode(source_repo.scm_instance().path))
275 safe_unicode(source_repo.scm_instance().path))
274
276
275 if source_commit.repository != target_commit.repository:
277 if source_commit.repository != target_commit.repository:
276 msg = _(
278 msg = _(
277 "Repositories unrelated. "
279 "Repositories unrelated. "
278 "Cannot compare commit %(commit1)s from repository %(repo1)s "
280 "Cannot compare commit %(commit1)s from repository %(repo1)s "
279 "with commit %(commit2)s from repository %(repo2)s.") % {
281 "with commit %(commit2)s from repository %(repo2)s.") % {
280 'commit1': h.show_id(source_commit),
282 'commit1': h.show_id(source_commit),
281 'repo1': source_repo.repo_name,
283 'repo1': source_repo.repo_name,
282 'commit2': h.show_id(target_commit),
284 'commit2': h.show_id(target_commit),
283 'repo2': target_repo.repo_name,
285 'repo2': target_repo.repo_name,
284 }
286 }
285 h.flash(msg, category='error')
287 h.flash(msg, category='error')
286 raise HTTPFound(
288 raise HTTPFound(
287 h.route_path('repo_compare_select',
289 h.route_path('repo_compare_select',
288 repo_name=self.db_repo_name))
290 repo_name=self.db_repo_name))
289
291
290 txt_diff = source_repo.scm_instance().get_diff(
292 txt_diff = source_repo.scm_instance().get_diff(
291 commit1=source_commit, commit2=target_commit,
293 commit1=source_commit, commit2=target_commit,
292 path=target_path, path1=source_path)
294 path=target_path, path1=source_path)
293
295
294 diff_processor = diffs.DiffProcessor(
296 diff_processor = diffs.DiffProcessor(
295 txt_diff, format='newdiff', diff_limit=diff_limit,
297 txt_diff, format='newdiff', diff_limit=diff_limit,
296 file_limit=file_limit, show_full_diff=c.fulldiff)
298 file_limit=file_limit, show_full_diff=c.fulldiff)
297 _parsed = diff_processor.prepare()
299 _parsed = diff_processor.prepare()
298
300
299 def _node_getter(commit):
301 def _node_getter(commit):
300 """ Returns a function that returns a node for a commit or None """
302 """ Returns a function that returns a node for a commit or None """
301 def get_node(fname):
303 def get_node(fname):
302 try:
304 try:
303 return commit.get_node(fname)
305 return commit.get_node(fname)
304 except NodeDoesNotExistError:
306 except NodeDoesNotExistError:
305 return None
307 return None
306 return get_node
308 return get_node
307
309
308 diffset = codeblocks.DiffSet(
310 diffset = codeblocks.DiffSet(
309 repo_name=source_repo.repo_name,
311 repo_name=source_repo.repo_name,
310 source_node_getter=_node_getter(source_commit),
312 source_node_getter=_node_getter(source_commit),
311 target_node_getter=_node_getter(target_commit),
313 target_node_getter=_node_getter(target_commit),
312 )
314 )
313 c.diffset = diffset.render_patchset(
315 c.diffset = diffset.render_patchset(
314 _parsed, source_ref, target_ref)
316 _parsed, source_ref, target_ref)
315
317
316 c.preview_mode = merge
318 c.preview_mode = merge
317 c.source_commit = source_commit
319 c.source_commit = source_commit
318 c.target_commit = target_commit
320 c.target_commit = target_commit
319
321
320 html = render(
322 html = render(
321 'rhodecode:templates/compare/compare_diff.mako',
323 'rhodecode:templates/compare/compare_diff.mako',
322 self._get_template_context(c), self.request)
324 self._get_template_context(c), self.request)
323 return Response(html) No newline at end of file
325 return Response(html)
General Comments 0
You need to be logged in to leave comments. Login now