Ticket #199: diff_module_alpha1.diff
| File diff_module_alpha1.diff, 28.9 KB (added by cboos, 3 years ago) |
|---|
-
htdocs/css/browser.css
45 45 #dirlist td.name a, #dirlist td.rev a { border-bottom: none; display: block } 46 46 #dirlist td.change * { font-size: 9px } 47 47 48 /* Log */ 49 #diff { margin: 0; } 50 48 51 /* Styles for the revision log table 49 52 (extends the styles for "table.listing") */ 50 53 #chglist { margin-top: 0 } -
trac/db_default.py
458 458 459 459 default_components = ('trac.About', 'trac.attachment', 'trac.Browser', 460 460 'trac.Changeset', 'trac.Search', 'trac.Settings', 461 'trac.Diff', 461 462 'trac.ticket.query', 'trac.ticket.report', 462 463 'trac.Roadmap', 463 464 'trac.ticket.web_ui', 'trac.Timeline', 'trac.wiki.web_ui', -
trac/versioncontrol/svn_fs.py
27 27 import os.path 28 28 import time 29 29 import weakref 30 import posixpath 30 31 31 32 from svn import fs, repos, core, delta 32 33 … … 176 177 self.close() 177 178 178 179 def normalize_path(self, path): 179 return path == '/' and path or path .strip('/')180 return path == '/' and path or path and path.strip('/') or '' 180 181 181 182 def normalize_rev(self, rev): 182 183 try: … … 361 362 props[name] = str(value) # Make sure the value is a proper string 362 363 return props 363 364 365 def get_diff(self, older_node, ignore_ancestry=1): 366 if self.isfile and not older_node.isfile: 367 raise TracError, "Trying to diff a %s with a %s." % (self.kind, older_node.kind) 368 editor = DiffChangeEditor() 369 e_ptr, e_baton = delta.make_editor(editor, self.pool) 370 old_root = fs.revision_root(self.fs_ptr, older_node.created_rev, self.pool) 371 new_root = fs.revision_root(self.fs_ptr, self.created_rev, self.pool) 372 if older_node.isfile: 373 old_dir, old_entry = posixpath.split(older_node.created_path) 374 else: 375 old_dir, old_entry = older_node.created_path, '' 376 def authz_cb(root, path, pool): return 1 377 text_deltas = 0 # as this is currently redone in Diff.py... 378 repos.svn_repos_dir_delta(old_root, old_dir, old_entry, 379 new_root, self.created_path, e_ptr, e_baton, authz_cb, 380 text_deltas, self.isdir, 0, ignore_ancestry, self.pool) 381 for d in editor.deltas: 382 yield d 383 364 384 def get_content_length(self): 365 385 if self.isdir: 366 386 return None … … 450 470 451 471 def _get_prop(self, name): 452 472 return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool) 473 474 475 # 476 # Delta editor for diffs between arbitrary nodes (recycling my old code for #295 :) ) 477 # 478 # Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 479 # because 'repos.svn_repos_dir_delta' *doesn't* provide it. 480 # 481 # Note 2: the 'dir_baton' is the path of the parent directory 482 # 483 484 class DiffChangeEditor(delta.Editor): 485 486 def __init__(self): 487 self.deltas = [] 488 self.skip_dir_prop_change = 0 489 490 def _norm(self, path): 491 """Path are normalized to __not__ have a leading slash""" 492 return path == '/' and path or path and path.strip('/') or '' 493 494 # -- svn.delta.Editor callbacks 495 496 def open_root(self, base_revision, dir_pool): 497 return '/' 498 499 def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, dir_pool): 500 self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 501 # don't create an additional 'Changeset.EDIT' entry for this directory 502 # in case there's also a dir property change: 503 self.skip_dir_prop_change = 1 504 return path 505 506 def open_directory(self, path, dir_baton, base_revision, dir_pool): 507 self.deltas.append((path, Node.DIRECTORY, Changeset.EDIT)) 508 self.skip_dir_prop_change = 0 509 return path 510 511 def change_dir_prop(self, dir_baton, name, value, pool): 512 if self.skip_dir_prop_change: 513 return 514 self.deltas.append((dir_baton, Node.DIRECTORY, Changeset.EDIT)) 515 self.skip_dir_prop_change = 1 516 517 def close_directory(self, dir_baton): 518 self.skip_dir_prop_change = 0 519 520 def delete_entry(self, path, revision, dir_baton, pool): 521 self.deltas.append((path, Node.FILE, Changeset.DELETE)) # should be Node.UNKNOWN 522 523 def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, dir_pool): 524 self.deltas.append((self._norm(path), Node.FILE, Changeset.ADD)) 525 526 def open_file(self, path, dir_baton, dummy_rev, file_pool): 527 self.deltas.append((self._norm(path), Node.FILE, Changeset.EDIT)) 528 -
trac/versioncontrol/main.py
162 162 """ 163 163 raise NotImplementedError 164 164 165 def get_diff(self, older_node, ignore_ancestry=1): 166 """ 167 Generator that yields (path, kind, chg) tuples representing 168 the diffs to this node, from the older node given as parameter. 169 """ 170 raise NotImplementedError 171 165 172 def get_content_length(self): 166 173 raise NotImplementedError 167 174 content_length = property(lambda x: x.get_content_length()) -
trac/Diff.py
24 24 from __future__ import generators 25 25 import time 26 26 import re 27 import posixpath 27 28 28 29 from trac import mimeview, perm, util 29 30 from trac.core import * 30 from trac.Timeline import ITimelineEventProvider31 31 from trac.versioncontrol import Changeset, Node 32 32 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff 33 33 from trac.web.chrome import add_link, add_stylesheet … … 35 35 from trac.wiki import wiki_to_html, wiki_to_oneliner 36 36 37 37 38 class ChangesetModule(Component): 38 class Diff(dict): 39 def __getattr__(self,str): 40 return self[str] 41 39 42 40 implements(IRequestHandler, ITimelineEventProvider) 43 class DiffModule(Component): 41 44 45 implements(IRequestHandler) 46 42 47 # IRequestHandler methods 43 48 44 49 def match_request(self, req): 45 match = re.match(r'/changeset/([0-9]+)$', req.path_info) 50 print 'match_request in DiffModule' 51 match = re.match(r'/diff(?:(/.*)|$)', req.path_info) 46 52 if match: 47 req.args[' rev'] = match.group(1)53 req.args['path'] = match.group(1) 48 54 return 1 49 55 50 56 def process_request(self, req): 51 57 req.perm.assert_permission(perm.CHANGESET_VIEW) 52 58 53 rev = req.args.get('rev')59 path = req.args.get('path') 54 60 repos = self.env.get_repository(req.authname) 61 path = repos.normalize_path(path) 62 rev = req.args.get('rev', repos.youngest_rev) # 'path history' mode 63 old = req.args.get('old') # 'arbitrary diff' mode 64 new = req.args.get('new') 65 old_path = req.args.get('old_path', path) 55 66 56 67 diff_options = get_diff_options(req) 57 68 if req.args.has_key('update'): 58 req.redirect(self.env.href.changeset(rev)) 69 if old or new: 70 req.redirect(self.env.href.diff(path, new=new, old_path=old_path, old=old)) 71 else: 72 req.redirect(self.env.href.diff(path, rev=rev)) 59 73 60 chgset = repos.get_changeset(rev) 61 req.check_modified(chgset.date, 62 diff_options[0] + ''.join(diff_options[1])) 74 if old or new: 75 chgset = None 76 if not new: 77 new = repos.next_rev(old) # FIXME: must lookup the next entry in node history 78 elif not old: 79 old = repos.previous_rev(new) 80 if not old_path: 81 old_path = path 82 diff = Diff(old_path=old_path, old_rev=old, 83 new_path=path, new_rev=new) 84 else: 85 chgset = repos.get_changeset(rev) 86 diff = Diff(old_path=path, old_rev=repos.previous_rev(rev), 87 new_path=path, new_rev=rev) 88 89 # TODO: 90 # req.check_modified(chgset.date, 91 # diff_options[0] + ''.join(diff_options[1])) 63 92 64 93 format = req.args.get('format') 65 94 if format == 'diff': 66 self._render_diff(req, repos, chgset, diff_options)95 self._render_diff(req, repos, diff, chgset, diff_options) 67 96 return 68 97 elif format == 'zip': 69 self._render_zip(req, repos, chgset)98 self._render_zip(req, repos, diff, chgset) 70 99 return 71 100 72 self._render_html(req, repos, chgset, diff_options)101 self._render_html(req, repos, diff, chgset, diff_options) 73 102 add_link(req, 'alternate', '?format=diff', 'Unified Diff', 74 103 'text/plain', 'diff') 75 104 add_link(req, 'alternate', '?format=zip', 'Zip Archive', 76 105 'application/zip', 'zip') 77 106 add_stylesheet(req, 'changeset.css') 78 107 add_stylesheet(req, 'diff.css') 79 return ' changeset.cs', None108 return 'diff.cs', None 80 109 81 # ITimelineEventProvider methods82 110 83 def get_timeline_filters(self, req):84 if req.perm.has_permission(perm.CHANGESET_VIEW):85 yield ('changeset', 'Repository checkins')86 87 def get_timeline_events(self, req, start, stop, filters):88 if 'changeset' in filters:89 absurls = req.args.get('format') == 'rss' # Kludge90 show_files = int(self.config.get('timeline',91 'changeset_show_files'))92 db = self.env.get_db_cnx()93 repos = self.env.get_repository()94 rev = repos.youngest_rev95 while rev:96 chgset = repos.get_changeset(rev)97 if chgset.date < start:98 return99 if chgset.date < stop:100 if absurls:101 href = self.env.abs_href.changeset(chgset.rev)102 else:103 href = self.env.href.changeset(chgset.rev)104 title = 'Changeset <em>[%s]</em> by %s' % (105 util.escape(chgset.rev), util.escape(chgset.author))106 message = wiki_to_oneliner(util.shorten_line(chgset.message or '--'),107 self.env, db, absurls=absurls)108 if show_files:109 files = []110 for chg in chgset.get_changes():111 if show_files > 0 and len(files) >= show_files:112 files.append('...')113 break114 files.append('<span class="%s">%s</span>'115 % (chg[2], util.escape(chg[0])))116 message = '<span class="changes">' + ', '.join(files) +\117 '</span>: ' + message118 yield 'changeset', href, title, chgset.date, chgset.author,\119 message120 rev = repos.previous_rev(rev)121 122 111 # Internal methods 123 112 124 def _render_html(self, req, repos, chgset, diff_options):113 def _render_html(self, req, repos, diff, chgset, diff_options): 125 114 """HTML version""" 126 req.hdf['title'] = '[%s]' % chgset.rev 127 req.hdf['changeset'] = { 128 'revision': chgset.rev, 129 'time': time.strftime('%c', time.localtime(chgset.date)), 130 'author': util.escape(chgset.author or 'anonymous'), 131 'message': wiki_to_html(chgset.message or '--', self.env, req, 132 escape_newlines=True) 133 } 115 req.hdf['diff'] = diff 116 req.hdf['diff.href'] = { 117 'new_rev': self.env.href.changeset(diff.new_rev), 118 'old_rev': self.env.href.changeset(diff.old_rev), 119 'new_path': self.env.href.browser(diff.new_path, rev=diff.new_rev), 120 'old_path': self.env.href.changeset(diff.old_path, rev=diff.old_rev) 121 } 122 if chgset: # 'path history' mode 123 req.hdf['title'] = 'Changes for %s at Revision %s' % (diff.new_path, chgset.rev) 124 req.hdf['changeset'] = { 125 'revision': chgset.rev, 126 'time': time.strftime('%c', time.localtime(chgset.date)), 127 'author': util.escape(chgset.author or 'anonymous'), 128 'message': wiki_to_html(chgset.message or '--', self.env, req, 129 escape_newlines=True) 130 } 134 131 135 oldest_rev = repos.oldest_rev 136 if chgset.rev != oldest_rev: 137 add_link(req, 'first', self.env.href.changeset(oldest_rev), 138 'Changeset %s' % oldest_rev) 139 previous_rev = repos.previous_rev(chgset.rev) 140 add_link(req, 'prev', self.env.href.changeset(previous_rev), 141 'Changeset %s' % previous_rev) 142 youngest_rev = repos.youngest_rev 143 if str(chgset.rev) != str(youngest_rev): 144 next_rev = repos.next_rev(chgset.rev) 145 add_link(req, 'next', self.env.href.changeset(next_rev), 146 'Changeset %s' % next_rev) 147 add_link(req, 'last', self.env.href.changeset(youngest_rev), 148 'Changeset %s' % youngest_rev) 132 oldest_rev = repos.oldest_rev 133 if chgset.rev != oldest_rev: 134 add_link(req, 'first', self.env.href.diff(diff.old_path, rev=oldest_rev), 135 'Changeset %s' % oldest_rev) # FIXME (use the history) 136 previous_rev = repos.previous_rev(chgset.rev) 137 add_link(req, 'prev', self.env.href.diff(diff.old_path, rev=previous_rev), 138 'Changeset %s' % previous_rev) 139 youngest_rev = repos.youngest_rev 140 if str(chgset.rev) != str(youngest_rev): 141 next_rev = repos.next_rev(chgset.rev) 142 add_link(req, 'next', self.env.href.diff(diff.new_path, rev=next_rev), 143 'Changeset %s' % next_rev) 144 add_link(req, 'last', self.env.href.diff(diff.new_path, rev=youngest_rev), 145 'Changeset %s' % youngest_rev) 146 elif diff.new_path == diff.old_path: # 'diff between 2 revisions' mode 147 req.hdf['title'] = 'Diff for %s between Revisions %s and %s' \ 148 % (diff.new_path, diff.old_rev, diff.new_rev) 149 else: # 'arbitrary diff' mode 150 req.hdf['title'] = 'Diff between %s at Revision %s and %s at Revision %s' \ 151 % (diff.old_path, diff.old_rev, 152 diff.new_path, diff.new_rev) 149 153 154 ## FIXME: move the get_diff at the level of the repos 155 try: 156 old_root_node = repos.get_node(diff.old_path, diff.old_rev) 157 except TracError: 158 old_root_node = None 159 try: 160 new_root_node = repos.get_node(diff.new_path, diff.new_rev) 161 except TracError: 162 new_root_node = None 163 164 if not old_root_node and not new_root_node: 165 raise TracError, 'Invalid diff request' # FIXME be more explicit 166 elif not old_root_node: 167 raise TracError, 'Unsupported diff request' # FIXME: add everything 168 elif not new_root_node: 169 raise TracError, 'Unsupported diff request' # FIXME: del everything 170 171 if old_root_node.isdir and new_root_node.isdir: 172 old_base = diff.old_path 173 new_base = diff.new_path 174 elif old_root_node.isfile and new_root_node.isfile: 175 old_base = posixpath.split(diff.old_path)[0] 176 new_base = posixpath.split(diff.new_path)[0] 177 else: 178 raise TracError, "Trying to diff a %s with a %s." \ 179 % (old_root_node.kind, new_root_node.kind) 150 180 edits = [] 151 181 idx = 0 152 for path, kind, change, base_path, base_rev in chgset.get_changes(): 182 for path, kind, change in new_root_node.get_diff(old_root_node): 183 old_path = posixpath.join(old_base, path) 184 new_path = posixpath.join(new_base, path) 185 print 'delta %d: %s %s delta from %s@%s to %s@%s' \ 186 % (idx, change, kind, old_path, diff.old_rev, new_path, diff.new_rev) 187 old_node = repos.get_node(old_path, diff.old_rev) 188 new_node = repos.get_node(new_path, diff.new_rev) 153 189 info = {'change': change} 190 base_path = old_path 191 base_rev = diff.old_rev 192 path = new_path 193 rev = diff.new_rev 154 194 if base_path: 155 195 info['path.old'] = base_path 156 196 info['rev.old'] = base_rev … … 158 198 rev=base_rev) 159 199 if path: 160 200 info['path.new'] = path 161 info['rev.new'] = chgset.rev201 info['rev.new'] = rev 162 202 info['browser_href.new'] = self.env.href.browser(path, 163 rev=chgset.rev) 164 if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE): 165 edits.append((idx, path, kind, base_path, base_rev)) 166 req.hdf['changeset.changes.%d' % idx] = info 167 idx += 1 168 169 for idx, path, kind, base_path, base_rev in edits: 170 old_node = repos.get_node(base_path or path, base_rev) 171 new_node = repos.get_node(path, chgset.rev) 172 203 rev=rev) 204 req.hdf['diff.changes.%d' % idx] = info 205 173 206 # Property changes 174 207 old_props = old_node.get_properties() 175 208 new_props = new_node.get_properties() … … 183 216 for k,v in new_props.items(): 184 217 if not k in old_props: 185 218 changed_props[k] = {'new': v} 186 req.hdf[' changeset.changes.%d.props' % idx] = changed_props219 req.hdf['diff.changes.%d.props' % idx] = changed_props 187 220 188 221 if kind == Node.DIRECTORY: 189 222 continue 190 223 191 224 # Content changes 192 225 default_charset = self.config.get('trac', 'default_charset') 193 old_content = old_node.get_content().read() 226 old_content = old_node.get_content().read() 194 227 if mimeview.is_binary(old_content): 195 228 continue 196 229 charset = mimeview.get_charset(old_node.content_type) or \ … … 217 250 ignore_blank_lines='-B' in diff_options[1], 218 251 ignore_case='-i' in diff_options[1], 219 252 ignore_space_changes='-b' in diff_options[1]) 220 req.hdf['changeset.changes.%d.diff' % idx] = changes 253 req.hdf['diff.changes.%d.diff' % idx] = changes 254 idx += 1 221 255 222 def _render_diff(self, req, repos, chgset, diff_options): 256 257 def _render_diff(self, req, repos, chgset, diff_options): # TODO 223 258 """Raw Unified Diff version""" 224 259 req.send_response(200) 225 260 req.send_header('Content-Type', 'text/plain;charset=utf-8') … … 284 319 ignore_space_changes='-b' in diff_options[1]): 285 320 req.write(line + util.CRLF) 286 321 287 def _render_zip(self, req, repos, chgset): 322 def _render_zip(self, req, repos, chgset): # TODO 288 323 """ZIP archive with all the added and/or modified files.""" 289 324 req.send_response(200) 290 325 req.send_header('Content-Type', 'application/zip') -
trac/Browser.py
118 118 'props': dict([(util.escape(name), util.escape(value)) 119 119 for name, value in node.get_properties().items()]), 120 120 'href': self.env.href.browser(path,rev=rev or repos.youngest_rev), 121 'diff_href': self.env.href.diff(path,rev=rev or repos.youngest_rev), 121 122 'log_href': self.env.href.log(path) 122 123 } 123 124 … … 268 269 stop_rev = req.args.get('stop_rev') 269 270 verbose = req.args.get('verbose') 270 271 limit = int(req.args.get('limit') or 100) 272 diff = req.args.get('diff') 273 old = req.args.get('old') 274 new = req.args.get('new') 271 275 276 repos = self.env.get_repository(req.authname) 277 normpath = repos.normalize_path(path) 278 rev = str(repos.normalize_rev(rev)) 279 old = old or str(repos.previous_rev(rev)) 280 new = new or rev 281 272 282 req.hdf['title'] = path + ' (log)' 273 283 req.hdf['log'] = { 274 284 'path': path, … … 276 286 'verbose': verbose, 277 287 'stop_rev': stop_rev, 278 288 'browser_href': self.env.href.browser(path, rev=rev), 279 'log_href': self.env.href.log(path, rev=rev) 289 'log_href': self.env.href.log(path, rev=rev), 290 'select_diff_href': self.env.href.log(path, rev=rev, diff=1), 291 'diff': diff, 292 'diff_href': self.env.href.diff(path, old=old, new=new), 293 'old': old, 294 'new': new 280 295 } 281 296 282 297 path_links = _get_path_links(self.env.href, path, rev) … … 284 299 if path_links: 285 300 add_link(req, 'up', path_links[-1]['href'], 'Parent directory') 286 301 287 repos = self.env.get_repository(req.authname)288 normpath = repos.normalize_path(path)289 rev = str(repos.normalize_rev(rev))290 291 302 # 'node' or 'path' history: use get_node()/get_history() or get_path_history() 292 303 if mode != 'path_history': 293 304 try: -
templates/log.cs
2 2 <?cs include "macros.cs"?> 3 3 4 4 <div id="ctxtnav" class="nav"> 5 <ul> 6 <li class="last"><a href="<?cs 7 var:log.browser_href ?>">View Latest Revision</a></li><?cs 5 <ul><?cs 6 if:!log.diff ?> 7 <li class="first"> 8 <a href="<?cs var:log.select_diff_href ?>">Select for Diff</a> 9 </li><?cs 10 /if ?> 11 <li class="last"> 12 <a href="<?cs var:log.browser_href ?>">View Latest Revision</a> 13 </li><?cs 8 14 if:len(chrome.links.prev) ?> 9 15 <li class="first<?cs if:!len(chrome.links.next) ?> last<?cs /if ?>"> 10 16 ← <a href="<?cs var:chrome.links.prev.0.href ?>" title="<?cs … … 74 80 </dl> 75 81 </div> 76 82 </div> 77 <table id="chglist" class="listing"> 78 <thead> 79 <tr> 83 <table id="chglist" class="listing"><?cs 84 if:log.diff ?> 85 <form action="<?cs var:log.diff_href ?>" method="get"><?cs 86 /if ?> 87 <thead><?cs 88 if:log.diff ?> 89 <tr> 90 <th colspan="2"> 91 <div class="buttons"> 92 <input type="submit" id="diff" value="Diff" 93 title="Diff from Old Revision to New Revision (select them below)" /> 94 </div> 95 </th> 96 </tr><?cs 97 /if ?> 98 <tr><?cs 99 if:log.diff ?> 100 <th>Old</th> 101 <th>New</th><?cs 102 /if ?> 80 103 <th class="change"></th> 81 104 <th class="data">Date</th> 82 105 <th class="rev">Rev</th> … … 98 121 elif:log.mode == "path_history" ?><?cs 99 122 set:indent = #1 ?><?cs 100 123 /if ?> 101 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 124 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"><?cs 125 if:log.diff ?> 126 <td><input type="radio" name="old" value="<?cs var:item.rev ?>" <?cs 127 if:item.rev == #log.old ?> checked="checked" <?cs /if ?> /></td> 128 <td><input type="radio" name="new" value="<?cs var:item.rev ?>" <?cs 129 if:item.rev == #log.new ?> checked="checked" <?cs /if ?> /></td><?cs 130 /if ?> 102 131 <td class="change" style="padding-left:<?cs var:indent ?>em"> 103 132 <a title="View log starting at this revision" href="<?cs var:item.log_href ?>"> 104 133 <div class="<?cs var:item.change ?>"></div> … … 116 145 <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 117 146 </tr><?cs 118 147 /each ?> 119 </tbody> 148 </tbody><?cs 149 if:log.diff ?> 150 </form><?cs 151 /if ?> 120 152 </table><?cs 121 153 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 122 154 if:len(links.prev) ?><li class="first<?cs -
templates/browser.cs
3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <ul> 6 <li class="first"><a href="<?cs var:browser.diff_href ?>">Diff to previous</a></li> 6 7 <li class="last"><a href="<?cs var:browser.log_href ?>">Revision Log</a></li> 7 8 </ul> 8 9 </div> -
templates/diff.cs
2 2 <?cs include "macros.cs"?> 3 3 4 4 <div id="ctxtnav" class="nav"> 5 <h2> ChangesetNavigation</h2><?cs5 <h2>Diff Navigation</h2><?cs 6 6 with:links = chrome.links ?> 7 7 <ul><?cs 8 8 if:len(links.prev) ?> 9 9 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 10 10 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 11 var:links.prev.0.title ?>">Previous Changeset</a>11 var:links.prev.0.title ?>">Previous Diff</a> 12 12 </li><?cs 13 13 /if ?><?cs 14 14 if:len(links.next) ?> 15 15 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last"> 16 16 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 17 var:links.next.0.title ?>">Next Changeset</a>17 var:links.next.0.title ?>">Next Diff</a> 18 18 </li><?cs 19 19 /if ?> 20 20 </ul><?cs … … 22 22 </div> 23 23 24 24 <div id="content" class="changeset"> 25 <h1>Changeset <?cs var:changeset.revision ?></h1> 25 <h1><?cs 26 if:len(changeset) > #0 ?> 27 Changes for <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 28 <?cs var:diff.new_path ?></a> 29 in Revision <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 30 <?cs var:diff.new_rev ?></a><?cs 31 elif:diff.new_path == diff.old_path ?> 32 Differences for <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 33 <?cs var:diff.new_path ?></a> 34 between Revisions <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 35 <?cs var:diff.old_rev ?></a> 36 and <a title="Show full changese
