##// END OF EJS Templates
Added extra check for very large diffs in changesets, sometimes for very large diffs the diff parser could kill CPU.
marcink -
r1274:7a0004ef beta
parent child Browse files
Show More
@@ -1,239 +1,254 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons showoing changes beetween
6 changeset controller for pylons showoing changes beetween
7 revisions
7 revisions
8
8
9 :created_on: Apr 25, 2010
9 :created_on: Apr 25, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons import tmpl_context as c, url, request, response
29 from pylons import tmpl_context as c, url, request, response
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32
32
33 import rhodecode.lib.helpers as h
33 import rhodecode.lib.helpers as h
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.utils import EmptyChangeset
36 from rhodecode.lib.utils import EmptyChangeset
37
37
38 from vcs.exceptions import RepositoryError, ChangesetError, \
38 from vcs.exceptions import RepositoryError, ChangesetError, \
39 ChangesetDoesNotExistError
39 ChangesetDoesNotExistError
40 from vcs.nodes import FileNode
40 from vcs.nodes import FileNode
41 from vcs.utils import diffs as differ
41 from vcs.utils import diffs as differ
42 from vcs.utils.ordered_dict import OrderedDict
42 from vcs.utils.ordered_dict import OrderedDict
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class ChangesetController(BaseRepoController):
47 class ChangesetController(BaseRepoController):
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 'repository.admin')
51 'repository.admin')
52 def __before__(self):
52 def __before__(self):
53 super(ChangesetController, self).__before__()
53 super(ChangesetController, self).__before__()
54 c.affected_files_cut_off = 60
54 c.affected_files_cut_off = 60
55
55
56 def index(self, revision):
56 def index(self, revision):
57
57
58 def wrap_to_table(str):
58 def wrap_to_table(str):
59
59
60 return '''<table class="code-difftable">
60 return '''<table class="code-difftable">
61 <tr class="line">
61 <tr class="line">
62 <td class="lineno new"></td>
62 <td class="lineno new"></td>
63 <td class="code"><pre>%s</pre></td>
63 <td class="code"><pre>%s</pre></td>
64 </tr>
64 </tr>
65 </table>''' % str
65 </table>''' % str
66
66
67 #get ranges of revisions if preset
67 #get ranges of revisions if preset
68 rev_range = revision.split('...')[:2]
68 rev_range = revision.split('...')[:2]
69
69
70 try:
70 try:
71 if len(rev_range) == 2:
71 if len(rev_range) == 2:
72 rev_start = rev_range[0]
72 rev_start = rev_range[0]
73 rev_end = rev_range[1]
73 rev_end = rev_range[1]
74 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
74 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
75 end=rev_end)
75 end=rev_end)
76 else:
76 else:
77 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
77 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
78
78
79 c.cs_ranges = list(rev_ranges)
79 c.cs_ranges = list(rev_ranges)
80
80
81 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
81 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
82 log.error(traceback.format_exc())
82 log.error(traceback.format_exc())
83 h.flash(str(e), category='warning')
83 h.flash(str(e), category='warning')
84 return redirect(url('home'))
84 return redirect(url('home'))
85
85
86 c.changes = OrderedDict()
86 c.changes = OrderedDict()
87 c.sum_added = 0
87 c.sum_added = 0
88 c.sum_removed = 0
88 c.sum_removed = 0
89 c.lines_added = 0
89 c.lines_added = 0
90 c.lines_deleted = 0
90 c.lines_deleted = 0
91 c.cut_off = False
91 c.cut_off = False # defines if cut off limit is reached
92
92
93 # Iterate over ranges (default changeset view is always one changeset)
93 for changeset in c.cs_ranges:
94 for changeset in c.cs_ranges:
94 c.changes[changeset.raw_id] = []
95 c.changes[changeset.raw_id] = []
95 try:
96 try:
96 changeset_parent = changeset.parents[0]
97 changeset_parent = changeset.parents[0]
97 except IndexError:
98 except IndexError:
98 changeset_parent = None
99 changeset_parent = None
99
100
100 #==================================================================
101 #==================================================================
101 # ADDED FILES
102 # ADDED FILES
102 #==================================================================
103 #==================================================================
103 for node in changeset.added:
104 for node in changeset.added:
105
104 filenode_old = FileNode(node.path, '', EmptyChangeset())
106 filenode_old = FileNode(node.path, '', EmptyChangeset())
105 if filenode_old.is_binary or node.is_binary:
107 if filenode_old.is_binary or node.is_binary:
106 diff = wrap_to_table(_('binary file'))
108 diff = wrap_to_table(_('binary file'))
107 st = (0, 0)
109 st = (0, 0)
108 else:
110 else:
111 # in this case node.size is good parameter since those are
112 # added nodes and their size defines how many changes were
113 # made
109 c.sum_added += node.size
114 c.sum_added += node.size
110 if c.sum_added < self.cut_off_limit:
115 if c.sum_added < self.cut_off_limit:
111 f_gitdiff = differ.get_gitdiff(filenode_old, node)
116 f_gitdiff = differ.get_gitdiff(filenode_old, node)
112 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
117 d = differ.DiffProcessor(f_gitdiff, format='gitdiff')
118
119 st = d.stat()
113 diff = d.as_html()
120 diff = d.as_html()
114 st = d.stat()
121
115 else:
122 else:
116 diff = wrap_to_table(_('Changeset is to big and '
123 diff = wrap_to_table(_('Changeset is to big and '
117 'was cut off, see raw '
124 'was cut off, see raw '
118 'changeset instead'))
125 'changeset instead'))
119 c.cut_off = True
126 c.cut_off = True
120 break
127 break
121
128
122 cs1 = None
129 cs1 = None
123 cs2 = node.last_changeset.raw_id
130 cs2 = node.last_changeset.raw_id
124 c.lines_added += st[0]
131 c.lines_added += st[0]
125 c.lines_deleted += st[1]
132 c.lines_deleted += st[1]
126 c.changes[changeset.raw_id].append(('added', node, diff,
133 c.changes[changeset.raw_id].append(('added', node, diff,
127 cs1, cs2, st))
134 cs1, cs2, st))
128
135
129 #==================================================================
136 #==================================================================
130 # CHANGED FILES
137 # CHANGED FILES
131 #==================================================================
138 #==================================================================
132 if not c.cut_off:
139 if not c.cut_off:
133 for node in changeset.changed:
140 for node in changeset.changed:
134 try:
141 try:
135 filenode_old = changeset_parent.get_node(node.path)
142 filenode_old = changeset_parent.get_node(node.path)
136 except ChangesetError:
143 except ChangesetError:
144 log.warning('Unable to fetch parent node for diff')
137 filenode_old = FileNode(node.path, '',
145 filenode_old = FileNode(node.path, '',
138 EmptyChangeset())
146 EmptyChangeset())
139
147
140 if filenode_old.is_binary or node.is_binary:
148 if filenode_old.is_binary or node.is_binary:
141 diff = wrap_to_table(_('binary file'))
149 diff = wrap_to_table(_('binary file'))
142 st = (0, 0)
150 st = (0, 0)
143 else:
151 else:
144
152
145 if c.sum_removed < self.cut_off_limit:
153 if c.sum_removed < self.cut_off_limit:
146 f_gitdiff = differ.get_gitdiff(filenode_old, node)
154 f_gitdiff = differ.get_gitdiff(filenode_old, node)
147 d = differ.DiffProcessor(f_gitdiff,
155 d = differ.DiffProcessor(f_gitdiff,
148 format='gitdiff')
156 format='gitdiff')
149 diff = d.as_html()
150 st = d.stat()
157 st = d.stat()
158 if (st[0] + st[1]) * 256 > self.cut_off_limit:
159 diff = wrap_to_table(_('Diff is to big '
160 'and was cut off, see '
161 'raw diff instead'))
162 else:
163 diff = d.as_html()
164
165
151 if diff:
166 if diff:
152 c.sum_removed += len(diff)
167 c.sum_removed += len(diff)
153 else:
168 else:
154 diff = wrap_to_table(_('Changeset is to big and '
169 diff = wrap_to_table(_('Changeset is to big and '
155 'was cut off, see raw '
170 'was cut off, see raw '
156 'changeset instead'))
171 'changeset instead'))
157 c.cut_off = True
172 c.cut_off = True
158 break
173 break
159
174
160 cs1 = filenode_old.last_changeset.raw_id
175 cs1 = filenode_old.last_changeset.raw_id
161 cs2 = node.last_changeset.raw_id
176 cs2 = node.last_changeset.raw_id
162 c.lines_added += st[0]
177 c.lines_added += st[0]
163 c.lines_deleted += st[1]
178 c.lines_deleted += st[1]
164 c.changes[changeset.raw_id].append(('changed', node, diff,
179 c.changes[changeset.raw_id].append(('changed', node, diff,
165 cs1, cs2, st))
180 cs1, cs2, st))
166
181
167 #==================================================================
182 #==================================================================
168 # REMOVED FILES
183 # REMOVED FILES
169 #==================================================================
184 #==================================================================
170 if not c.cut_off:
185 if not c.cut_off:
171 for node in changeset.removed:
186 for node in changeset.removed:
172 c.changes[changeset.raw_id].append(('removed', node, None,
187 c.changes[changeset.raw_id].append(('removed', node, None,
173 None, None, (0, 0)))
188 None, None, (0, 0)))
174
189
175 if len(c.cs_ranges) == 1:
190 if len(c.cs_ranges) == 1:
176 c.changeset = c.cs_ranges[0]
191 c.changeset = c.cs_ranges[0]
177 c.changes = c.changes[c.changeset.raw_id]
192 c.changes = c.changes[c.changeset.raw_id]
178
193
179 return render('changeset/changeset.html')
194 return render('changeset/changeset.html')
180 else:
195 else:
181 return render('changeset/changeset_range.html')
196 return render('changeset/changeset_range.html')
182
197
183 def raw_changeset(self, revision):
198 def raw_changeset(self, revision):
184
199
185 method = request.GET.get('diff', 'show')
200 method = request.GET.get('diff', 'show')
186 try:
201 try:
187 c.scm_type = c.rhodecode_repo.alias
202 c.scm_type = c.rhodecode_repo.alias
188 c.changeset = c.rhodecode_repo.get_changeset(revision)
203 c.changeset = c.rhodecode_repo.get_changeset(revision)
189 except RepositoryError:
204 except RepositoryError:
190 log.error(traceback.format_exc())
205 log.error(traceback.format_exc())
191 return redirect(url('home'))
206 return redirect(url('home'))
192 else:
207 else:
193 try:
208 try:
194 c.changeset_parent = c.changeset.parents[0]
209 c.changeset_parent = c.changeset.parents[0]
195 except IndexError:
210 except IndexError:
196 c.changeset_parent = None
211 c.changeset_parent = None
197 c.changes = []
212 c.changes = []
198
213
199 for node in c.changeset.added:
214 for node in c.changeset.added:
200 filenode_old = FileNode(node.path, '')
215 filenode_old = FileNode(node.path, '')
201 if filenode_old.is_binary or node.is_binary:
216 if filenode_old.is_binary or node.is_binary:
202 diff = _('binary file') + '\n'
217 diff = _('binary file') + '\n'
203 else:
218 else:
204 f_gitdiff = differ.get_gitdiff(filenode_old, node)
219 f_gitdiff = differ.get_gitdiff(filenode_old, node)
205 diff = differ.DiffProcessor(f_gitdiff,
220 diff = differ.DiffProcessor(f_gitdiff,
206 format='gitdiff').raw_diff()
221 format='gitdiff').raw_diff()
207
222
208 cs1 = None
223 cs1 = None
209 cs2 = node.last_changeset.raw_id
224 cs2 = node.last_changeset.raw_id
210 c.changes.append(('added', node, diff, cs1, cs2))
225 c.changes.append(('added', node, diff, cs1, cs2))
211
226
212 for node in c.changeset.changed:
227 for node in c.changeset.changed:
213 filenode_old = c.changeset_parent.get_node(node.path)
228 filenode_old = c.changeset_parent.get_node(node.path)
214 if filenode_old.is_binary or node.is_binary:
229 if filenode_old.is_binary or node.is_binary:
215 diff = _('binary file')
230 diff = _('binary file')
216 else:
231 else:
217 f_gitdiff = differ.get_gitdiff(filenode_old, node)
232 f_gitdiff = differ.get_gitdiff(filenode_old, node)
218 diff = differ.DiffProcessor(f_gitdiff,
233 diff = differ.DiffProcessor(f_gitdiff,
219 format='gitdiff').raw_diff()
234 format='gitdiff').raw_diff()
220
235
221 cs1 = filenode_old.last_changeset.raw_id
236 cs1 = filenode_old.last_changeset.raw_id
222 cs2 = node.last_changeset.raw_id
237 cs2 = node.last_changeset.raw_id
223 c.changes.append(('changed', node, diff, cs1, cs2))
238 c.changes.append(('changed', node, diff, cs1, cs2))
224
239
225 response.content_type = 'text/plain'
240 response.content_type = 'text/plain'
226
241
227 if method == 'download':
242 if method == 'download':
228 response.content_disposition = 'attachment; filename=%s.patch' \
243 response.content_disposition = 'attachment; filename=%s.patch' \
229 % revision
244 % revision
230
245
231 parent = True if len(c.changeset.parents) > 0 else False
246 parent = True if len(c.changeset.parents) > 0 else False
232 c.parent_tmpl = 'Parent %s' \
247 c.parent_tmpl = 'Parent %s' \
233 % c.changeset.parents[0].raw_id if parent else ''
248 % c.changeset.parents[0].raw_id if parent else ''
234
249
235 c.diffs = ''
250 c.diffs = ''
236 for x in c.changes:
251 for x in c.changes:
237 c.diffs += x[2]
252 c.diffs += x[2]
238
253
239 return render('changeset/raw_changeset.html')
254 return render('changeset/raw_changeset.html')
General Comments 0
You need to be logged in to leave comments. Login now