Ticket #986: db-changes-986-r2865.2.diff
| File db-changes-986-r2865.2.diff, 28.9 kB (added by Manuzhai, 3 years ago) |
|---|
-
trac/db_default.py
18 18 from trac.db import Table, Column, Index 19 19 20 20 # Database version identifier. Used for automatic upgrades. 21 db_version = 1 621 db_version = 17 22 22 23 23 def __mkreports(reports): 24 24 """Utility function used to create report data in same syntax as the … … 83 83 Column('author'), 84 84 Column('message'), 85 85 Index(['time'])], 86 Table('node_change', key=('rev', 'path', 'change '))[86 Table('node_change', key=('rev', 'path', 'change_type'))[ 87 87 Column('rev'), 88 88 Column('path'), 89 Column(' kind', size=1),90 Column('change ', size=1),89 Column('node_type', size=1), 90 Column('change_type', size=1), 91 91 Column('base_path'), 92 92 Column('base_rev'), 93 93 Index(['rev'])], -
trac/versioncontrol/api.py
207 207 DIRECTORY = "dir" 208 208 FILE = "file" 209 209 210 def __init__(self, path, rev, kind):211 assert kind in (Node.DIRECTORY, Node.FILE), "Unknown node kind %s" % kind210 def __init__(self, path, rev, node_type): 211 assert node_type in (Node.DIRECTORY, Node.FILE), "Unknown node node_type %s" % node_type 212 212 self.path = str(path) 213 213 self.rev = rev 214 self. kind = kind214 self.node_type = node_type 215 215 216 216 def get_content(self): 217 217 """ … … 274 274 raise NotImplementedError 275 275 last_modified = property(lambda x: x.get_last_modified()) 276 276 277 isdir = property(lambda x: x. kind== Node.DIRECTORY)278 isfile = property(lambda x: x. kind== Node.FILE)277 isdir = property(lambda x: x.node_type == Node.DIRECTORY) 278 isfile = property(lambda x: x.node_type == Node.FILE) 279 279 280 280 281 281 class Changeset(object): … … 297 297 298 298 def get_changes(self): 299 299 """ 300 Generator that produces a (path, kind, change, base_rev, base_path)300 Generator that produces a (path, node_type, change_type, base_rev, base_path) 301 301 tuple for every change in the changeset, where change can be one of 302 302 Changeset.ADD, Changeset.COPY, Changeset.DELETE, Changeset.EDIT or 303 Changeset.MOVE, and kindis one of Node.FILE or Node.DIRECTORY.303 Changeset.MOVE, and node_type is one of Node.FILE or Node.DIRECTORY. 304 304 """ 305 305 raise NotImplementedError 306 306 -
trac/versioncontrol/tests/cache.py
68 68 self.assertEquals(('0', 41000, '', ''), cursor.fetchone()) 69 69 self.assertEquals(('1', 42000, 'joe', 'Import'), cursor.fetchone()) 70 70 self.assertEquals(None, cursor.fetchone()) 71 cursor.execute("SELECT rev,path, kind,change,base_path,base_rev "71 cursor.execute("SELECT rev,path,node_type,change_type,base_path,base_rev " 72 72 "FROM node_change") 73 73 self.assertEquals(('1', 'trunk', 'D', 'A', None, None), 74 74 cursor.fetchone()) … … 82 82 "VALUES (0,41000,'','')") 83 83 cursor.execute("INSERT INTO revision (rev,time,author,message) " 84 84 "VALUES (1,42000,'joe','Import')") 85 cursor.executemany("INSERT INTO node_change (rev,path, kind,change,"85 cursor.executemany("INSERT INTO node_change (rev,path,node_type,change_type," 86 86 "base_path,base_rev) VALUES ('1',%s,%s,%s,%s,%s)", 87 87 [('trunk', 'D', 'A', None, None), 88 88 ('trunk/README', 'F', 'A', None, None)]) … … 101 101 cursor.execute("SELECT time,author,message FROM revision WHERE rev='2'") 102 102 self.assertEquals((42042, 'joe', 'Update'), cursor.fetchone()) 103 103 self.assertEquals(None, cursor.fetchone()) 104 cursor.execute("SELECT path, kind,change,base_path,base_rev "104 cursor.execute("SELECT path,node_type,change_type,base_path,base_rev " 105 105 "FROM node_change WHERE rev='2'") 106 106 self.assertEquals(('trunk/README', 'F', 'E', 'trunk/README', '1'), 107 107 cursor.fetchone()) … … 113 113 "VALUES (0,41000,'','')") 114 114 cursor.execute("INSERT INTO revision (rev,time,author,message) " 115 115 "VALUES (1,42000,'joe','Import')") 116 cursor.executemany("INSERT INTO node_change (rev,path, kind,change,"116 cursor.executemany("INSERT INTO node_change (rev,path,node_type,change_type," 117 117 "base_path,base_rev) VALUES ('1',%s,%s,%s,%s,%s)", 118 118 [('trunk', 'D', 'A', None, None), 119 119 ('trunk/README', 'F', 'A', None, None)]) -
trac/versioncontrol/svn_fs.py
40 40 def apr_pool_clear(): pass 41 41 Editor = object 42 42 delta = core = dummy_svn() 43 44 43 45 _kindmap = {core.svn_node_dir: Node.DIRECTORY,46 core.svn_node_file: Node.FILE}47 44 45 _node_type_map = {core.svn_node_dir: Node.DIRECTORY, 46 core.svn_node_file: Node.FILE} 48 47 48 49 49 application_pool = None 50 50 51 51 def _get_history(path, authz, fs_ptr, pool, start, end, limit=None): 52 52 history = [] 53 53 if hasattr(repos, 'svn_repos_history2'): … … 93 93 apr_pool_destroy = staticmethod(core.apr_pool_destroy) 94 94 apr_terminate = staticmethod(core.apr_terminate) 95 95 apr_pool_clear = staticmethod(core.apr_pool_clear) 96 96 97 97 def __init__(self, parent_pool=None): 98 98 """Create a new memory pool""" 99 99 … … 224 224 (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_MICRO) 225 225 226 226 self.pool = Pool() 227 227 228 228 # Remove any trailing slash or else subversion might abort 229 229 path = os.path.normpath(path).replace('\\', '/') 230 230 self.path = repos.svn_repos_find_root_path(path, self.pool()) … … 234 234 235 235 self.repos = repos.svn_repos_open(self.path, self.pool()) 236 236 self.fs_ptr = repos.svn_repos_fs(self.repos) 237 237 238 238 uuid = fs.get_uuid(self.fs_ptr, self.pool()) 239 239 name = 'svn:%s:%s' % (uuid, path) 240 240 … … 259 259 pool = self.pool 260 260 rev_root = fs.revision_root(self.fs_ptr, rev, pool()) 261 261 node_type = fs.check_path(rev_root, self.scope + path, pool()) 262 return node_type in _ kindmap262 return node_type in _node_type_map 263 263 264 264 def normalize_path(self, path): 265 265 return _normalize_path(path) … … 410 410 raise TracError, ('The Target for Diff is invalid: path %s' 411 411 ' doesn\'t exist in revision %s' \ 412 412 % (new_path, new_rev)) 413 if new_node. kind != old_node.kind:413 if new_node.node_type != old_node.node_type: 414 414 raise TracError, ('Diff mismatch: Base is a %s (%s in revision %s) ' 415 415 'and Target is a %s (%s in revision %s).' \ 416 % (old_node. kind, old_path, old_rev,417 new_node. kind, new_path, new_rev))416 % (old_node.node_type, old_path, old_rev, 417 new_node.node_type, new_path, new_rev)) 418 418 subpool = Pool(self.pool) 419 419 if new_node.isdir: 420 420 editor = DiffChangeEditor() … … 434 434 entry_props, 435 435 ignore_ancestry, 436 436 subpool()) 437 for path, kind, change in editor.deltas:437 for path, node_type, change_type in editor.deltas: 438 438 old_node = new_node = None 439 if change != Changeset.ADD:439 if change_type != Changeset.ADD: 440 440 old_node = self.get_node(posixpath.join(old_path, path), 441 441 old_rev) 442 if change != Changeset.DELETE:442 if change_type != Changeset.DELETE: 443 443 new_node = self.get_node(posixpath.join(new_path, path), 444 444 new_rev) 445 445 else: 446 kind = _kindmap[fs.check_path(old_root,446 node_type = _node_type_map[fs.check_path(old_root, 447 447 self.scope + old_node.path, 448 448 subpool())] 449 yield (old_node, new_node, kind, change)449 yield (old_node, new_node, node_type, change_type) 450 450 else: 451 451 old_root = fs.revision_root(self.fs_ptr, old_rev, subpool()) 452 452 new_root = fs.revision_root(self.fs_ptr, new_rev, subpool()) … … 471 471 472 472 self.root = fs.revision_root(fs_ptr, rev, self.pool()) 473 473 node_type = fs.check_path(self.root, self.scoped_path, self.pool()) 474 if not node_type in _ kindmap:474 if not node_type in _node_type_map: 475 475 raise TracError, "No node at %s in revision %s" % (path, rev) 476 476 self.created_rev = fs.node_created_rev(self.root, self.scoped_path, 477 477 self.pool()) … … 484 484 # * the node existed at (created_path,created_rev) 485 485 # TODO: check node id 486 486 self.rev = self.created_rev 487 488 Node.__init__(self, path, self.rev, _kindmap[node_type])489 487 488 Node.__init__(self, path, self.rev, _node_type_map[node_type]) 489 490 490 def get_content(self): 491 491 if self.isdir: 492 492 return None … … 608 608 revroots[b_rev] = b_root 609 609 change.base_path = fs.node_created_path(b_root, b_path, tmp()) 610 610 change.base_rev = fs.node_created_rev(b_root, b_path, tmp()) 611 kind = _ kindmap[change.item_kind]611 kind = _node_type_map[change.item_kind] 612 612 path = path[len(self.scope) - 1:] 613 613 base_path = _path_within_scope(self.scope, change.base_path) 614 614 changes.append([path, kind, action, base_path, change.base_rev]) … … 642 642 # Note 2: the 'dir_baton' is the path of the parent directory 643 643 # 644 644 645 class DiffChangeEditor(delta.Editor): 645 class DiffChangeEditor(delta.Editor): 646 646 647 647 def __init__(self): 648 648 self.deltas = [] 649 649 650 650 # -- svn.delta.Editor callbacks 651 651 652 652 def open_root(self, base_revision, dir_pool): -
trac/versioncontrol/cache.py
18 18 from trac.versioncontrol import Changeset, Node, Repository, Authorizer 19 19 20 20 21 _ kindmap = {'D': Node.DIRECTORY, 'F': Node.FILE}22 _ actionmap = {'A': Changeset.ADD, 'C': Changeset.COPY,21 _node_typemap = {'D': Node.DIRECTORY, 'F': Node.FILE} 22 _change_typemap = {'A': Changeset.ADD, 'C': Changeset.COPY, 23 23 'D': Changeset.DELETE, 'E': Changeset.EDIT, 24 24 'M': Changeset.MOVE} 25 25 … … 64 64 authz = self.repos.authz 65 65 self.repos.authz = Authorizer() # remove permission checking 66 66 67 kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))68 actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))67 node_typemap = dict(zip(_node_typemap.values(), _node_typemap.keys())) 68 change_typemap = dict(zip(_change_typemap.values(), _change_typemap.keys())) 69 69 self.log.info("Syncing with repository (%s to %s)" 70 70 % (youngest_stored, self.repos.youngest_rev)) 71 71 if youngest_stored: … … 82 82 "VALUES (%s,%s,%s,%s)", (str(current_rev), 83 83 changeset.date, changeset.author, 84 84 changeset.message)) 85 for path, kind,action,base_path,base_rev in changeset.get_changes():85 for path,node_type,change_type,base_path,base_rev in changeset.get_changes(): 86 86 self.log.debug("Caching node change in [%s]: %s" 87 % (current_rev, (path, kind, action,87 % (current_rev, (path, node_type, change_type, 88 88 base_path, base_rev))) 89 kind = kindmap[kind]90 action = actionmap[action]91 cursor.execute("INSERT INTO node_change (rev,path, kind,"92 "change ,base_path,base_rev) "89 node_type = node_typemap[node_type] 90 change_type = change_typemap[change_type] 91 cursor.execute("INSERT INTO node_change (rev,path,node_type," 92 "change_type,base_path,base_rev) " 93 93 "VALUES (%s,%s,%s,%s,%s,%s)", 94 (str(current_rev), path, kind, action,94 (str(current_rev), path, node_type, change_type, 95 95 base_path, base_rev)) 96 96 current_rev = self.repos.next_rev(current_rev) 97 97 self.db.commit() … … 148 148 149 149 def get_changes(self): 150 150 cursor = self.db.cursor() 151 cursor.execute("SELECT path, kind,change,base_path,base_rev "151 cursor.execute("SELECT path,node_type,change_type,base_path,base_rev " 152 152 "FROM node_change WHERE rev=%s " 153 153 "ORDER BY path", (self.rev,)) 154 for path, kind, change, base_path, base_rev in cursor:154 for path, node_type, change_type, base_path, base_rev in cursor: 155 155 if not self.authz.has_permission(path): 156 156 # FIXME: what about the base_path? 157 157 continue 158 kind = _kindmap[kind]159 change = _actionmap[change]160 yield path, kind, change, base_path, base_rev158 node_type = _node_typemap[node_type] 159 change_type = _change_typemap[change_type] 160 yield path, node_type, change_type, base_path, base_rev -
trac/versioncontrol/web_ui/changeset.py
77 77 # IRequestHandler methods 78 78 79 79 _request_re = re.compile(r"/changeset(?:/([^/]+))?(/.*)?$") 80 80 81 81 def match_request(self, req): 82 82 match = re.match(self._request_re, req.path_info) 83 83 if match: … … 95 95 * If `new_path` and `old_path` are equal (or `old_path` is omitted) 96 96 and `new` and `old` are equal (or `old` is omitted), 97 97 then we're about to view a revision Changeset: `chgset` is True. 98 Furthermore, if the path is not the root, the changeset is 98 Furthermore, if the path is not the root, the changeset is 99 99 ''restricted'' to that path (only the changes affecting that path, 100 100 its children or its ancestor directories will be shown). 101 101 * In any other case, the set of changes corresponds to arbitrary … … 103 103 are equal, the ''restricted'' flag will also be set, meaning in this 104 104 case that the differences between two revisions are restricted to 105 105 those occurring on that path. 106 106 107 107 In any case, either path@rev pairs must exist. 108 108 """ 109 109 req.perm.assert_permission('CHANGESET_VIEW') 110 110 111 111 # -- retrieve arguments 112 112 new_path = req.args.get('new_path') 113 113 new = req.args.get('new') … … 245 245 rev=diff.old_rev), 246 246 } 247 247 } 248 248 249 249 if chgset: # Changeset Mode (possibly restricted on a path) 250 250 path, rev = diff.new_path, diff.new_rev 251 251 252 # -- getting the change summary from the Changeset.get_changes 252 # -- getting the change summary from the Changeset.get_changes 253 253 def get_changes(): 254 254 old_node = new_node = None 255 for npath, kind, change, opath, orev in chgset.get_changes():255 for npath, node_type, change_type, opath, orev in chgset.get_changes(): 256 256 if restricted and \ 257 257 not (npath.startswith(path) # npath is below 258 258 or path.startswith(npath)): # npath is above … … 261 261 old_node = repos.get_node(opath, orev) 262 262 if change != Changeset.DELETE: 263 263 new_node = repos.get_node(npath, rev) 264 yield old_node, new_node, kind, change265 264 yield old_node, new_node, node_type, change_type 265 266 266 def _changeset_title(rev): 267 267 if restricted: 268 268 return 'Changeset %s for %s' % (rev, path) … … 314 314 add_link(req, 'next', next_href, _changeset_title(next_rev)) 315 315 316 316 else: # Diff Mode 317 # -- getting the change summary from the Repository.get_changes 317 # -- getting the change summary from the Repository.get_changes 318 318 def get_changes(): 319 319 for d in repos.get_changes(**diff): 320 320 yield d 321 321 322 322 reverse_href = self.env.href.changeset(diff.old_rev, diff.old_path, 323 323 old=diff.new_rev, 324 324 old_path=diff.new_path) … … 411 411 return [] 412 412 413 413 idx = 0 414 for old_node, new_node, kind, change in get_changes():415 if change != Changeset.EDIT:414 for old_node, new_node, node_type, change_type in get_changes(): 415 if change_type != Changeset.EDIT: 416 416 show_entry = True 417 417 else: 418 418 show_entry = False … … 421 421 if props: 422 422 req.hdf['changeset.changes.%d.props' % idx] = props 423 423 show_entry = True 424 if kind== Node.FILE:424 if node_type == Node.FILE: 425 425 diffs = _content_changes(old_node, new_node) 426 426 if diffs != []: 427 427 if diffs: … … 429 429 # elif None (means: manually compare to (previous)) 430 430 show_entry = True 431 431 if show_entry: 432 info = _change_info(old_node, new_node, change )432 info = _change_info(old_node, new_node, change_type) 433 433 req.hdf['changeset.changes.%d' % idx] = info 434 434 idx += 1 # the sequence should be immutable 435 435 … … 442 442 req.end_headers() 443 443 444 444 mimeview = Mimeview(self.env) 445 for old_node, new_node, kind, change in repos.get_changes(**diff):445 for old_node, new_node, node_type, change_type in repos.get_changes(**diff): 446 446 # TODO: Property changes 447 447 448 448 # Content changes 449 if kind== Node.DIRECTORY:449 if node_type == Node.DIRECTORY: 450 450 continue 451 451 452 452 new_content = old_content = '' … … 463 463 data = new_node.get_content().read() 464 464 if is_binary(data): 465 465 continue 466 new_content = mimeview.to_utf8(data, new_node.content_type) 466 new_content = mimeview.to_utf8(data, new_node.content_type) 467 467 new_node_info = (new_node.path, new_node.rev) 468 468 new_path = new_node.path 469 469 else: … … 510 510 511 511 buf = StringIO() 512 512 zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) 513 for old_node, new_node, kind, change in repos.get_changes(**diff):514 if kind == Node.FILE and change != Changeset.DELETE:513 for old_node, new_node, node_type, change_type in repos.get_changes(**diff): 514 if node_type == Node.FILE and change_type != Changeset.DELETE: 515 515 assert new_node 516 516 zipinfo = ZipInfo() 517 517 zipinfo.filename = new_node.path … … 523 523 buf.seek(0, 2) # be sure to be at the end 524 524 req.send_header("Content-Length", buf.tell()) 525 525 req.end_headers() 526 526 527 527 req.write(buf.getvalue()) 528 528 529 529 def title_for_diff(self, diff): … … 582 582 583 583 def get_wiki_syntax(self): 584 584 yield ( 585 # [...] form: start with optional intertrac: [T... or [trac ... 585 # [...] form: start with optional intertrac: [T... or [trac ... 586 586 r"!?\[(?P<it_changeset>%s\s*)?" % Formatter.INTERTRAC_SCHEME + 587 587 # digits + optional path for the restricted changeset 588 r"\d+(?:/[^\]]*)?\]|" 588 r"\d+(?:/[^\]]*)?\]|" 589 589 # r... form: allow r1 but not r1:2 (handled by the log syntax) 590 590 r"(?:\b|!)r\d+\b(?!:\d)", 591 591 lambda x, y, z: … … 631 631 old, new = pathrev(p1), pathrev(p2) 632 632 diff = DiffArgs(old_path=old[0], old_rev=old[1], 633 633 new_path=new[0], new_rev=new[1]) 634 else: 634 else: 635 635 old_path, old_rev = pathrev(params) 636 636 new_rev = None 637 637 if old_rev and ':' in old_rev: … … 640 640 new_path=old_path, new_rev=new_rev) 641 641 title = self.title_for_diff(diff) 642 642 href = formatter.href.changeset(new_path=diff.new_path or None, 643 new=diff.new_rev, 643 new=diff.new_rev, 644 644 old_path=diff.old_path or None, 645 645 old=diff.old_rev) 646 646 return '<a class="changeset" title="%s" href="%s">%s</a>' \ 647 647 % (title, href, label) 648 648 649 649 # ISearchProvider methods 650 650 651 651 def get_search_filters(self, req): … … 685 685 old_path = req.args.get('old_path') 686 686 old_rev = req.args.get('old_rev') 687 687 688 # -- normalize 688 # -- normalize 689 689 repos = self.env.get_repository(req.authname) 690 690 new_path = repos.normalize_path(new_path) 691 691 new_rev = repos.normalize_rev(new_rev) … … 695 695 authzperm = SubversionAuthorizer(self.env, req.authname) 696 696 authzperm.assert_permission_for_changeset(new_rev) 697 697 authzperm.assert_permission_for_changeset(old_rev) 698 698 699 699 # -- prepare rendering 700 700 req.hdf['anydiff'] = { 701 701 'new_path': new_path, -
trac/upgrades/db17.py
1 from trac.db import Table, Column, Index, DatabaseManager 2 3 def do_upgrade(env, ver, cursor): 4 cursor.execute("CREATE TEMP TABLE node_change_old AS SELECT * FROM node_change") 5 cursor.execute("DROP TABLE node_change") 6 7 db = env.get_db_cnx() 8 node_change_table = Table('node_change', key=('rev', 'path', 'change_type'))[ 9 Column('rev'), 10 Column('path'), 11 Column('node_type', size=1), 12 Column('change_type', size=1), 13 Column('base_path'), 14 Column('base_rev'), 15 Index(['rev'])] 16 db_backend, _ = DatabaseManager(env)._get_connector() 17 for stmt in db_backend.to_sql(node_change_table): 18 cursor.execute(stmt) 19 20 cursor.execute("INSERT INTO node_change (rev,path,node_type,change_type,base_path,base_rev) " 21 "SELECT rev,path,kind,change,base_path,base_rev " 22 "FROM node_change_old") -
templates/changeset.cs
8 8 if:changeset.chgset ?><?cs 9 9 if:changeset.restricted ?><?cs 10 10 set:change = "Change" ?><?cs 11 else ?><?cs 11 else ?><?cs 12 12 set:change = "Changeset" ?><?cs 13 13 /if ?> 14 14 <li class="first"><?cs 15 if:len(links.prev) ?> ← 15 if:len(links.prev) ?> ← 16 16 <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 17 var:links.prev.0.title ?>">Previous <?cs var:change ?></a> <?cs 17 var:links.prev.0.title ?>">Previous <?cs var:change ?></a> <?cs 18 18 else ?> 19 <span class="missing">← Previous <?cs var:change ?></span><?cs 19 <span class="missing">← Previous <?cs var:change ?></span><?cs 20 20 /if ?> 21 21 </li> 22 22 <li class="last"><?cs 23 23 if:len(links.next) ?> 24 24 <a class="next" href="<?cs var:links.next.0.href ?>" title="<?cs 25 var:links.next.0.title ?>">Next <?cs var:change ?></a> → <?cs 25 var:links.next.0.title ?>">Next <?cs var:change ?></a> → <?cs 26 26 else ?> 27 27 <span class="missing">Next <?cs var:change ?> →</span><?cs 28 28 /if ?>
