Ticket #2028: trac-diff-for-0.9.patch
| File trac-diff-for-0.9.patch, 91.6 KB (added by cboos, 6 years ago) |
|---|
-
htdocs/css/changeset.css
26 26 27 27 .diff ul.props { font-size: 90%; list-style: disc; margin: .5em 0 0; padding: 0 .5em 1em 2em } 28 28 .diff ul.props li { margin: 0; padding: 0 } 29 30 31 #title dl { 32 display: inline; 33 font-size: 110% 34 } 35 #title dt { 36 font-size: 110%; 37 font-weight: bold; 38 display: inline; 39 margin-left: 3em; 40 } 41 #title dd { 42 display: inline; 43 margin-left: 0.4em; 44 } -
htdocs/css/browser.css
46 46 #dirlist td.rev { text-align: right } 47 47 #dirlist td.change { font-size: 85%; vertical-align: middle; white-space: nowrap } 48 48 49 /* Log */ 50 tr.diff input { 51 padding: 0 1em 0 1em; 52 margin: 0; 53 } 54 55 div.buttons { 56 clear: left; 57 } 58 59 #anydiff { 60 margin: 0 0 1em; 61 float: left; 62 } 63 #anydiff form, #anydiff div, #anydiff h2 { 64 display: inline; 65 } 66 #anydiff input { 67 vertical-align: baseline; 68 margin: 0 -0.5em 0 1em; 69 } 70 71 49 72 /* Styles for the revision log table 50 73 (extends the styles for "table.listing") */ 51 74 #chglist { margin-top: 0 } -
wiki-default/WikiStart
1 = Welcome to Trac 0.9 =2 3 Trac is a '''minimalistic''' approach to '''web-based''' management of 4 '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress. 5 6 All aspects of Trac have been designed with the single goal to 7 '''help developers write great software''' while '''staying out of the way''' 8 and imposing as little as possible on a team's established process and 9 culture. 10 11 As all Wiki pages, this page is editable, this means that you can 12 modify the contents of this page simply by using your 13 web-browser. Simply click on the "Edit this page" link at the bottom 14 of the page. WikiFormatting will give you a detailed description of 15 available Wiki formatting commands. 16 17 "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created 18 a new Trac environment, containing a default set of wiki pages and some sample 19 data. This newly created environment also contains 20 [wiki:TracGuide documentation] to help you get started with your project. 21 22 You can use [wiki:TracAdmin trac-admin] to configure 23 [http://trac.edgewall.com/ Trac] to better fit your project, especially in 24 regard to ''components'', ''versions'' and ''milestones''. 25 26 27 TracGuide is a good place to start. 28 29 Enjoy! [[BR]] 30 ''The Trac Team'' 31 32 == Starting Points == 33 34 * TracGuide -- Built-in Documentation 35 * [http://projects.edgewall.com/trac/ The Trac project] -- Trac Open Source Project 36 * [http://projects.edgewall.com/trac/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions 37 * TracSupport -- Trac Support 38 39 For a complete list of local wiki pages, see TitleIndex. 40 41 Trac is brought to you by [http://www.edgewall.com/ Edgewall Software], 42 providing professional Linux and software development services to clients 43 worldwide. Visit http://www.edgewall.com/ for more information. 1 = Welcome to Trac 0.9-trac-diff = 2 3 Trac is a '''minimalistic''' approach to '''web-based''' management of 4 '''software projects'''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress. 5 6 All aspects of Trac have been designed with the single goal to 7 '''help developers write great software''' while '''staying out of the way''' 8 and imposing as little as possible on a team's established process and 9 culture. 10 11 As all Wiki pages, this page is editable, this means that you can 12 modify the contents of this page simply by using your 13 web-browser. Simply click on the "Edit this page" link at the bottom 14 of the page. WikiFormatting will give you a detailed description of 15 available Wiki formatting commands. 16 17 "[wiki:TracAdmin trac-admin] ''yourenvdir'' initenv" created 18 a new Trac environment, containing a default set of wiki pages and some sample 19 data. This newly created environment also contains 20 [wiki:TracGuide documentation] to help you get started with your project. 21 22 You can use [wiki:TracAdmin trac-admin] to configure 23 [http://trac.edgewall.com/ Trac] to better fit your project, especially in 24 regard to ''components'', ''versions'' and ''milestones''. 25 26 27 TracGuide is a good place to start. 28 29 Enjoy! [[BR]] 30 ''The Trac Team'' 31 32 == Starting Points == 33 34 * TracGuide -- Built-in Documentation 35 * [http://projects.edgewall.com/trac/ The Trac project] -- Trac Open Source Project 36 * [http://projects.edgewall.com/trac/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions 37 * TracSupport -- Trac Support 38 39 For a complete list of local wiki pages, see TitleIndex. 40 41 Trac is brought to you by [http://www.edgewall.com/ Edgewall Software], 42 providing professional Linux and software development services to clients 43 worldwide. Visit http://www.edgewall.com/ for more information. -
trac/__init__.py
10 10 """ 11 11 __docformat__ = 'epytext en' 12 12 13 __version__ = '0.9 '13 __version__ = '0.9-trac-diff' 14 14 __url__ = 'http://trac.edgewall.com/' 15 15 __copyright__ = '(C) 2003-2005 Edgewall Software' 16 16 __license__ = 'BSD' -
trac/versioncontrol/diff.py
217 217 ignore_space_changes) 218 218 for group in _group_opcodes(opcodes, context): 219 219 i1, i2, j1, j2 = group[0][1], group[-1][2], group[0][3], group[-1][4] 220 if i1 == 0 and i2 == 0: 221 i1, i2 = -1, -1 # support for 'A'dd changes 220 222 yield '@@ -%d,%d +%d,%d @@' % (i1 + 1, i2 - i1, j1 + 1, j2 - j1) 221 223 for tag, i1, i2, j1, j2 in group: 222 224 if tag == 'equal': -
trac/versioncontrol/api.py
122 122 'None' is a valid revision value and represents the youngest revision. 123 123 """ 124 124 return NotImplementedError 125 126 125 126 def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 127 """ 128 Generator that yields change tuples (old_node, new_node, kind, change) 129 for each node change between the two arbitrary (path,rev) pairs. 130 131 The old_node is assumed to be None when the change is an ADD, 132 the new_node is assumed to be None when the change is a DELETE. 133 """ 134 raise NotImplementedError 135 136 127 137 class Node(object): 128 138 """ 129 139 Represents a directory or file in the repository. … … 164 174 """ 165 175 raise NotImplementedError 166 176 177 def get_previous(self): 178 """ 179 Return the (path, rev, chg) tuple corresponding to the previous 180 revision for that node. 181 """ 182 skip = True 183 for p in self.get_history(2): 184 if skip: 185 skip = False 186 else: 187 return p 188 167 189 def get_properties(self): 168 190 """ 169 191 Returns a dictionary containing the properties (meta-data) of the node. -
trac/versioncontrol/tests/svn_fs.py
219 219 self.assertEqual(('tags/v1', 7, 'unknown'), history.next()) 220 220 self.assertRaises(StopIteration, history.next) 221 221 222 # Diffs 223 224 def _cmp_diff(self, expected, got): 225 if expected[0]: 226 old = self.repos.get_node(*expected[0]) 227 self.assertEqual((old.path, old.rev), (got[0].path, got[0].rev)) 228 if expected[1]: 229 new = self.repos.get_node(*expected[1]) 230 self.assertEqual((new.path, new.rev), (got[1].path, got[1].rev)) 231 self.assertEqual(expected[2], (got[2], got[3])) 232 233 def test_diff_file_different_revs(self): 234 diffs = self.repos.get_changes('trunk/README.txt', 2, 'trunk/README.txt', 3) 235 self._cmp_diff((('trunk/README.txt', 2), 236 ('trunk/README.txt', 3), 237 (Node.FILE, Changeset.EDIT)), diffs.next()) 238 self.assertRaises(StopIteration, diffs.next) 239 240 def test_diff_file_different_files(self): 241 diffs = self.repos.get_changes('branches/v1x/README.txt', 12, 242 'branches/v1x/README2.txt', 12) 243 self._cmp_diff((('branches/v1x/README.txt', 12), 244 ('branches/v1x/README2.txt', 12), 245 (Node.FILE, Changeset.EDIT)), diffs.next()) 246 self.assertRaises(StopIteration, diffs.next) 247 248 def test_diff_file_no_change(self): 249 diffs = self.repos.get_changes('trunk/README.txt', 7, 250 'tags/v1/README.txt', 7) 251 self.assertRaises(StopIteration, diffs.next) 252 253 def test_diff_dir_different_revs(self): 254 diffs = self.repos.get_changes('trunk', 4, 'trunk', 8) 255 self._cmp_diff((None, ('trunk/dir1/dir2', 8), 256 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 257 self._cmp_diff((None, ('trunk/dir1/dir3', 8), 258 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 259 self._cmp_diff((None, ('trunk/README2.txt', 6), 260 (Node.FILE, Changeset.ADD)), diffs.next()) 261 self._cmp_diff((('trunk/dir2', 4), None, 262 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 263 self._cmp_diff((('trunk/dir3', 4), None, 264 (Node.DIRECTORY, Changeset.DELETE)), diffs.next()) 265 self.assertRaises(StopIteration, diffs.next) 266 267 def test_diff_dir_different_dirs(self): 268 diffs = self.repos.get_changes('trunk', 1, 'branches/v1x', 12) 269 self._cmp_diff((None, ('branches/v1x/dir1', 12), 270 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 271 self._cmp_diff((None, ('branches/v1x/dir1/dir2', 12), 272 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 273 self._cmp_diff((None, ('branches/v1x/dir1/dir3', 12), 274 (Node.DIRECTORY, Changeset.ADD)), diffs.next()) 275 self._cmp_diff((None, ('branches/v1x/README.txt', 12), 276 (Node.FILE, Changeset.ADD)), diffs.next()) 277 self._cmp_diff((None, ('branches/v1x/README2.txt', 12), 278 (Node.FILE, Changeset.ADD)), diffs.next()) 279 self.assertRaises(StopIteration, diffs.next) 280 281 def test_diff_dir_no_change(self): 282 diffs = self.repos.get_changes('trunk', 7, 283 'tags/v1', 7) 284 self.assertRaises(StopIteration, diffs.next) 285 286 # Changesets 287 222 288 def test_changeset_repos_creation(self): 223 289 chgset = self.repos.get_changeset(0) 224 290 self.assertEqual(0, chgset.rev) -
trac/versioncontrol/svn_fs.py
349 349 expect_deletion = True 350 350 rev = self.previous_rev(rev) 351 351 352 def get_changes(self, old_path, old_rev, new_path, new_rev, 353 ignore_ancestry=0): 354 old_node = new_node = None 355 old_rev = self.normalize_rev(old_rev) 356 new_rev = self.normalize_rev(new_rev) 357 if self.has_node(old_path, old_rev): 358 old_node = self.get_node(old_path, old_rev) 359 else: 360 raise TracError, ('The Base for Diff is invalid: path %s' 361 ' doesn\'t exist in revision %s' \ 362 % (old_path, old_rev)) 363 if self.has_node(new_path, new_rev): 364 new_node = self.get_node(new_path, new_rev) 365 else: 366 raise TracError, ('The Target for Diff is invalid: path %s' 367 ' doesn\'t exist in revision %s' \ 368 % (new_path, new_rev)) 369 if new_node.kind != old_node.kind: 370 raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 371 'and Target is a %s (%s in revision %s).' \ 372 % (old_node.kind, old_path, old_rev, 373 new_node.kind, new_path, new_rev)) 374 subpool = Pool(self.pool) 375 if new_node.isdir: 376 editor = DiffChangeEditor() 377 e_ptr, e_baton = delta.make_editor(editor, subpool()) 378 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 379 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 380 def authz_cb(root, path, pool): return 1 381 text_deltas = 0 # as this is anyway re-done in Diff.py... 382 entry_props = 0 # "... typically used only for working copy updates" 383 repos.svn_repos_dir_delta(old_root, 384 (self.scope + old_path).strip('/'), '', 385 new_root, 386 (self.scope + new_path).strip('/'), 387 e_ptr, e_baton, authz_cb, 388 text_deltas, 389 1, # directory 390 entry_props, 391 ignore_ancestry, 392 subpool()) 393 for path, kind, change in editor.deltas: 394 old_node = new_node = None 395 if change != Changeset.ADD: 396 old_node = self.get_node(posixpath.join(old_path, path), 397 old_rev) 398 if change != Changeset.DELETE: 399 new_node = self.get_node(posixpath.join(new_path, path), 400 new_rev) 401 else: 402 kind = _kindmap[fs.check_path(old_root, 403 self.scope + old_node.path, 404 subpool())] 405 yield (old_node, new_node, kind, change) 406 else: 407 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 408 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) 409 if fs.contents_changed(old_root, self.scope + old_path, 410 new_root, self.scope + new_path, 411 subpool()): 412 yield (old_node, new_node, Node.FILE, Changeset.EDIT) 352 413 414 353 415 class SubversionNode(Node): 354 416 355 417 def __init__(self, path, rev, authz, scope, fs_ptr, pool=None): … … 371 433 self.pool()) 372 434 self.created_path = fs.node_created_path(self.root, self.scoped_path, 373 435 self.pool()) 374 # 'created_path' differs from 'path' if the last operation is a copy, 375 # and furthermore, 'path' might not exist at 'create_rev' 436 # Note: 'created_path' differs from 'path' if the last change was a copy, 437 # and furthermore, 'path' might not exist at 'create_rev'. 438 # The only guarantees are: 439 # * this node exists at (path,rev) 440 # * the node existed at (created_path,created_rev) 441 # TODO: check node id 376 442 self.rev = self.created_rev 377 443 378 444 Node.__init__(self, path, self.rev, _kindmap[node_type]) … … 417 483 if newer: 418 484 yield newer 419 485 486 # def get_previous(self): 487 # # FIXME: redo it with fs.node_history 488 420 489 def get_properties(self): 421 490 props = fs.node_proplist(self.root, self.scoped_path, self.pool()) 422 491 for name,value in props.items(): … … 506 575 507 576 def _get_prop(self, name): 508 577 return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool()) 578 579 580 # 581 # Delta editor for diffs between arbitrary nodes 582 # 583 # Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used 584 # because 'repos.svn_repos_dir_delta' *doesn't* provide it. 585 # 586 # Note 2: the 'dir_baton' is the path of the parent directory 587 # 588 589 class DiffChangeEditor(delta.Editor): 590 591 def __init__(self): 592 self.deltas = [] 593 594 # -- svn.delta.Editor callbacks 595 596 def open_root(self, base_revision, dir_pool): 597 return ('/', Changeset.EDIT) 598 599 def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev, 600 dir_pool): 601 self.deltas.append((path, Node.DIRECTORY, Changeset.ADD)) 602 return (path, Changeset.ADD) 603 604 def open_directory(self, path, dir_baton, base_revision, dir_pool): 605 return (path, dir_baton[1]) 606 607 def change_dir_prop(self, dir_baton, name, value, pool): 608 path, change = dir_baton 609 if change != Changeset.ADD: 610 self.deltas.append((path, Node.DIRECTORY, change)) 611 612 def delete_entry(self, path, revision, dir_baton, pool): 613 self.deltas.append((path, None, Changeset.DELETE)) 614 615 def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision, 616 dir_pool): 617 self.deltas.append((path, Node.FILE, Changeset.ADD)) 618 619 def open_file(self, path, dir_baton, dummy_rev, file_pool): 620 self.deltas.append((path, Node.FILE, Changeset.EDIT)) 621 -
trac/versioncontrol/cache.py
125 125 def normalize_rev(self, rev): 126 126 return self.repos.normalize_rev(rev) 127 127 128 def get_changes(self, old_path, old_rev, new_path, new_rev, ignore_ancestry=1): 129 return self.repos.get_changes(old_path, old_rev, new_path, new_rev, ignore_ancestry) 128 130 131 129 132 class CachedChangeset(Changeset): 130 133 131 134 def __init__(self, rev, db, authz): -
trac/versioncontrol/web_ui/diff.py
1 # -*- coding: iso-8859-1 -*- 2 # 3 # Copyright (C) 2003-2005 Edgewall Software 4 # Copyright (C) 2003-2005 Jonas Borgström <jonas@edgewall.com> 5 # Copyright (C) 2004-2005 Christopher Lenz <cmlenz@gmx.de> 6 # All rights reserved. 7 # 8 # This software is licensed as described in the file COPYING, which 9 # you should have received as part of this distribution. The terms 10 # are also available at http://trac.edgewall.com/license.html. 11 # 12 # This software consists of voluntary contributions made by many 13 # individuals. For the exact contribution history, see the revision 14 # history and logs, available at http://projects.edgewall.com/trac/. 15 # 16 # Author: Jonas Borgström <jonas@edgewall.com> 17 # Christopher Lenz <cmlenz@gmx.de> 18 # Christian Boos <cboos@neuf.fr> 19 20 import time 21 import re 22 import posixpath 23 from urllib import urlencode 24 25 from trac import mimeview, util 26 from trac.core import * 27 from trac.perm import IPermissionRequestor 28 from trac.versioncontrol import Changeset, Node 29 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff 30 from trac.versioncontrol.svn_authz import SubversionAuthorizer 31 from trac.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet 33 from trac.wiki import wiki_to_html, IWikiSyntaxProvider 34 35 class DiffArgs(dict): 36 def __getattr__(self,str): 37 return self[str] 38 39 40 class ChangesPermission(Component): 41 """Simple permission provider for changes related modules.""" 42 43 implements(IPermissionRequestor) 44 45 def get_permission_actions(self): 46 return ['CHANGESET_VIEW'] 47 48 49 class AbstractDiffModule(Component): 50 """Provide flexible functionality for showing sets of differences. 51 52 If the differences shown are coming from a specific changeset, 53 then that changeset informations can be shown too. 54 55 In addition, it is possible to show only a subset of the changeset: 56 Only the changes affecting a given path will be shown. 57 This is called the ''restricted'' changeset. 58 59 But the differences can also be computed in a more general way, 60 between two arbitrary paths and/or between two arbitrary revisions. 61 In that case, there's no changeset information displayed. 62 """ 63 64 abstract = True 65 66 implements(IRequestHandler) 67 68 # IRequestHandler methods 69 70 def match_request(self, req): 71 raise NotImplementedError 72 73 def process_request(self, req): 74 """The appropriate mode of operation is inferred from 75 the request parameters: 76 * If `old` and `new` parameters are given, it will be an 77 arbitrary set of differences: `chgset` is False. 78 * If `old_path` is given and is different from `path`, 79 it's a generalized diff, from `old_path@old` 80 to `path@new`: `restricted` is False. 81 * Otherwise those are differences between two arbitrary revisions 82 of a given path: `restricted` is True. 83 * Otherwise, we are dealing with a changeset only: `chgset` is True. 84 * If the `path` is not empty or not the root, then only 85 the changes affecting that path (i.e. itself, children or 86 ancestors) will be considered: `restricted` is True. 87 * Otherwise, it's the full changeset: `restricted` is False. 88 89 In any case, the given path@rev pair must exist. 90 """ 91 req.perm.assert_permission('CHANGESET_VIEW') 92 93 # -- retrieve arguments 94 path = req.args.get('path') 95 rev = req.args.get('rev') 96 old = req.args.get('old') 97 new = req.args.get('new') 98 old_path = req.args.get('old_path') 99 100 # -- normalize and check for special case 101 repos = self.env.get_repository(req.authname) 102 path = repos.normalize_path(path) 103 rev = repos.normalize_rev(rev) 104 if old_path: # Note: normalize_path now returns '/' if given 'None' 105 old_path = repos.normalize_path(old_path) 106 107 authzperm = SubversionAuthorizer(self.env, req.authname) 108 authzperm.assert_permission_for_changeset(rev) 109 110 if old_path == path and old and old == new: # revert to Changeset 111 rev = old 112 old_path = old = new = None 113 114 diff_options = get_diff_options(req) 115 116 # -- setup the `chgset` and `restricted` flags, see docstring above. 117 chgset = not old and not new and not old_path 118 if chgset: 119 restricted = path != '' and path != '/' # (subset or not) 120 else: 121 restricted = old_path == path # (same path or not) 122 123 # -- redirect if changing the diff options 124 if req.args.has_key('update'): 125 if chgset: 126 if restricted: 127 req.redirect(self.env.href.diff(path, rev=rev)) 128 else: 129 req.redirect(self.env.href.changeset(rev)) 130 else: 131 req.redirect(self.env.href.diff(path, new=new, 132 old_path=old_path, old=old)) 133 134 # -- preparing the diff arguments 135 if chgset: 136 prev = repos.get_node(path, rev).get_previous() 137 if prev: 138 prev_path, prev_rev = prev[:2] 139 else: 140 prev_path, prev_rev = path, repos.previous_rev(rev) 141 diff_args = DiffArgs(old_path=prev_path, old_rev=prev_rev, 142 new_path=path, new_rev=rev) 143 else: 144 if not new: 145 new = repos.youngest_rev 146 elif not old: 147 old = repos.youngest_rev 148 if not old_path: 149 old_path = path 150 diff_args = DiffArgs(old_path=old_path, old_rev=old, 151 new_path=path, new_rev=new) 152 if chgset: 153 chgset = repos.get_changeset(rev) 154 req.check_modified(chgset.date, 155 diff_options[0] + ''.join(diff_options[1])) 156 else: 157 pass # FIXME: what date should we choose for a diff? 158 159 req.hdf['diff'] = diff_args 160 161 format = req.args.get('format') 162 163 if format in ['diff', 'zip']: 164 # choosing an appropriate filename 165 rpath = path.replace('/','_') 166 if chgset: 167 if restricted: 168 filename = 'changeset_%s_r%s' % (rpath, rev) 169 else: 170 filename = 'changeset_r%s' % rev 171 else: 172 if restricted: 173 filename = 'diff-%s-from-r%s-to-r%s' \ 174 % (rpath, old, new) 175 elif old_path == '/': # special case for download (#238) 176 filename = '%s-r%s' % (rpath, old) 177 else: 178 filename = 'diff-from-%s-r%s-to-%s-r%s' \ 179 % (old_path.replace('/','_'), old, rpath, new) 180 if format == 'diff': 181 self._render_diff(req, filename, repos, diff_args, 182 diff_options) 183 return 184 elif format == 'zip': 185 self._render_zip(req, filename, repos, diff_args) 186 return 187 188 # -- HTML format 189 self._render_html(req, repos, chgset, restricted, 190 diff_args, diff_options) 191 if chgset: 192 diff_params = 'rev=%s' % rev 193 else: 194 diff_params = urlencode({'path': path, 195 'new': new, 196 'old_path': old_path, 197 'old': old}) 198 add_link(req, 'alternate', '?format=diff&'+diff_params, 'Unified Diff', 199 'text/plain', 'diff') 200 add_link(req, 'alternate', '?format=zip&'+diff_params, 'Zip Archive', 201 'application/zip', 'zip') 202 add_stylesheet(req, 'common/css/changeset.css') 203 add_stylesheet(req, 'common/css/diff.css') 204 add_stylesheet(req, 'common/css/code.css') 205 return 'diff.cs', None 206 207 208 # Internal methods 209 210 def _render_html(self, req, repos, chgset, restricted, diff, diff_options): 211 """ 212 HTML version 213 """ 214 req.hdf['diff'] = { 215 'chgset': chgset and True, 216 'restricted': restricted, 217 'href': { 'new_rev': self.env.href.changeset(diff.new_rev), 218 'old_rev': self.env.href.changeset(diff.old_rev), 219 'new_path': self.env.href.browser(diff.new_path, 220 rev=diff.new_rev), 221 'old_path': self.env.href.browser(diff.old_path, 222 rev=diff.old_rev) 223 } 224 } 225 226 if chgset: # Changeset Mode (possibly restricted on a path) 227 path, rev = diff.new_path, diff.new_rev 228 229 # -- getting the change summary from the Changeset.get_changes method 230 def get_changes(): 231 old_node = new_node = None 232 for npath, kind, change, opath, orev in chgset.get_changes(): 233 if restricted and \ 234 not (npath.startswith(path) # npath is below 235 or path.startswith(npath)): # npath is above 236 continue 237 if change != Changeset.ADD: 238 old_node = repos.get_node(opath, orev) 239 if change != Changeset.DELETE: 240 new_node = repos.get_node(npath, rev) 241 yield old_node, new_node, kind, change 242 243 def _changeset_title(rev): 244 if restricted: 245 return 'Changeset %s for %s' % (rev, path) 246 else: 247 return 'Changeset %s' % rev 248 249 title = _changeset_title(rev) 250 req.hdf['changeset'] = { 251 'revision': chgset.rev, 252 'time': util.format_datetime(chgset.date), 253 'author': util.escape(chgset.author or 'anonymous'), 254 'message': wiki_to_html(chgset.message or '--', self.env, req, 255 escape_newlines=True) 256 } 257 oldest_rev = repos.oldest_rev 258 if chgset.rev != oldest_rev: 259 if restricted: 260 prev = repos.get_node(path, rev).get_previous() 261 if prev: 262 prev_path, prev_rev = prev[:2] 263 prev_href = self.env.href.changeset(prev_rev, prev_path) 264 else: 265 prev_path = prev_rev = None 266 else: 267 prev_path = diff.old_path 268 prev_rev = repos.previous_rev(chgset.rev) 269 add_link(req, 'first', self.env.href.changeset(oldest_rev), 270 'Changeset %s' % oldest_rev) 271 prev_href = self.env.href.changeset(prev_rev) 272 if prev_rev: 273 add_link(req, 'prev', prev_href, _changeset_title(prev_rev)) 274 youngest_rev = repos.youngest_rev 275 if str(chgset.rev) != str(youngest_rev): 276 if restricted: 277 next_rev = next_href = None 278 # FIXME: find an effective way to find the next rev 279 else: 280 next_rev = repos.next_rev(chgset.rev) 281 next_href = self.env.href.changeset(next_rev) 282 add_link(req, 'last', 283 self.env.href.diff(path, rev=youngest_rev), 284 'Changeset %s' % youngest_rev) 285 if next_rev: 286 add_link(req, 'next', next_href, _changeset_title(next_rev)) 287 288 else: # Diff Mode 289 # -- getting the change summary from the Repository.get_changes method 290 def get_changes(): 291 for d in repos.get_changes(**diff): 292 yield d 293 294 reverse_href = self.env.href.diff(diff.old_path, 295 new=diff.old_rev, 296 old_path=diff.new_path, 297 old=diff.new_rev) 298 req.hdf['diff.reverse_href'] = reverse_href 299 title = self.title_for_diff(diff) 300 req.hdf['title'] = title 301 302 def _change_info(old_node, new_node, change): 303 info = {'change': change} 304 if old_node: 305 info['path.old'] = old_node.path 306 info['rev.old'] = old_node.rev # this is the created rev. 307 old_href = self.env.href.browser(old_node.path, 308 rev=diff.old_rev) 309 # Reminder: old_node.path may not exist at old_node.rev 310 info['browser_href.old'] = old_href 311 if new_node: 312 info['path.new'] = new_node.path 313 info['rev.new'] = new_node.rev # created rev. 314 new_href = self.env.href.browser(new_node.path, 315 rev=diff.new_rev) 316 # (same remark as above) 317 info['browser_href.new'] = new_href 318 return info 319 320 hidden_properties = [p.strip() for p 321 in self.config.get('browser', 'hide_properties', 322 'svk:merge').split(',')] 323 324 def _prop_changes(old_node, new_node): 325 old_props = old_node.get_properties() 326 new_props = new_node.get_properties() 327 changed_props = {} 328 if old_props != new_props: 329 for k,v in old_props.items(): 330 if not k in new_props: 331 changed_props[k] = {'old': v} 332 elif v != new_props[k]: 333 changed_props[k] = {'old': v, 'new': new_props[k]} 334 for k,v in new_props.items(): 335 if not k in old_props: 336 changed_props[k] = {'new': v} 337 for k in hidden_properties: 338 if k in changed_props: 339 del changed_props[k] 340 return changed_props 341 342 def _content_changes(old_node, new_node): 343 """ 344 Returns the list of differences. 345 The list is empty when no differences between comparable files 346 are detected, but the return value is None for non-comparable files. 347 """ 348 default_charset = self.config.get('trac', 'default_charset') 349 old_content = old_node.get_content().read() 350 if mimeview.is_binary(old_content): 351 return None 352 charset = mimeview.get_charset(old_node.content_type) 353 if not charset: 354 charset = mimeview.detect_unicode(old_content) 355 old_content = util.to_utf8(old_content, charset or default_charset) 356 357 new_content = new_node.get_content().read() 358 if mimeview.is_binary(new_content): 359 return None 360 charset = mimeview.get_charset(new_node.content_type) 361 if not charset: 362 charset = mimeview.detect_unicode(new_content) 363 new_content = util.to_utf8(new_content, charset or default_charset) 364 365 if old_content != new_content: 366 context = 3 367 options = diff_options[1] 368 for option in options: 369 if option.startswith('-U'): 370 context = int(option[2:]) 371 break 372 if context < 0: 373 context = None 374 tabwidth = int(self.config.get('diff', 'tab_width', 375 self.config.get('mimeviewer', 376 'tab_width'))) 377 return hdf_diff(old_content.splitlines(), 378 new_content.splitlines(), 379 context, tabwidth, 380 ignore_blank_lines='-B' in options, 381 ignore_case='-i' in options, 382 ignore_space_changes='-b' in options) 383 else: 384 return [] 385 386 idx = 0 387 for old_node, new_node, kind, change in get_changes(): 388 if change != Changeset.EDIT: 389 show_entry = True 390 else: 391 show_entry = False 392 assert old_node and new_node 393 props = _prop_changes(old_node, new_node) 394 if props: 395 req.hdf['diff.changes.%d.props' % idx] = props 396 show_entry = True 397 if kind == Node.FILE: 398 diffs = _content_changes(old_node, new_node) 399 if diffs != []: 400 if diffs: 401 req.hdf['diff.changes.%d.diff' % idx] = diffs 402 # elif None (means: manually compare to (previous)) 403 show_entry = True 404 if show_entry: 405 info = _change_info(old_node, new_node, change) 406 req.hdf['diff.changes.%d' % idx] = info 407 idx += 1 # the sequence should be immutable 408 409 def _render_diff(self, req, filename, repos, diff, diff_options): 410 """Raw Unified Diff version""" 411 req.send_response(200) 412 req.send_header('Content-Type', 'text/plain;charset=utf-8') 413 req.send_header('Content-Disposition', 414 'filename=%s.diff' % filename) 415 req.end_headers() 416 417 for old_node, new_node, kind, change in repos.get_deltas(**diff): 418 # TODO: Property changes 419 420 # Content changes 421 if kind == Node.DIRECTORY: 422 continue 423 424 default_charset = self.config.get('trac', 'default_charset') 425 new_content = old_content = '' 426 new_node_info = old_node_info = ('','') 427 428 if old_node: 429 charset = mimeview.get_charset(old_node.content_type) or \ 430 default_charset 431 old_content = util.to_utf8(old_node.get_content().read(), 432 charset) 433 old_node_info = (old_node.path, old_node.rev) 434 if mimeview.is_binary(old_content): 435 continue 436 437 if new_node: 438 charset = mimeview.get_charset(new_node.content_type) or \ 439 default_charset 440 new_content = util.to_utf8(new_node.get_content().read(), 441 charset) 442 new_node_info = (new_node.path, new_node.rev) 443 if mimeview.is_binary(new_content): 444 continue 445 new_path = new_node.path 446 else: 447 old_node_path = repos.normalize_path(old_node.path) 448 diff_old_path = repos.normalize_path(diff.old_path) 449 new_path = posixpath.join(diff.new_path, 450 old_node_path[len(diff_old_path)+1:]) 451 452 if old_content != new_content: 453 context = 3 454 options = diff_options[1] 455 for option in options: 456 if option.startswith('-U'): 457 context = int(option[2:]) 458 break 459 if not old_node_info[0]: 460 old_node_info = new_node_info # support for 'A'dd changes 461 req.write('Index: ' + new_path + util.CRLF) 462 req.write('=' * 67 + util.CRLF) 463 req.write('--- %s (revision %s)' % old_node_info + 464 util.CRLF) 465 req.write('+++ %s (revision %s)' % new_node_info + 466 util.CRLF) 467 for line in unified_diff(old_content.splitlines(), 468 new_content.splitlines(), context, 469 ignore_blank_lines='-B' in options, 470 ignore_case='-i' in options, 471 ignore_space_changes='-b' in options): 472 req.write(line + util.CRLF) 473 474 def _render_zip(self, req, filename, repos, diff): 475 """ZIP archive with all the added and/or modified files.""" 476 new_rev = diff.new_rev 477 req.send_response(200) 478 req.send_header('Content-Type', 'application/zip') 479 req.send_header('Content-Disposition', 480 'filename=%s.zip' % filename) 481 482 try: 483 from cStringIO import StringIO 484 except ImportError: 485 from StringIO import StringIO 486 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED 487 488 buf = StringIO() 489 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 490 for old_node, new_node, kind, change in repos.get_deltas(**diff): 491 if kind == Node.FILE and change != Changeset.DELETE: 492 assert new_node 493 zipinfo = ZipInfo() 494 zipinfo.filename = new_node.path 495 zipinfo.date_time = time.gmtime(new_node.last_modified)[:6] 496 zipinfo.compress_type = ZIP_DEFLATED 497 zipfile.writestr(zipinfo, new_node.get_content().read()) 498 zipfile.close() 499 500 buf.seek(0, 2) # be sure to be at the end 501 req.send_header("Content-Length", buf.tell()) 502 req.end_headers() 503 504 req.write(buf.getvalue()) 505 506 def title_for_diff(self, diff): 507 if diff.new_path == diff.old_path: # ''diff between 2 revisions'' mode 508 return 'Diff r%s:%s for %s' \ 509 % (diff.old_rev or 'latest', diff.new_rev or 'latest', 510 diff.new_path or '/') 511 else: # ''arbitrary diff'' mode 512 return 'Diff from %s@%s to %s@%s' \ 513 % (diff.old_path or '/', diff.old_rev or 'latest', 514 diff.new_path or '/', diff.new_rev or 'latest') 515 516 517 518 class DiffModule(AbstractDiffModule): 519 520 implements(IWikiSyntaxProvider) 521 522 # (reimplemented) IRequestHandler methods 523 524 def match_request(self, req): 525 match = re.match(r'/diff(?:(/.*)|$)', req.path_info) 526 if match: 527 if match.group(1): 528 req.args['path'] = match.group(1) 529 return 1 530 531 # IWikiSyntaxProvider methods 532 533 def get_wiki_syntax(self): 534 return [] 535 536 def get_link_resolvers(self): 537 yield ('diff', self._format_link) 538 539 def _format_link(self, formatter, ns, params, label): 540 def pathrev(path): 541 if '@' in path: 542 return path.split('@', 1) 543 else: 544 return (path, None) 545 if '//' in params: 546 p1, p2 = params.split('//', 1) 547 old, new = pathrev(p1), pathrev(p2) 548 diff = DiffArgs(old_path=old[0], old_rev=old[1], 549 new_path=new[0], new_rev=new[1]) 550 else: 551 old_path, old_rev = pathrev(params) 552 new_rev = None 553 if old_rev and ':' in old_rev: 554 old_rev, new_rev = old_rev.split(':', 1) 555 diff = DiffArgs(old_path=old_path, old_rev=old_rev, 556 new_path=old_path, new_rev=new_rev) 557 title = self.title_for_diff(diff) 558 href = formatter.href.diff(diff.new_path, new=diff.new_rev, 559 old_path=diff.old_path, old=diff.old_rev) 560 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 561 % (title, href, label) 562 563 564 class AnyDiffModule(Component): 565 566 implements(IRequestHandler) 567 568 # IRequestHandler methods 569 570 def match_request(self, req): 571 return re.match(r'/anydiff$', req.path_info) 572 573 def process_request(self, req): 574 # -- retrieve arguments 575 new_path = req.args.get('new_path') 576 new_rev = req.args.get('new_rev') 577 old_path = req.args.get('old_path') 578 old_rev = req.args.get('old_rev') 579 580 # -- normalize 581 repos = self.env.get_repository(req.authname) 582 new_path = repos.normalize_path(new_path) 583 new_rev = repos.normalize_rev(new_rev) 584 old_path = repos.normalize_path(old_path) 585 old_rev = repos.normalize_rev(old_rev) 586 587 authzperm = SubversionAuthorizer(self.env, req.authname) 588 authzperm.assert_permission_for_changeset(new_rev) 589 authzperm.assert_permission_for_changeset(old_rev) 590 591 # -- prepare rendering 592 req.hdf['anydiff'] = { 593 'new_path': new_path, 594 'new_rev': new_rev, 595 'old_path': old_path, 596 'old_rev': old_rev, 597 'diff_href': self.env.href.diff(), 598 } 599 600 return 'anydiff.cs', None -
trac/versioncontrol/web_ui/__init__.py
Property changes on: trac\versioncontrol\web_ui\diff.py ___________________________________________________________________ Name: svn:eol-style + native
1 1 from trac.versioncontrol.web_ui.browser import * 2 2 from trac.versioncontrol.web_ui.changeset import * 3 3 from trac.versioncontrol.web_ui.log import * 4 from trac.versioncontrol.web_ui.diff import * -
trac/versioncontrol/web_ui/changeset.py
22 22 23 23 from trac import mimeview, util 24 24 from trac.core import * 25 from trac.perm import IPermissionRequestor26 25 from trac.Search import ISearchSource, query_to_sql, shorten_result 27 26 from trac.Timeline import ITimelineEventProvider 28 27 from trac.versioncontrol import Changeset, Node 29 28 from trac.versioncontrol.svn_authz import SubversionAuthorizer 30 from trac.versioncontrol.diff import get_diff_options, hdf_diff, unified_diff31 29 from trac.web import IRequestHandler 32 from trac.web.chrome import add_link, add_stylesheet,INavigationContributor30 from trac.web.chrome import INavigationContributor 33 31 from trac.wiki import wiki_to_html, wiki_to_oneliner, IWikiSyntaxProvider 32 from trac.versioncontrol.web_ui.diff import AbstractDiffModule 34 33 34 class ChangesetModule(AbstractDiffModule): 35 35 36 class ChangesetModule(Component): 37 38 implements(INavigationContributor, IPermissionRequestor, IRequestHandler, 36 implements(INavigationContributor, 39 37 ITimelineEventProvider, IWikiSyntaxProvider, ISearchSource) 40 38 41 39 # INavigationContributor methods … … 46 44 def get_navigation_items(self, req): 47 45 return [] 48 46 49 # IPermissionRequestor methods47 # (reimplemented) IRequestHandler methods 50 48 51 def get_permission_actions(self):52 return ['CHANGESET_VIEW']53 54 # IRequestHandler methods55 56 49 def match_request(self, req): 57 match = re.match(r'/changeset/([0-9]+) $', req.path_info)50 match = re.match(r'/changeset/([0-9]+)(/.*)?$', req.path_info) 58 51 if match: 59 52 req.args['rev'] = match.group(1) 53 path = match.group(2) 54 if path: 55 req.args['path'] = path 60 56 return 1 61 57 62 def process_request(self, req):63 req.perm.assert_permission('CHANGESET_VIEW')64 65 rev = req.args.get('rev')66 repos = self.env.get_repository(req.authname)67 authzperm = SubversionAuthorizer(self.env, req.authname)68 authzperm.assert_permission_for_changeset(rev)69 70 diff_options = get_diff_options(req)71 if req.args.has_key('update'):72 req.redirect(self.env.href.changeset(rev))73 74 chgset = repos.get_changeset(rev)75 req.check_modified(chgset.date,76 diff_options[0] + ''.join(diff_options[1]))77 78 format = req.args.get('format')79 if format == 'diff':80 self._render_diff(req, repos, chgset, diff_options)81 return82 elif format == 'zip':83 self._render_zip(req, repos, chgset)84 return85 86 self._render_html(req, repos, chgset, diff_options)87 add_link(req, 'alternate', '?format=diff', 'Unified Diff',88 'text/plain', 'diff')89 add_link(req, 'alternate', '?format=zip', 'Zip Archive',90 'application/zip', 'zip')91 add_stylesheet(req, 'common/css/changeset.css')92 add_stylesheet(req, 'common/css/diff.css')93 add_stylesheet(req, 'common/css/code.css')94 return 'changeset.cs', None95 96 58 # ITimelineEventProvider methods 97 59 98 60 def get_timeline_filters(self, req): … … 146 108 message 147 109 rev = repos.previous_rev(rev) 148 110 149 # Internal methods150 111 151 def _render_html(self, req, repos, chgset, diff_options):152 """HTML version"""153 req.hdf['title'] = '[%s]' % chgset.rev154 req.hdf['changeset'] = {155 'revision': chgset.rev,156 'time': util.format_datetime(chgset.date),157 'author': util.escape(chgset.author or 'anonymous'),158 'message': wiki_to_html(chgset.message or '--', self.env, req,159 escape_newlines=True)160 }161 162 oldest_rev = repos.oldest_rev163 if chgset.rev != oldest_rev:164 add_link(req, 'first', self.env.href.changeset(oldest_rev),165 'Changeset %s' % oldest_rev)166 previous_rev = repos.previous_rev(chgset.rev)167 add_link(req, 'prev', self.env.href.changeset(previous_rev),168 'Changeset %s' % previous_rev)169 youngest_rev = repos.youngest_rev170 if str(chgset.rev) != str(youngest_rev):171 next_rev = repos.next_rev(chgset.rev)172 add_link(req, 'next', self.env.href.changeset(next_rev),173 'Changeset %s' % next_rev)174 add_link(req, 'last', self.env.href.changeset(youngest_rev),175 'Changeset %s' % youngest_rev)176 177 edits = []178 idx = 0179 for path, kind, change, base_path, base_rev in chgset.get_changes():180 info = {'change': change}181 if base_path:182 info['path.old'] = base_path183 info['rev.old'] = base_rev184 info['browser_href.old'] = self.env.href.browser(base_path,185 rev=base_rev)186 if path:187 info['path.new'] = path188 info['rev.new'] = chgset.rev189 info['browser_href.new'] = self.env.href.browser(path,190 rev=chgset.rev)191 if change in (Changeset.COPY, Changeset.EDIT, Changeset.MOVE):192 edits.append((idx, path, kind, base_path, base_rev))193 req.hdf['changeset.changes.%d' % idx] = info194 idx += 1195 196 hidden_properties = [p.strip() for p197 in self.config.get('browser', 'hide_properties',198 'svk:merge').split(',')]199 200 for idx, path, kind, base_path, base_rev in edits:201 old_node = repos.get_node(base_path or path, base_rev)202 new_node = repos.get_node(path, chgset.rev)203 204 # Property changes205 old_props = old_node.get_properties()206 new_props = new_node.get_properties()207 changed_props = {}208 if old_props != new_props:209 for k,v in old_props.items():210 if not k in new_props:211 changed_props[k] = {'old': v}212 elif v != new_props[k]:213 changed_props[k] = {'old': v, 'new': new_props[k]}214 for k,v in new_props.items():215 if not k in old_props:216 changed_props[k] = {'new': v}217 for k in hidden_properties:218 if k in changed_props:219 del changed_props[k]220 req.hdf['changeset.changes.%d.props' % idx] = changed_props221 222 if kind == Node.DIRECTORY:223 continue224 225 # Content changes226 default_charset = self.config.get('trac', 'default_charset')227 old_content = old_node.get_content().read()228 if mimeview.is_binary(old_content):229 continue230 charset = mimeview.get_charset(old_node.content_type)231 if not charset:232 charset = mimeview.detect_unicode(old_content)233 old_content = util.to_utf8(old_content, charset or default_charset)234 235 new_content = new_node.get_content().read()236 if mimeview.is_binary(new_content):237 continue238 charset = mimeview.get_charset(new_node.content_type)239 if not charset:240 charset = mimeview.detect_unicode(new_content)241 new_content = util.to_utf8(new_content, charset or default_charset)242 243 if old_content != new_content:244 context = 3245 for option in diff_options[1]:246 if option.startswith('-U'):247 context = int(option[2:])248 break249 if context < 0:250 context = None251 tabwidth = int(self.config.get('diff', 'tab_width',252 self.config.get('mimeviewer',253 'tab_width')))254 changes = hdf_diff(old_content.splitlines(),255 new_content.splitlines(),256 context, tabwidth,257 ignore_blank_lines='-B' in diff_options[1],258 ignore_case='-i' in diff_options[1],259 ignore_space_changes='-b' in diff_options[1])260 req.hdf['changeset.changes.%d.diff' % idx] = changes261 262 def _render_diff(self, req, repos, chgset, diff_options):263 """Raw Unified Diff version"""264 req.send_response(200)265 req.send_header('Content-Type', 'text/plain;charset=utf-8')266 req.send_header('Content-Disposition',267 'filename=Changeset%s.diff' % req.args.get('rev'))268 req.end_headers()269 270 for path, kind, change, base_path, base_rev in chgset.get_changes():271 if change == Changeset.ADD:272 old_node = None273 else:274 old_node = repos.get_node(base_path or path, base_rev)275 if change == Changeset.DELETE:276 new_node = None277 else:278 new_node = repos.get_node(path, chgset.rev)279 280 # TODO: Property changes281 282 # Content changes283 if kind == 'dir':284 continue285 286 default_charset = self.config.get('trac', 'default_charset')287 new_content = old_content = ''288 new_node_info = old_node_info = ('','')289 290 if old_node:291 charset = mimeview.get_charset(old_node.content_type) or \292 default_charset293 old_content = util.to_utf8(old_node.get_content().read(),294 charset)295 old_node_info = (old_node.path, old_node.rev)296 if mimeview.is_binary(old_content):297 continue298 299 if new_node:300 charset = mimeview.get_charset(new_node.content_type) or \301 default_charset302 new_content = util.to_utf8(new_node.get_content().read(),303 charset)304 new_node_info = (new_node.path, new_node.rev)305 if mimeview.is_binary(new_content):306 continue307 308 if old_content != new_content:309 context = 3310 for option in diff_options[1]:311 if option.startswith('-U'):312 context = int(option[2:])313 break314 req.write('Index: ' + path + util.CRLF)315 req.write('=' * 67 + util.CRLF)316 req.write('--- %s (revision %s)' % old_node_info +317 util.CRLF)318 req.write('+++ %s (revision %s)' % new_node_info +319 util.CRLF)320 for line in unified_diff(old_content.splitlines(),321 new_content.splitlines(), context,322 ignore_blank_lines='-B' in diff_options[1],323 ignore_case='-i' in diff_options[1],324 ignore_space_changes='-b' in diff_options[1]):325 req.write(line + util.CRLF)326 327 def _render_zip(self, req, repos, chgset):328 """ZIP archive with all the added and/or modified files."""329 req.send_response(200)330 req.send_header('Content-Type', 'application/zip')331 req.send_header('Content-Disposition',332 'filename=Changeset%s.zip' % chgset.rev)333 req.end_headers()334 335 try:336 from cStringIO import StringIO337 except ImportError:338 from StringIO import StringIO339 from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED340 341 buf = StringIO()342 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED)343 for path, kind, change, base_path, base_rev in chgset.get_changes():344 if kind == Node.FILE and change != Changeset.DELETE:345 node = repos.get_node(path, chgset.rev)346 zipinfo = ZipInfo()347 zipinfo.filename = node.path348 zipinfo.date_time = time.gmtime(node.last_modified)[:6]349 zipinfo.compress_type = ZIP_DEFLATED350 zipfile.writestr(zipinfo, node.get_content().read())351 zipfile.close()352 req.write(buf.getvalue())353 354 112 # IWikiSyntaxProvider methods 355 113 356 114 def get_wiki_syntax(self): 357 yield (r"!?\[\d+ \]|(?:\b|!)r\d+\b(?!:\d)",115 yield (r"!?\[\d+(?:/[^\]]*)?\]|(?:\b|!)r\d+\b(?!:\d)", 358 116 lambda x, y, z: self._format_link(x, 'changeset', 359 117 y[0] == 'r' and y[1:] 360 or y[1:-1], y ))118 or y[1:-1], y, z)) 361 119 362 120 def get_link_resolvers(self): 363 121 yield ('changeset', self._format_link) 364 122 365 def _format_link(self, formatter, ns, rev, label): 123 def _format_link(self, formatter, ns, chgset, label, fullmatch=None): 124 sep = chgset.find('/') 125 if sep > 0: 126 rev, path = chgset[:sep], chgset[sep:] 127 else: 128 rev, path = chgset, None 366 129 cursor = formatter.db.cursor() 367 130 cursor.execute('SELECT message FROM revision WHERE rev=%s', (rev,)) 368 131 row = cursor.fetchone() 369 132 if row: 370 133 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 371 134 % (util.escape(util.shorten_line(row[0])), 372 formatter.href.changeset(rev ), label)135 formatter.href.changeset(rev, path), label) 373 136 else: 374 return '<a class="missing changeset" href="%s" rel="nofollow">%s</a>' \ 375 % (formatter.href.changeset(rev), label) 137 return '<a class="missing changeset" href="%s"' \ 138 ' rel="nofollow">%s</a>' \ 139 % (formatter.href.changeset(rev, path), label) 376 140 377 141 # ISearchProvider methods 378 142 -
trac/versioncontrol/web_ui/log.py
67 67 stop_rev = req.args.get('stop_rev') 68 68 verbose = req.args.get('verbose') 69 69 limit = LOG_LIMIT 70 old = req.args.get('old') 71 new = req.args.get('new') 70 72 73 repos = self.env.get_repository(req.authname) 74 normpath = repos.normalize_path(path) 75 rev = str(repos.normalize_rev(rev)) 76 77 if old and new: 78 osep = util.unescape(old).rindex('#') 79 nsep = util.unescape(new).rindex('#') 80 old_path, old_rev = old[:osep], old[osep+1:] 81 new_path, new_rev = new[:nsep], new[nsep+1:] 82 req.redirect(self.env.href.diff(new_path, new=new_rev, 83 old_path=old_path, old=old_rev)) 84 71 85 req.hdf['title'] = path + ' (log)' 72 86 req.hdf['log'] = { 73 87 'mode': mode, … … 84 98 if path_links: 85 99 add_link(req, 'up', path_links[-1]['href'], 'Parent directory') 86 100 87 repos = self.env.get_repository(req.authname)88 normpath = repos.normalize_path(path)89 rev = str(repos.normalize_rev(rev))90 101 91 102 # ''Node history'' uses `Node.history()`, 92 103 # ''Path history'' uses `Repository.get_path_history()` -
trac/versioncontrol/web_ui/browser.py
88 88 89 89 repos = self.env.get_repository(req.authname) 90 90 node = get_existing_node(self.env, repos, path, rev) 91 rev = repos.normalize_rev(rev) 91 92 92 93 hidden_properties = [p.strip() for p 93 94 in self.config.get('browser', 'hide_properties', 94 95 'svk:merge').split(',')] 96 95 97 req.hdf['title'] = path 96 req.hdf['browser']= {98 browser_hdf = { 97 99 'path': path, 98 'revision': rev or repos.youngest_rev,100 'revision': rev, 99 101 'props': dict([(util.escape(name), util.escape(value)) 100 102 for name, value in node.get_properties().items() 101 if not name in hidden_properties]), 102 'href': util.escape(self.env.href.browser(path, rev=rev or 103 repos.youngest_rev)), 104 'log_href': util.escape(self.env.href.log(path, rev=rev or None)) 105 } 103 if not name in hidden_properties]) 104 } 105 browser_hrefs = { 106 'href': self.env.href.browser(path,rev=rev), 107 'restr_changeset_href': self.env.href.changeset(node.rev, path), 108 'anydiff_href': self.env.href.anydiff(), 109 'log_href': self.env.href.log(path, rev=rev) 110 } 111 browser_hdf.update(dict([(key, util.escape(href)) for key, href in 112 browser_hrefs.items()])) 113 req.hdf['browser'] = browser_hdf 106 114 107 115 path_links = get_path_links(self.env.href, path, rev) 108 116 if len(path_links) > 1: … … 162 170 163 171 req.hdf['browser.items'] = info 164 172 req.hdf['browser.changes'] = changes 165 173 if node.path != '': 174 zip_href = self.env.href.diff(node.path, new=rev, old=rev, 175 old_path='/', # special case (#238) 176 format='zip') 177 add_link(req, 'alternate', zip_href, 'Zip Archive', 178 'application/zip', 'zip') 179 180 166 181 def _render_file(self, req, repos, node, rev=None): 167 182 req.perm.assert_permission('FILE_VIEW') 168 183 -
trac/wiki/tests/wiki-tests.txt
50 50 ||Foo||Bar||Baz|| 51 51 <a class="ext-link" href="http://www.edgewall.com/"><span class="icon"></span>http://www.edgewall.com/</a> 52 52 ============================== 53 #1, [1], r1, {1} 53 #1, {1} 54 [1], r1 55 [1/README.txt] 54 56 ------------------------------ 55 57 <p> 56 <a class="missing ticket" href="/ticket/1" rel="nofollow">#1</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">[1]</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">r1</a>, <a class="report" href="/report/1">{1}</a> 58 <a class="missing ticket" href="/ticket/1" rel="nofollow">#1</a>, <a class="report" href="/report/1">{1}</a> 59 <a class="missing changeset" href="/changeset/1" rel="nofollow">[1]</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">r1</a> 60 <a class="missing changeset" href="/changeset/1/README.txt" rel="nofollow">[1/README.txt]</a> 57 61 </p> 58 62 ------------------------------ 59 63 ============================== … … 80 84 ------------------------------ 81 85 [1:2], r1:2, [12:23], r12:23 82 86 ============================== 83 ticket:1, changeset:1, report:1, source:foo/bar 87 ticket:1, report:1, source:foo/bar 88 changeset:1, changeset:1/README.txt 84 89 85 90 Issue [ticket:1], CS[changeset:1], Listing [report:1], File [source:foo/bar] 86 91 ------------------------------ 87 92 <p> 88 <a class="missing ticket" href="/ticket/1" rel="nofollow">ticket:1</a>, <a class="missing changeset" href="/changeset/1" rel="nofollow">changeset:1</a>, <a class="report" href="/report/1">report:1</a>, <a class="source" href="/browser/foo/bar">source:foo/bar</a> 93 <a class="missing ticket" href="/ticket/1" rel="nofollow">ticket:1</a>, <a class="report" href="/report/1">report:1</a>, <a class="source" href="/browser/foo/bar">source:foo/bar</a> 94 <a class="missing changeset" href="/changeset/1" rel="nofollow">changeset:1</a>, <a class="missing changeset" href="/changeset/1/README.txt" rel="nofollow">changeset:1/README.txt</a> 89 95 </p> 90 96 <p> 91 97 Issue <a class="missing ticket" href="/ticket/1" rel="nofollow">1</a>, CS<a class="missing changeset" href="/changeset/1" rel="nofollow">1</a>, Listing <a class="report" href="/report/1">1</a>, File <a class="source" href="/browser/foo/bar">foo/bar</a> … … 110 116 </p> 111 117 ------------------------------ 112 118 ============================== 119 diff:trunk//branch 120 diff:trunk@12//branch@23 121 diff:trunk@12:23 122 diff:@12:23 123 ------------------------------ 124 <p> 125 <a class="changeset" title="Diff from trunk@latest to branch@latest" href="/diff/branch?old_path=trunk">diff:trunk//branch</a> 126 <a class="changeset" title="Diff from trunk@12 to branch@23" href="/diff/branch?new=23&old=12&old_path=trunk">diff:trunk@12//branch@23</a> 127 <a class="changeset" title="Diff r12:23 for trunk" href="/diff/trunk?new=23&old=12&old_path=trunk">diff:trunk@12:23</a> 128 <a class="changeset" title="Diff r12:23 for /" href="/diff/?new=23&old=12&old_path=">diff:@12:23</a> 129 </p> 130 ------------------------------ 131 ============================== 113 132 Add-on to changeset:123: 114 133 Some change. 115 134 ticket:1 -
templates/anydiff.cs
1 <?cs include "header.cs"?> 2 3 <div id="ctxtnav" class="nav"> 4 <h2>Navigation</h2><?cs 5 with:links = chrome.links ?> 6 <ul> 7 </ul><?cs 8 /with ?> 9 </div> 10 11 <div id="content" class="changeset"> 12 <div id="title"> 13 <h1>Select Base and Target for Diff:</h1> 14 </div> 15 16 <div id="anydiff"> 17 <form action="<?cs var:anydiff.diff_href ?>" method="post"> 18 <table> 19 <tr> 20 <th><label for="old_path">From:</label></th> 21 <td> 22 <input type="text" id="old_path" name="old_path" value="<?cs 23 var:anydiff.old_path ?>" size="44" /> 24 <label for="old_rev">at Revision:</label> 25 <input type="text" id="old_rev" name="old" value="<?cs 26 var:anydiff.old_rev ?>" size="4" /> 27 </td> 28 </tr> 29 <tr> 30 <th><label for="new_path">To:</label></th> 31 <td> 32 <input type="text" id="new_path" name="path" value="<?cs 33 var:anydiff.new_path ?>" size="44" /> 34 <label for="new_rev">at Revision:</label> 35 <input type="text" id="new_rev" name="new" value="<?cs 36 var:anydiff.new_rev ?>" size="4" /> 37 </td> 38 </tr> 39 </table> 40 <div class="buttons"> 41 <input type="submit" value="View changes" /> 42 </div> 43 </form> 44 </div> 45 </div> 46 47 <?cs include "footer.cs"?> -
templates/log.cs
Property changes on: templates\anydiff.cs ___________________________________________________________________ Name: svn:eol-style + native
3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <ul> 6 <li class="last"><a href="<?cs 7 var:log.browser_href ?>">View Latest Revision</a></li><?cs 6 <li class="last"> 7 <a href="<?cs var:log.browser_href ?>">View Latest Revision</a> 8 </li><?cs 8 9 if:len(chrome.links.prev) ?> 9 10 <li class="first<?cs if:!len(chrome.links.next) ?> last<?cs /if ?>"> 10 11 ← <a href="<?cs var:chrome.links.prev.0.href ?>" title="<?cs … … 61 62 title="Warning: by updating, you will clear the page history" /> 62 63 </div> 63 64 </form> 65 64 66 <div class="diff"> 65 67 <div id="legend"> 66 68 <h3>Legend:</h3> … … 74 76 </dl> 75 77 </div> 76 78 </div> 79 80 <form action="<?cs var:log.href ?>" method="post"> 81 <div class="buttons"><input type="submit" value="View changes" 82 title="Diff from Old Revision to New Revision (select them below)" /> 83 </div> 77 84 <table id="chglist" class="listing"> 78 85 <thead> 79 86 <tr> 87 <th>Old</th> 88 <th>New</th> 80 89 <th class="change"></th> 81 90 <th class="data">Date</th> 82 91 <th class="rev">Rev</th> … … 87 96 </thead> 88 97 <tbody><?cs 89 98 set:indent = #1 ?><?cs 99 set:idx = #0 ?><?cs 90 100 each:item = log.items ?><?cs 91 101 if:item.copyfrom_path ?> 92 102 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 93 <td class="copyfrom_path" colspan=" 6" style="padding-left: <?cs var:indent ?>em">103 <td class="copyfrom_path" colspan="8" style="padding-left: <?cs var:indent ?>em"> 94 104 copied from <a href="<?cs var:item.browser_href ?>"?><?cs var:item.copyfrom_path ?></a>: 95 105 </td> 96 106 </tr><?cs … … 99 109 set:indent = #1 ?><?cs 100 110 /if ?> 101 111 <tr class="<?cs if:name(item) % #2 ?>even<?cs else ?>odd<?cs /if ?>"> 112 <td><input type="radio" name="old" 113 value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 114 if:idx == #1 ?> checked="checked" <?cs /if ?> /></td> 115 <td><input type="radio" name="new" 116 value="<?cs var:item.path ?>#<?cs var:item.rev ?>" <?cs 117 if:idx == #0 ?> checked="checked" <?cs /if ?> /></td> 102 118 <td class="change" style="padding-left:<?cs var:indent ?>em"> 103 119 <a title="View log starting at this revision" href="<?cs var:item.log_href ?>"> 104 120 <span class="<?cs var:item.change ?>"></span> … … 117 133 <td class="author"><?cs var:log.changes[item.rev].author ?></td> 118 134 <td class="summary"><?cs var:log.changes[item.rev].message ?></td> 119 135 </tr><?cs 136 set:idx = idx + 1 ?><?cs 120 137 /each ?> 121 138 </tbody> 122 </table><?cs 139 </table> 140 <div class="buttons"><input type="submit" value="View changes" 141 title="Diff from Old Revision to New Revision (select them above)" /> 142 </div> 143 </form><?cs 123 144 if:len(links.prev) || len(links.next) ?><div id="paging" class="nav"><ul><?cs 124 145 if:len(links.prev) ?><li class="first<?cs 125 146 if:!len(links.next) ?> last<?cs /if ?>">← <a href="<?cs -
templates/browser.cs
3 3 4 4 <div id="ctxtnav" class="nav"> 5 5 <ul> 6 <li class="last"><a href="<?cs var:browser.log_href ?>">Revision Log</a></li> 6 <li class="first"><a href="<?cs var:browser.restr_changeset_href ?>"> 7 Last Change</a></li> 8 <li class="last"><a href="<?cs var:browser.log_href ?>"> 9 Revision Log</a></li> 7 10 </ul> 8 11 </div> 9 12 13 10 14 <div id="content" class="browser"> 11 15 <h1><?cs call:browser_path_links(browser.path, browser) ?></h1> 12 16 13 17 <div id="jumprev"> 14 <form action="" method="get"><div> 15 <label for="rev">View revision:</label> 16 <input type="text" id="rev" name="rev" value="<?cs 17 var:browser.revision ?>" size="4" /> 18 </div></form> 18 <form action="" method="get"> 19 <div> 20 <label for="rev">View revision:</label> 21 <input type="text" id="rev" name="rev" value="<?cs 22 var:browser.revision ?>" size="4" /> 23 </div> 24 </form> 19 25 </div> 20 26 21 27 <?cs if:browser.is_dir ?> … … 114 120 ?>/TracBrowser">TracBrowser</a> for help on using the browser. 115 121 </div> 116 122 123 <div id="anydiff"><?cs 124 if len(browser.path) > #1 ?> 125 <form action="<?cs var:browser.anydiff_href ?>" method="get"> 126 <input type="hidden" name="new_path" value="<?cs var:browser.path ?>" /> 127 <input type="hidden" name="old_path" value="<?cs var:browser.path ?>" /> 128 <input type="hidden" name="new_rev" value="<?cs var:browser.revision ?>" /> 129 <input type="hidden" name="old_rev" value="<?cs var:browser.revision ?>" /> 130 <div class="buttons"> 131 <input type="submit" value="View changes..." title="Prepare an Arbitrary Diff" /> 132 </div> 133 </form><?cs 134 /if ?> 135 </div> 136 117 137 </div> 118 138 <?cs include:"footer.cs"?> -
templates/wiki.cs
154 154 var:wiki.page_name ?></a></h1> 155 155 <?cs if:len(wiki.history) ?><form method="get" action=""> 156 156 <input type="hidden" name="action" value="diff" /> 157 <div class="buttons"> 158 <input type="submit" value="View changes" /> 159 </div> 157 160 <table id="wikihist" class="listing" summary="Change history"> 158 161 <thead><tr> 159 162 <th class="diff"></th> -
templates/diff.cs
1 <?cs include "header.cs"?> 2 <?cs include "macros.cs"?> 3 4 <div id="ctxtnav" class="nav"> 5 <h2>Navigation</h2><?cs 6 with:links = chrome.links ?> 7 <ul><?cs 8 if:diff.chgset ?><?cs 9 if:len(links.prev) ?> 10 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 11 ← <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 12 var:links.prev.0.title ?>">Previous <?cs 13 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> 14 </li><?cs 15 /if ?><?cs 16 if:len(links.next) ?> 17 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last"> 18 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 19 var:links.next.0.title ?>">Next <?cs 20 if:diff.restricted ?>Change<?cs else ?>Changeset<?cs /if ?></a> → 21 </li><?cs 22 /if ?><?cs 23 else ?> 24 <li class="first"><a href="<?cs var:diff.reverse_href ?>">Reverse Diff</a></li><?cs 25 /if ?> 26 </ul><?cs 27 /with ?> 28 </div> 29 30 <div id="content" class="changeset"> 31 <div id="title"><?cs 32 if:diff.chgset ?><?cs 33 if:diff.restricted ?> 34 <h1>Changeset <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 35 <?cs var:diff.new_rev ?></a> 36 for <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 37 <?cs var:diff.new_path ?></a> 38 </h1><?cs 39 else ?> 40 <h1>Changeset <?cs var:diff.new_rev ?></h1><?cs 41 /if ?><?cs 42 else ?><?cs 43 if:diff.restricted ?> 44 <h1>Changes in <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 45 <?cs var:diff.new_path ?></a> 46 from revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 47 <?cs var:diff.old_rev ?></a> 48 to <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 49 <?cs var:diff.new_rev ?></a> 50 </h1><?cs 51 else ?> 52 <h1>Changes from <a title="Show entry in browser" href="<?cs var:diff.href.old_path ?>"> 53 <?cs var:diff.old_path ?></a> 54 at revision <a title="Show full changeset" href="<?cs var:diff.href.old_rev ?>"> 55 <?cs var:diff.old_rev ?></a> 56 to <a title="Show entry in browser" href="<?cs var:diff.href.new_path ?>"> 57 <?cs var:diff.new_path ?></a> 58 at revision <a title="Show full changeset" href="<?cs var:diff.href.new_rev ?>"> 59 <?cs var:diff.new_rev ?></a> 60 </h1><?cs 61 /if ?><?cs 62 /if ?> 63 </div> 64 65 <?cs each:change = diff.changes ?><?cs 66 if:len(change.diff) ?><?cs 67 set:has_diffs = 1 ?><?cs 68 /if ?><?cs 69 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines 70 || diff.options.ignorecase || diff.options.ignorewhitespace ?> 71 <form method="post" id="prefs" action=""> 72 <div><?cs 73 if:!diff.chgset ?> 74 <input type="hidden" name="old_path" value="<?cs var:diff.old_path ?>" /> 75 <input type="hidden" name="path" value="<?cs var:diff.new_path ?>" /> 76 <input type="hidden" name="old" value="<?cs var:diff.old_rev ?>" /> 77 <input type="hidden" name="new" value="<?cs var:diff.new_rev ?>" /><?cs 78 /if ?> 79 <label for="style">View differences</label> 80 <select id="style" name="style"> 81 <option value="inline"<?cs 82 if:diff.style == 'inline' ?> selected="selected"<?cs 83 /if ?>>inline</option> 84 <option value="sidebyside"<?cs 85 if:diff.style == 'sidebyside' ?> selected="selected"<?cs 86 /if ?>>side by side</option> 87 </select> 88 <div class="field"> 89 Show <input type="text" name="contextlines" id="contextlines" size="2" 90 maxlength="3" value="<?cs var:diff.options.contextlines ?>" /> 91 <label for="contextlines">lines around each change</label> 92 </div> 93 <fieldset id="ignore"> 94 <legend>Ignore:</legend> 95 <div class="field"> 96 <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs 97 if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> /> 98 <label for="blanklines">Blank lines</label> 99 </div> 100 <div class="field"> 101 <input type="checkbox" id="case" name="ignorecase"<?cs 102 if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> /> 103 <label for="case">Case changes</label> 104 </div> 105 <div class="field"> 106 <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs 107 if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> /> 108 <label for="whitespace">White space changes</label> 109 </div> 110 </fieldset> 111 <div class="buttons"> 112 <input type="submit" name="update" value="Update" /> 113 </div> 114 </div> 115 </form><?cs /if ?> 116 117 <?cs def:node_change(item,cl,kind) ?><?cs 118 set:ndiffs = len(item.diff) ?><?cs 119 set:nprops = len(item.props) ?> 120 <div class="<?cs var:cl ?>"></div><?cs 121 if:cl == "rem" ?> 122 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs 123 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs 124 else ?> 125 <a title="Show entry in browser" href="<?cs 126 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs 127 /if ?> 128 <span class="comment">(<?cs var:kind ?>)</span><?cs 129 if:item.path.old && item.change == 'copy' || item.change == 'move' ?> 130 <small><em>(<?cs var:kind ?> from <a href="<?cs 131 var:item.browser_href.old ?>" title="Show original file (rev. <?cs 132 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs 133 /if ?><?cs 134 if:$ndiffs + $nprops > #0 ?> 135 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs 136 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs 137 /if ?><?cs 138 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs 139 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs 140 /if ?></a>)<?cs 141 elif:cl == "mod" ?> 142 (<a href="<?cs var:item.browser_href.old ?>" 143 title="Show previous version in browser">previous</a>)<?cs 144 /if ?> 145 <?cs /def ?> 146 147 <dl id="overview"><?cs 148 if:diff.chgset ?> 149 <dt class="time">Timestamp:</dt> 150 <dd class="time"><?cs var:changeset.time ?></dd> 151 <dt class="author">Author:</dt> 152 <dd class="author"><?cs var:changeset.author ?></dd> 153 <dt class="message">Message:</dt> 154 <dd class="message" id="searchable"><?cs 155 alt:changeset.message ?> <?cs /alt ?></dd><?cs 156 /if ?> 157 <dt class="files"><?cs 158 if:len(diff.changes) > #0 ?> 159 Files:<?cs 160 else ?> 161 (None)<?cs 162 /if ?> 163 </dt> 164 <dd class="files"> 165 <ul><?cs each:item = diff.changes ?> 166 <li><?cs 167 if:item.change == 'add' ?><?cs 168 call:node_change(item, 'add', 'added') ?><?cs 169 elif:item.change == 'delete' ?><?cs 170 call:node_change(item, 'rem', 'deleted') ?><?cs 171 elif:item.change == 'copy' ?><?cs 172 call:node_change(item, 'cp', 'copied') ?><?cs 173 elif:item.change == 'move' ?><?cs 174 call:node_change(item, 'mv', 'moved') ?><?cs 175 elif:item.change == 'edit' ?><?cs 176 call:node_change(item, 'mod', 'modified') ?><?cs 177 /if ?> 178 </li> 179 <?cs /each ?></ul> 180 </dd> 181 </dl> 182 183 <div class="diff"> 184 <div id="legend"> 185 <h3>Legend:</h3> 186 <dl> 187 <dt class="unmod"></dt><dd>Unmodified</dd> 188 <dt class="add"></dt><dd>Added</dd> 189 <dt class="rem"></dt><dd>Removed</dd> 190 <dt class="mod"></dt><dd>Modified</dd> 191 <dt class="cp"></dt><dd>Copied</dd> 192 <dt class="mv"></dt><dd>Moved</dd> 193 </dl> 194 </div> 195 <ul class="entries"><?cs 196 each:item = diff.changes ?><?cs 197 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs 198 var:name(item) ?>"><h2><a href="<?cs 199 var:item.browser_href.new ?>" title="Show new revision <?cs 200 var:item.rev.new ?> of this file in browser"><?cs 201 var:item.path.new ?></a></h2><?cs 202 if:len(item.props) ?><ul class="props"><?cs 203 each:prop = item.props ?><li>Property <strong><?cs 204 var:name(prop) ?></strong> <?cs 205 if:prop.old && prop.new ?>changed from <?cs 206 elif:!prop.old ?>set<?cs 207 else ?>deleted<?cs 208 /if ?><?cs 209 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs 210 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs 211 /each ?></ul><?cs 212 /if ?><?cs 213 if:len(item.diff) ?><table class="<?cs 214 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs 215 if:diff.style == 'sidebyside' ?> 216 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup> 217 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup> 218 <thead><tr> 219 <th colspan="2"><a href="<?cs 220 var:item.browser_href.old ?>" title="Show old rev. <?cs 221 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs 222 var:item.rev.old ?></a></th> 223 <th colspan="2"><a href="<?cs 224 var:item.browser_href.new ?>" title="Show new rev. <?cs 225 var:item.rev.new ?> of <?cs var:item.path.new ?>">Revision <?cs 226 var:item.rev.new ?></a></th> 227 </tr> 228 </thead><?cs 229 each:change = item.diff ?><tbody><?cs 230 call:diff_display(change, diff.style) ?></tbody><?cs 231 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 232 <th>…</th><td> </td><th>…</th><td> </td> 233 </tr></tbody><?cs /if ?><?cs 234 /each ?><?cs 235 else ?> 236 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup> 237 <thead><tr> 238 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs 239 var:item.browser_href.old ?>" title="Show old version of <?cs 240 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th> 241 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs 242 var:item.browser_href.new ?>" title="Show new version of <?cs 243 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th> 244 <th> </th></tr> 245 </thead><?cs 246 each:change = item.diff ?><?cs 247 call:diff_display(change, diff.style) ?><?cs 248 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr> 249 <th>…</th><th>…</th><td> </td> 250 </tr></tbody><?cs /if ?><?cs 251 /each ?><?cs 252 /if ?></table><?cs 253 /if ?></li><?cs 254 /if ?><?cs 255 /each ?></ul> 256 </div> 257 258 </div> 259 <?cs include "footer.cs"?> -
templates/changeset.cs
Property changes on: templates\diff.cs ___________________________________________________________________ Name: svn:eol-style + native
1 <?cs include "header.cs"?>2 <?cs include "macros.cs"?>3 4 <div id="ctxtnav" class="nav">5 <h2>Changeset Navigation</h2><?cs6 with:links = chrome.links ?>7 <ul><?cs8 if:len(links.prev) ?>9 <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>">10 ← <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs11 var:links.prev.0.title ?>">Previous Changeset</a>12 </li><?cs13 /if ?><?cs14 if:len(links.next) ?>15 <li class="<?cs if:len(links.prev) ?>first <?cs /if ?>last">16 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs17 var:links.next.0.title ?>">Next Changeset</a> →18 </li><?cs19 /if ?>20 </ul><?cs21 /with ?>22 </div>23 24 <div id="content" class="changeset">25 <h1>Changeset <?cs var:changeset.revision ?></h1>26 27 <?cs each:change = changeset.changes ?><?cs28 if:len(change.diff) ?><?cs29 set:has_diffs = 1 ?><?cs30 /if ?><?cs31 /each ?><?cs if:has_diffs || diff.options.ignoreblanklines32 || diff.options.ignorecase || diff.options.ignorewhitespace ?>33 <form method="post" id="prefs" action="">34 <div>35 <label for="style">View differences</label>36 <select id="style" name="style">37 <option value="inline"<?cs38 if:diff.style == 'inline' ?> selected="selected"<?cs39 /if ?>>inline</option>40 <option value="sidebyside"<?cs41 if:diff.style == 'sidebyside' ?> selected="selected"<?cs42 /if ?>>side by side</option>43 </select>44 <div class="field">45 Show <input type="text" name="contextlines" id="contextlines" size="2"46 maxlength="3" value="<?cs var:diff.options.contextlines ?>" />47 <label for="contextlines">lines around each change</label>48 </div>49 <fieldset id="ignore">50 <legend>Ignore:</legend>51 <div class="field">52 <input type="checkbox" id="blanklines" name="ignoreblanklines"<?cs53 if:diff.options.ignoreblanklines ?> checked="checked"<?cs /if ?> />54 <label for="blanklines">Blank lines</label>55 </div>56 <div class="field">57 <input type="checkbox" id="case" name="ignorecase"<?cs58 if:diff.options.ignorecase ?> checked="checked"<?cs /if ?> />59 <label for="case">Case changes</label>60 </div>61 <div class="field">62 <input type="checkbox" id="whitespace" name="ignorewhitespace"<?cs63 if:diff.options.ignorewhitespace ?> checked="checked"<?cs /if ?> />64 <label for="whitespace">White space changes</label>65 </div>66 </fieldset>67 <div class="buttons">68 <input type="submit" name="update" value="Update" />69 </div>70 </div>71 </form><?cs /if ?>72 73 <?cs def:node_change(item,cl,kind) ?><?cs74 set:ndiffs = len(item.diff) ?><?cs75 set:nprops = len(item.props) ?>76 <div class="<?cs var:cl ?>"></div><?cs77 if:cl == "rem" ?>78 <a title="Show what was removed (rev. <?cs var:item.rev.old ?>)" href="<?cs79 var:item.browser_href.old ?>"><?cs var:item.path.old ?></a><?cs80 else ?>81 <a title="Show entry in browser" href="<?cs82 var:item.browser_href.new ?>"><?cs var:item.path.new ?></a><?cs83 /if ?>84 <span class="comment">(<?cs var:kind ?>)</span><?cs85 if:item.path.old && item.change == 'copy' || item.change == 'move' ?>86 <small><em>(<?cs var:kind ?> from <a href="<?cs87 var:item.browser_href.old ?>" title="Show original file (rev. <?cs88 var:item.rev.old ?>)"><?cs var:item.path.old ?></a>)</em></small><?cs89 /if ?><?cs90 if:$ndiffs + $nprops > #0 ?>91 (<a href="#file<?cs var:name(item) ?>" title="Show differences"><?cs92 if:$ndiffs > #0 ?><?cs var:ndiffs ?> diff<?cs if:$ndiffs > #1 ?>s<?cs /if ?><?cs93 /if ?><?cs94 if:$ndiffs && $nprops ?>, <?cs /if ?><?cs95 if:$nprops > #0 ?><?cs var:nprops ?> prop<?cs if:$nprops > #1 ?>s<?cs /if ?><?cs96 /if ?></a>)<?cs97 elif:cl == "mod" ?>98 (<a href="<?cs var:item.browser_href.old ?>"99 title="Show previous version in browser">previous</a>)<?cs100 /if ?>101 <?cs /def ?>102 103 <dl id="overview">104 <dt class="time">Timestamp:</dt>105 <dd class="time"><?cs var:changeset.time ?></dd>106 <dt class="author">Author:</dt>107 <dd class="author"><?cs var:changeset.author ?></dd>108 <dt class="message">Message:</dt>109 <dd class="message" id="searchable"><?cs110 alt:changeset.message ?> <?cs /alt ?></dd>111 <dt class="files">Files:</dt>112 <dd class="files">113 <ul><?cs each:item = changeset.changes ?>114 <li><?cs115 if:item.change == 'add' ?><?cs116 call:node_change(item, 'add', 'added') ?><?cs117 elif:item.change == 'delete' ?><?cs118 call:node_change(item, 'rem', 'deleted') ?><?cs119 elif:item.change == 'copy' ?><?cs120 call:node_change(item, 'cp', 'copied') ?><?cs121 elif:item.change == 'move' ?><?cs122 call:node_change(item, 'mv', 'moved') ?><?cs123 elif:item.change == 'edit' ?><?cs124 call:node_change(item, 'mod', 'modified') ?><?cs125 /if ?>126 </li>127 <?cs /each ?></ul>128 </dd>129 </dl>130 131 <div class="diff">132 <div id="legend">133 <h3>Legend:</h3>134 <dl>135 <dt class="unmod"></dt><dd>Unmodified</dd>136 <dt class="add"></dt><dd>Added</dd>137 <dt class="rem"></dt><dd>Removed</dd>138 <dt class="mod"></dt><dd>Modified</dd>139 <dt class="cp"></dt><dd>Copied</dd>140 <dt class="mv"></dt><dd>Moved</dd>141 </dl>142 </div>143 <ul class="entries"><?cs144 each:item = changeset.changes ?><?cs145 if:len(item.diff) || len(item.props) ?><li class="entry" id="file<?cs146 var:name(item) ?>"><h2><a href="<?cs147 var:item.browser_href.new ?>" title="Show new revision <?cs148 var:item.rev.new ?> of this file in browser"><?cs149 var:item.path.new ?></a></h2><?cs150 if:len(item.props) ?><ul class="props"><?cs151 each:prop = item.props ?><li>Property <strong><?cs152 var:name(prop) ?></strong> <?cs153 if:prop.old && prop.new ?>changed from <?cs154 elif:!prop.old ?>set<?cs155 else ?>deleted<?cs156 /if ?><?cs157 if:prop.old && prop.new ?><em><tt><?cs var:prop.old ?></tt></em><?cs /if ?><?cs158 if:prop.new ?> to <em><tt><?cs var:prop.new ?></tt></em><?cs /if ?></li><?cs159 /each ?></ul><?cs160 /if ?><?cs161 if:len(item.diff) ?><table class="<?cs162 var:diff.style ?>" summary="Differences" cellspacing="0"><?cs163 if:diff.style == 'sidebyside' ?>164 <colgroup class="l"><col class="lineno" /><col class="content" /></colgroup>165 <colgroup class="r"><col class="lineno" /><col class="content" /></colgroup>166 <thead><tr>167 <th colspan="2"><a href="<?cs168 var:item.browser_href.old ?>" title="Show old rev. <?cs169 var:item.rev.old ?> of <?cs var:item.path.old ?>">Revision <?cs170 var:item.rev.old ?></a></th>171 <th colspan="2"><a href="<?cs172 var:item.browser_href.new ?>" title="Show new rev. <?cs173 var:item.rev.new ?> of <?cs var:item.path.new ?>">Revision <?cs174 var:item.rev.new ?></a></th>175 </tr>176 </thead><?cs177 each:change = item.diff ?><tbody><?cs178 call:diff_display(change, diff.style) ?></tbody><?cs179 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>180 <th>…</th><td> </td><th>…</th><td> </td>181 </tr></tbody><?cs /if ?><?cs182 /each ?><?cs183 else ?>184 <colgroup><col class="lineno" /><col class="lineno" /><col class="content" /></colgroup>185 <thead><tr>186 <th title="Revision <?cs var:item.rev.old ?>"><a href="<?cs187 var:item.browser_href.old ?>" title="Show old version of <?cs188 var:item.path.old ?>">r<?cs var:item.rev.old ?></a></th>189 <th title="Revision <?cs var:item.rev.new ?>"><a href="<?cs190 var:item.browser_href.new ?>" title="Show new version of <?cs191 var:item.path.new ?>">r<?cs var:item.rev.new ?></a></th>192 <th> </th></tr>193 </thead><?cs194 each:change = item.diff ?><?cs195 call:diff_display(change, diff.style) ?><?cs196 if:name(change) < len(item.diff) - 1 ?><tbody class="skipped"><tr>197 <th>…</th><th>…</th><td> </td>198 </tr></tbody><?cs /if ?><?cs199 /each ?><?cs200 /if ?></table><?cs201 /if ?></li><?cs202 /if ?><?cs203 /each ?></ul>204 </div>205 206 </div>207 <?cs include "footer.cs"?>
