📄 changeset.py
字号:
rev_href = req.href.changeset(old, old_path, old=new, old_path=new_path) add_ctxtnav(req, _('Reverse Diff'), href=rev_href) return 'changeset.html', data, None # Internal methods def _render_html(self, req, repos, chgset, restricted, xhr, data): """HTML version""" data['restricted'] = restricted browser = BrowserModule(self.env) if chgset: # Changeset Mode (possibly restricted on a path) path, rev = data['new_path'], data['new_rev'] # -- getting the change summary from the Changeset.get_changes def get_changes(): for npath, kind, change, opath, orev in chgset.get_changes(): old_node = new_node = None if (restricted and not (npath == path or # same path npath.startswith(path + '/') or # npath is below path.startswith(npath + '/'))): # npath is above continue if change != Changeset.ADD: old_node = repos.get_node(opath, orev) if change != Changeset.DELETE: new_node = repos.get_node(npath, rev) yield old_node, new_node, kind, change def _changeset_title(rev): if restricted: return _('Changeset %(id)s for %(path)s', id=rev, path=path) else: return _('Changeset %(id)s', id=rev) data['changeset'] = chgset title = _changeset_title(rev) # Support for revision properties (#2545) context = Context.from_request(req, 'changeset', chgset.rev) revprops = chgset.get_properties() data['properties'] = browser.render_properties('revprop', context, revprops) oldest_rev = repos.oldest_rev if chgset.rev != oldest_rev: if restricted: prev = repos.get_node(path, rev).get_previous() if prev: prev_path, prev_rev = prev[:2] if prev_rev: prev_href = req.href.changeset(prev_rev, prev_path) else: prev_path = prev_rev = None else: add_link(req, 'first', req.href.changeset(oldest_rev), _('Changeset %(id)s', id=oldest_rev)) prev_path = data['old_path'] prev_rev = repos.previous_rev(chgset.rev) if prev_rev: prev_href = req.href.changeset(prev_rev) if prev_rev: add_link(req, 'prev', prev_href, _changeset_title(prev_rev)) youngest_rev = repos.youngest_rev if str(chgset.rev) != str(youngest_rev): if restricted: next_rev = repos.next_rev(chgset.rev, path) if next_rev: if repos.has_node(path, next_rev): next_href = req.href.changeset(next_rev, path) else: # must be a 'D'elete or 'R'ename, show full cset next_href = req.href.changeset(next_rev) else: add_link(req, 'last', req.href.changeset(youngest_rev), _('Changeset %(id)s', id=youngest_rev)) next_rev = repos.next_rev(chgset.rev) if next_rev: next_href = req.href.changeset(next_rev) if next_rev: add_link(req, 'next', next_href, _changeset_title(next_rev)) else: # Diff Mode # -- getting the change summary from the Repository.get_changes def get_changes(): for d in repos.get_changes( new_path=data['new_path'], new_rev=data['new_rev'], old_path=data['old_path'], old_rev=data['old_rev']): yield d title = self.title_for_diff(data) data['changeset'] = False data['title'] = title if 'BROWSER_VIEW' not in req.perm: return def node_info(node, annotated): return {'path': node.path, 'rev': node.rev, 'shortrev': repos.short_rev(node.rev), 'href': req.href.browser(node.created_path, rev=node.created_rev, annotate=annotated and 'blame' or \ None), 'title': (_('Show revision %(rev)s of this file in browser', rev=node.rev))} # Reminder: node.path may not exist at node.rev # as long as node.rev==node.created_rev # ... and data['old_rev'] may have nothing to do # with _that_ node specific history... options = data['diff']['options'] def _prop_changes(old_node, new_node): old_source = Resource('source', old_node.created_path, version=old_node.created_rev) new_source = Resource('source', new_node.created_path, version=new_node.created_rev) old_props = new_props = [] if 'FILE_VIEW' in req.perm(old_source): old_props = old_node.get_properties() if 'FILE_VIEW' in req.perm(new_source): new_props = new_node.get_properties() old_ctx = Context.from_request(req, old_source) new_ctx = Context.from_request(req, new_source) changed_properties = [] if old_props != new_props: for k,v in old_props.items(): new = old = diff = None if not k in new_props: old = v # won't be displayed, no need to render it elif v != new_props[k]: diff = self.render_property_diff( k, old_ctx, old_props, new_ctx, new_props, options) if not diff: old = browser.render_property(k, 'changeset', old_ctx, old_props) new = browser.render_property(k, 'changeset', new_ctx, new_props) if new or old or diff: changed_properties.append({'name': k, 'old': old, 'new': new, 'diff': diff}) for k,v in new_props.items(): if not k in old_props: new = browser.render_property(k, 'changeset', new_ctx, new_props) changed_properties.append({'name': k, 'new': new, 'old': None}) return changed_properties def _estimate_changes(old_node, new_node): old_size = old_node.get_content_length() new_size = new_node.get_content_length() return old_size + new_size def _content_changes(old_node, new_node): """Returns the list of differences. The list is empty when no differences between comparable files are detected, but the return value is None for non-comparable files. """ old_content = old_node.get_content().read() if is_binary(old_content): return None new_content = new_node.get_content().read() if is_binary(new_content): return None mview = Mimeview(self.env) old_content = mview.to_unicode(old_content, old_node.content_type) new_content = mview.to_unicode(new_content, new_node.content_type) if old_content != new_content: context = options.get('contextlines', 3) if context < 0: context = None tabwidth = self.config['diff'].getint('tab_width') or \ self.config['mimeviewer'].getint('tab_width', 8) ignore_blank_lines = options.get('ignoreblanklines') ignore_case = options.get('ignorecase') ignore_space = options.get('ignorewhitespace') return diff_blocks(old_content.splitlines(), new_content.splitlines(), context, tabwidth, ignore_blank_lines=ignore_blank_lines, ignore_case=ignore_case, ignore_space_changes=ignore_space) else: return [] if 'FILE_VIEW' in req.perm: diff_bytes = diff_files = 0 if self.max_diff_bytes or self.max_diff_files: for old_node, new_node, kind, change in get_changes(): if change in Changeset.DIFF_CHANGES and kind == Node.FILE: diff_files += 1 diff_bytes += _estimate_changes(old_node, new_node) show_diffs = (not self.max_diff_files or \ diff_files <= self.max_diff_files) and \ (not self.max_diff_bytes or \ diff_bytes <= self.max_diff_bytes or \ diff_files == 1) else: show_diffs = False # XHR is used for blame support: display the changeset view without # the navigation and with the changes concerning the annotated file annotated = False if xhr: show_diffs = False annotated = repos.normalize_path(req.args.get('annotate')) has_diffs = False filestats = self._prepare_filestats() changes = [] files = [] for old_node, new_node, kind, change in get_changes(): props = [] diffs = [] show_entry = change != Changeset.EDIT show_diff = show_diffs or (new_node and new_node.path == annotated) if change in Changeset.DIFF_CHANGES and 'FILE_VIEW' in req.perm: assert old_node and new_node props = _prop_changes(old_node, new_node) if props: show_entry = True if kind == Node.FILE and show_diff: diffs = _content_changes(old_node, new_node) if diffs != []: if diffs: has_diffs = True # elif None (means: manually compare to (previous)) show_entry = True if show_entry or not show_diff: info = {'change': change, 'old': old_node and node_info(old_node, annotated), 'new': new_node and node_info(new_node, annotated), 'props': props, 'diffs': diffs} files.append(new_node and new_node.path or \ old_node and old_node.path or '') filestats[change] += 1 if change in Changeset.DIFF_CHANGES: if chgset: href = req.href.changeset(new_node.rev, new_node.path) title = _('Show the changeset %(id)s restricted to ' '%(path)s', id=new_node.rev, path=new_node.path) else: href = req.href.changeset( new_node.created_rev, new_node.created_path, old=old_node.created_rev, old_path=old_node.created_path) title = _('Show the %(range)s differences restricted ' 'to %(path)s', range='r%s:%s' % (old_node.rev, new_node.rev), path=new_node.path) info['href'] = href info['title'] = old_node and title if change in Changeset.DIFF_CHANGES and not show_diff: info['hide_diff'] = True else: info = None changes.append(info) # the sequence should be immutable data.update({'has_diffs': has_diffs, 'changes': changes, 'xhr': xhr, 'filestats': filestats, 'annotated': annotated, 'files': files, 'location': self._get_parent_location(files), 'longcol': 'Revision', 'shortcol': 'r'}) if xhr: # render and return the content only stream = Chrome(self.env).render_template(req, 'changeset.html', data, fragment=True) content = stream.select('//div[@id="content"]') req.write(content.render('xhtml')) raise RequestDone return data def _render_diff(self, req, filename, repos, data): """Raw Unified Diff version""" req.send_response(200) req.send_header('Content-Type', 'text/x-patch;charset=utf-8') req.send_header('Content-Disposition', content_disposition('inline;', filename + '.diff')) req.end_headers() mimeview = Mimeview(self.env) for old_node, new_node, kind, change in repos.get_changes( new_path=data['new_path'], new_rev=data['new_rev'], old_path=data['old_path'], old_rev=data['old_rev']): # TODO: Property changes # Content changes if kind == Node.DIRECTORY: continue new_content = old_content = '' new_node_info = old_node_info = ('','') mimeview = Mimeview(self.env) if old_node: old_content = old_node.get_content().read() if is_binary(old_content): continue old_node_info = (old_node.path, old_node.rev) old_content = mimeview.to_unicode(old_content, old_node.content_type) if new_node: new_content = new_node.get_content().read() if is_binary(new_content): continue new_node_info = (new_node.path, new_node.rev) new_path = new_node.path new_content = mimeview.to_unicode(new_content, new_node.content_type) else: old_node_path = repos.normalize_path(old_node.path) diff_old_path = repos.normalize_path(data['old_path']) new_path = posixpath.join(data['new_path'], old_node_path[len(diff_old_path)+1:]) if old_content != new_content: options = data['diff']['options'] context = options.get('contextlines', 3) if context < 0: context = 3 # FIXME: unified_diff bugs with context=None ignore_blank_lines = options.get('ignoreblanklines') ignore_case = options.get('ignorecase') ignore_space = options.get('ignorewhitespace') if not old_node_info[0]: old_node_info = new_node_info # support for 'A'dd changes req.write('Index: ' + new_path + CRLF) req.write('=' * 67 + CRLF) req.write('--- %s (revision %s)' % old_node_info + CRLF) req.write('+++ %s (revision %s)' % new_node_info + CRLF) for line in unified_diff(old_content.splitlines(), new_content.splitlines(), context, ignore_blank_lines=ignore_blank_lines, ignore_case=ignore_case,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -