##// END OF EJS Templates
pullrequest: disable pr on commits that dont share ancestor
dan -
r64:0b85876a default
parent child Browse files
Show More
@@ -1,265 +1,267 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
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 from rhodecode.model.db import Repository, ChangesetStatus
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class CompareController(BaseRepoController):
47 47
48 48 def __before__(self):
49 49 super(CompareController, self).__before__()
50 50
51 51 def _get_commit_or_redirect(
52 52 self, ref, ref_type, repo, redirect_after=True, partial=False):
53 53 """
54 54 This is a safe way to get a commit. If an error occurs it
55 55 redirects to a commit with a proper message. If partial is set
56 56 then it does not do redirect raise and throws an exception instead.
57 57 """
58 58 try:
59 59 return get_commit_from_ref_name(repo, safe_str(ref), ref_type)
60 60 except EmptyRepositoryError:
61 61 if not redirect_after:
62 62 return repo.scm_instance().EMPTY_COMMIT
63 63 h.flash(h.literal(_('There are no commits yet')),
64 64 category='warning')
65 65 redirect(url('summary_home', repo_name=repo.repo_name))
66 66
67 67 except RepositoryError as e:
68 68 msg = safe_str(e)
69 69 log.exception(msg)
70 70 h.flash(msg, category='warning')
71 71 if not partial:
72 72 redirect(h.url('summary_home', repo_name=repo.repo_name))
73 73 raise HTTPBadRequest()
74 74
75 75 @LoginRequired()
76 76 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
77 77 'repository.admin')
78 78 def index(self, repo_name):
79 79 c.compare_home = True
80 80 c.commit_ranges = []
81 81 c.files = []
82 82 c.limited_diff = False
83 83 source_repo = c.rhodecode_db_repo.repo_name
84 84 target_repo = request.GET.get('target_repo', source_repo)
85 85 c.source_repo = Repository.get_by_repo_name(source_repo)
86 86 c.target_repo = Repository.get_by_repo_name(target_repo)
87 87 c.source_ref = c.target_ref = _('Select commit')
88 88 c.source_ref_type = ""
89 89 c.target_ref_type = ""
90 90 c.commit_statuses = ChangesetStatus.STATUSES
91 91 c.preview_mode = False
92 92 return render('compare/compare_diff.html')
93 93
94 94 @LoginRequired()
95 95 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
96 96 'repository.admin')
97 97 def compare(self, repo_name, source_ref_type, source_ref,
98 98 target_ref_type, target_ref):
99 99 # source_ref will be evaluated in source_repo
100 100 source_repo_name = c.rhodecode_db_repo.repo_name
101 101 source_path, source_id = parse_path_ref(source_ref)
102 102
103 103 # target_ref will be evaluated in target_repo
104 104 target_repo_name = request.GET.get('target_repo', source_repo_name)
105 105 target_path, target_id = parse_path_ref(target_ref)
106 106
107 107 c.commit_statuses = ChangesetStatus.STATUSES
108 108
109 109 # if merge is True
110 110 # Show what changes since the shared ancestor commit of target/source
111 111 # the source would get if it was merged with target. Only commits
112 112 # which are in target but not in source will be shown.
113 113 merge = str2bool(request.GET.get('merge'))
114 114 # if merge is False
115 115 # Show a raw diff of source/target refs even if no ancestor exists
116 116
117 117
118 118 # c.fulldiff disables cut_off_limit
119 119 c.fulldiff = str2bool(request.GET.get('fulldiff'))
120 120
121 121 # if partial, returns just compare_commits.html (commits log)
122 122 partial = request.is_xhr
123 123
124 124 # swap url for compare_diff page
125 125 c.swap_url = h.url(
126 126 'compare_url',
127 127 repo_name=target_repo_name,
128 128 source_ref_type=target_ref_type,
129 129 source_ref=target_ref,
130 130 target_repo=source_repo_name,
131 131 target_ref_type=source_ref_type,
132 132 target_ref=source_ref,
133 133 merge=merge and '1' or '')
134 134
135 135 source_repo = Repository.get_by_repo_name(source_repo_name)
136 136 target_repo = Repository.get_by_repo_name(target_repo_name)
137 137
138 138 if source_repo is None:
139 139 msg = _('Could not find the original repo: %(repo)s') % {
140 140 'repo': source_repo}
141 141
142 142 log.error(msg)
143 143 h.flash(msg, category='error')
144 144 return redirect(url('compare_home', repo_name=c.repo_name))
145 145
146 146 if target_repo is None:
147 147 msg = _('Could not find the other repo: %(repo)s') % {
148 148 'repo': target_repo_name}
149 149 log.error(msg)
150 150 h.flash(msg, category='error')
151 151 return redirect(url('compare_home', repo_name=c.repo_name))
152 152
153 153 source_alias = source_repo.scm_instance().alias
154 154 target_alias = target_repo.scm_instance().alias
155 155 if source_alias != target_alias:
156 156 msg = _('The comparison of two different kinds of remote repos '
157 157 'is not available')
158 158 log.error(msg)
159 159 h.flash(msg, category='error')
160 160 return redirect(url('compare_home', repo_name=c.repo_name))
161 161
162 162 source_commit = self._get_commit_or_redirect(
163 163 ref=source_id, ref_type=source_ref_type, repo=source_repo,
164 164 partial=partial)
165 165 target_commit = self._get_commit_or_redirect(
166 166 ref=target_id, ref_type=target_ref_type, repo=target_repo,
167 167 partial=partial)
168 168
169 169 c.compare_home = False
170 170 c.source_repo = source_repo
171 171 c.target_repo = target_repo
172 172 c.source_ref = source_ref
173 173 c.target_ref = target_ref
174 174 c.source_ref_type = source_ref_type
175 175 c.target_ref_type = target_ref_type
176 176
177 177 source_scm = source_repo.scm_instance()
178 178 target_scm = target_repo.scm_instance()
179 179
180 180 pre_load = ["author", "branch", "date", "message"]
181 181 c.ancestor = None
182 182 try:
183 183 c.commit_ranges = source_scm.compare(
184 184 source_commit.raw_id, target_commit.raw_id,
185 185 target_scm, merge, pre_load=pre_load)
186 186 if merge:
187 187 c.ancestor = source_scm.get_common_ancestor(
188 188 source_commit.raw_id, target_commit.raw_id, target_scm)
189 189 except RepositoryRequirementError:
190 190 msg = _('Could not compare repos with different '
191 191 'large file settings')
192 192 log.error(msg)
193 193 if partial:
194 194 return msg
195 195 h.flash(msg, category='error')
196 196 return redirect(url('compare_home', repo_name=c.repo_name))
197 197
198 198 c.statuses = c.rhodecode_db_repo.statuses(
199 199 [x.raw_id for x in c.commit_ranges])
200 200
201 if partial:
201 if partial: # for PR ajax commits loader
202 if not c.ancestor:
203 return '' # cannot merge if there is no ancestor
202 204 return render('compare/compare_commits.html')
203 205
204 206 if c.ancestor:
205 207 # case we want a simple diff without incoming commits,
206 208 # previewing what will be merged.
207 209 # Make the diff on target repo (which is known to have target_ref)
208 210 log.debug('Using ancestor %s as source_ref instead of %s'
209 211 % (c.ancestor, source_ref))
210 212 source_repo = target_repo
211 213 source_commit = target_repo.get_commit(commit_id=c.ancestor)
212 214
213 215 # diff_limit will cut off the whole diff if the limit is applied
214 216 # otherwise it will just hide the big files from the front-end
215 217 diff_limit = self.cut_off_limit_diff
216 218 file_limit = self.cut_off_limit_file
217 219
218 220 log.debug('calculating diff between '
219 221 'source_ref:%s and target_ref:%s for repo `%s`',
220 222 source_commit, target_commit,
221 223 safe_unicode(source_repo.scm_instance().path))
222 224
223 225 if source_commit.repository != target_commit.repository:
224 226 msg = _(
225 227 "Repositories unrelated. "
226 228 "Cannot compare commit %(commit1)s from repository %(repo1)s "
227 229 "with commit %(commit2)s from repository %(repo2)s.") % {
228 230 'commit1': h.show_id(source_commit),
229 231 'repo1': source_repo.repo_name,
230 232 'commit2': h.show_id(target_commit),
231 233 'repo2': target_repo.repo_name,
232 234 }
233 235 h.flash(msg, category='error')
234 236 raise HTTPBadRequest()
235 237
236 238 txtdiff = source_repo.scm_instance().get_diff(
237 239 commit1=source_commit, commit2=target_commit,
238 240 path1=source_path, path=target_path)
239 241 diff_processor = diffs.DiffProcessor(
240 242 txtdiff, format='gitdiff', diff_limit=diff_limit,
241 243 file_limit=file_limit, show_full_diff=c.fulldiff)
242 244 _parsed = diff_processor.prepare()
243 245
244 246 c.limited_diff = False
245 247 if isinstance(_parsed, diffs.LimitedDiffContainer):
246 248 c.limited_diff = True
247 249
248 250 c.files = []
249 251 c.changes = {}
250 252 c.lines_added = 0
251 253 c.lines_deleted = 0
252 254 for f in _parsed:
253 255 st = f['stats']
254 256 if not st['binary']:
255 257 c.lines_added += st['added']
256 258 c.lines_deleted += st['deleted']
257 259 fid = h.FID('', f['filename'])
258 260 c.files.append([fid, f['operation'], f['filename'], f['stats'], f])
259 261 htmldiff = diff_processor.as_html(
260 262 enable_comments=False, parsed_lines=[f])
261 263 c.changes[fid] = [f['operation'], f['filename'], htmldiff, f]
262 264
263 265 c.preview_mode = merge
264 266
265 267 return render('compare/compare_diff.html')
General Comments 0
You need to be logged in to leave comments. Login now