##// END OF EJS Templates
fix error when diff path is a directory, edge case generated by google bots
marcink -
r3037:789f2068 beta
parent child Browse files
Show More
@@ -1,594 +1,594 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 from __future__ import with_statement
26 26 import os
27 27 import logging
28 28 import traceback
29 29 import tempfile
30 30
31 31 from pylons import request, response, tmpl_context as c, url
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34 from pylons.decorators import jsonify
35 35
36 36 from rhodecode.lib import diffs
37 37 from rhodecode.lib import helpers as h
38 38
39 39 from rhodecode.lib.compat import OrderedDict
40 40 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str,\
41 41 str2bool
42 42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 43 from rhodecode.lib.base import BaseRepoController, render
44 44 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 45 from rhodecode.lib.vcs.conf import settings
46 46 from rhodecode.lib.vcs.exceptions import RepositoryError, \
47 47 ChangesetDoesNotExistError, EmptyRepositoryError, \
48 48 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,\
49 NodeDoesNotExistError, ChangesetError
49 NodeDoesNotExistError, ChangesetError, NodeError
50 50 from rhodecode.lib.vcs.nodes import FileNode
51 51
52 52 from rhodecode.model.repo import RepoModel
53 53 from rhodecode.model.scm import ScmModel
54 54 from rhodecode.model.db import Repository
55 55
56 56 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
57 57 _context_url, get_line_ctx, get_ignore_ws
58 58
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class FilesController(BaseRepoController):
64 64
65 65 def __before__(self):
66 66 super(FilesController, self).__before__()
67 67 c.cut_off_limit = self.cut_off_limit
68 68
69 69 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
70 70 """
71 71 Safe way to get changeset if error occur it redirects to tip with
72 72 proper message
73 73
74 74 :param rev: revision to fetch
75 75 :param repo_name: repo name to redirect after
76 76 """
77 77
78 78 try:
79 79 return c.rhodecode_repo.get_changeset(rev)
80 80 except EmptyRepositoryError, e:
81 81 if not redirect_after:
82 82 return None
83 83 url_ = url('files_add_home',
84 84 repo_name=c.repo_name,
85 85 revision=0, f_path='')
86 86 add_new = '<a href="%s">[%s]</a>' % (url_, _('click here to add new file'))
87 87 h.flash(h.literal(_('There are no files yet %s') % add_new),
88 88 category='warning')
89 89 redirect(h.url('summary_home', repo_name=repo_name))
90 90
91 91 except RepositoryError, e:
92 92 h.flash(str(e), category='warning')
93 93 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
94 94
95 95 def __get_filenode_or_redirect(self, repo_name, cs, path):
96 96 """
97 97 Returns file_node, if error occurs or given path is directory,
98 98 it'll redirect to top level path
99 99
100 100 :param repo_name: repo_name
101 101 :param cs: given changeset
102 102 :param path: path to lookup
103 103 """
104 104
105 105 try:
106 106 file_node = cs.get_node(path)
107 107 if file_node.is_dir():
108 108 raise RepositoryError('given path is a directory')
109 109 except RepositoryError, e:
110 110 h.flash(str(e), category='warning')
111 111 redirect(h.url('files_home', repo_name=repo_name,
112 112 revision=cs.raw_id))
113 113
114 114 return file_node
115 115
116 116 @LoginRequired()
117 117 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
118 118 'repository.admin')
119 119 def index(self, repo_name, revision, f_path, annotate=False):
120 120 # redirect to given revision from form if given
121 121 post_revision = request.POST.get('at_rev', None)
122 122 if post_revision:
123 123 cs = self.__get_cs_or_redirect(post_revision, repo_name)
124 124 redirect(url('files_home', repo_name=c.repo_name,
125 125 revision=cs.raw_id, f_path=f_path))
126 126
127 127 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
128 128 c.branch = request.GET.get('branch', None)
129 129 c.f_path = f_path
130 130 c.annotate = annotate
131 131 cur_rev = c.changeset.revision
132 132
133 133 # prev link
134 134 try:
135 135 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
136 136 c.url_prev = url('files_home', repo_name=c.repo_name,
137 137 revision=prev_rev.raw_id, f_path=f_path)
138 138 if c.branch:
139 139 c.url_prev += '?branch=%s' % c.branch
140 140 except (ChangesetDoesNotExistError, VCSError):
141 141 c.url_prev = '#'
142 142
143 143 # next link
144 144 try:
145 145 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
146 146 c.url_next = url('files_home', repo_name=c.repo_name,
147 147 revision=next_rev.raw_id, f_path=f_path)
148 148 if c.branch:
149 149 c.url_next += '?branch=%s' % c.branch
150 150 except (ChangesetDoesNotExistError, VCSError):
151 151 c.url_next = '#'
152 152
153 153 # files or dirs
154 154 try:
155 155 c.file = c.changeset.get_node(f_path)
156 156
157 157 if c.file.is_file():
158 158 c.load_full_history = False
159 159 file_last_cs = c.file.last_changeset
160 160 c.file_changeset = (c.changeset
161 161 if c.changeset.revision < file_last_cs.revision
162 162 else file_last_cs)
163 163 _hist = []
164 164 c.file_history = []
165 165 if c.load_full_history:
166 166 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
167 167
168 168 c.authors = []
169 169 for a in set([x.author for x in _hist]):
170 170 c.authors.append((h.email(a), h.person(a)))
171 171 else:
172 172 c.authors = c.file_history = []
173 173 except RepositoryError, e:
174 174 h.flash(str(e), category='warning')
175 175 redirect(h.url('files_home', repo_name=repo_name,
176 176 revision='tip'))
177 177
178 178 if request.environ.get('HTTP_X_PARTIAL_XHR'):
179 179 return render('files/files_ypjax.html')
180 180
181 181 return render('files/files.html')
182 182
183 183 def history(self, repo_name, revision, f_path, annotate=False):
184 184 if request.environ.get('HTTP_X_PARTIAL_XHR'):
185 185 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
186 186 c.f_path = f_path
187 187 c.annotate = annotate
188 188 c.file = c.changeset.get_node(f_path)
189 189 if c.file.is_file():
190 190 file_last_cs = c.file.last_changeset
191 191 c.file_changeset = (c.changeset
192 192 if c.changeset.revision < file_last_cs.revision
193 193 else file_last_cs)
194 194 c.file_history, _hist = self._get_node_history(c.changeset, f_path)
195 195 c.authors = []
196 196 for a in set([x.author for x in _hist]):
197 197 c.authors.append((h.email(a), h.person(a)))
198 198 return render('files/files_history_box.html')
199 199
200 200 @LoginRequired()
201 201 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
202 202 'repository.admin')
203 203 def rawfile(self, repo_name, revision, f_path):
204 204 cs = self.__get_cs_or_redirect(revision, repo_name)
205 205 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
206 206
207 207 response.content_disposition = 'attachment; filename=%s' % \
208 208 safe_str(f_path.split(Repository.url_sep())[-1])
209 209
210 210 response.content_type = file_node.mimetype
211 211 return file_node.content
212 212
213 213 @LoginRequired()
214 214 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
215 215 'repository.admin')
216 216 def raw(self, repo_name, revision, f_path):
217 217 cs = self.__get_cs_or_redirect(revision, repo_name)
218 218 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
219 219
220 220 raw_mimetype_mapping = {
221 221 # map original mimetype to a mimetype used for "show as raw"
222 222 # you can also provide a content-disposition to override the
223 223 # default "attachment" disposition.
224 224 # orig_type: (new_type, new_dispo)
225 225
226 226 # show images inline:
227 227 'image/x-icon': ('image/x-icon', 'inline'),
228 228 'image/png': ('image/png', 'inline'),
229 229 'image/gif': ('image/gif', 'inline'),
230 230 'image/jpeg': ('image/jpeg', 'inline'),
231 231 'image/svg+xml': ('image/svg+xml', 'inline'),
232 232 }
233 233
234 234 mimetype = file_node.mimetype
235 235 try:
236 236 mimetype, dispo = raw_mimetype_mapping[mimetype]
237 237 except KeyError:
238 238 # we don't know anything special about this, handle it safely
239 239 if file_node.is_binary:
240 240 # do same as download raw for binary files
241 241 mimetype, dispo = 'application/octet-stream', 'attachment'
242 242 else:
243 243 # do not just use the original mimetype, but force text/plain,
244 244 # otherwise it would serve text/html and that might be unsafe.
245 245 # Note: underlying vcs library fakes text/plain mimetype if the
246 246 # mimetype can not be determined and it thinks it is not
247 247 # binary.This might lead to erroneous text display in some
248 248 # cases, but helps in other cases, like with text files
249 249 # without extension.
250 250 mimetype, dispo = 'text/plain', 'inline'
251 251
252 252 if dispo == 'attachment':
253 253 dispo = 'attachment; filename=%s' % \
254 254 safe_str(f_path.split(os.sep)[-1])
255 255
256 256 response.content_disposition = dispo
257 257 response.content_type = mimetype
258 258 return file_node.content
259 259
260 260 @LoginRequired()
261 261 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
262 262 def edit(self, repo_name, revision, f_path):
263 263 repo = Repository.get_by_repo_name(repo_name)
264 264 if repo.enable_locking and repo.locked[0]:
265 265 h.flash(_('This repository is has been locked by %s on %s')
266 266 % (h.person_by_id(repo.locked[0]),
267 267 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
268 268 'warning')
269 269 return redirect(h.url('files_home',
270 270 repo_name=repo_name, revision='tip'))
271 271
272 272 r_post = request.POST
273 273
274 274 c.cs = self.__get_cs_or_redirect(revision, repo_name)
275 275 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
276 276
277 277 if c.file.is_binary:
278 278 return redirect(url('files_home', repo_name=c.repo_name,
279 279 revision=c.cs.raw_id, f_path=f_path))
280 280
281 281 c.f_path = f_path
282 282
283 283 if r_post:
284 284
285 285 old_content = c.file.content
286 286 sl = old_content.splitlines(1)
287 287 first_line = sl[0] if sl else ''
288 288 # modes: 0 - Unix, 1 - Mac, 2 - DOS
289 289 mode = detect_mode(first_line, 0)
290 290 content = convert_line_endings(r_post.get('content'), mode)
291 291
292 292 message = r_post.get('message') or (_('Edited %s via RhodeCode')
293 293 % (f_path))
294 294 author = self.rhodecode_user.full_contact
295 295
296 296 if content == old_content:
297 297 h.flash(_('No changes'),
298 298 category='warning')
299 299 return redirect(url('changeset_home', repo_name=c.repo_name,
300 300 revision='tip'))
301 301
302 302 try:
303 303 self.scm_model.commit_change(repo=c.rhodecode_repo,
304 304 repo_name=repo_name, cs=c.cs,
305 305 user=self.rhodecode_user,
306 306 author=author, message=message,
307 307 content=content, f_path=f_path)
308 308 h.flash(_('Successfully committed to %s') % f_path,
309 309 category='success')
310 310
311 311 except Exception:
312 312 log.error(traceback.format_exc())
313 313 h.flash(_('Error occurred during commit'), category='error')
314 314 return redirect(url('changeset_home',
315 315 repo_name=c.repo_name, revision='tip'))
316 316
317 317 return render('files/files_edit.html')
318 318
319 319 @LoginRequired()
320 320 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
321 321 def add(self, repo_name, revision, f_path):
322 322
323 323 repo = Repository.get_by_repo_name(repo_name)
324 324 if repo.enable_locking and repo.locked[0]:
325 325 h.flash(_('This repository is has been locked by %s on %s')
326 326 % (h.person_by_id(repo.locked[0]),
327 327 h.fmt_date(h.time_to_datetime(repo.locked[1]))),
328 328 'warning')
329 329 return redirect(h.url('files_home',
330 330 repo_name=repo_name, revision='tip'))
331 331
332 332 r_post = request.POST
333 333 c.cs = self.__get_cs_or_redirect(revision, repo_name,
334 334 redirect_after=False)
335 335 if c.cs is None:
336 336 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
337 337
338 338 c.f_path = f_path
339 339
340 340 if r_post:
341 341 unix_mode = 0
342 342 content = convert_line_endings(r_post.get('content'), unix_mode)
343 343
344 344 message = r_post.get('message') or (_('Added %s via RhodeCode')
345 345 % (f_path))
346 346 location = r_post.get('location')
347 347 filename = r_post.get('filename')
348 348 file_obj = r_post.get('upload_file', None)
349 349
350 350 if file_obj is not None and hasattr(file_obj, 'filename'):
351 351 filename = file_obj.filename
352 352 content = file_obj.file
353 353
354 354 node_path = os.path.join(location, filename)
355 355 author = self.rhodecode_user.full_contact
356 356
357 357 if not content:
358 358 h.flash(_('No content'), category='warning')
359 359 return redirect(url('changeset_home', repo_name=c.repo_name,
360 360 revision='tip'))
361 361 if not filename:
362 362 h.flash(_('No filename'), category='warning')
363 363 return redirect(url('changeset_home', repo_name=c.repo_name,
364 364 revision='tip'))
365 365
366 366 try:
367 367 self.scm_model.create_node(repo=c.rhodecode_repo,
368 368 repo_name=repo_name, cs=c.cs,
369 369 user=self.rhodecode_user,
370 370 author=author, message=message,
371 371 content=content, f_path=node_path)
372 372 h.flash(_('Successfully committed to %s') % node_path,
373 373 category='success')
374 374 except NodeAlreadyExistsError, e:
375 375 h.flash(_(e), category='error')
376 376 except Exception:
377 377 log.error(traceback.format_exc())
378 378 h.flash(_('Error occurred during commit'), category='error')
379 379 return redirect(url('changeset_home',
380 380 repo_name=c.repo_name, revision='tip'))
381 381
382 382 return render('files/files_add.html')
383 383
384 384 @LoginRequired()
385 385 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
386 386 'repository.admin')
387 387 def archivefile(self, repo_name, fname):
388 388
389 389 fileformat = None
390 390 revision = None
391 391 ext = None
392 392 subrepos = request.GET.get('subrepos') == 'true'
393 393
394 394 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
395 395 archive_spec = fname.split(ext_data[1])
396 396 if len(archive_spec) == 2 and archive_spec[1] == '':
397 397 fileformat = a_type or ext_data[1]
398 398 revision = archive_spec[0]
399 399 ext = ext_data[1]
400 400
401 401 try:
402 402 dbrepo = RepoModel().get_by_repo_name(repo_name)
403 403 if dbrepo.enable_downloads is False:
404 404 return _('downloads disabled')
405 405
406 406 if c.rhodecode_repo.alias == 'hg':
407 407 # patch and reset hooks section of UI config to not run any
408 408 # hooks on fetching archives with subrepos
409 409 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
410 410 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
411 411
412 412 cs = c.rhodecode_repo.get_changeset(revision)
413 413 content_type = settings.ARCHIVE_SPECS[fileformat][0]
414 414 except ChangesetDoesNotExistError:
415 415 return _('Unknown revision %s') % revision
416 416 except EmptyRepositoryError:
417 417 return _('Empty repository')
418 418 except (ImproperArchiveTypeError, KeyError):
419 419 return _('Unknown archive type')
420 420
421 421 fd, archive = tempfile.mkstemp()
422 422 t = open(archive, 'wb')
423 423 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
424 424 t.close()
425 425
426 426 def get_chunked_archive(archive):
427 427 stream = open(archive, 'rb')
428 428 while True:
429 429 data = stream.read(16 * 1024)
430 430 if not data:
431 431 stream.close()
432 432 os.close(fd)
433 433 os.remove(archive)
434 434 break
435 435 yield data
436 436
437 437 response.content_disposition = str('attachment; filename=%s-%s%s' \
438 438 % (repo_name, revision[:12], ext))
439 439 response.content_type = str(content_type)
440 440 return get_chunked_archive(archive)
441 441
442 442 @LoginRequired()
443 443 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
444 444 'repository.admin')
445 445 def diff(self, repo_name, f_path):
446 446 ignore_whitespace = request.GET.get('ignorews') == '1'
447 447 line_context = request.GET.get('context', 3)
448 448 diff1 = request.GET.get('diff1', '')
449 449 diff2 = request.GET.get('diff2', '')
450 450 c.action = request.GET.get('diff')
451 451 c.no_changes = diff1 == diff2
452 452 c.f_path = f_path
453 453 c.big_diff = False
454 454 c.anchor_url = anchor_url
455 455 c.ignorews_url = _ignorews_url
456 456 c.context_url = _context_url
457 457 c.changes = OrderedDict()
458 458 c.changes[diff2] = []
459 459
460 460 #special case if we want a show rev only, it's impl here
461 461 #to reduce JS and callbacks
462 462
463 463 if request.GET.get('show_rev'):
464 464 if str2bool(request.GET.get('annotate', 'False')):
465 465 _url = url('files_annotate_home', repo_name=c.repo_name,
466 466 revision=diff1, f_path=c.f_path)
467 467 else:
468 468 _url = url('files_home', repo_name=c.repo_name,
469 469 revision=diff1, f_path=c.f_path)
470 470
471 471 return redirect(_url)
472 472 try:
473 473 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
474 474 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
475 475 try:
476 476 node1 = c.changeset_1.get_node(f_path)
477 477 except NodeDoesNotExistError:
478 478 c.changeset_1 = EmptyChangeset(cs=diff1,
479 479 revision=c.changeset_1.revision,
480 480 repo=c.rhodecode_repo)
481 481 node1 = FileNode(f_path, '', changeset=c.changeset_1)
482 482 else:
483 483 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
484 484 node1 = FileNode(f_path, '', changeset=c.changeset_1)
485 485
486 486 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
487 487 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
488 488 try:
489 489 node2 = c.changeset_2.get_node(f_path)
490 490 except NodeDoesNotExistError:
491 491 c.changeset_2 = EmptyChangeset(cs=diff2,
492 492 revision=c.changeset_2.revision,
493 493 repo=c.rhodecode_repo)
494 494 node2 = FileNode(f_path, '', changeset=c.changeset_2)
495 495 else:
496 496 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
497 497 node2 = FileNode(f_path, '', changeset=c.changeset_2)
498 except RepositoryError:
498 except (RepositoryError, NodeError):
499 499 log.error(traceback.format_exc())
500 500 return redirect(url('files_home', repo_name=c.repo_name,
501 501 f_path=f_path))
502 502
503 503 if c.action == 'download':
504 504 _diff = diffs.get_gitdiff(node1, node2,
505 505 ignore_whitespace=ignore_whitespace,
506 506 context=line_context)
507 507 diff = diffs.DiffProcessor(_diff, format='gitdiff')
508 508
509 509 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
510 510 response.content_type = 'text/plain'
511 511 response.content_disposition = (
512 512 'attachment; filename=%s' % diff_name
513 513 )
514 514 return diff.as_raw()
515 515
516 516 elif c.action == 'raw':
517 517 _diff = diffs.get_gitdiff(node1, node2,
518 518 ignore_whitespace=ignore_whitespace,
519 519 context=line_context)
520 520 diff = diffs.DiffProcessor(_diff, format='gitdiff')
521 521 response.content_type = 'text/plain'
522 522 return diff.as_raw()
523 523
524 524 else:
525 525 fid = h.FID(diff2, node2.path)
526 526 line_context_lcl = get_line_ctx(fid, request.GET)
527 527 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
528 528
529 529 lim = request.GET.get('fulldiff') or self.cut_off_limit
530 530 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
531 531 filenode_new=node2,
532 532 cut_off_limit=lim,
533 533 ignore_whitespace=ign_whitespace_lcl,
534 534 line_context=line_context_lcl,
535 535 enable_comments=False)
536 536 op = ''
537 537 filename = node1.path
538 538 cs_changes = {
539 539 'fid': [cs1, cs2, op, filename, diff, st]
540 540 }
541 541 c.changes = cs_changes
542 542
543 543 return render('files/file_diff.html')
544 544
545 545 def _get_node_history(self, cs, f_path, changesets=None):
546 546 """
547 547 get changesets history for given node
548 548
549 549 :param cs: changeset to calculate history
550 550 :param f_path: path for node to calculate history for
551 551 :param changesets: if passed don't calculate history and take
552 552 changesets defined in this list
553 553 """
554 554 # calculate history based on tip
555 555 tip_cs = c.rhodecode_repo.get_changeset()
556 556 if changesets is None:
557 557 try:
558 558 changesets = tip_cs.get_file_history(f_path)
559 559 except (NodeDoesNotExistError, ChangesetError):
560 560 #this node is not present at tip !
561 561 changesets = cs.get_file_history(f_path)
562 562 hist_l = []
563 563
564 564 changesets_group = ([], _("Changesets"))
565 565 branches_group = ([], _("Branches"))
566 566 tags_group = ([], _("Tags"))
567 567 _hg = cs.repository.alias == 'hg'
568 568 for chs in changesets:
569 569 #_branch = '(%s)' % chs.branch if _hg else ''
570 570 _branch = chs.branch
571 571 n_desc = 'r%s:%s (%s)' % (chs.revision, chs.short_id, _branch)
572 572 changesets_group[0].append((chs.raw_id, n_desc,))
573 573 hist_l.append(changesets_group)
574 574
575 575 for name, chs in c.rhodecode_repo.branches.items():
576 576 branches_group[0].append((chs, name),)
577 577 hist_l.append(branches_group)
578 578
579 579 for name, chs in c.rhodecode_repo.tags.items():
580 580 tags_group[0].append((chs, name),)
581 581 hist_l.append(tags_group)
582 582
583 583 return hist_l, changesets
584 584
585 585 @LoginRequired()
586 586 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
587 587 'repository.admin')
588 588 @jsonify
589 589 def nodelist(self, repo_name, revision, f_path):
590 590 if request.environ.get('HTTP_X_PARTIAL_XHR'):
591 591 cs = self.__get_cs_or_redirect(revision, repo_name)
592 592 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
593 593 flat=False)
594 594 return {'nodes': _d + _f}
General Comments 0
You need to be logged in to leave comments. Login now