##// END OF EJS Templates
files: fixed SVN refs switcher that used old format of diff between files....
dan -
r4293:78ed0439 default
parent child Browse files
Show More
@@ -1,1552 +1,1555 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2019 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 import itertools
22 22 import logging
23 23 import os
24 24 import shutil
25 25 import tempfile
26 26 import collections
27 27 import urllib
28 28 import pathlib2
29 29
30 30 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
31 31 from pyramid.view import view_config
32 32 from pyramid.renderers import render
33 33 from pyramid.response import Response
34 34
35 35 import rhodecode
36 36 from rhodecode.apps._base import RepoAppView
37 37
38 38
39 39 from rhodecode.lib import diffs, helpers as h, rc_cache
40 40 from rhodecode.lib import audit_logger
41 41 from rhodecode.lib.view_utils import parse_path_ref
42 42 from rhodecode.lib.exceptions import NonRelativePathError
43 43 from rhodecode.lib.codeblocks import (
44 44 filenode_as_lines_tokens, filenode_as_annotated_lines_tokens)
45 45 from rhodecode.lib.utils2 import (
46 46 convert_line_endings, detect_mode, safe_str, str2bool, safe_int, sha1, safe_unicode)
47 47 from rhodecode.lib.auth import (
48 48 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
49 49 from rhodecode.lib.vcs import path as vcspath
50 50 from rhodecode.lib.vcs.backends.base import EmptyCommit
51 51 from rhodecode.lib.vcs.conf import settings
52 52 from rhodecode.lib.vcs.nodes import FileNode
53 53 from rhodecode.lib.vcs.exceptions import (
54 54 RepositoryError, CommitDoesNotExistError, EmptyRepositoryError,
55 55 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError,
56 56 NodeDoesNotExistError, CommitError, NodeError)
57 57
58 58 from rhodecode.model.scm import ScmModel
59 59 from rhodecode.model.db import Repository
60 60
61 61 log = logging.getLogger(__name__)
62 62
63 63
64 64 class RepoFilesView(RepoAppView):
65 65
66 66 @staticmethod
67 67 def adjust_file_path_for_svn(f_path, repo):
68 68 """
69 69 Computes the relative path of `f_path`.
70 70
71 71 This is mainly based on prefix matching of the recognized tags and
72 72 branches in the underlying repository.
73 73 """
74 74 tags_and_branches = itertools.chain(
75 75 repo.branches.iterkeys(),
76 76 repo.tags.iterkeys())
77 77 tags_and_branches = sorted(tags_and_branches, key=len, reverse=True)
78 78
79 79 for name in tags_and_branches:
80 80 if f_path.startswith('{}/'.format(name)):
81 81 f_path = vcspath.relpath(f_path, name)
82 82 break
83 83 return f_path
84 84
85 85 def load_default_context(self):
86 86 c = self._get_local_tmpl_context(include_app_defaults=True)
87 87 c.rhodecode_repo = self.rhodecode_vcs_repo
88 88 c.enable_downloads = self.db_repo.enable_downloads
89 89 return c
90 90
91 91 def _ensure_not_locked(self, commit_id='tip'):
92 92 _ = self.request.translate
93 93
94 94 repo = self.db_repo
95 95 if repo.enable_locking and repo.locked[0]:
96 96 h.flash(_('This repository has been locked by %s on %s')
97 97 % (h.person_by_id(repo.locked[0]),
98 98 h.format_date(h.time_to_datetime(repo.locked[1]))),
99 99 'warning')
100 100 files_url = h.route_path(
101 101 'repo_files:default_path',
102 102 repo_name=self.db_repo_name, commit_id=commit_id)
103 103 raise HTTPFound(files_url)
104 104
105 105 def forbid_non_head(self, is_head, f_path, commit_id='tip', json_mode=False):
106 106 _ = self.request.translate
107 107
108 108 if not is_head:
109 109 message = _('Cannot modify file. '
110 110 'Given commit `{}` is not head of a branch.').format(commit_id)
111 111 h.flash(message, category='warning')
112 112
113 113 if json_mode:
114 114 return message
115 115
116 116 files_url = h.route_path(
117 117 'repo_files', repo_name=self.db_repo_name, commit_id=commit_id,
118 118 f_path=f_path)
119 119 raise HTTPFound(files_url)
120 120
121 121 def check_branch_permission(self, branch_name, commit_id='tip', json_mode=False):
122 122 _ = self.request.translate
123 123
124 124 rule, branch_perm = self._rhodecode_user.get_rule_and_branch_permission(
125 125 self.db_repo_name, branch_name)
126 126 if branch_perm and branch_perm not in ['branch.push', 'branch.push_force']:
127 127 message = _('Branch `{}` changes forbidden by rule {}.').format(
128 128 branch_name, rule)
129 129 h.flash(message, 'warning')
130 130
131 131 if json_mode:
132 132 return message
133 133
134 134 files_url = h.route_path(
135 135 'repo_files:default_path', repo_name=self.db_repo_name, commit_id=commit_id)
136 136
137 137 raise HTTPFound(files_url)
138 138
139 139 def _get_commit_and_path(self):
140 140 default_commit_id = self.db_repo.landing_rev[1]
141 141 default_f_path = '/'
142 142
143 143 commit_id = self.request.matchdict.get(
144 144 'commit_id', default_commit_id)
145 145 f_path = self._get_f_path(self.request.matchdict, default_f_path)
146 146 return commit_id, f_path
147 147
148 148 def _get_default_encoding(self, c):
149 149 enc_list = getattr(c, 'default_encodings', [])
150 150 return enc_list[0] if enc_list else 'UTF-8'
151 151
152 152 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
153 153 """
154 154 This is a safe way to get commit. If an error occurs it redirects to
155 155 tip with proper message
156 156
157 157 :param commit_id: id of commit to fetch
158 158 :param redirect_after: toggle redirection
159 159 """
160 160 _ = self.request.translate
161 161
162 162 try:
163 163 return self.rhodecode_vcs_repo.get_commit(commit_id)
164 164 except EmptyRepositoryError:
165 165 if not redirect_after:
166 166 return None
167 167
168 168 _url = h.route_path(
169 169 'repo_files_add_file',
170 170 repo_name=self.db_repo_name, commit_id=0, f_path='')
171 171
172 172 if h.HasRepoPermissionAny(
173 173 'repository.write', 'repository.admin')(self.db_repo_name):
174 174 add_new = h.link_to(
175 175 _('Click here to add a new file.'), _url, class_="alert-link")
176 176 else:
177 177 add_new = ""
178 178
179 179 h.flash(h.literal(
180 180 _('There are no files yet. %s') % add_new), category='warning')
181 181 raise HTTPFound(
182 182 h.route_path('repo_summary', repo_name=self.db_repo_name))
183 183
184 184 except (CommitDoesNotExistError, LookupError):
185 185 msg = _('No such commit exists for this repository')
186 186 h.flash(msg, category='error')
187 187 raise HTTPNotFound()
188 188 except RepositoryError as e:
189 189 h.flash(safe_str(h.escape(e)), category='error')
190 190 raise HTTPNotFound()
191 191
192 192 def _get_filenode_or_redirect(self, commit_obj, path):
193 193 """
194 194 Returns file_node, if error occurs or given path is directory,
195 195 it'll redirect to top level path
196 196 """
197 197 _ = self.request.translate
198 198
199 199 try:
200 200 file_node = commit_obj.get_node(path)
201 201 if file_node.is_dir():
202 202 raise RepositoryError('The given path is a directory')
203 203 except CommitDoesNotExistError:
204 204 log.exception('No such commit exists for this repository')
205 205 h.flash(_('No such commit exists for this repository'), category='error')
206 206 raise HTTPNotFound()
207 207 except RepositoryError as e:
208 208 log.warning('Repository error while fetching filenode `%s`. Err:%s', path, e)
209 209 h.flash(safe_str(h.escape(e)), category='error')
210 210 raise HTTPNotFound()
211 211
212 212 return file_node
213 213
214 214 def _is_valid_head(self, commit_id, repo):
215 215 branch_name = sha_commit_id = ''
216 216 is_head = False
217 217 log.debug('Checking if commit_id `%s` is a head for %s.', commit_id, repo)
218 218
219 219 for _branch_name, branch_commit_id in repo.branches.items():
220 220 # simple case we pass in branch name, it's a HEAD
221 221 if commit_id == _branch_name:
222 222 is_head = True
223 223 branch_name = _branch_name
224 224 sha_commit_id = branch_commit_id
225 225 break
226 226 # case when we pass in full sha commit_id, which is a head
227 227 elif commit_id == branch_commit_id:
228 228 is_head = True
229 229 branch_name = _branch_name
230 230 sha_commit_id = branch_commit_id
231 231 break
232 232
233 233 if h.is_svn(repo) and not repo.is_empty():
234 234 # Note: Subversion only has one head.
235 235 if commit_id == repo.get_commit(commit_idx=-1).raw_id:
236 236 is_head = True
237 237 return branch_name, sha_commit_id, is_head
238 238
239 239 # checked branches, means we only need to try to get the branch/commit_sha
240 240 if not repo.is_empty():
241 241 commit = repo.get_commit(commit_id=commit_id)
242 242 if commit:
243 243 branch_name = commit.branch
244 244 sha_commit_id = commit.raw_id
245 245
246 246 return branch_name, sha_commit_id, is_head
247 247
248 248 def _get_tree_at_commit(self, c, commit_id, f_path, full_load=False):
249 249
250 250 repo_id = self.db_repo.repo_id
251 251 force_recache = self.get_recache_flag()
252 252
253 253 cache_seconds = safe_int(
254 254 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
255 255 cache_on = not force_recache and cache_seconds > 0
256 256 log.debug(
257 257 'Computing FILE TREE for repo_id %s commit_id `%s` and path `%s`'
258 258 'with caching: %s[TTL: %ss]' % (
259 259 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
260 260
261 261 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
262 262 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
263 263
264 264 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
265 265 condition=cache_on)
266 266 def compute_file_tree(ver, repo_id, commit_id, f_path, full_load):
267 267 log.debug('Generating cached file tree at ver:%s for repo_id: %s, %s, %s',
268 268 ver, repo_id, commit_id, f_path)
269 269
270 270 c.full_load = full_load
271 271 return render(
272 272 'rhodecode:templates/files/files_browser_tree.mako',
273 273 self._get_template_context(c), self.request)
274 274
275 275 return compute_file_tree(
276 276 rc_cache.FILE_TREE_CACHE_VER, self.db_repo.repo_id, commit_id, f_path, full_load)
277 277
278 278 def _get_archive_spec(self, fname):
279 279 log.debug('Detecting archive spec for: `%s`', fname)
280 280
281 281 fileformat = None
282 282 ext = None
283 283 content_type = None
284 284 for a_type, content_type, extension in settings.ARCHIVE_SPECS:
285 285
286 286 if fname.endswith(extension):
287 287 fileformat = a_type
288 288 log.debug('archive is of type: %s', fileformat)
289 289 ext = extension
290 290 break
291 291
292 292 if not fileformat:
293 293 raise ValueError()
294 294
295 295 # left over part of whole fname is the commit
296 296 commit_id = fname[:-len(ext)]
297 297
298 298 return commit_id, ext, fileformat, content_type
299 299
300 300 def create_pure_path(self, *parts):
301 301 # Split paths and sanitize them, removing any ../ etc
302 302 sanitized_path = [
303 303 x for x in pathlib2.PurePath(*parts).parts
304 304 if x not in ['.', '..']]
305 305
306 306 pure_path = pathlib2.PurePath(*sanitized_path)
307 307 return pure_path
308 308
309 309 def _is_lf_enabled(self, target_repo):
310 310 lf_enabled = False
311 311
312 312 lf_key_for_vcs_map = {
313 313 'hg': 'extensions_largefiles',
314 314 'git': 'vcs_git_lfs_enabled'
315 315 }
316 316
317 317 lf_key_for_vcs = lf_key_for_vcs_map.get(target_repo.repo_type)
318 318
319 319 if lf_key_for_vcs:
320 320 lf_enabled = self._get_repo_setting(target_repo, lf_key_for_vcs)
321 321
322 322 return lf_enabled
323 323
324 324 @LoginRequired()
325 325 @HasRepoPermissionAnyDecorator(
326 326 'repository.read', 'repository.write', 'repository.admin')
327 327 @view_config(
328 328 route_name='repo_archivefile', request_method='GET',
329 329 renderer=None)
330 330 def repo_archivefile(self):
331 331 # archive cache config
332 332 from rhodecode import CONFIG
333 333 _ = self.request.translate
334 334 self.load_default_context()
335 335 default_at_path = '/'
336 336 fname = self.request.matchdict['fname']
337 337 subrepos = self.request.GET.get('subrepos') == 'true'
338 338 at_path = self.request.GET.get('at_path') or default_at_path
339 339
340 340 if not self.db_repo.enable_downloads:
341 341 return Response(_('Downloads disabled'))
342 342
343 343 try:
344 344 commit_id, ext, fileformat, content_type = \
345 345 self._get_archive_spec(fname)
346 346 except ValueError:
347 347 return Response(_('Unknown archive type for: `{}`').format(
348 348 h.escape(fname)))
349 349
350 350 try:
351 351 commit = self.rhodecode_vcs_repo.get_commit(commit_id)
352 352 except CommitDoesNotExistError:
353 353 return Response(_('Unknown commit_id {}').format(
354 354 h.escape(commit_id)))
355 355 except EmptyRepositoryError:
356 356 return Response(_('Empty repository'))
357 357
358 358 try:
359 359 at_path = commit.get_node(at_path).path or default_at_path
360 360 except Exception:
361 361 return Response(_('No node at path {} for this repository').format(at_path))
362 362
363 363 path_sha = sha1(at_path)[:8]
364 364
365 365 # original backward compat name of archive
366 366 clean_name = safe_str(self.db_repo_name.replace('/', '_'))
367 367 short_sha = safe_str(commit.short_id)
368 368
369 369 if at_path == default_at_path:
370 370 archive_name = '{}-{}{}{}'.format(
371 371 clean_name,
372 372 '-sub' if subrepos else '',
373 373 short_sha,
374 374 ext)
375 375 # custom path and new name
376 376 else:
377 377 archive_name = '{}-{}{}-{}{}'.format(
378 378 clean_name,
379 379 '-sub' if subrepos else '',
380 380 short_sha,
381 381 path_sha,
382 382 ext)
383 383
384 384 use_cached_archive = False
385 385 archive_cache_enabled = CONFIG.get(
386 386 'archive_cache_dir') and not self.request.GET.get('no_cache')
387 387 cached_archive_path = None
388 388
389 389 if archive_cache_enabled:
390 390 # check if we it's ok to write
391 391 if not os.path.isdir(CONFIG['archive_cache_dir']):
392 392 os.makedirs(CONFIG['archive_cache_dir'])
393 393 cached_archive_path = os.path.join(
394 394 CONFIG['archive_cache_dir'], archive_name)
395 395 if os.path.isfile(cached_archive_path):
396 396 log.debug('Found cached archive in %s', cached_archive_path)
397 397 fd, archive = None, cached_archive_path
398 398 use_cached_archive = True
399 399 else:
400 400 log.debug('Archive %s is not yet cached', archive_name)
401 401
402 402 if not use_cached_archive:
403 403 # generate new archive
404 404 fd, archive = tempfile.mkstemp()
405 405 log.debug('Creating new temp archive in %s', archive)
406 406 try:
407 407 commit.archive_repo(archive, kind=fileformat, subrepos=subrepos,
408 408 archive_at_path=at_path)
409 409 except ImproperArchiveTypeError:
410 410 return _('Unknown archive type')
411 411 if archive_cache_enabled:
412 412 # if we generated the archive and we have cache enabled
413 413 # let's use this for future
414 414 log.debug('Storing new archive in %s', cached_archive_path)
415 415 shutil.move(archive, cached_archive_path)
416 416 archive = cached_archive_path
417 417
418 418 # store download action
419 419 audit_logger.store_web(
420 420 'repo.archive.download', action_data={
421 421 'user_agent': self.request.user_agent,
422 422 'archive_name': archive_name,
423 423 'archive_spec': fname,
424 424 'archive_cached': use_cached_archive},
425 425 user=self._rhodecode_user,
426 426 repo=self.db_repo,
427 427 commit=True
428 428 )
429 429
430 430 def get_chunked_archive(archive_path):
431 431 with open(archive_path, 'rb') as stream:
432 432 while True:
433 433 data = stream.read(16 * 1024)
434 434 if not data:
435 435 if fd: # fd means we used temporary file
436 436 os.close(fd)
437 437 if not archive_cache_enabled:
438 438 log.debug('Destroying temp archive %s', archive_path)
439 439 os.remove(archive_path)
440 440 break
441 441 yield data
442 442
443 443 response = Response(app_iter=get_chunked_archive(archive))
444 444 response.content_disposition = str(
445 445 'attachment; filename=%s' % archive_name)
446 446 response.content_type = str(content_type)
447 447
448 448 return response
449 449
450 450 def _get_file_node(self, commit_id, f_path):
451 451 if commit_id not in ['', None, 'None', '0' * 12, '0' * 40]:
452 452 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
453 453 try:
454 454 node = commit.get_node(f_path)
455 455 if node.is_dir():
456 456 raise NodeError('%s path is a %s not a file'
457 457 % (node, type(node)))
458 458 except NodeDoesNotExistError:
459 459 commit = EmptyCommit(
460 460 commit_id=commit_id,
461 461 idx=commit.idx,
462 462 repo=commit.repository,
463 463 alias=commit.repository.alias,
464 464 message=commit.message,
465 465 author=commit.author,
466 466 date=commit.date)
467 467 node = FileNode(f_path, '', commit=commit)
468 468 else:
469 469 commit = EmptyCommit(
470 470 repo=self.rhodecode_vcs_repo,
471 471 alias=self.rhodecode_vcs_repo.alias)
472 472 node = FileNode(f_path, '', commit=commit)
473 473 return node
474 474
475 475 @LoginRequired()
476 476 @HasRepoPermissionAnyDecorator(
477 477 'repository.read', 'repository.write', 'repository.admin')
478 478 @view_config(
479 479 route_name='repo_files_diff', request_method='GET',
480 480 renderer=None)
481 481 def repo_files_diff(self):
482 482 c = self.load_default_context()
483 483 f_path = self._get_f_path(self.request.matchdict)
484 484 diff1 = self.request.GET.get('diff1', '')
485 485 diff2 = self.request.GET.get('diff2', '')
486 486
487 487 path1, diff1 = parse_path_ref(diff1, default_path=f_path)
488 488
489 489 ignore_whitespace = str2bool(self.request.GET.get('ignorews'))
490 490 line_context = self.request.GET.get('context', 3)
491 491
492 492 if not any((diff1, diff2)):
493 493 h.flash(
494 494 'Need query parameter "diff1" or "diff2" to generate a diff.',
495 495 category='error')
496 496 raise HTTPBadRequest()
497 497
498 498 c.action = self.request.GET.get('diff')
499 499 if c.action not in ['download', 'raw']:
500 500 compare_url = h.route_path(
501 501 'repo_compare',
502 502 repo_name=self.db_repo_name,
503 503 source_ref_type='rev',
504 504 source_ref=diff1,
505 505 target_repo=self.db_repo_name,
506 506 target_ref_type='rev',
507 507 target_ref=diff2,
508 508 _query=dict(f_path=f_path))
509 509 # redirect to new view if we render diff
510 510 raise HTTPFound(compare_url)
511 511
512 512 try:
513 513 node1 = self._get_file_node(diff1, path1)
514 514 node2 = self._get_file_node(diff2, f_path)
515 515 except (RepositoryError, NodeError):
516 516 log.exception("Exception while trying to get node from repository")
517 517 raise HTTPFound(
518 518 h.route_path('repo_files', repo_name=self.db_repo_name,
519 519 commit_id='tip', f_path=f_path))
520 520
521 521 if all(isinstance(node.commit, EmptyCommit)
522 522 for node in (node1, node2)):
523 523 raise HTTPNotFound()
524 524
525 525 c.commit_1 = node1.commit
526 526 c.commit_2 = node2.commit
527 527
528 528 if c.action == 'download':
529 529 _diff = diffs.get_gitdiff(node1, node2,
530 530 ignore_whitespace=ignore_whitespace,
531 531 context=line_context)
532 532 diff = diffs.DiffProcessor(_diff, format='gitdiff')
533 533
534 534 response = Response(self.path_filter.get_raw_patch(diff))
535 535 response.content_type = 'text/plain'
536 536 response.content_disposition = (
537 537 'attachment; filename=%s_%s_vs_%s.diff' % (f_path, diff1, diff2)
538 538 )
539 539 charset = self._get_default_encoding(c)
540 540 if charset:
541 541 response.charset = charset
542 542 return response
543 543
544 544 elif c.action == 'raw':
545 545 _diff = diffs.get_gitdiff(node1, node2,
546 546 ignore_whitespace=ignore_whitespace,
547 547 context=line_context)
548 548 diff = diffs.DiffProcessor(_diff, format='gitdiff')
549 549
550 550 response = Response(self.path_filter.get_raw_patch(diff))
551 551 response.content_type = 'text/plain'
552 552 charset = self._get_default_encoding(c)
553 553 if charset:
554 554 response.charset = charset
555 555 return response
556 556
557 557 # in case we ever end up here
558 558 raise HTTPNotFound()
559 559
560 560 @LoginRequired()
561 561 @HasRepoPermissionAnyDecorator(
562 562 'repository.read', 'repository.write', 'repository.admin')
563 563 @view_config(
564 564 route_name='repo_files_diff_2way_redirect', request_method='GET',
565 565 renderer=None)
566 566 def repo_files_diff_2way_redirect(self):
567 567 """
568 568 Kept only to make OLD links work
569 569 """
570 570 f_path = self._get_f_path_unchecked(self.request.matchdict)
571 571 diff1 = self.request.GET.get('diff1', '')
572 572 diff2 = self.request.GET.get('diff2', '')
573 573
574 574 if not any((diff1, diff2)):
575 575 h.flash(
576 576 'Need query parameter "diff1" or "diff2" to generate a diff.',
577 577 category='error')
578 578 raise HTTPBadRequest()
579 579
580 580 compare_url = h.route_path(
581 581 'repo_compare',
582 582 repo_name=self.db_repo_name,
583 583 source_ref_type='rev',
584 584 source_ref=diff1,
585 585 target_ref_type='rev',
586 586 target_ref=diff2,
587 587 _query=dict(f_path=f_path, diffmode='sideside',
588 588 target_repo=self.db_repo_name,))
589 589 raise HTTPFound(compare_url)
590 590
591 591 @LoginRequired()
592 592 @HasRepoPermissionAnyDecorator(
593 593 'repository.read', 'repository.write', 'repository.admin')
594 594 @view_config(
595 595 route_name='repo_files', request_method='GET',
596 596 renderer=None)
597 597 @view_config(
598 598 route_name='repo_files:default_path', request_method='GET',
599 599 renderer=None)
600 600 @view_config(
601 601 route_name='repo_files:default_commit', request_method='GET',
602 602 renderer=None)
603 603 @view_config(
604 604 route_name='repo_files:rendered', request_method='GET',
605 605 renderer=None)
606 606 @view_config(
607 607 route_name='repo_files:annotated', request_method='GET',
608 608 renderer=None)
609 609 def repo_files(self):
610 610 c = self.load_default_context()
611 611
612 612 view_name = getattr(self.request.matched_route, 'name', None)
613 613
614 614 c.annotate = view_name == 'repo_files:annotated'
615 615 # default is false, but .rst/.md files later are auto rendered, we can
616 616 # overwrite auto rendering by setting this GET flag
617 617 c.renderer = view_name == 'repo_files:rendered' or \
618 618 not self.request.GET.get('no-render', False)
619 619
620 620 # redirect to given commit_id from form if given
621 621 get_commit_id = self.request.GET.get('at_rev', None)
622 622 if get_commit_id:
623 623 self._get_commit_or_redirect(get_commit_id)
624 624
625 625 commit_id, f_path = self._get_commit_and_path()
626 626 c.commit = self._get_commit_or_redirect(commit_id)
627 627 c.branch = self.request.GET.get('branch', None)
628 628 c.f_path = f_path
629 629
630 630 # prev link
631 631 try:
632 632 prev_commit = c.commit.prev(c.branch)
633 633 c.prev_commit = prev_commit
634 634 c.url_prev = h.route_path(
635 635 'repo_files', repo_name=self.db_repo_name,
636 636 commit_id=prev_commit.raw_id, f_path=f_path)
637 637 if c.branch:
638 638 c.url_prev += '?branch=%s' % c.branch
639 639 except (CommitDoesNotExistError, VCSError):
640 640 c.url_prev = '#'
641 641 c.prev_commit = EmptyCommit()
642 642
643 643 # next link
644 644 try:
645 645 next_commit = c.commit.next(c.branch)
646 646 c.next_commit = next_commit
647 647 c.url_next = h.route_path(
648 648 'repo_files', repo_name=self.db_repo_name,
649 649 commit_id=next_commit.raw_id, f_path=f_path)
650 650 if c.branch:
651 651 c.url_next += '?branch=%s' % c.branch
652 652 except (CommitDoesNotExistError, VCSError):
653 653 c.url_next = '#'
654 654 c.next_commit = EmptyCommit()
655 655
656 656 # files or dirs
657 657 try:
658 658 c.file = c.commit.get_node(f_path)
659 659 c.file_author = True
660 660 c.file_tree = ''
661 661
662 662 # load file content
663 663 if c.file.is_file():
664 664 c.lf_node = {}
665 665
666 666 has_lf_enabled = self._is_lf_enabled(self.db_repo)
667 667 if has_lf_enabled:
668 668 c.lf_node = c.file.get_largefile_node()
669 669
670 670 c.file_source_page = 'true'
671 671 c.file_last_commit = c.file.last_commit
672 672
673 673 c.file_size_too_big = c.file.size > c.visual.cut_off_limit_file
674 674
675 675 if not (c.file_size_too_big or c.file.is_binary):
676 676 if c.annotate: # annotation has precedence over renderer
677 677 c.annotated_lines = filenode_as_annotated_lines_tokens(
678 678 c.file
679 679 )
680 680 else:
681 681 c.renderer = (
682 682 c.renderer and h.renderer_from_filename(c.file.path)
683 683 )
684 684 if not c.renderer:
685 685 c.lines = filenode_as_lines_tokens(c.file)
686 686
687 687 _branch_name, _sha_commit_id, is_head = self._is_valid_head(
688 688 commit_id, self.rhodecode_vcs_repo)
689 689 c.on_branch_head = is_head
690 690
691 691 branch = c.commit.branch if (
692 692 c.commit.branch and '/' not in c.commit.branch) else None
693 693 c.branch_or_raw_id = branch or c.commit.raw_id
694 694 c.branch_name = c.commit.branch or h.short_id(c.commit.raw_id)
695 695
696 696 author = c.file_last_commit.author
697 697 c.authors = [[
698 698 h.email(author),
699 699 h.person(author, 'username_or_name_or_email'),
700 700 1
701 701 ]]
702 702
703 703 else: # load tree content at path
704 704 c.file_source_page = 'false'
705 705 c.authors = []
706 706 # this loads a simple tree without metadata to speed things up
707 707 # later via ajax we call repo_nodetree_full and fetch whole
708 708 c.file_tree = self._get_tree_at_commit(c, c.commit.raw_id, f_path)
709 709
710 710 c.readme_data, c.readme_file = \
711 711 self._get_readme_data(self.db_repo, c.visual.default_renderer,
712 712 c.commit.raw_id, f_path)
713 713
714 714 except RepositoryError as e:
715 715 h.flash(safe_str(h.escape(e)), category='error')
716 716 raise HTTPNotFound()
717 717
718 718 if self.request.environ.get('HTTP_X_PJAX'):
719 719 html = render('rhodecode:templates/files/files_pjax.mako',
720 720 self._get_template_context(c), self.request)
721 721 else:
722 722 html = render('rhodecode:templates/files/files.mako',
723 723 self._get_template_context(c), self.request)
724 724 return Response(html)
725 725
726 726 @HasRepoPermissionAnyDecorator(
727 727 'repository.read', 'repository.write', 'repository.admin')
728 728 @view_config(
729 729 route_name='repo_files:annotated_previous', request_method='GET',
730 730 renderer=None)
731 731 def repo_files_annotated_previous(self):
732 732 self.load_default_context()
733 733
734 734 commit_id, f_path = self._get_commit_and_path()
735 735 commit = self._get_commit_or_redirect(commit_id)
736 736 prev_commit_id = commit.raw_id
737 737 line_anchor = self.request.GET.get('line_anchor')
738 738 is_file = False
739 739 try:
740 740 _file = commit.get_node(f_path)
741 741 is_file = _file.is_file()
742 742 except (NodeDoesNotExistError, CommitDoesNotExistError, VCSError):
743 743 pass
744 744
745 745 if is_file:
746 746 history = commit.get_path_history(f_path)
747 747 prev_commit_id = history[1].raw_id \
748 748 if len(history) > 1 else prev_commit_id
749 749 prev_url = h.route_path(
750 750 'repo_files:annotated', repo_name=self.db_repo_name,
751 751 commit_id=prev_commit_id, f_path=f_path,
752 752 _anchor='L{}'.format(line_anchor))
753 753
754 754 raise HTTPFound(prev_url)
755 755
756 756 @LoginRequired()
757 757 @HasRepoPermissionAnyDecorator(
758 758 'repository.read', 'repository.write', 'repository.admin')
759 759 @view_config(
760 760 route_name='repo_nodetree_full', request_method='GET',
761 761 renderer=None, xhr=True)
762 762 @view_config(
763 763 route_name='repo_nodetree_full:default_path', request_method='GET',
764 764 renderer=None, xhr=True)
765 765 def repo_nodetree_full(self):
766 766 """
767 767 Returns rendered html of file tree that contains commit date,
768 768 author, commit_id for the specified combination of
769 769 repo, commit_id and file path
770 770 """
771 771 c = self.load_default_context()
772 772
773 773 commit_id, f_path = self._get_commit_and_path()
774 774 commit = self._get_commit_or_redirect(commit_id)
775 775 try:
776 776 dir_node = commit.get_node(f_path)
777 777 except RepositoryError as e:
778 778 return Response('error: {}'.format(h.escape(safe_str(e))))
779 779
780 780 if dir_node.is_file():
781 781 return Response('')
782 782
783 783 c.file = dir_node
784 784 c.commit = commit
785 785
786 786 html = self._get_tree_at_commit(
787 787 c, commit.raw_id, dir_node.path, full_load=True)
788 788
789 789 return Response(html)
790 790
791 791 def _get_attachement_headers(self, f_path):
792 792 f_name = safe_str(f_path.split(Repository.NAME_SEP)[-1])
793 793 safe_path = f_name.replace('"', '\\"')
794 794 encoded_path = urllib.quote(f_name)
795 795
796 796 return "attachment; " \
797 797 "filename=\"{}\"; " \
798 798 "filename*=UTF-8\'\'{}".format(safe_path, encoded_path)
799 799
800 800 @LoginRequired()
801 801 @HasRepoPermissionAnyDecorator(
802 802 'repository.read', 'repository.write', 'repository.admin')
803 803 @view_config(
804 804 route_name='repo_file_raw', request_method='GET',
805 805 renderer=None)
806 806 def repo_file_raw(self):
807 807 """
808 808 Action for show as raw, some mimetypes are "rendered",
809 809 those include images, icons.
810 810 """
811 811 c = self.load_default_context()
812 812
813 813 commit_id, f_path = self._get_commit_and_path()
814 814 commit = self._get_commit_or_redirect(commit_id)
815 815 file_node = self._get_filenode_or_redirect(commit, f_path)
816 816
817 817 raw_mimetype_mapping = {
818 818 # map original mimetype to a mimetype used for "show as raw"
819 819 # you can also provide a content-disposition to override the
820 820 # default "attachment" disposition.
821 821 # orig_type: (new_type, new_dispo)
822 822
823 823 # show images inline:
824 824 # Do not re-add SVG: it is unsafe and permits XSS attacks. One can
825 825 # for example render an SVG with javascript inside or even render
826 826 # HTML.
827 827 'image/x-icon': ('image/x-icon', 'inline'),
828 828 'image/png': ('image/png', 'inline'),
829 829 'image/gif': ('image/gif', 'inline'),
830 830 'image/jpeg': ('image/jpeg', 'inline'),
831 831 'application/pdf': ('application/pdf', 'inline'),
832 832 }
833 833
834 834 mimetype = file_node.mimetype
835 835 try:
836 836 mimetype, disposition = raw_mimetype_mapping[mimetype]
837 837 except KeyError:
838 838 # we don't know anything special about this, handle it safely
839 839 if file_node.is_binary:
840 840 # do same as download raw for binary files
841 841 mimetype, disposition = 'application/octet-stream', 'attachment'
842 842 else:
843 843 # do not just use the original mimetype, but force text/plain,
844 844 # otherwise it would serve text/html and that might be unsafe.
845 845 # Note: underlying vcs library fakes text/plain mimetype if the
846 846 # mimetype can not be determined and it thinks it is not
847 847 # binary.This might lead to erroneous text display in some
848 848 # cases, but helps in other cases, like with text files
849 849 # without extension.
850 850 mimetype, disposition = 'text/plain', 'inline'
851 851
852 852 if disposition == 'attachment':
853 853 disposition = self._get_attachement_headers(f_path)
854 854
855 855 stream_content = file_node.stream_bytes()
856 856
857 857 response = Response(app_iter=stream_content)
858 858 response.content_disposition = disposition
859 859 response.content_type = mimetype
860 860
861 861 charset = self._get_default_encoding(c)
862 862 if charset:
863 863 response.charset = charset
864 864
865 865 return response
866 866
867 867 @LoginRequired()
868 868 @HasRepoPermissionAnyDecorator(
869 869 'repository.read', 'repository.write', 'repository.admin')
870 870 @view_config(
871 871 route_name='repo_file_download', request_method='GET',
872 872 renderer=None)
873 873 @view_config(
874 874 route_name='repo_file_download:legacy', request_method='GET',
875 875 renderer=None)
876 876 def repo_file_download(self):
877 877 c = self.load_default_context()
878 878
879 879 commit_id, f_path = self._get_commit_and_path()
880 880 commit = self._get_commit_or_redirect(commit_id)
881 881 file_node = self._get_filenode_or_redirect(commit, f_path)
882 882
883 883 if self.request.GET.get('lf'):
884 884 # only if lf get flag is passed, we download this file
885 885 # as LFS/Largefile
886 886 lf_node = file_node.get_largefile_node()
887 887 if lf_node:
888 888 # overwrite our pointer with the REAL large-file
889 889 file_node = lf_node
890 890
891 891 disposition = self._get_attachement_headers(f_path)
892 892
893 893 stream_content = file_node.stream_bytes()
894 894
895 895 response = Response(app_iter=stream_content)
896 896 response.content_disposition = disposition
897 897 response.content_type = file_node.mimetype
898 898
899 899 charset = self._get_default_encoding(c)
900 900 if charset:
901 901 response.charset = charset
902 902
903 903 return response
904 904
905 905 def _get_nodelist_at_commit(self, repo_name, repo_id, commit_id, f_path):
906 906
907 907 cache_seconds = safe_int(
908 908 rhodecode.CONFIG.get('rc_cache.cache_repo.expiration_time'))
909 909 cache_on = cache_seconds > 0
910 910 log.debug(
911 911 'Computing FILE SEARCH for repo_id %s commit_id `%s` and path `%s`'
912 912 'with caching: %s[TTL: %ss]' % (
913 913 repo_id, commit_id, f_path, cache_on, cache_seconds or 0))
914 914
915 915 cache_namespace_uid = 'cache_repo.{}'.format(repo_id)
916 916 region = rc_cache.get_or_create_region('cache_repo', cache_namespace_uid)
917 917
918 918 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
919 919 condition=cache_on)
920 920 def compute_file_search(repo_id, commit_id, f_path):
921 921 log.debug('Generating cached nodelist for repo_id:%s, %s, %s',
922 922 repo_id, commit_id, f_path)
923 923 try:
924 924 _d, _f = ScmModel().get_quick_filter_nodes(repo_name, commit_id, f_path)
925 925 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
926 926 log.exception(safe_str(e))
927 927 h.flash(safe_str(h.escape(e)), category='error')
928 928 raise HTTPFound(h.route_path(
929 929 'repo_files', repo_name=self.db_repo_name,
930 930 commit_id='tip', f_path='/'))
931 931
932 932 return _d + _f
933 933
934 934 result = compute_file_search(self.db_repo.repo_id, commit_id, f_path)
935 935 return filter(lambda n: self.path_filter.path_access_allowed(n['name']), result)
936 936
937 937 @LoginRequired()
938 938 @HasRepoPermissionAnyDecorator(
939 939 'repository.read', 'repository.write', 'repository.admin')
940 940 @view_config(
941 941 route_name='repo_files_nodelist', request_method='GET',
942 942 renderer='json_ext', xhr=True)
943 943 def repo_nodelist(self):
944 944 self.load_default_context()
945 945
946 946 commit_id, f_path = self._get_commit_and_path()
947 947 commit = self._get_commit_or_redirect(commit_id)
948 948
949 949 metadata = self._get_nodelist_at_commit(
950 950 self.db_repo_name, self.db_repo.repo_id, commit.raw_id, f_path)
951 951 return {'nodes': metadata}
952 952
953 953 def _create_references(self, branches_or_tags, symbolic_reference, f_path, ref_type):
954 954 items = []
955 955 for name, commit_id in branches_or_tags.items():
956 956 sym_ref = symbolic_reference(commit_id, name, f_path, ref_type)
957 957 items.append((sym_ref, name, ref_type))
958 958 return items
959 959
960 960 def _symbolic_reference(self, commit_id, name, f_path, ref_type):
961 961 return commit_id
962 962
963 963 def _symbolic_reference_svn(self, commit_id, name, f_path, ref_type):
964 return commit_id
965
966 # NOTE(dan): old code we used in "diff" mode compare
964 967 new_f_path = vcspath.join(name, f_path)
965 968 return u'%s@%s' % (new_f_path, commit_id)
966 969
967 970 def _get_node_history(self, commit_obj, f_path, commits=None):
968 971 """
969 972 get commit history for given node
970 973
971 974 :param commit_obj: commit to calculate history
972 975 :param f_path: path for node to calculate history for
973 976 :param commits: if passed don't calculate history and take
974 977 commits defined in this list
975 978 """
976 979 _ = self.request.translate
977 980
978 981 # calculate history based on tip
979 982 tip = self.rhodecode_vcs_repo.get_commit()
980 983 if commits is None:
981 984 pre_load = ["author", "branch"]
982 985 try:
983 986 commits = tip.get_path_history(f_path, pre_load=pre_load)
984 987 except (NodeDoesNotExistError, CommitError):
985 988 # this node is not present at tip!
986 989 commits = commit_obj.get_path_history(f_path, pre_load=pre_load)
987 990
988 991 history = []
989 992 commits_group = ([], _("Changesets"))
990 993 for commit in commits:
991 994 branch = ' (%s)' % commit.branch if commit.branch else ''
992 995 n_desc = 'r%s:%s%s' % (commit.idx, commit.short_id, branch)
993 996 commits_group[0].append((commit.raw_id, n_desc, 'sha'))
994 997 history.append(commits_group)
995 998
996 999 symbolic_reference = self._symbolic_reference
997 1000
998 1001 if self.rhodecode_vcs_repo.alias == 'svn':
999 1002 adjusted_f_path = RepoFilesView.adjust_file_path_for_svn(
1000 1003 f_path, self.rhodecode_vcs_repo)
1001 1004 if adjusted_f_path != f_path:
1002 1005 log.debug(
1003 1006 'Recognized svn tag or branch in file "%s", using svn '
1004 1007 'specific symbolic references', f_path)
1005 1008 f_path = adjusted_f_path
1006 1009 symbolic_reference = self._symbolic_reference_svn
1007 1010
1008 1011 branches = self._create_references(
1009 1012 self.rhodecode_vcs_repo.branches, symbolic_reference, f_path, 'branch')
1010 1013 branches_group = (branches, _("Branches"))
1011 1014
1012 1015 tags = self._create_references(
1013 1016 self.rhodecode_vcs_repo.tags, symbolic_reference, f_path, 'tag')
1014 1017 tags_group = (tags, _("Tags"))
1015 1018
1016 1019 history.append(branches_group)
1017 1020 history.append(tags_group)
1018 1021
1019 1022 return history, commits
1020 1023
1021 1024 @LoginRequired()
1022 1025 @HasRepoPermissionAnyDecorator(
1023 1026 'repository.read', 'repository.write', 'repository.admin')
1024 1027 @view_config(
1025 1028 route_name='repo_file_history', request_method='GET',
1026 1029 renderer='json_ext')
1027 1030 def repo_file_history(self):
1028 1031 self.load_default_context()
1029 1032
1030 1033 commit_id, f_path = self._get_commit_and_path()
1031 1034 commit = self._get_commit_or_redirect(commit_id)
1032 1035 file_node = self._get_filenode_or_redirect(commit, f_path)
1033 1036
1034 1037 if file_node.is_file():
1035 1038 file_history, _hist = self._get_node_history(commit, f_path)
1036 1039
1037 1040 res = []
1038 1041 for obj in file_history:
1039 1042 res.append({
1040 1043 'text': obj[1],
1041 1044 'children': [{'id': o[0], 'text': o[1], 'type': o[2]} for o in obj[0]]
1042 1045 })
1043 1046
1044 1047 data = {
1045 1048 'more': False,
1046 1049 'results': res
1047 1050 }
1048 1051 return data
1049 1052
1050 1053 log.warning('Cannot fetch history for directory')
1051 1054 raise HTTPBadRequest()
1052 1055
1053 1056 @LoginRequired()
1054 1057 @HasRepoPermissionAnyDecorator(
1055 1058 'repository.read', 'repository.write', 'repository.admin')
1056 1059 @view_config(
1057 1060 route_name='repo_file_authors', request_method='GET',
1058 1061 renderer='rhodecode:templates/files/file_authors_box.mako')
1059 1062 def repo_file_authors(self):
1060 1063 c = self.load_default_context()
1061 1064
1062 1065 commit_id, f_path = self._get_commit_and_path()
1063 1066 commit = self._get_commit_or_redirect(commit_id)
1064 1067 file_node = self._get_filenode_or_redirect(commit, f_path)
1065 1068
1066 1069 if not file_node.is_file():
1067 1070 raise HTTPBadRequest()
1068 1071
1069 1072 c.file_last_commit = file_node.last_commit
1070 1073 if self.request.GET.get('annotate') == '1':
1071 1074 # use _hist from annotation if annotation mode is on
1072 1075 commit_ids = set(x[1] for x in file_node.annotate)
1073 1076 _hist = (
1074 1077 self.rhodecode_vcs_repo.get_commit(commit_id)
1075 1078 for commit_id in commit_ids)
1076 1079 else:
1077 1080 _f_history, _hist = self._get_node_history(commit, f_path)
1078 1081 c.file_author = False
1079 1082
1080 1083 unique = collections.OrderedDict()
1081 1084 for commit in _hist:
1082 1085 author = commit.author
1083 1086 if author not in unique:
1084 1087 unique[commit.author] = [
1085 1088 h.email(author),
1086 1089 h.person(author, 'username_or_name_or_email'),
1087 1090 1 # counter
1088 1091 ]
1089 1092
1090 1093 else:
1091 1094 # increase counter
1092 1095 unique[commit.author][2] += 1
1093 1096
1094 1097 c.authors = [val for val in unique.values()]
1095 1098
1096 1099 return self._get_template_context(c)
1097 1100
1098 1101 @LoginRequired()
1099 1102 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1100 1103 @view_config(
1101 1104 route_name='repo_files_remove_file', request_method='GET',
1102 1105 renderer='rhodecode:templates/files/files_delete.mako')
1103 1106 def repo_files_remove_file(self):
1104 1107 _ = self.request.translate
1105 1108 c = self.load_default_context()
1106 1109 commit_id, f_path = self._get_commit_and_path()
1107 1110
1108 1111 self._ensure_not_locked()
1109 1112 _branch_name, _sha_commit_id, is_head = \
1110 1113 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1111 1114
1112 1115 self.forbid_non_head(is_head, f_path)
1113 1116 self.check_branch_permission(_branch_name)
1114 1117
1115 1118 c.commit = self._get_commit_or_redirect(commit_id)
1116 1119 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1117 1120
1118 1121 c.default_message = _(
1119 1122 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1120 1123 c.f_path = f_path
1121 1124
1122 1125 return self._get_template_context(c)
1123 1126
1124 1127 @LoginRequired()
1125 1128 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1126 1129 @CSRFRequired()
1127 1130 @view_config(
1128 1131 route_name='repo_files_delete_file', request_method='POST',
1129 1132 renderer=None)
1130 1133 def repo_files_delete_file(self):
1131 1134 _ = self.request.translate
1132 1135
1133 1136 c = self.load_default_context()
1134 1137 commit_id, f_path = self._get_commit_and_path()
1135 1138
1136 1139 self._ensure_not_locked()
1137 1140 _branch_name, _sha_commit_id, is_head = \
1138 1141 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1139 1142
1140 1143 self.forbid_non_head(is_head, f_path)
1141 1144 self.check_branch_permission(_branch_name)
1142 1145
1143 1146 c.commit = self._get_commit_or_redirect(commit_id)
1144 1147 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1145 1148
1146 1149 c.default_message = _(
1147 1150 'Deleted file {} via RhodeCode Enterprise').format(f_path)
1148 1151 c.f_path = f_path
1149 1152 node_path = f_path
1150 1153 author = self._rhodecode_db_user.full_contact
1151 1154 message = self.request.POST.get('message') or c.default_message
1152 1155 try:
1153 1156 nodes = {
1154 1157 node_path: {
1155 1158 'content': ''
1156 1159 }
1157 1160 }
1158 1161 ScmModel().delete_nodes(
1159 1162 user=self._rhodecode_db_user.user_id, repo=self.db_repo,
1160 1163 message=message,
1161 1164 nodes=nodes,
1162 1165 parent_commit=c.commit,
1163 1166 author=author,
1164 1167 )
1165 1168
1166 1169 h.flash(
1167 1170 _('Successfully deleted file `{}`').format(
1168 1171 h.escape(f_path)), category='success')
1169 1172 except Exception:
1170 1173 log.exception('Error during commit operation')
1171 1174 h.flash(_('Error occurred during commit'), category='error')
1172 1175 raise HTTPFound(
1173 1176 h.route_path('repo_commit', repo_name=self.db_repo_name,
1174 1177 commit_id='tip'))
1175 1178
1176 1179 @LoginRequired()
1177 1180 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1178 1181 @view_config(
1179 1182 route_name='repo_files_edit_file', request_method='GET',
1180 1183 renderer='rhodecode:templates/files/files_edit.mako')
1181 1184 def repo_files_edit_file(self):
1182 1185 _ = self.request.translate
1183 1186 c = self.load_default_context()
1184 1187 commit_id, f_path = self._get_commit_and_path()
1185 1188
1186 1189 self._ensure_not_locked()
1187 1190 _branch_name, _sha_commit_id, is_head = \
1188 1191 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1189 1192
1190 1193 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1191 1194 self.check_branch_permission(_branch_name, commit_id=commit_id)
1192 1195
1193 1196 c.commit = self._get_commit_or_redirect(commit_id)
1194 1197 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1195 1198
1196 1199 if c.file.is_binary:
1197 1200 files_url = h.route_path(
1198 1201 'repo_files',
1199 1202 repo_name=self.db_repo_name,
1200 1203 commit_id=c.commit.raw_id, f_path=f_path)
1201 1204 raise HTTPFound(files_url)
1202 1205
1203 1206 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1204 1207 c.f_path = f_path
1205 1208
1206 1209 return self._get_template_context(c)
1207 1210
1208 1211 @LoginRequired()
1209 1212 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1210 1213 @CSRFRequired()
1211 1214 @view_config(
1212 1215 route_name='repo_files_update_file', request_method='POST',
1213 1216 renderer=None)
1214 1217 def repo_files_update_file(self):
1215 1218 _ = self.request.translate
1216 1219 c = self.load_default_context()
1217 1220 commit_id, f_path = self._get_commit_and_path()
1218 1221
1219 1222 self._ensure_not_locked()
1220 1223
1221 1224 c.commit = self._get_commit_or_redirect(commit_id)
1222 1225 c.file = self._get_filenode_or_redirect(c.commit, f_path)
1223 1226
1224 1227 if c.file.is_binary:
1225 1228 raise HTTPFound(h.route_path('repo_files', repo_name=self.db_repo_name,
1226 1229 commit_id=c.commit.raw_id, f_path=f_path))
1227 1230
1228 1231 _branch_name, _sha_commit_id, is_head = \
1229 1232 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1230 1233
1231 1234 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1232 1235 self.check_branch_permission(_branch_name, commit_id=commit_id)
1233 1236
1234 1237 c.default_message = _('Edited file {} via RhodeCode Enterprise').format(f_path)
1235 1238 c.f_path = f_path
1236 1239
1237 1240 old_content = c.file.content
1238 1241 sl = old_content.splitlines(1)
1239 1242 first_line = sl[0] if sl else ''
1240 1243
1241 1244 r_post = self.request.POST
1242 1245 # line endings: 0 - Unix, 1 - Mac, 2 - DOS
1243 1246 line_ending_mode = detect_mode(first_line, 0)
1244 1247 content = convert_line_endings(r_post.get('content', ''), line_ending_mode)
1245 1248
1246 1249 message = r_post.get('message') or c.default_message
1247 1250 org_node_path = c.file.unicode_path
1248 1251 filename = r_post['filename']
1249 1252
1250 1253 root_path = c.file.dir_path
1251 1254 pure_path = self.create_pure_path(root_path, filename)
1252 1255 node_path = safe_unicode(bytes(pure_path))
1253 1256
1254 1257 default_redirect_url = h.route_path('repo_commit', repo_name=self.db_repo_name,
1255 1258 commit_id=commit_id)
1256 1259 if content == old_content and node_path == org_node_path:
1257 1260 h.flash(_('No changes detected on {}').format(h.escape(org_node_path)),
1258 1261 category='warning')
1259 1262 raise HTTPFound(default_redirect_url)
1260 1263
1261 1264 try:
1262 1265 mapping = {
1263 1266 org_node_path: {
1264 1267 'org_filename': org_node_path,
1265 1268 'filename': node_path,
1266 1269 'content': content,
1267 1270 'lexer': '',
1268 1271 'op': 'mod',
1269 1272 'mode': c.file.mode
1270 1273 }
1271 1274 }
1272 1275
1273 1276 commit = ScmModel().update_nodes(
1274 1277 user=self._rhodecode_db_user.user_id,
1275 1278 repo=self.db_repo,
1276 1279 message=message,
1277 1280 nodes=mapping,
1278 1281 parent_commit=c.commit,
1279 1282 )
1280 1283
1281 1284 h.flash(_('Successfully committed changes to file `{}`').format(
1282 1285 h.escape(f_path)), category='success')
1283 1286 default_redirect_url = h.route_path(
1284 1287 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1285 1288
1286 1289 except Exception:
1287 1290 log.exception('Error occurred during commit')
1288 1291 h.flash(_('Error occurred during commit'), category='error')
1289 1292
1290 1293 raise HTTPFound(default_redirect_url)
1291 1294
1292 1295 @LoginRequired()
1293 1296 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1294 1297 @view_config(
1295 1298 route_name='repo_files_add_file', request_method='GET',
1296 1299 renderer='rhodecode:templates/files/files_add.mako')
1297 1300 @view_config(
1298 1301 route_name='repo_files_upload_file', request_method='GET',
1299 1302 renderer='rhodecode:templates/files/files_upload.mako')
1300 1303 def repo_files_add_file(self):
1301 1304 _ = self.request.translate
1302 1305 c = self.load_default_context()
1303 1306 commit_id, f_path = self._get_commit_and_path()
1304 1307
1305 1308 self._ensure_not_locked()
1306 1309
1307 1310 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1308 1311 if c.commit is None:
1309 1312 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1310 1313
1311 1314 if self.rhodecode_vcs_repo.is_empty():
1312 1315 # for empty repository we cannot check for current branch, we rely on
1313 1316 # c.commit.branch instead
1314 1317 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1315 1318 else:
1316 1319 _branch_name, _sha_commit_id, is_head = \
1317 1320 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1318 1321
1319 1322 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1320 1323 self.check_branch_permission(_branch_name, commit_id=commit_id)
1321 1324
1322 1325 c.default_message = (_('Added file via RhodeCode Enterprise'))
1323 1326 c.f_path = f_path.lstrip('/') # ensure not relative path
1324 1327
1325 1328 return self._get_template_context(c)
1326 1329
1327 1330 @LoginRequired()
1328 1331 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1329 1332 @CSRFRequired()
1330 1333 @view_config(
1331 1334 route_name='repo_files_create_file', request_method='POST',
1332 1335 renderer=None)
1333 1336 def repo_files_create_file(self):
1334 1337 _ = self.request.translate
1335 1338 c = self.load_default_context()
1336 1339 commit_id, f_path = self._get_commit_and_path()
1337 1340
1338 1341 self._ensure_not_locked()
1339 1342
1340 1343 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1341 1344 if c.commit is None:
1342 1345 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1343 1346
1344 1347 # calculate redirect URL
1345 1348 if self.rhodecode_vcs_repo.is_empty():
1346 1349 default_redirect_url = h.route_path(
1347 1350 'repo_summary', repo_name=self.db_repo_name)
1348 1351 else:
1349 1352 default_redirect_url = h.route_path(
1350 1353 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1351 1354
1352 1355 if self.rhodecode_vcs_repo.is_empty():
1353 1356 # for empty repository we cannot check for current branch, we rely on
1354 1357 # c.commit.branch instead
1355 1358 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1356 1359 else:
1357 1360 _branch_name, _sha_commit_id, is_head = \
1358 1361 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1359 1362
1360 1363 self.forbid_non_head(is_head, f_path, commit_id=commit_id)
1361 1364 self.check_branch_permission(_branch_name, commit_id=commit_id)
1362 1365
1363 1366 c.default_message = (_('Added file via RhodeCode Enterprise'))
1364 1367 c.f_path = f_path
1365 1368
1366 1369 r_post = self.request.POST
1367 1370 message = r_post.get('message') or c.default_message
1368 1371 filename = r_post.get('filename')
1369 1372 unix_mode = 0
1370 1373 content = convert_line_endings(r_post.get('content', ''), unix_mode)
1371 1374
1372 1375 if not filename:
1373 1376 # If there's no commit, redirect to repo summary
1374 1377 if type(c.commit) is EmptyCommit:
1375 1378 redirect_url = h.route_path(
1376 1379 'repo_summary', repo_name=self.db_repo_name)
1377 1380 else:
1378 1381 redirect_url = default_redirect_url
1379 1382 h.flash(_('No filename specified'), category='warning')
1380 1383 raise HTTPFound(redirect_url)
1381 1384
1382 1385 root_path = f_path
1383 1386 pure_path = self.create_pure_path(root_path, filename)
1384 1387 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1385 1388
1386 1389 author = self._rhodecode_db_user.full_contact
1387 1390 nodes = {
1388 1391 node_path: {
1389 1392 'content': content
1390 1393 }
1391 1394 }
1392 1395
1393 1396 try:
1394 1397
1395 1398 commit = ScmModel().create_nodes(
1396 1399 user=self._rhodecode_db_user.user_id,
1397 1400 repo=self.db_repo,
1398 1401 message=message,
1399 1402 nodes=nodes,
1400 1403 parent_commit=c.commit,
1401 1404 author=author,
1402 1405 )
1403 1406
1404 1407 h.flash(_('Successfully committed new file `{}`').format(
1405 1408 h.escape(node_path)), category='success')
1406 1409
1407 1410 default_redirect_url = h.route_path(
1408 1411 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1409 1412
1410 1413 except NonRelativePathError:
1411 1414 log.exception('Non Relative path found')
1412 1415 h.flash(_('The location specified must be a relative path and must not '
1413 1416 'contain .. in the path'), category='warning')
1414 1417 raise HTTPFound(default_redirect_url)
1415 1418 except (NodeError, NodeAlreadyExistsError) as e:
1416 1419 h.flash(_(h.escape(e)), category='error')
1417 1420 except Exception:
1418 1421 log.exception('Error occurred during commit')
1419 1422 h.flash(_('Error occurred during commit'), category='error')
1420 1423
1421 1424 raise HTTPFound(default_redirect_url)
1422 1425
1423 1426 @LoginRequired()
1424 1427 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
1425 1428 @CSRFRequired()
1426 1429 @view_config(
1427 1430 route_name='repo_files_upload_file', request_method='POST',
1428 1431 renderer='json_ext')
1429 1432 def repo_files_upload_file(self):
1430 1433 _ = self.request.translate
1431 1434 c = self.load_default_context()
1432 1435 commit_id, f_path = self._get_commit_and_path()
1433 1436
1434 1437 self._ensure_not_locked()
1435 1438
1436 1439 c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False)
1437 1440 if c.commit is None:
1438 1441 c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias)
1439 1442
1440 1443 # calculate redirect URL
1441 1444 if self.rhodecode_vcs_repo.is_empty():
1442 1445 default_redirect_url = h.route_path(
1443 1446 'repo_summary', repo_name=self.db_repo_name)
1444 1447 else:
1445 1448 default_redirect_url = h.route_path(
1446 1449 'repo_commit', repo_name=self.db_repo_name, commit_id='tip')
1447 1450
1448 1451 if self.rhodecode_vcs_repo.is_empty():
1449 1452 # for empty repository we cannot check for current branch, we rely on
1450 1453 # c.commit.branch instead
1451 1454 _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True
1452 1455 else:
1453 1456 _branch_name, _sha_commit_id, is_head = \
1454 1457 self._is_valid_head(commit_id, self.rhodecode_vcs_repo)
1455 1458
1456 1459 error = self.forbid_non_head(is_head, f_path, json_mode=True)
1457 1460 if error:
1458 1461 return {
1459 1462 'error': error,
1460 1463 'redirect_url': default_redirect_url
1461 1464 }
1462 1465 error = self.check_branch_permission(_branch_name, json_mode=True)
1463 1466 if error:
1464 1467 return {
1465 1468 'error': error,
1466 1469 'redirect_url': default_redirect_url
1467 1470 }
1468 1471
1469 1472 c.default_message = (_('Uploaded file via RhodeCode Enterprise'))
1470 1473 c.f_path = f_path
1471 1474
1472 1475 r_post = self.request.POST
1473 1476
1474 1477 message = c.default_message
1475 1478 user_message = r_post.getall('message')
1476 1479 if isinstance(user_message, list) and user_message:
1477 1480 # we take the first from duplicated results if it's not empty
1478 1481 message = user_message[0] if user_message[0] else message
1479 1482
1480 1483 nodes = {}
1481 1484
1482 1485 for file_obj in r_post.getall('files_upload') or []:
1483 1486 content = file_obj.file
1484 1487 filename = file_obj.filename
1485 1488
1486 1489 root_path = f_path
1487 1490 pure_path = self.create_pure_path(root_path, filename)
1488 1491 node_path = safe_unicode(bytes(pure_path).lstrip('/'))
1489 1492
1490 1493 nodes[node_path] = {
1491 1494 'content': content
1492 1495 }
1493 1496
1494 1497 if not nodes:
1495 1498 error = 'missing files'
1496 1499 return {
1497 1500 'error': error,
1498 1501 'redirect_url': default_redirect_url
1499 1502 }
1500 1503
1501 1504 author = self._rhodecode_db_user.full_contact
1502 1505
1503 1506 try:
1504 1507 commit = ScmModel().create_nodes(
1505 1508 user=self._rhodecode_db_user.user_id,
1506 1509 repo=self.db_repo,
1507 1510 message=message,
1508 1511 nodes=nodes,
1509 1512 parent_commit=c.commit,
1510 1513 author=author,
1511 1514 )
1512 1515 if len(nodes) == 1:
1513 1516 flash_message = _('Successfully committed {} new files').format(len(nodes))
1514 1517 else:
1515 1518 flash_message = _('Successfully committed 1 new file')
1516 1519
1517 1520 h.flash(flash_message, category='success')
1518 1521
1519 1522 default_redirect_url = h.route_path(
1520 1523 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id)
1521 1524
1522 1525 except NonRelativePathError:
1523 1526 log.exception('Non Relative path found')
1524 1527 error = _('The location specified must be a relative path and must not '
1525 1528 'contain .. in the path')
1526 1529 h.flash(error, category='warning')
1527 1530
1528 1531 return {
1529 1532 'error': error,
1530 1533 'redirect_url': default_redirect_url
1531 1534 }
1532 1535 except (NodeError, NodeAlreadyExistsError) as e:
1533 1536 error = h.escape(e)
1534 1537 h.flash(error, category='error')
1535 1538
1536 1539 return {
1537 1540 'error': error,
1538 1541 'redirect_url': default_redirect_url
1539 1542 }
1540 1543 except Exception:
1541 1544 log.exception('Error occurred during commit')
1542 1545 error = _('Error occurred during commit')
1543 1546 h.flash(error, category='error')
1544 1547 return {
1545 1548 'error': error,
1546 1549 'redirect_url': default_redirect_url
1547 1550 }
1548 1551
1549 1552 return {
1550 1553 'error': None,
1551 1554 'redirect_url': default_redirect_url
1552 1555 }
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r625:ead8f28a4bc2", "type": "sha", "id": "ead8f28a4bc2f45ecfb148a6b8a89758b9654a84"}, {"text": "r535:c093f94d6d35", "type": "sha", "id": "c093f94d6d358f13c55a687da66c30c41cca4153"}, {"text": "r534:559f640ec08b", "type": "sha", "id": "559f640ec08b2a14c4a9ac863d8ca273545b8885"}, {"text": "r490:02a940b4ee37", "type": "sha", "id": "02a940b4ee371ec64ef5b4c4870a5c89dc7fb98a"}, {"text": "r464:b45a4153a2d7", "type": "sha", "id": "b45a4153a2d7adb8a78b63d35d39fac44a4320a6"}, {"text": "r460:0a54e66b9450", "type": "sha", "id": "0a54e66b94502409074b163cd93c1233dcc0413f"}, {"text": "r457:a7bf2f6bf3d5", "type": "sha", "id": "a7bf2f6bf3d5273da4bcd2032a891acae5a45e2b"}, {"text": "r456:7266de0154b4", "type": "sha", "id": "7266de0154b4da7c42ba3d788876056dbf116b5a"}, {"text": "r455:666de4ee6507", "type": "sha", "id": "666de4ee65074cd3e37ea01e75f65bd3e4c336bb"}, {"text": "r453:91acc599141c", "type": "sha", "id": "91acc599141c87f03e0e3551dcaacf4492632e58"}, {"text": "r442:40a2d5d71b75", "type": "sha", "id": "40a2d5d71b758e7eafc84a324ed55142cba22f42"}, {"text": "r440:d1f898326327", "type": "sha", "id": "d1f898326327e20524fe22417c22d71064fe54a1"}, {"text": "r420:162a36830c23", "type": "sha", "id": "162a36830c23ccf1bf1873157fd0c8d0dfc7c817"}, {"text": "r345:c994f0de03b2", "type": "sha", "id": "c994f0de03b2a0aa848a04fc2c0d7e737dba31fc"}, {"text": "r340:5d3d4d2c262e", "type": "sha", "id": "5d3d4d2c262e17b247d405feceeb09ff7408c940"}, {"text": "r334:4d4278a6390e", "type": "sha", "id": "4d4278a6390e42f4fc777ecf1b9b628e77da8e22"}, {"text": "r298:00dffb625166", "type": "sha", "id": "00dffb62516650bc5050d818eb47ea1ca207954d"}, {"text": "r297:47b6be9a812e", "type": "sha", "id": "47b6be9a812ec3ed0384001a458a759f0f583fe2"}, {"text": "r289:1589fed841cd", "type": "sha", "id": "1589fed841cd9ef33155f8560727809ac3ada2c8"}, {"text": "r285:afafd0ee2821", "type": "sha", "id": "afafd0ee28218ab979678213cb96e9e4dbd7359b"}, {"text": "r284:639b115ed2b0", "type": "sha", "id": "639b115ed2b02017824005b5ae66282c6e25eba8"}, {"text": "r283:fcf7562d7305", "type": "sha", "id": "fcf7562d7305affc94fe20dc89a34aefd2b8aa1e"}, {"text": "r256:ec8cbdb5f364", "type": "sha", "id": "ec8cbdb5f364fce7843cbf148c3d95d86f935339"}, {"text": "r255:0d74d2e2bdf3", "type": "sha", "id": "0d74d2e2bdf3dcd5ee9fe4fcfe9016c5c6486f35"}, {"text": "r243:6894ad7d8223", "type": "sha", "id": "6894ad7d8223b1e6853e9fdaa2c38d3f0cef1e38"}, {"text": "r231:31b3f4b599fa", "type": "sha", "id": "31b3f4b599fae5f12cf438c73403679cdf923d75"}, {"text": "r220:3d2515dd21fb", "type": "sha", "id": "3d2515dd21fb34fe6c5d0029075a863f3e92f5f6"}, {"text": "r186:f804e27aa496", "type": "sha", "id": "f804e27aa4961f2e327f2a10ee373235df20ee21"}, {"text": "r182:7f00513785a1", "type": "sha", "id": "7f00513785a13f273a4387ef086bb795b37f013c"}, {"text": "r181:6efcdc61028c", "type": "sha", "id": "6efcdc61028c8edd1c787b3439fae71b77a17357"}, {"text": "r175:6c0ce52b229a", "type": "sha", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "r165:09788a0b8a54", "type": "sha", "id": "09788a0b8a5455e9678c3959214246574e546d4f"}, {"text": "r163:0164ee729def", "type": "sha", "id": "0164ee729def0a253d6dcb594b5ee2a52fef4748"}, {"text": "r140:33fa32233551", "type": "sha", "id": "33fa3223355104431402a888fa77a4e9956feb3e"}, {"text": "r126:fa014c12c26d", "type": "sha", "id": "fa014c12c26d10ba682fadb78f2a11c24c8118e1"}, {"text": "r111:e686b958768e", "type": "sha", "id": "e686b958768ee96af8029fe19c6050b1a8dd3b2b"}, {"text": "r109:ab5721ca0a08", "type": "sha", "id": "ab5721ca0a081f26bf43d9051e615af2cc99952f"}, {"text": "r108:c877b68d18e7", "type": "sha", "id": "c877b68d18e792a66b7f4c529ea02c8f80801542"}, {"text": "r107:4313566d2e41", "type": "sha", "id": "4313566d2e417cb382948f8d9d7c765330356054"}, {"text": "r104:6c2303a79367", "type": "sha", "id": "6c2303a793671e807d1cfc70134c9ca0767d98c2"}, {"text": "r102:54386793436c", "type": "sha", "id": "54386793436c938cff89326944d4c2702340037d"}, {"text": "r101:54000345d2e7", "type": "sha", "id": "54000345d2e78b03a99d561399e8e548de3f3203"}, {"text": "r99:1c6b3677b37e", "type": "sha", "id": "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b"}, {"text": "r93:2d03ca750a44", "type": "sha", "id": "2d03ca750a44440fb5ea8b751176d1f36f8e8f46"}, {"text": "r92:2a08b128c206", "type": "sha", "id": "2a08b128c206db48c2f0b8f70df060e6db0ae4f8"}, {"text": "r91:30c26513ff1e", "type": "sha", "id": "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b"}, {"text": "r82:ac71e9503c2c", "type": "sha", "id": "ac71e9503c2ca95542839af0ce7b64011b72ea7c"}, {"text": "r81:12669288fd13", "type": "sha", "id": "12669288fd13adba2a9b7dd5b870cc23ffab92d2"}, {"text": "r76:5a0c84f3e6fe", "type": "sha", "id": "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382"}, {"text": "r73:12f2f5e2b38e", "type": "sha", "id": "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5"}, {"text": "r61:5eab1222a7cd", "type": "sha", "id": "5eab1222a7cd4bfcbabc218ca6d04276d4e27378"}, {"text": "r60:f50f42baeed5", "type": "sha", "id": "f50f42baeed5af6518ef4b0cb2f1423f3851a941"}, {"text": "r59:d7e390a45f6a", "type": "sha", "id": "d7e390a45f6aa96f04f5e7f583ad4f867431aa25"}, {"text": "r58:f15c21f97864", "type": "sha", "id": "f15c21f97864b4f071cddfbf2750ec2e23859414"}, {"text": "r57:e906ef056cf5", "type": "sha", "id": "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade"}, {"text": "r56:ea2b108b48aa", "type": "sha", "id": "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b"}, {"text": "r50:84dec09632a4", "type": "sha", "id": "84dec09632a4458f79f50ddbbd155506c460b4f9"}, {"text": "r48:0115510b70c7", "type": "sha", "id": "0115510b70c7229dbc5dc49036b32e7d91d23acd"}, {"text": "r46:2a13f185e452", "type": "sha", "id": "2a13f185e4525f9d4b59882791a2d397b90d5ddc"}, {"text": "r30:3bf1c5868e57", "type": "sha", "id": "3bf1c5868e570e39569d094f922d33ced2fa3b2b"}, {"text": "r26:b8d040125747", "type": "sha", "id": "b8d04012574729d2c29886e53b1a43ef16dd00a1"}, {"text": "r24:6970b057cffe", "type": "sha", "id": "6970b057cffe4aab0a792aa634c89f4bebf01441"}, {"text": "r8:dd80b0f6cf50", "type": "sha", "id": "dd80b0f6cf5052f17cc738c2951c4f2070200d7f"}, {"text": "r7:ff7ca51e58c5", "type": "sha", "id": "ff7ca51e58c505fec0dd2491de52c622bb7a806b"}]}, {"text": "Branches", "children": [{"text": "master", "type": "branch", "id": "fd627b9e0dd80b47be81af07c4a98518244ed2f7"}]}, {"text": "Tags", "children": [{"text": "v0.2.2", "type": "tag", "id": "137fea89f304a42321d40488091ee2ed419a3686"}, {"text": "v0.2.1", "type": "tag", "id": "5051d0fa344d4408a2659d9a0348eb2d41868ecf"}, {"text": "v0.2.0", "type": "tag", "id": "599ba911aa24d2981225f3966eb659dfae9e9f30"}, {"text": "v0.1.9", "type": "tag", "id": "341d28f0eec5ddf0b6b77871e13c2bbd6bec685c"}, {"text": "v0.1.8", "type": "tag", "id": "74ebce002c088b8a5ecf40073db09375515ecd68"}, {"text": "v0.1.7", "type": "tag", "id": "4d78bf73b5c22c82b68f902f138f7881b4fffa2c"}, {"text": "v0.1.6", "type": "tag", "id": "0205cb3f44223fb3099d12a77a69c81b798772d9"}, {"text": "v0.1.5", "type": "tag", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "v0.1.4", "type": "tag", "id": "7d735150934cd7645ac3051903add952390324a5"}, {"text": "v0.1.3", "type": "tag", "id": "5a3a8fb005554692b16e21dee62bf02667d8dc3e"}, {"text": "v0.1.2", "type": "tag", "id": "0ba5f8a4660034ff25c0cac2a5baabf5d2791d63"}, {"text": "v0.1.11", "type": "tag", "id": "c60f01b77c42dce653d6b1d3b04689862c261929"}, {"text": "v0.1.10", "type": "tag", "id": "10cddef6b794696066fb346434014f0a56810218"}, {"text": "v0.1.1", "type": "tag", "id": "e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0"}]}], "more": false}
1 {"results": [{"text": "Changesets", "children": [{"text": "r625:ead8f28a4bc2", "at_rev": "", "type": "sha", "id": "ead8f28a4bc2f45ecfb148a6b8a89758b9654a84"}, {"text": "r535:c093f94d6d35", "at_rev": "", "type": "sha", "id": "c093f94d6d358f13c55a687da66c30c41cca4153"}, {"text": "r534:559f640ec08b", "at_rev": "", "type": "sha", "id": "559f640ec08b2a14c4a9ac863d8ca273545b8885"}, {"text": "r490:02a940b4ee37", "at_rev": "", "type": "sha", "id": "02a940b4ee371ec64ef5b4c4870a5c89dc7fb98a"}, {"text": "r464:b45a4153a2d7", "at_rev": "", "type": "sha", "id": "b45a4153a2d7adb8a78b63d35d39fac44a4320a6"}, {"text": "r460:0a54e66b9450", "at_rev": "", "type": "sha", "id": "0a54e66b94502409074b163cd93c1233dcc0413f"}, {"text": "r457:a7bf2f6bf3d5", "at_rev": "", "type": "sha", "id": "a7bf2f6bf3d5273da4bcd2032a891acae5a45e2b"}, {"text": "r456:7266de0154b4", "at_rev": "", "type": "sha", "id": "7266de0154b4da7c42ba3d788876056dbf116b5a"}, {"text": "r455:666de4ee6507", "at_rev": "", "type": "sha", "id": "666de4ee65074cd3e37ea01e75f65bd3e4c336bb"}, {"text": "r453:91acc599141c", "at_rev": "", "type": "sha", "id": "91acc599141c87f03e0e3551dcaacf4492632e58"}, {"text": "r442:40a2d5d71b75", "at_rev": "", "type": "sha", "id": "40a2d5d71b758e7eafc84a324ed55142cba22f42"}, {"text": "r440:d1f898326327", "at_rev": "", "type": "sha", "id": "d1f898326327e20524fe22417c22d71064fe54a1"}, {"text": "r420:162a36830c23", "at_rev": "", "type": "sha", "id": "162a36830c23ccf1bf1873157fd0c8d0dfc7c817"}, {"text": "r345:c994f0de03b2", "at_rev": "", "type": "sha", "id": "c994f0de03b2a0aa848a04fc2c0d7e737dba31fc"}, {"text": "r340:5d3d4d2c262e", "at_rev": "", "type": "sha", "id": "5d3d4d2c262e17b247d405feceeb09ff7408c940"}, {"text": "r334:4d4278a6390e", "at_rev": "", "type": "sha", "id": "4d4278a6390e42f4fc777ecf1b9b628e77da8e22"}, {"text": "r298:00dffb625166", "at_rev": "", "type": "sha", "id": "00dffb62516650bc5050d818eb47ea1ca207954d"}, {"text": "r297:47b6be9a812e", "at_rev": "", "type": "sha", "id": "47b6be9a812ec3ed0384001a458a759f0f583fe2"}, {"text": "r289:1589fed841cd", "at_rev": "", "type": "sha", "id": "1589fed841cd9ef33155f8560727809ac3ada2c8"}, {"text": "r285:afafd0ee2821", "at_rev": "", "type": "sha", "id": "afafd0ee28218ab979678213cb96e9e4dbd7359b"}, {"text": "r284:639b115ed2b0", "at_rev": "", "type": "sha", "id": "639b115ed2b02017824005b5ae66282c6e25eba8"}, {"text": "r283:fcf7562d7305", "at_rev": "", "type": "sha", "id": "fcf7562d7305affc94fe20dc89a34aefd2b8aa1e"}, {"text": "r256:ec8cbdb5f364", "at_rev": "", "type": "sha", "id": "ec8cbdb5f364fce7843cbf148c3d95d86f935339"}, {"text": "r255:0d74d2e2bdf3", "at_rev": "", "type": "sha", "id": "0d74d2e2bdf3dcd5ee9fe4fcfe9016c5c6486f35"}, {"text": "r243:6894ad7d8223", "at_rev": "", "type": "sha", "id": "6894ad7d8223b1e6853e9fdaa2c38d3f0cef1e38"}, {"text": "r231:31b3f4b599fa", "at_rev": "", "type": "sha", "id": "31b3f4b599fae5f12cf438c73403679cdf923d75"}, {"text": "r220:3d2515dd21fb", "at_rev": "", "type": "sha", "id": "3d2515dd21fb34fe6c5d0029075a863f3e92f5f6"}, {"text": "r186:f804e27aa496", "at_rev": "", "type": "sha", "id": "f804e27aa4961f2e327f2a10ee373235df20ee21"}, {"text": "r182:7f00513785a1", "at_rev": "", "type": "sha", "id": "7f00513785a13f273a4387ef086bb795b37f013c"}, {"text": "r181:6efcdc61028c", "at_rev": "", "type": "sha", "id": "6efcdc61028c8edd1c787b3439fae71b77a17357"}, {"text": "r175:6c0ce52b229a", "at_rev": "", "type": "sha", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "r165:09788a0b8a54", "at_rev": "", "type": "sha", "id": "09788a0b8a5455e9678c3959214246574e546d4f"}, {"text": "r163:0164ee729def", "at_rev": "", "type": "sha", "id": "0164ee729def0a253d6dcb594b5ee2a52fef4748"}, {"text": "r140:33fa32233551", "at_rev": "", "type": "sha", "id": "33fa3223355104431402a888fa77a4e9956feb3e"}, {"text": "r126:fa014c12c26d", "at_rev": "", "type": "sha", "id": "fa014c12c26d10ba682fadb78f2a11c24c8118e1"}, {"text": "r111:e686b958768e", "at_rev": "", "type": "sha", "id": "e686b958768ee96af8029fe19c6050b1a8dd3b2b"}, {"text": "r109:ab5721ca0a08", "at_rev": "", "type": "sha", "id": "ab5721ca0a081f26bf43d9051e615af2cc99952f"}, {"text": "r108:c877b68d18e7", "at_rev": "", "type": "sha", "id": "c877b68d18e792a66b7f4c529ea02c8f80801542"}, {"text": "r107:4313566d2e41", "at_rev": "", "type": "sha", "id": "4313566d2e417cb382948f8d9d7c765330356054"}, {"text": "r104:6c2303a79367", "at_rev": "", "type": "sha", "id": "6c2303a793671e807d1cfc70134c9ca0767d98c2"}, {"text": "r102:54386793436c", "at_rev": "", "type": "sha", "id": "54386793436c938cff89326944d4c2702340037d"}, {"text": "r101:54000345d2e7", "at_rev": "", "type": "sha", "id": "54000345d2e78b03a99d561399e8e548de3f3203"}, {"text": "r99:1c6b3677b37e", "at_rev": "", "type": "sha", "id": "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b"}, {"text": "r93:2d03ca750a44", "at_rev": "", "type": "sha", "id": "2d03ca750a44440fb5ea8b751176d1f36f8e8f46"}, {"text": "r92:2a08b128c206", "at_rev": "", "type": "sha", "id": "2a08b128c206db48c2f0b8f70df060e6db0ae4f8"}, {"text": "r91:30c26513ff1e", "at_rev": "", "type": "sha", "id": "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b"}, {"text": "r82:ac71e9503c2c", "at_rev": "", "type": "sha", "id": "ac71e9503c2ca95542839af0ce7b64011b72ea7c"}, {"text": "r81:12669288fd13", "at_rev": "", "type": "sha", "id": "12669288fd13adba2a9b7dd5b870cc23ffab92d2"}, {"text": "r76:5a0c84f3e6fe", "at_rev": "", "type": "sha", "id": "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382"}, {"text": "r73:12f2f5e2b38e", "at_rev": "", "type": "sha", "id": "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5"}, {"text": "r61:5eab1222a7cd", "at_rev": "", "type": "sha", "id": "5eab1222a7cd4bfcbabc218ca6d04276d4e27378"}, {"text": "r60:f50f42baeed5", "at_rev": "", "type": "sha", "id": "f50f42baeed5af6518ef4b0cb2f1423f3851a941"}, {"text": "r59:d7e390a45f6a", "at_rev": "", "type": "sha", "id": "d7e390a45f6aa96f04f5e7f583ad4f867431aa25"}, {"text": "r58:f15c21f97864", "at_rev": "", "type": "sha", "id": "f15c21f97864b4f071cddfbf2750ec2e23859414"}, {"text": "r57:e906ef056cf5", "at_rev": "", "type": "sha", "id": "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade"}, {"text": "r56:ea2b108b48aa", "at_rev": "", "type": "sha", "id": "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b"}, {"text": "r50:84dec09632a4", "at_rev": "", "type": "sha", "id": "84dec09632a4458f79f50ddbbd155506c460b4f9"}, {"text": "r48:0115510b70c7", "at_rev": "", "type": "sha", "id": "0115510b70c7229dbc5dc49036b32e7d91d23acd"}, {"text": "r46:2a13f185e452", "at_rev": "", "type": "sha", "id": "2a13f185e4525f9d4b59882791a2d397b90d5ddc"}, {"text": "r30:3bf1c5868e57", "at_rev": "", "type": "sha", "id": "3bf1c5868e570e39569d094f922d33ced2fa3b2b"}, {"text": "r26:b8d040125747", "at_rev": "", "type": "sha", "id": "b8d04012574729d2c29886e53b1a43ef16dd00a1"}, {"text": "r24:6970b057cffe", "at_rev": "", "type": "sha", "id": "6970b057cffe4aab0a792aa634c89f4bebf01441"}, {"text": "r8:dd80b0f6cf50", "at_rev": "", "type": "sha", "id": "dd80b0f6cf5052f17cc738c2951c4f2070200d7f"}, {"text": "r7:ff7ca51e58c5", "at_rev": "", "type": "sha", "id": "ff7ca51e58c505fec0dd2491de52c622bb7a806b"}]}, {"text": "Branches", "children": [{"text": "master", "at_rev": "master", "type": "branch", "id": "fd627b9e0dd80b47be81af07c4a98518244ed2f7"}]}, {"text": "Tags", "children": [{"text": "v0.2.2", "at_rev": "v0.2.2", "type": "tag", "id": "137fea89f304a42321d40488091ee2ed419a3686"}, {"text": "v0.2.1", "at_rev": "v0.2.1", "type": "tag", "id": "5051d0fa344d4408a2659d9a0348eb2d41868ecf"}, {"text": "v0.2.0", "at_rev": "v0.2.0", "type": "tag", "id": "599ba911aa24d2981225f3966eb659dfae9e9f30"}, {"text": "v0.1.9", "at_rev": "v0.1.9", "type": "tag", "id": "341d28f0eec5ddf0b6b77871e13c2bbd6bec685c"}, {"text": "v0.1.8", "at_rev": "v0.1.8", "type": "tag", "id": "74ebce002c088b8a5ecf40073db09375515ecd68"}, {"text": "v0.1.7", "at_rev": "v0.1.7", "type": "tag", "id": "4d78bf73b5c22c82b68f902f138f7881b4fffa2c"}, {"text": "v0.1.6", "at_rev": "v0.1.6", "type": "tag", "id": "0205cb3f44223fb3099d12a77a69c81b798772d9"}, {"text": "v0.1.5", "at_rev": "v0.1.5", "type": "tag", "id": "6c0ce52b229aa978889e91b38777f800e85f330b"}, {"text": "v0.1.4", "at_rev": "v0.1.4", "type": "tag", "id": "7d735150934cd7645ac3051903add952390324a5"}, {"text": "v0.1.3", "at_rev": "v0.1.3", "type": "tag", "id": "5a3a8fb005554692b16e21dee62bf02667d8dc3e"}, {"text": "v0.1.2", "at_rev": "v0.1.2", "type": "tag", "id": "0ba5f8a4660034ff25c0cac2a5baabf5d2791d63"}, {"text": "v0.1.11", "at_rev": "v0.1.11", "type": "tag", "id": "c60f01b77c42dce653d6b1d3b04689862c261929"}, {"text": "v0.1.10", "at_rev": "v0.1.10", "type": "tag", "id": "10cddef6b794696066fb346434014f0a56810218"}, {"text": "v0.1.1", "at_rev": "v0.1.1", "type": "tag", "id": "e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0"}]}], "more": false}
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r648:dbec37a0d5ca (default)", "type": "sha", "id": "dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6"}, {"text": "r639:1d20ed9eda94 (default)", "type": "sha", "id": "1d20ed9eda9482d46ff0a6af5812550218b3ff15"}, {"text": "r547:0173395e8227 (default)", "type": "sha", "id": "0173395e822797f098799ed95c1a81b6a547a9ad"}, {"text": "r546:afbb45ade933 (default)", "type": "sha", "id": "afbb45ade933a8182f1d8ec5d4d1bb2de2572043"}, {"text": "r502:6f093e30cac3 (default)", "type": "sha", "id": "6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7"}, {"text": "r476:c7e2212dd2ae (default)", "type": "sha", "id": "c7e2212dd2ae975d1d06534a3d7e317165c06960"}, {"text": "r472:45477506df79 (default)", "type": "sha", "id": "45477506df79f701bf69419aac3e1f0fed3c5bcf"}, {"text": "r469:5fc76cb25d11 (default)", "type": "sha", "id": "5fc76cb25d11e07c60de040f78b8cd265ff10d53"}, {"text": "r468:b073433cf899 (default)", "type": "sha", "id": "b073433cf8994969ee5cd7cce84cbe587bb880b2"}, {"text": "r467:7a74dbfcacd1 (default)", "type": "sha", "id": "7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96"}, {"text": "r465:71ee52cc4d62 (default)", "type": "sha", "id": "71ee52cc4d629096bdbee036325975dac2af4501"}, {"text": "r452:a5b217d26c5f (default)", "type": "sha", "id": "a5b217d26c5f111e72bae4de672b084ee0fbf75c"}, {"text": "r450:47aedd538bf6 (default)", "type": "sha", "id": "47aedd538bf616eedcb0e7d630ea476df0e159c7"}, {"text": "r432:8e4915fa32d7 (default)", "type": "sha", "id": "8e4915fa32d727dcbf09746f637a5f82e539511e"}, {"text": "r356:25213a5fbb04 (default)", "type": "sha", "id": "25213a5fbb048dff8ba65d21e466a835536e5b70"}, {"text": "r351:23debcedddc1 (default)", "type": "sha", "id": "23debcedddc1c23c14be33e713e7786d4a9de471"}, {"text": "r342:61e25b2a90a1 (default)", "type": "sha", "id": "61e25b2a90a19e7fffd75dea1e4c7e20df526bbe"}, {"text": "r318:fb95b340e0d0 (webvcs)", "type": "sha", "id": "fb95b340e0d03fa51f33c56c991c08077c99303e"}, {"text": "r303:bda35e0e564f (default)", "type": "sha", "id": "bda35e0e564fbbc5cd26fe0a37fb647a254c99fe"}, {"text": "r302:97ff74896d7d (default)", "type": "sha", "id": "97ff74896d7dbf3115a337a421d44b55154acc89"}, {"text": "r293:cec3473c3fdb (default)", "type": "sha", "id": "cec3473c3fdb9599c98067182a075b49bde570f9"}, {"text": "r289:0e86c43eef86 (default)", "type": "sha", "id": "0e86c43eef866a013a587666a877c879899599bb"}, {"text": "r288:91a27c312808 (default)", "type": "sha", "id": "91a27c312808100cf20a602f78befbbff9d89bfd"}, {"text": "r287:400e36a1670a (default)", "type": "sha", "id": "400e36a1670a57d11e3edcb5b07bf82c30006d0b"}, {"text": "r261:014fb17dfc95 (default)", "type": "sha", "id": "014fb17dfc95b0995e838c565376bf9a993e230a"}, {"text": "r260:cca7aebbc4d6 (default)", "type": "sha", "id": "cca7aebbc4d6125798446b11e69dc8847834a982"}, {"text": "r258:14cdb2957c01 (workdir)", "type": "sha", "id": "14cdb2957c011a5feba36f50d960d9832ba0f0c1"}, {"text": "r245:34df20118ed7 (default)", "type": "sha", "id": "34df20118ed74b5987d22a579e8a60e903da5bf8"}, {"text": "r233:0375d9042a64 (workdir)", "type": "sha", "id": "0375d9042a64a1ac1641528f0f0668f9a339e86d"}, {"text": "r222:94aa45fc1806 (workdir)", "type": "sha", "id": "94aa45fc1806c04d4ba640933edf682c22478453"}, {"text": "r188:7ed99bc73881 (default)", "type": "sha", "id": "7ed99bc738818879941e3ce20243f8856a7cfc84"}, {"text": "r184:1e85975528bc (default)", "type": "sha", "id": "1e85975528bcebe853732a9e5fb8dbf4461f6bb2"}, {"text": "r183:ed30beddde7b (default)", "type": "sha", "id": "ed30beddde7bbddb26042625be19bcd11576c1dd"}, {"text": "r177:a6664e18181c (default)", "type": "sha", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "r167:8911406ad776 (default)", "type": "sha", "id": "8911406ad776fdd3d0b9932a2e89677e57405a48"}, {"text": "r165:aa957ed78c35 (default)", "type": "sha", "id": "aa957ed78c35a1541f508d2ec90e501b0a9e3167"}, {"text": "r140:48e11b73e94c (default)", "type": "sha", "id": "48e11b73e94c0db33e736eaeea692f990cb0b5f1"}, {"text": "r126:adf3cbf48329 (default)", "type": "sha", "id": "adf3cbf483298563b968a6c673cd5bde5f7d5eea"}, {"text": "r113:6249fd0fb2cf (git)", "type": "sha", "id": "6249fd0fb2cfb1411e764129f598e2cf0de79a6f"}, {"text": "r109:75feb4c33e81 (default)", "type": "sha", "id": "75feb4c33e81186c87eac740cee2447330288412"}, {"text": "r108:9a4dc232ecdc (default)", "type": "sha", "id": "9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d"}, {"text": "r107:595cce4efa21 (default)", "type": "sha", "id": "595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d"}, {"text": "r104:4a8bd421fbc2 (default)", "type": "sha", "id": "4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da"}, {"text": "r102:57be63fc8f85 (default)", "type": "sha", "id": "57be63fc8f85e65a0106a53187f7316f8c487ffa"}, {"text": "r101:5530bd87f7e2 (git)", "type": "sha", "id": "5530bd87f7e2e124a64d07cb2654c997682128be"}, {"text": "r99:e516008b1c93 (default)", "type": "sha", "id": "e516008b1c93f142263dc4b7961787cbad654ce1"}, {"text": "r93:41f43fc74b8b (default)", "type": "sha", "id": "41f43fc74b8b285984554532eb105ac3be5c434f"}, {"text": "r92:cc66b61b8455 (default)", "type": "sha", "id": "cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e"}, {"text": "r91:73ab5b616b32 (default)", "type": "sha", "id": "73ab5b616b3271b0518682fb4988ce421de8099f"}, {"text": "r82:e0da75f308c0 (default)", "type": "sha", "id": "e0da75f308c0f18f98e9ce6257626009fdda2b39"}, {"text": "r81:fb2e41e0f081 (default)", "type": "sha", "id": "fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611"}, {"text": "r76:602ae2f5e7ad (default)", "type": "sha", "id": "602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028"}, {"text": "r73:a066b25d5df7 (default)", "type": "sha", "id": "a066b25d5df7016b45a41b7e2a78c33b57adc235"}, {"text": "r61:637a933c9059 (web)", "type": "sha", "id": "637a933c905958ce5151f154147c25c1c7b68832"}, {"text": "r60:0c21004effeb (web)", "type": "sha", "id": "0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc"}, {"text": "r59:a1f39c56d3f1 (web)", "type": "sha", "id": "a1f39c56d3f1d52d5fb5920370a2a2716cd9a444"}, {"text": "r58:97d32df05c71 (web)", "type": "sha", "id": "97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f"}, {"text": "r57:08eaf1451771 (web)", "type": "sha", "id": "08eaf14517718dccea4b67755a93368341aca919"}, {"text": "r56:22f71ad26526 (web)", "type": "sha", "id": "22f71ad265265a53238359c883aa976e725aa07d"}, {"text": "r49:97501f02b7b4 (web)", "type": "sha", "id": "97501f02b7b4330924b647755663a2d90a5e638d"}, {"text": "r47:86ede6754f2b (web)", "type": "sha", "id": "86ede6754f2b27309452bb11f997386ae01d0e5a"}, {"text": "r45:014c40c0203c (web)", "type": "sha", "id": "014c40c0203c423dc19ecf94644f7cac9d4cdce0"}, {"text": "r30:ee87846a61c1 (default)", "type": "sha", "id": "ee87846a61c12153b51543bf860e1026c6d3dcba"}, {"text": "r26:9bb326a04ae5 (default)", "type": "sha", "id": "9bb326a04ae5d98d437dece54be04f830cf1edd9"}, {"text": "r24:536c1a194283 (default)", "type": "sha", "id": "536c1a19428381cfea92ac44985304f6a8049569"}, {"text": "r8:dc5d2c0661b6 (default)", "type": "sha", "id": "dc5d2c0661b61928834a785d3e64a3f80d3aad9c"}, {"text": "r7:3803844fdbd3 (default)", "type": "sha", "id": "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"}]}, {"text": "Branches", "children": [{"text": "default", "type": "branch", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}, {"text": "stable", "type": "branch", "id": "4f7e2131323e0749a740c0a56ab68ae9269c562a"}]}, {"text": "Tags", "children": [{"text": "v0.2.0", "type": "tag", "id": "2c96c02def9a7c997f33047761a53943e6254396"}, {"text": "v0.1.9", "type": "tag", "id": "8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9"}, {"text": "v0.1.8", "type": "tag", "id": "ecb25ba9c96faf1e65a0bc3fd914918420a2f116"}, {"text": "v0.1.7", "type": "tag", "id": "f67633a2894edaf28513706d558205fa93df9209"}, {"text": "v0.1.6", "type": "tag", "id": "02b38c0eb6f982174750c0e309ff9faddc0c7e12"}, {"text": "v0.1.5", "type": "tag", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "v0.1.4", "type": "tag", "id": "fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200"}, {"text": "v0.1.3", "type": "tag", "id": "17544fbfcd33ffb439e2b728b5d526b1ef30bfcf"}, {"text": "v0.1.2", "type": "tag", "id": "a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720"}, {"text": "v0.1.11", "type": "tag", "id": "fef5bfe1dc17611d5fb59a7f6f95c55c3606f933"}, {"text": "v0.1.10", "type": "tag", "id": "92831aebf2f8dd4879e897024b89d09af214df1c"}, {"text": "v0.1.1", "type": "tag", "id": "eb3a60fc964309c1a318b8dfe26aa2d1586c85ae"}, {"text": "tip", "type": "tag", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}]}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r648:dbec37a0d5ca (default)", "at_rev": "", "type": "sha", "id": "dbec37a0d5cab8ff39af4cfc4a4cd3996e4acfc6"}, {"text": "r639:1d20ed9eda94 (default)", "at_rev": "", "type": "sha", "id": "1d20ed9eda9482d46ff0a6af5812550218b3ff15"}, {"text": "r547:0173395e8227 (default)", "at_rev": "", "type": "sha", "id": "0173395e822797f098799ed95c1a81b6a547a9ad"}, {"text": "r546:afbb45ade933 (default)", "at_rev": "", "type": "sha", "id": "afbb45ade933a8182f1d8ec5d4d1bb2de2572043"}, {"text": "r502:6f093e30cac3 (default)", "at_rev": "", "type": "sha", "id": "6f093e30cac34e6b4b11275a9f22f80c5d7ad1f7"}, {"text": "r476:c7e2212dd2ae (default)", "at_rev": "", "type": "sha", "id": "c7e2212dd2ae975d1d06534a3d7e317165c06960"}, {"text": "r472:45477506df79 (default)", "at_rev": "", "type": "sha", "id": "45477506df79f701bf69419aac3e1f0fed3c5bcf"}, {"text": "r469:5fc76cb25d11 (default)", "at_rev": "", "type": "sha", "id": "5fc76cb25d11e07c60de040f78b8cd265ff10d53"}, {"text": "r468:b073433cf899 (default)", "at_rev": "", "type": "sha", "id": "b073433cf8994969ee5cd7cce84cbe587bb880b2"}, {"text": "r467:7a74dbfcacd1 (default)", "at_rev": "", "type": "sha", "id": "7a74dbfcacd1dbcb58bb9c860b2f29fbb22c4c96"}, {"text": "r465:71ee52cc4d62 (default)", "at_rev": "", "type": "sha", "id": "71ee52cc4d629096bdbee036325975dac2af4501"}, {"text": "r452:a5b217d26c5f (default)", "at_rev": "", "type": "sha", "id": "a5b217d26c5f111e72bae4de672b084ee0fbf75c"}, {"text": "r450:47aedd538bf6 (default)", "at_rev": "", "type": "sha", "id": "47aedd538bf616eedcb0e7d630ea476df0e159c7"}, {"text": "r432:8e4915fa32d7 (default)", "at_rev": "", "type": "sha", "id": "8e4915fa32d727dcbf09746f637a5f82e539511e"}, {"text": "r356:25213a5fbb04 (default)", "at_rev": "", "type": "sha", "id": "25213a5fbb048dff8ba65d21e466a835536e5b70"}, {"text": "r351:23debcedddc1 (default)", "at_rev": "", "type": "sha", "id": "23debcedddc1c23c14be33e713e7786d4a9de471"}, {"text": "r342:61e25b2a90a1 (default)", "at_rev": "", "type": "sha", "id": "61e25b2a90a19e7fffd75dea1e4c7e20df526bbe"}, {"text": "r318:fb95b340e0d0 (webvcs)", "at_rev": "", "type": "sha", "id": "fb95b340e0d03fa51f33c56c991c08077c99303e"}, {"text": "r303:bda35e0e564f (default)", "at_rev": "", "type": "sha", "id": "bda35e0e564fbbc5cd26fe0a37fb647a254c99fe"}, {"text": "r302:97ff74896d7d (default)", "at_rev": "", "type": "sha", "id": "97ff74896d7dbf3115a337a421d44b55154acc89"}, {"text": "r293:cec3473c3fdb (default)", "at_rev": "", "type": "sha", "id": "cec3473c3fdb9599c98067182a075b49bde570f9"}, {"text": "r289:0e86c43eef86 (default)", "at_rev": "", "type": "sha", "id": "0e86c43eef866a013a587666a877c879899599bb"}, {"text": "r288:91a27c312808 (default)", "at_rev": "", "type": "sha", "id": "91a27c312808100cf20a602f78befbbff9d89bfd"}, {"text": "r287:400e36a1670a (default)", "at_rev": "", "type": "sha", "id": "400e36a1670a57d11e3edcb5b07bf82c30006d0b"}, {"text": "r261:014fb17dfc95 (default)", "at_rev": "", "type": "sha", "id": "014fb17dfc95b0995e838c565376bf9a993e230a"}, {"text": "r260:cca7aebbc4d6 (default)", "at_rev": "", "type": "sha", "id": "cca7aebbc4d6125798446b11e69dc8847834a982"}, {"text": "r258:14cdb2957c01 (workdir)", "at_rev": "", "type": "sha", "id": "14cdb2957c011a5feba36f50d960d9832ba0f0c1"}, {"text": "r245:34df20118ed7 (default)", "at_rev": "", "type": "sha", "id": "34df20118ed74b5987d22a579e8a60e903da5bf8"}, {"text": "r233:0375d9042a64 (workdir)", "at_rev": "", "type": "sha", "id": "0375d9042a64a1ac1641528f0f0668f9a339e86d"}, {"text": "r222:94aa45fc1806 (workdir)", "at_rev": "", "type": "sha", "id": "94aa45fc1806c04d4ba640933edf682c22478453"}, {"text": "r188:7ed99bc73881 (default)", "at_rev": "", "type": "sha", "id": "7ed99bc738818879941e3ce20243f8856a7cfc84"}, {"text": "r184:1e85975528bc (default)", "at_rev": "", "type": "sha", "id": "1e85975528bcebe853732a9e5fb8dbf4461f6bb2"}, {"text": "r183:ed30beddde7b (default)", "at_rev": "", "type": "sha", "id": "ed30beddde7bbddb26042625be19bcd11576c1dd"}, {"text": "r177:a6664e18181c (default)", "at_rev": "", "type": "sha", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "r167:8911406ad776 (default)", "at_rev": "", "type": "sha", "id": "8911406ad776fdd3d0b9932a2e89677e57405a48"}, {"text": "r165:aa957ed78c35 (default)", "at_rev": "", "type": "sha", "id": "aa957ed78c35a1541f508d2ec90e501b0a9e3167"}, {"text": "r140:48e11b73e94c (default)", "at_rev": "", "type": "sha", "id": "48e11b73e94c0db33e736eaeea692f990cb0b5f1"}, {"text": "r126:adf3cbf48329 (default)", "at_rev": "", "type": "sha", "id": "adf3cbf483298563b968a6c673cd5bde5f7d5eea"}, {"text": "r113:6249fd0fb2cf (git)", "at_rev": "", "type": "sha", "id": "6249fd0fb2cfb1411e764129f598e2cf0de79a6f"}, {"text": "r109:75feb4c33e81 (default)", "at_rev": "", "type": "sha", "id": "75feb4c33e81186c87eac740cee2447330288412"}, {"text": "r108:9a4dc232ecdc (default)", "at_rev": "", "type": "sha", "id": "9a4dc232ecdc763ef2e98ae2238cfcbba4f6ad8d"}, {"text": "r107:595cce4efa21 (default)", "at_rev": "", "type": "sha", "id": "595cce4efa21fda2f2e4eeb4fe5f2a6befe6fa2d"}, {"text": "r104:4a8bd421fbc2 (default)", "at_rev": "", "type": "sha", "id": "4a8bd421fbc2dfbfb70d85a3fe064075ab2c49da"}, {"text": "r102:57be63fc8f85 (default)", "at_rev": "", "type": "sha", "id": "57be63fc8f85e65a0106a53187f7316f8c487ffa"}, {"text": "r101:5530bd87f7e2 (git)", "at_rev": "", "type": "sha", "id": "5530bd87f7e2e124a64d07cb2654c997682128be"}, {"text": "r99:e516008b1c93 (default)", "at_rev": "", "type": "sha", "id": "e516008b1c93f142263dc4b7961787cbad654ce1"}, {"text": "r93:41f43fc74b8b (default)", "at_rev": "", "type": "sha", "id": "41f43fc74b8b285984554532eb105ac3be5c434f"}, {"text": "r92:cc66b61b8455 (default)", "at_rev": "", "type": "sha", "id": "cc66b61b8455b264a7a8a2d8ddc80fcfc58c221e"}, {"text": "r91:73ab5b616b32 (default)", "at_rev": "", "type": "sha", "id": "73ab5b616b3271b0518682fb4988ce421de8099f"}, {"text": "r82:e0da75f308c0 (default)", "at_rev": "", "type": "sha", "id": "e0da75f308c0f18f98e9ce6257626009fdda2b39"}, {"text": "r81:fb2e41e0f081 (default)", "at_rev": "", "type": "sha", "id": "fb2e41e0f0810be4d7103bc2a4c7be16ee3ec611"}, {"text": "r76:602ae2f5e7ad (default)", "at_rev": "", "type": "sha", "id": "602ae2f5e7ade70b3b66a58cdd9e3e613dc8a028"}, {"text": "r73:a066b25d5df7 (default)", "at_rev": "", "type": "sha", "id": "a066b25d5df7016b45a41b7e2a78c33b57adc235"}, {"text": "r61:637a933c9059 (web)", "at_rev": "", "type": "sha", "id": "637a933c905958ce5151f154147c25c1c7b68832"}, {"text": "r60:0c21004effeb (web)", "at_rev": "", "type": "sha", "id": "0c21004effeb8ce2d2d5b4a8baf6afa8394b6fbc"}, {"text": "r59:a1f39c56d3f1 (web)", "at_rev": "", "type": "sha", "id": "a1f39c56d3f1d52d5fb5920370a2a2716cd9a444"}, {"text": "r58:97d32df05c71 (web)", "at_rev": "", "type": "sha", "id": "97d32df05c715a3bbf936bf3cc4e32fb77fe1a7f"}, {"text": "r57:08eaf1451771 (web)", "at_rev": "", "type": "sha", "id": "08eaf14517718dccea4b67755a93368341aca919"}, {"text": "r56:22f71ad26526 (web)", "at_rev": "", "type": "sha", "id": "22f71ad265265a53238359c883aa976e725aa07d"}, {"text": "r49:97501f02b7b4 (web)", "at_rev": "", "type": "sha", "id": "97501f02b7b4330924b647755663a2d90a5e638d"}, {"text": "r47:86ede6754f2b (web)", "at_rev": "", "type": "sha", "id": "86ede6754f2b27309452bb11f997386ae01d0e5a"}, {"text": "r45:014c40c0203c (web)", "at_rev": "", "type": "sha", "id": "014c40c0203c423dc19ecf94644f7cac9d4cdce0"}, {"text": "r30:ee87846a61c1 (default)", "at_rev": "", "type": "sha", "id": "ee87846a61c12153b51543bf860e1026c6d3dcba"}, {"text": "r26:9bb326a04ae5 (default)", "at_rev": "", "type": "sha", "id": "9bb326a04ae5d98d437dece54be04f830cf1edd9"}, {"text": "r24:536c1a194283 (default)", "at_rev": "", "type": "sha", "id": "536c1a19428381cfea92ac44985304f6a8049569"}, {"text": "r8:dc5d2c0661b6 (default)", "at_rev": "", "type": "sha", "id": "dc5d2c0661b61928834a785d3e64a3f80d3aad9c"}, {"text": "r7:3803844fdbd3 (default)", "at_rev": "", "type": "sha", "id": "3803844fdbd3b711175fc3da9bdacfcd6d29a6fb"}]}, {"text": "Branches", "children": [{"text": "default", "at_rev": "default", "type": "branch", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}, {"text": "stable", "at_rev": "stable", "type": "branch", "id": "4f7e2131323e0749a740c0a56ab68ae9269c562a"}]}, {"text": "Tags", "children": [{"text": "v0.2.0", "at_rev": "v0.2.0", "type": "tag", "id": "2c96c02def9a7c997f33047761a53943e6254396"}, {"text": "v0.1.9", "at_rev": "v0.1.9", "type": "tag", "id": "8680b1d1cee3aa3c1ab3734b76ee164bbedbc5c9"}, {"text": "v0.1.8", "at_rev": "v0.1.8", "type": "tag", "id": "ecb25ba9c96faf1e65a0bc3fd914918420a2f116"}, {"text": "v0.1.7", "at_rev": "v0.1.7", "type": "tag", "id": "f67633a2894edaf28513706d558205fa93df9209"}, {"text": "v0.1.6", "at_rev": "v0.1.6", "type": "tag", "id": "02b38c0eb6f982174750c0e309ff9faddc0c7e12"}, {"text": "v0.1.5", "at_rev": "v0.1.5", "type": "tag", "id": "a6664e18181c6fc81b751a8d01474e7e1a3fe7fc"}, {"text": "v0.1.4", "at_rev": "v0.1.4", "type": "tag", "id": "fd4bdb5e9b2a29b4393a4ac6caef48c17ee1a200"}, {"text": "v0.1.3", "at_rev": "v0.1.3", "type": "tag", "id": "17544fbfcd33ffb439e2b728b5d526b1ef30bfcf"}, {"text": "v0.1.2", "at_rev": "v0.1.2", "type": "tag", "id": "a7e60bff65d57ac3a1a1ce3b12a70f8a9e8a7720"}, {"text": "v0.1.11", "at_rev": "v0.1.11", "type": "tag", "id": "fef5bfe1dc17611d5fb59a7f6f95c55c3606f933"}, {"text": "v0.1.10", "at_rev": "v0.1.10", "type": "tag", "id": "92831aebf2f8dd4879e897024b89d09af214df1c"}, {"text": "v0.1.1", "at_rev": "v0.1.1", "type": "tag", "id": "eb3a60fc964309c1a318b8dfe26aa2d1586c85ae"}, {"text": "tip", "at_rev": "tip", "type": "tag", "id": "2062ec7beeeaf9f44a1c25c41479565040b930b2"}]}], "more": false} No newline at end of file
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r15:16", "type": "sha", "id": "16"}, {"text": "r12:13", "type": "sha", "id": "13"}, {"text": "r7:8", "type": "sha", "id": "8"}, {"text": "r3:4", "type": "sha", "id": "4"}, {"text": "r2:3", "type": "sha", "id": "3"}]}, {"text": "Branches", "children": [{"text": "branches/add-docs", "type": "branch", "id": "branches/add-docs/example.py@26"}, {"text": "branches/argparse", "type": "branch", "id": "branches/argparse/example.py@26"}, {"text": "trunk", "type": "branch", "id": "trunk/example.py@26"}]}, {"text": "Tags", "children": [{"text": "tags/v0.1", "type": "tag", "id": "tags/v0.1/example.py@26"}, {"text": "tags/v0.2", "type": "tag", "id": "tags/v0.2/example.py@26"}, {"text": "tags/v0.3", "type": "tag", "id": "tags/v0.3/example.py@26"}, {"text": "tags/v0.5", "type": "tag", "id": "tags/v0.5/example.py@26"}]}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r15:16", "at_rev": "", "type": "sha", "id": "16"}, {"text": "r12:13", "at_rev": "", "type": "sha", "id": "13"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}, {"text": "r3:4", "at_rev": "", "type": "sha", "id": "4"}, {"text": "r2:3", "at_rev": "", "type": "sha", "id": "3"}]}, {"text": "Branches", "children": [{"text": "branches/add-docs", "at_rev": "branches/add-docs", "type": "branch", "id": "26"}, {"text": "branches/argparse", "at_rev": "branches/argparse", "type": "branch", "id": "26"}, {"text": "trunk", "at_rev": "trunk", "type": "branch", "id": "26"}]}, {"text": "Tags", "children": [{"text": "tags/v0.1", "at_rev": "tags/v0.1", "type": "tag", "id": "26"}, {"text": "tags/v0.2", "at_rev": "tags/v0.2", "type": "tag", "id": "26"}, {"text": "tags/v0.3", "at_rev": "tags/v0.3", "type": "tag", "id": "26"}, {"text": "tags/v0.5", "at_rev": "tags/v0.5", "type": "tag", "id": "26"}]}], "more": false} No newline at end of file
@@ -1,1 +1,1 b''
1 {"results": [{"text": "Changesets", "children": [{"text": "r382:383", "type": "sha", "id": "383"}, {"text": "r323:324", "type": "sha", "id": "324"}, {"text": "r322:323", "type": "sha", "id": "323"}, {"text": "r299:300", "type": "sha", "id": "300"}, {"text": "r277:278", "type": "sha", "id": "278"}, {"text": "r273:274", "type": "sha", "id": "274"}, {"text": "r270:271", "type": "sha", "id": "271"}, {"text": "r269:270", "type": "sha", "id": "270"}, {"text": "r263:264", "type": "sha", "id": "264"}, {"text": "r261:262", "type": "sha", "id": "262"}, {"text": "r251:252", "type": "sha", "id": "252"}, {"text": "r208:209", "type": "sha", "id": "209"}, {"text": "r202:203", "type": "sha", "id": "203"}, {"text": "r173:174", "type": "sha", "id": "174"}, {"text": "r172:173", "type": "sha", "id": "173"}, {"text": "r171:172", "type": "sha", "id": "172"}, {"text": "r145:146", "type": "sha", "id": "146"}, {"text": "r144:145", "type": "sha", "id": "145"}, {"text": "r140:141", "type": "sha", "id": "141"}, {"text": "r134:135", "type": "sha", "id": "135"}, {"text": "r107:108", "type": "sha", "id": "108"}, {"text": "r106:107", "type": "sha", "id": "107"}, {"text": "r100:101", "type": "sha", "id": "101"}, {"text": "r94:95", "type": "sha", "id": "95"}, {"text": "r85:86", "type": "sha", "id": "86"}, {"text": "r73:74", "type": "sha", "id": "74"}, {"text": "r72:73", "type": "sha", "id": "73"}, {"text": "r71:72", "type": "sha", "id": "72"}, {"text": "r69:70", "type": "sha", "id": "70"}, {"text": "r67:68", "type": "sha", "id": "68"}, {"text": "r63:64", "type": "sha", "id": "64"}, {"text": "r62:63", "type": "sha", "id": "63"}, {"text": "r61:62", "type": "sha", "id": "62"}, {"text": "r50:51", "type": "sha", "id": "51"}, {"text": "r49:50", "type": "sha", "id": "50"}, {"text": "r48:49", "type": "sha", "id": "49"}, {"text": "r47:48", "type": "sha", "id": "48"}, {"text": "r46:47", "type": "sha", "id": "47"}, {"text": "r45:46", "type": "sha", "id": "46"}, {"text": "r41:42", "type": "sha", "id": "42"}, {"text": "r39:40", "type": "sha", "id": "40"}, {"text": "r37:38", "type": "sha", "id": "38"}, {"text": "r25:26", "type": "sha", "id": "26"}, {"text": "r23:24", "type": "sha", "id": "24"}, {"text": "r8:9", "type": "sha", "id": "9"}, {"text": "r7:8", "type": "sha", "id": "8"}]}, {"text": "Branches", "children": []}, {"text": "Tags", "children": []}], "more": false} No newline at end of file
1 {"results": [{"text": "Changesets", "children": [{"text": "r382:383", "at_rev": "", "type": "sha", "id": "383"}, {"text": "r323:324", "at_rev": "", "type": "sha", "id": "324"}, {"text": "r322:323", "at_rev": "", "type": "sha", "id": "323"}, {"text": "r299:300", "at_rev": "", "type": "sha", "id": "300"}, {"text": "r277:278", "at_rev": "", "type": "sha", "id": "278"}, {"text": "r273:274", "at_rev": "", "type": "sha", "id": "274"}, {"text": "r270:271", "at_rev": "", "type": "sha", "id": "271"}, {"text": "r269:270", "at_rev": "", "type": "sha", "id": "270"}, {"text": "r263:264", "at_rev": "", "type": "sha", "id": "264"}, {"text": "r261:262", "at_rev": "", "type": "sha", "id": "262"}, {"text": "r251:252", "at_rev": "", "type": "sha", "id": "252"}, {"text": "r208:209", "at_rev": "", "type": "sha", "id": "209"}, {"text": "r202:203", "at_rev": "", "type": "sha", "id": "203"}, {"text": "r173:174", "at_rev": "", "type": "sha", "id": "174"}, {"text": "r172:173", "at_rev": "", "type": "sha", "id": "173"}, {"text": "r171:172", "at_rev": "", "type": "sha", "id": "172"}, {"text": "r145:146", "at_rev": "", "type": "sha", "id": "146"}, {"text": "r144:145", "at_rev": "", "type": "sha", "id": "145"}, {"text": "r140:141", "at_rev": "", "type": "sha", "id": "141"}, {"text": "r134:135", "at_rev": "", "type": "sha", "id": "135"}, {"text": "r107:108", "at_rev": "", "type": "sha", "id": "108"}, {"text": "r106:107", "at_rev": "", "type": "sha", "id": "107"}, {"text": "r100:101", "at_rev": "", "type": "sha", "id": "101"}, {"text": "r94:95", "at_rev": "", "type": "sha", "id": "95"}, {"text": "r85:86", "at_rev": "", "type": "sha", "id": "86"}, {"text": "r73:74", "at_rev": "", "type": "sha", "id": "74"}, {"text": "r72:73", "at_rev": "", "type": "sha", "id": "73"}, {"text": "r71:72", "at_rev": "", "type": "sha", "id": "72"}, {"text": "r69:70", "at_rev": "", "type": "sha", "id": "70"}, {"text": "r67:68", "at_rev": "", "type": "sha", "id": "68"}, {"text": "r63:64", "at_rev": "", "type": "sha", "id": "64"}, {"text": "r62:63", "at_rev": "", "type": "sha", "id": "63"}, {"text": "r61:62", "at_rev": "", "type": "sha", "id": "62"}, {"text": "r50:51", "at_rev": "", "type": "sha", "id": "51"}, {"text": "r49:50", "at_rev": "", "type": "sha", "id": "50"}, {"text": "r48:49", "at_rev": "", "type": "sha", "id": "49"}, {"text": "r47:48", "at_rev": "", "type": "sha", "id": "48"}, {"text": "r46:47", "at_rev": "", "type": "sha", "id": "47"}, {"text": "r45:46", "at_rev": "", "type": "sha", "id": "46"}, {"text": "r41:42", "at_rev": "", "type": "sha", "id": "42"}, {"text": "r39:40", "at_rev": "", "type": "sha", "id": "40"}, {"text": "r37:38", "at_rev": "", "type": "sha", "id": "38"}, {"text": "r25:26", "at_rev": "", "type": "sha", "id": "26"}, {"text": "r23:24", "at_rev": "", "type": "sha", "id": "24"}, {"text": "r8:9", "at_rev": "", "type": "sha", "id": "9"}, {"text": "r7:8", "at_rev": "", "type": "sha", "id": "8"}]}, {"text": "Branches", "children": []}, {"text": "Tags", "children": []}], "more": false} No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now