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