##// END OF EJS Templates
remove branch from git filenode history drop-down
marcink -
r2046:4e1e265a beta
parent child Browse files
Show More
@@ -1,492 +1,493 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.files
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Files controller for RhodeCode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import traceback
29 29
30 30 from pylons import request, response, tmpl_context as c, url
31 31 from pylons.i18n.translation import _
32 32 from pylons.controllers.util import redirect
33 33 from pylons.decorators import jsonify
34 34
35 35 from rhodecode.lib.vcs.conf import settings
36 36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
37 37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError, \
38 38 NodeAlreadyExistsError
39 39 from rhodecode.lib.vcs.nodes import FileNode
40 40
41 41 from rhodecode.lib.compat import OrderedDict
42 42 from rhodecode.lib import convert_line_endings, detect_mode, safe_str
43 43 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
44 44 from rhodecode.lib.base import BaseRepoController, render
45 45 from rhodecode.lib.utils import EmptyChangeset
46 46 from rhodecode.lib import diffs
47 47 import rhodecode.lib.helpers as h
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
50 50 _context_url, get_line_ctx, get_ignore_ws
51 51 from rhodecode.lib.diffs import wrapped_diff
52 52 from rhodecode.model.scm import ScmModel
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56
57 57 class FilesController(BaseRepoController):
58 58
59 59 @LoginRequired()
60 60 def __before__(self):
61 61 super(FilesController, self).__before__()
62 62 c.cut_off_limit = self.cut_off_limit
63 63
64 64 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
65 65 """
66 66 Safe way to get changeset if error occur it redirects to tip with
67 67 proper message
68 68
69 69 :param rev: revision to fetch
70 70 :param repo_name: repo name to redirect after
71 71 """
72 72
73 73 try:
74 74 return c.rhodecode_repo.get_changeset(rev)
75 75 except EmptyRepositoryError, e:
76 76 if not redirect_after:
77 77 return None
78 78 url_ = url('files_add_home',
79 79 repo_name=c.repo_name,
80 80 revision=0, f_path='')
81 81 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
82 82 h.flash(h.literal(_('There are no files yet %s' % add_new)),
83 83 category='warning')
84 84 redirect(h.url('summary_home', repo_name=repo_name))
85 85
86 86 except RepositoryError, e:
87 87 h.flash(str(e), category='warning')
88 88 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
89 89
90 90 def __get_filenode_or_redirect(self, repo_name, cs, path):
91 91 """
92 92 Returns file_node, if error occurs or given path is directory,
93 93 it'll redirect to top level path
94 94
95 95 :param repo_name: repo_name
96 96 :param cs: given changeset
97 97 :param path: path to lookup
98 98 """
99 99
100 100 try:
101 101 file_node = cs.get_node(path)
102 102 if file_node.is_dir():
103 103 raise RepositoryError('given path is a directory')
104 104 except RepositoryError, e:
105 105 h.flash(str(e), category='warning')
106 106 redirect(h.url('files_home', repo_name=repo_name,
107 107 revision=cs.raw_id))
108 108
109 109 return file_node
110 110
111 111 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
112 112 'repository.admin')
113 113 def index(self, repo_name, revision, f_path):
114 114 # redirect to given revision from form if given
115 115 post_revision = request.POST.get('at_rev', None)
116 116 if post_revision:
117 117 cs = self.__get_cs_or_redirect(post_revision, repo_name)
118 118 redirect(url('files_home', repo_name=c.repo_name,
119 119 revision=cs.raw_id, f_path=f_path))
120 120
121 121 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
122 122 c.branch = request.GET.get('branch', None)
123 123 c.f_path = f_path
124 124
125 125 cur_rev = c.changeset.revision
126 126
127 127 # prev link
128 128 try:
129 129 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
130 130 c.url_prev = url('files_home', repo_name=c.repo_name,
131 131 revision=prev_rev.raw_id, f_path=f_path)
132 132 if c.branch:
133 133 c.url_prev += '?branch=%s' % c.branch
134 134 except (ChangesetDoesNotExistError, VCSError):
135 135 c.url_prev = '#'
136 136
137 137 # next link
138 138 try:
139 139 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
140 140 c.url_next = url('files_home', repo_name=c.repo_name,
141 141 revision=next_rev.raw_id, f_path=f_path)
142 142 if c.branch:
143 143 c.url_next += '?branch=%s' % c.branch
144 144 except (ChangesetDoesNotExistError, VCSError):
145 145 c.url_next = '#'
146 146
147 147 # files or dirs
148 148 try:
149 149 c.file = c.changeset.get_node(f_path)
150 150
151 151 if c.file.is_file():
152 152 c.file_history = self._get_node_history(c.changeset, f_path)
153 153 else:
154 154 c.file_history = []
155 155 except RepositoryError, e:
156 156 h.flash(str(e), category='warning')
157 157 redirect(h.url('files_home', repo_name=repo_name,
158 158 revision=revision))
159 159
160 160 return render('files/files.html')
161 161
162 162 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
163 163 'repository.admin')
164 164 def rawfile(self, repo_name, revision, f_path):
165 165 cs = self.__get_cs_or_redirect(revision, repo_name)
166 166 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
167 167
168 168 response.content_disposition = 'attachment; filename=%s' % \
169 169 safe_str(f_path.split(os.sep)[-1])
170 170
171 171 response.content_type = file_node.mimetype
172 172 return file_node.content
173 173
174 174 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
175 175 'repository.admin')
176 176 def raw(self, repo_name, revision, f_path):
177 177 cs = self.__get_cs_or_redirect(revision, repo_name)
178 178 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
179 179
180 180 raw_mimetype_mapping = {
181 181 # map original mimetype to a mimetype used for "show as raw"
182 182 # you can also provide a content-disposition to override the
183 183 # default "attachment" disposition.
184 184 # orig_type: (new_type, new_dispo)
185 185
186 186 # show images inline:
187 187 'image/x-icon': ('image/x-icon', 'inline'),
188 188 'image/png': ('image/png', 'inline'),
189 189 'image/gif': ('image/gif', 'inline'),
190 190 'image/jpeg': ('image/jpeg', 'inline'),
191 191 'image/svg+xml': ('image/svg+xml', 'inline'),
192 192 }
193 193
194 194 mimetype = file_node.mimetype
195 195 try:
196 196 mimetype, dispo = raw_mimetype_mapping[mimetype]
197 197 except KeyError:
198 198 # we don't know anything special about this, handle it safely
199 199 if file_node.is_binary:
200 200 # do same as download raw for binary files
201 201 mimetype, dispo = 'application/octet-stream', 'attachment'
202 202 else:
203 203 # do not just use the original mimetype, but force text/plain,
204 204 # otherwise it would serve text/html and that might be unsafe.
205 205 # Note: underlying vcs library fakes text/plain mimetype if the
206 206 # mimetype can not be determined and it thinks it is not
207 207 # binary.This might lead to erroneous text display in some
208 208 # cases, but helps in other cases, like with text files
209 209 # without extension.
210 210 mimetype, dispo = 'text/plain', 'inline'
211 211
212 212 if dispo == 'attachment':
213 213 dispo = 'attachment; filename=%s' % \
214 214 safe_str(f_path.split(os.sep)[-1])
215 215
216 216 response.content_disposition = dispo
217 217 response.content_type = mimetype
218 218 return file_node.content
219 219
220 220 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
221 221 'repository.admin')
222 222 def annotate(self, repo_name, revision, f_path):
223 223 c.cs = self.__get_cs_or_redirect(revision, repo_name)
224 224 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
225 225
226 226 c.file_history = self._get_node_history(c.cs, f_path)
227 227 c.f_path = f_path
228 228 return render('files/files_annotate.html')
229 229
230 230 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
231 231 def edit(self, repo_name, revision, f_path):
232 232 r_post = request.POST
233 233
234 234 c.cs = self.__get_cs_or_redirect(revision, repo_name)
235 235 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
236 236
237 237 if c.file.is_binary:
238 238 return redirect(url('files_home', repo_name=c.repo_name,
239 239 revision=c.cs.raw_id, f_path=f_path))
240 240
241 241 c.f_path = f_path
242 242
243 243 if r_post:
244 244
245 245 old_content = c.file.content
246 246 sl = old_content.splitlines(1)
247 247 first_line = sl[0] if sl else ''
248 248 # modes: 0 - Unix, 1 - Mac, 2 - DOS
249 249 mode = detect_mode(first_line, 0)
250 250 content = convert_line_endings(r_post.get('content'), mode)
251 251
252 252 message = r_post.get('message') or (_('Edited %s via RhodeCode')
253 253 % (f_path))
254 254 author = self.rhodecode_user.full_contact
255 255
256 256 if content == old_content:
257 257 h.flash(_('No changes'),
258 258 category='warning')
259 259 return redirect(url('changeset_home', repo_name=c.repo_name,
260 260 revision='tip'))
261 261
262 262 try:
263 263 self.scm_model.commit_change(repo=c.rhodecode_repo,
264 264 repo_name=repo_name, cs=c.cs,
265 265 user=self.rhodecode_user,
266 266 author=author, message=message,
267 267 content=content, f_path=f_path)
268 268 h.flash(_('Successfully committed to %s' % f_path),
269 269 category='success')
270 270
271 271 except Exception:
272 272 log.error(traceback.format_exc())
273 273 h.flash(_('Error occurred during commit'), category='error')
274 274 return redirect(url('changeset_home',
275 275 repo_name=c.repo_name, revision='tip'))
276 276
277 277 return render('files/files_edit.html')
278 278
279 279 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
280 280 def add(self, repo_name, revision, f_path):
281 281 r_post = request.POST
282 282 c.cs = self.__get_cs_or_redirect(revision, repo_name,
283 283 redirect_after=False)
284 284 if c.cs is None:
285 285 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
286 286
287 287 c.f_path = f_path
288 288
289 289 if r_post:
290 290 unix_mode = 0
291 291 content = convert_line_endings(r_post.get('content'), unix_mode)
292 292
293 293 message = r_post.get('message') or (_('Added %s via RhodeCode')
294 294 % (f_path))
295 295 location = r_post.get('location')
296 296 filename = r_post.get('filename')
297 297 file_obj = r_post.get('upload_file', None)
298 298
299 299 if file_obj is not None and hasattr(file_obj, 'filename'):
300 300 filename = file_obj.filename
301 301 content = file_obj.file
302 302
303 303 node_path = os.path.join(location, filename)
304 304 author = self.rhodecode_user.full_contact
305 305
306 306 if not content:
307 307 h.flash(_('No content'), category='warning')
308 308 return redirect(url('changeset_home', repo_name=c.repo_name,
309 309 revision='tip'))
310 310 if not filename:
311 311 h.flash(_('No filename'), category='warning')
312 312 return redirect(url('changeset_home', repo_name=c.repo_name,
313 313 revision='tip'))
314 314
315 315 try:
316 316 self.scm_model.create_node(repo=c.rhodecode_repo,
317 317 repo_name=repo_name, cs=c.cs,
318 318 user=self.rhodecode_user,
319 319 author=author, message=message,
320 320 content=content, f_path=node_path)
321 321 h.flash(_('Successfully committed to %s' % node_path),
322 322 category='success')
323 323 except NodeAlreadyExistsError, e:
324 324 h.flash(_(e), category='error')
325 325 except Exception:
326 326 log.error(traceback.format_exc())
327 327 h.flash(_('Error occurred during commit'), category='error')
328 328 return redirect(url('changeset_home',
329 329 repo_name=c.repo_name, revision='tip'))
330 330
331 331 return render('files/files_add.html')
332 332
333 333 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
334 334 'repository.admin')
335 335 def archivefile(self, repo_name, fname):
336 336
337 337 fileformat = None
338 338 revision = None
339 339 ext = None
340 340 subrepos = request.GET.get('subrepos') == 'true'
341 341
342 342 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
343 343 archive_spec = fname.split(ext_data[1])
344 344 if len(archive_spec) == 2 and archive_spec[1] == '':
345 345 fileformat = a_type or ext_data[1]
346 346 revision = archive_spec[0]
347 347 ext = ext_data[1]
348 348
349 349 try:
350 350 dbrepo = RepoModel().get_by_repo_name(repo_name)
351 351 if dbrepo.enable_downloads is False:
352 352 return _('downloads disabled')
353 353
354 354 if c.rhodecode_repo.alias == 'hg':
355 355 # patch and reset hooks section of UI config to not run any
356 356 # hooks on fetching archives with subrepos
357 357 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
358 358 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
359 359
360 360 cs = c.rhodecode_repo.get_changeset(revision)
361 361 content_type = settings.ARCHIVE_SPECS[fileformat][0]
362 362 except ChangesetDoesNotExistError:
363 363 return _('Unknown revision %s') % revision
364 364 except EmptyRepositoryError:
365 365 return _('Empty repository')
366 366 except (ImproperArchiveTypeError, KeyError):
367 367 return _('Unknown archive type')
368 368
369 369 response.content_type = content_type
370 370 response.content_disposition = 'attachment; filename=%s-%s%s' \
371 371 % (repo_name, revision, ext)
372 372
373 373 import tempfile
374 374 archive = tempfile.mkstemp()[1]
375 375 t = open(archive, 'wb')
376 376 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
377 377
378 378 def get_chunked_archive(archive):
379 379 stream = open(archive, 'rb')
380 380 while True:
381 381 data = stream.read(4096)
382 382 if not data:
383 383 os.remove(archive)
384 384 break
385 385 yield data
386 386
387 387 return get_chunked_archive(archive)
388 388
389 389 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
390 390 'repository.admin')
391 391 def diff(self, repo_name, f_path):
392 392 ignore_whitespace = request.GET.get('ignorews') == '1'
393 393 line_context = request.GET.get('context', 3)
394 394 diff1 = request.GET.get('diff1', '')
395 395 diff2 = request.GET.get('diff2', '')
396 396 c.action = request.GET.get('diff')
397 397 c.no_changes = diff1 == diff2
398 398 c.f_path = f_path
399 399 c.big_diff = False
400 400 c.anchor_url = anchor_url
401 401 c.ignorews_url = _ignorews_url
402 402 c.context_url = _context_url
403 403 c.changes = OrderedDict()
404 404 c.changes[diff2] = []
405 405 try:
406 406 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
407 407 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
408 408 node1 = c.changeset_1.get_node(f_path)
409 409 else:
410 410 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
411 411 node1 = FileNode('.', '', changeset=c.changeset_1)
412 412
413 413 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
414 414 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
415 415 node2 = c.changeset_2.get_node(f_path)
416 416 else:
417 417 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
418 418 node2 = FileNode('.', '', changeset=c.changeset_2)
419 419 except RepositoryError:
420 420 return redirect(url('files_home', repo_name=c.repo_name,
421 421 f_path=f_path))
422 422
423 423 if c.action == 'download':
424 424 _diff = diffs.get_gitdiff(node1, node2,
425 425 ignore_whitespace=ignore_whitespace,
426 426 context=line_context)
427 427 diff = diffs.DiffProcessor(_diff, format='gitdiff')
428 428
429 429 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
430 430 response.content_type = 'text/plain'
431 431 response.content_disposition = 'attachment; filename=%s' \
432 432 % diff_name
433 433 return diff.raw_diff()
434 434
435 435 elif c.action == 'raw':
436 436 _diff = diffs.get_gitdiff(node1, node2,
437 437 ignore_whitespace=ignore_whitespace,
438 438 context=line_context)
439 439 diff = diffs.DiffProcessor(_diff, format='gitdiff')
440 440 response.content_type = 'text/plain'
441 441 return diff.raw_diff()
442 442
443 443 else:
444 444 fid = h.FID(diff2, node2.path)
445 445 line_context_lcl = get_line_ctx(fid, request.GET)
446 446 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
447 447
448 448 lim = request.GET.get('fulldiff') or self.cut_off_limit
449 449 _, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
450 450 filenode_new=node2,
451 451 cut_off_limit=lim,
452 452 ignore_whitespace=ign_whitespace_lcl,
453 453 line_context=line_context_lcl,
454 454 enable_comments=False)
455 455
456 456 c.changes = [('', node2, diff, cs1, cs2, st,)]
457 457
458 458 return render('files/file_diff.html')
459 459
460 460 def _get_node_history(self, cs, f_path):
461 461 changesets = cs.get_file_history(f_path)
462 462 hist_l = []
463 463
464 464 changesets_group = ([], _("Changesets"))
465 465 branches_group = ([], _("Branches"))
466 466 tags_group = ([], _("Tags"))
467
467 _hg = cs.repository.alias == 'hg'
468 468 for chs in changesets:
469 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, chs.branch)
469 _branch = '(%s)' % chs.branch if _hg else ''
470 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
470 471 changesets_group[0].append((chs.raw_id, n_desc,))
471 472
472 473 hist_l.append(changesets_group)
473 474
474 475 for name, chs in c.rhodecode_repo.branches.items():
475 476 branches_group[0].append((chs, name),)
476 477 hist_l.append(branches_group)
477 478
478 479 for name, chs in c.rhodecode_repo.tags.items():
479 480 tags_group[0].append((chs, name),)
480 481 hist_l.append(tags_group)
481 482
482 483 return hist_l
483 484
484 485 @jsonify
485 486 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
486 487 'repository.admin')
487 488 def nodelist(self, repo_name, revision, f_path):
488 489 if request.environ.get('HTTP_X_PARTIAL_XHR'):
489 490 cs = self.__get_cs_or_redirect(revision, repo_name)
490 491 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
491 492 flat=False)
492 493 return _d + _f
General Comments 0
You need to be logged in to leave comments. Login now