📄 changeset.py
字号:
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, ignore_space_changes=ignore_space): req.write(line + CRLF) raise RequestDone def _render_zip(self, req, filename, repos, data): """ZIP archive with all the added and/or modified files.""" new_rev = data['new_rev'] req.send_response(200) req.send_header('Content-Type', 'application/zip') req.send_header('Content-Disposition', content_disposition('inline;', filename + '.zip')) from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED buf = StringIO() zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 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']): if kind == Node.FILE and change != Changeset.DELETE: assert new_node zipinfo = ZipInfo() zipinfo.filename = new_node.path.strip('/').encode('utf-8') # Note: unicode filenames are not supported by zipfile. # UTF-8 is not supported by all Zip tools either, # but as some does, I think UTF-8 is the best option here. zipinfo.date_time = new_node.last_modified.utctimetuple()[:6] zipinfo.compress_type = ZIP_DEFLATED zipfile.writestr(zipinfo, new_node.get_content().read()) zipfile.close() buf.seek(0, 2) # be sure to be at the end req.send_header("Content-Length", buf.tell()) req.end_headers() req.write(buf.getvalue()) raise RequestDone def title_for_diff(self, data): if data['new_path'] == data['old_path']: # ''diff between 2 revisions'' mode return 'Diff r%s:%s for %s' \ % (data['old_rev'] or 'latest', data['new_rev'] or 'latest', data['new_path'] or '/') else: # ''generalized diff'' mode return 'Diff from %s@%s to %s@%s' \ % (data['old_path'] or '/', data['old_rev'] or 'latest', data['new_path'] or '/', data['new_rev'] or 'latest') def render_property_diff(self, name, old_node, old_props, new_node, new_props, options): """Renders diffs of a node property to HTML.""" candidates = [] for renderer in self.property_diff_renderers: quality = renderer.match_property_diff(name) if quality > 0: candidates.append((quality, renderer)) if candidates: renderer = sorted(candidates, reverse=True)[0][1] return renderer.render_property_diff(name, old_node, old_props, new_node, new_props, options) else: return None def _get_location(self, files): return '/'.join(os.path.commonprefix([f.split('/') for f in files])) def _prepare_filestats(self): filestats = {} for chg in Changeset.ALL_CHANGES: filestats[chg] = 0 return filestats # ITimelineEventProvider methods def get_timeline_filters(self, req): if 'CHANGESET_VIEW' in req.perm: yield ('changeset', _('Repository checkins')) def get_timeline_events(self, req, start, stop, filters): if 'changeset' in filters: show_files = self.timeline_show_files show_location = show_files == 'location' if show_files in ('-1', 'unlimited'): show_files = -1 elif show_files.isdigit(): show_files = int(show_files) else: show_files = 0 # disabled repos = self.env.get_repository(req.authname) if self.timeline_collapse: collapse_changesets = lambda c: (c.author, c.message) else: collapse_changesets = lambda c: c.rev for _, changesets in groupby(repos.get_changesets(start, stop), key=collapse_changesets): permitted_changesets = [] for chgset in changesets: if 'CHANGESET_VIEW' in req.perm('changeset', chgset.rev): permitted_changesets.append(chgset) if permitted_changesets: chgset = permitted_changesets[-1] yield ('changeset', chgset.date, chgset.author, (permitted_changesets, chgset.message or '', show_location, show_files)) def render_timeline_event(self, context, field, event): changesets, message, show_location, show_files = event[3] rev_b, rev_a = changesets[0].rev, changesets[-1].rev if field == 'url': if rev_a == rev_b: return context.href.changeset(rev_a) else: return context.href.log(rev=rev_b, stop_rev=rev_a) elif field == 'description': if not self.timeline_long_messages: message = shorten_line(message) if self.wiki_format_messages: markup = '' else: markup = message message = None if 'BROWSER_VIEW' in context.perm: files = [] if show_location: filestats = self._prepare_filestats() for c in changesets: for chg in c.get_changes(): filestats[chg[2]] += 1 files.append(chg[0]) stats = [(tag.div(class_=kind), tag.span(count, ' ', count > 1 and (kind == 'copy' and 'copies' or kind + 's') or kind)) for kind in Changeset.ALL_CHANGES for count in (filestats[kind],) if count] markup = tag.ul( tag.li(stats, ' in ', tag.strong(self._get_location(files))), markup, class_="changes") elif show_files: for c in changesets: for chg in c.get_changes(): if show_files > 0 and len(files) > show_files: break files.append(tag.li(tag.div(class_=chg[2]), chg[0] or '/')) if show_files > 0 and len(files) > show_files: files = files[:show_files] + [tag.li(u'\u2026')] markup = tag(tag.ul(files, class_="changes"), markup) if message: markup += format_to_html(self.env, context, message) return markup if rev_a == rev_b: title = tag('Changeset ', tag.em('[%s]' % rev_a)) else: title = tag('Changesets ', tag.em('[', rev_a, '-', rev_b, ']')) if field == 'title': return title elif field == 'summary': return '%s: %s' % (title, shorten_line(message)) # IWikiSyntaxProvider methods CHANGESET_ID = r"(?:\d+|[a-fA-F\d]{8,})" # only "long enough" hexa ids def get_wiki_syntax(self): yield ( # [...] form: start with optional intertrac: [T... or [trac ... r"!?\[(?P<it_changeset>%s\s*)" % WikiParser.INTERTRAC_SCHEME + # hex digits + optional /path for the restricted changeset # + optional query and fragment r"%s(?:/[^\]]*)?(?:\?[^\]]*)?(?:#[^\]]*)?\]|" % self.CHANGESET_ID + # r... form: allow r1 but not r1:2 (handled by the log syntax) r"(?:\b|!)r\d+\b(?!:\d)", lambda x, y, z: self._format_changeset_link(x, 'changeset', y[0] == 'r' and y[1:] or y[1:-1], y, z)) def get_link_resolvers(self): yield ('changeset', self._format_changeset_link) yield ('diff', self._format_diff_link) def _format_changeset_link(self, formatter, ns, chgset, label, fullmatch=None): intertrac = formatter.shorthand_intertrac_helper(ns, chgset, label, fullmatch) if intertrac: return intertrac chgset, params, fragment = formatter.split_link(chgset) sep = chgset.find('/') if sep > 0: rev, path = chgset[:sep], chgset[sep:] else: rev, path = chgset, None try: changeset = self.env.get_repository().get_changeset(rev) return tag.a(label, class_="changeset", title=shorten_line(changeset.message), href=(formatter.href.changeset(rev, path) + params + fragment)) except TracError, e: return tag.a(label, class_="missing changeset", href=formatter.href.changeset(rev, path), title=unicode(e), rel="nofollow") def _format_diff_link(self, formatter, ns, target, label): params, query, fragment = formatter.split_link(target) def pathrev(path): if '@' in path: return path.split('@', 1) else: return (path, None) if '//' in params: p1, p2 = params.split('//', 1) old, new = pathrev(p1), pathrev(p2) data = {'old_path': old[0], 'old_rev': old[1], 'new_path': new[0], 'new_rev': new[1]} else: old_path, old_rev = pathrev(params) new_rev = None if old_rev and ':' in old_rev: old_rev, new_rev = old_rev.split(':', 1) data = {'old_path': old_path, 'old_rev': old_rev, 'new_path': old_path, 'new_rev': new_rev} title = self.title_for_diff(data) href = None if any(data.values()): if query: query = '&' + query[1:] href = formatter.href.changeset(new_path=data['new_path'] or None, new=data['new_rev'], old_path=data['old_path'] or None, old=data['old_rev']) + query return tag.a(label, class_="changeset", title=title, href=href) # ISearchSource methods def get_search_filters(self, req): if 'CHANGESET_VIEW' in req.perm: yield ('changeset', _('Changesets')) def get_search_results(self, req, terms, filters): if not 'changeset' in filters: return repos = self.env.get_repository(req.authname) db = self.env.get_db_cnx() sql, args = search_to_sql(db, ['rev', 'message', 'author'], terms) cursor = db.cursor() cursor.execute("SELECT rev,time,author,message " "FROM revision WHERE " + sql, args) for rev, ts, author, log in cursor: if not repos.authz.has_permission_for_changeset(rev): continue yield (req.href.changeset(rev), '[%s]: %s' % (rev, shorten_line(log)), datetime.fromtimestamp(ts, utc), author, shorten_result(log, terms))class AnyDiffModule(Component): implements(IRequestHandler) # IRequestHandler methods def match_request(self, req): return re.match(r'/diff$', req.path_info) def process_request(self, req): repos = self.env.get_repository(req.authname) if req.get_header('X-Requested-With') == 'XMLHttpRequest': dirname, prefix = posixpath.split(req.args.get('q')) prefix = prefix.lower() node = repos.get_node(dirname) def kind_order(entry): def name_order(entry): return embedded_numbers(entry.name) return entry.isfile, name_order(entry) html = tag.ul( [tag.li(is_dir and tag.b(path) or path) for e in sorted(node.get_entries(), key=kind_order) for is_dir, path in [(e.isdir, '/' + e.path.lstrip('/'))] if e.name.lower().startswith(prefix)] ) req.write(html.generate().render('xhtml')) return # -- retrieve arguments new_path = req.args.get('new_path') new_rev = req.args.get('new_rev') old_path = req.args.get('old_path') old_rev = req.args.get('old_rev') # -- normalize new_path = repos.normalize_path(new_path) if not new_path.startswith('/'): new_path = '/' + new_path new_rev = repos.normalize_rev(new_rev) old_path = repos.normalize_path(old_path) if not old_path.startswith('/'): old_path = '/' + old_path old_rev = repos.normalize_rev(old_rev) repos.authz.assert_permission_for_changeset(new_rev) repos.authz.assert_permission_for_changeset(old_rev) # -- prepare rendering data = {'new_path': new_path, 'new_rev': new_rev, 'old_path': old_path, 'old_rev': old_rev} add_script(req, 'common/js/suggest.js') return 'diff_form.html', data, None
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -