Edgewall Software

Ticket #8731: 8731-surrogate-keys-r8662.2.patch

File 8731-surrogate-keys-r8662.2.patch, 35.0 KB (added by rblank, 3 years ago)

Complete patch.

  • trac/admin/tests/console-tests.txt

    diff --git a/trac/admin/tests/console-tests.txt b/trac/admin/tests/console-tests.txt
    a b  
    4141repository alias     Create an alias for a repository 
    4242repository list      List source repositories 
    4343repository remove    Remove a source repository 
    44 repository rename    Rename a source repository 
    4544repository resync    Re-synchronize trac with repositories 
    4645repository set       Set an attribute of a repository 
    4746repository sync      Resume synchronization of repositories 
  • trac/db_default.py

    diff --git a/trac/db_default.py b/trac/db_default.py
    a b  
    1717from trac.db import Table, Column, Index 
    1818 
    1919# Database version identifier. Used for automatic upgrades. 
    20 db_version = 23 
     20db_version = 24 
    2121 
    2222def __mkreports(reports): 
    2323    """Utility function used to create report data in same syntax as the 
     
    8686 
    8787    # Version control cache 
    8888    Table('repository', key=('id', 'name'))[ 
    89         Column('id'), 
     89        Column('id', type='int'), 
    9090        Column('name'), 
    9191        Column('value')], 
    9292    Table('revision', key=('repos', 'rev'))[ 
    93         Column('repos'), 
     93        Column('repos', type='int'), 
    9494        Column('rev'), 
    9595        Column('time', type='int'), 
    9696        Column('author'), 
    9797        Column('message'), 
    9898        Index(['repos', 'time'])], 
    9999    Table('node_change', key=('repos', 'rev', 'path', 'change_type'))[ 
    100         Column('repos'), 
     100        Column('repos', type='int'), 
    101101        Column('rev'), 
    102102        Column('path'), 
    103103        Column('node_type', size=1), 
  • trac/env.py

    diff --git a/trac/env.py b/trac/env.py
    a b  
    451451    def upgrade(self, backup=False, backup_dest=None): 
    452452        """Upgrade database. 
    453453         
    454         Each db version should have its own upgrade module, names 
     454        Each db version should have its own upgrade module, named 
    455455        upgrades/dbN.py, where 'N' is the version number (int). 
    456456 
    457457        @param backup: whether or not to backup before upgrading 
  • new file trac/upgrades/db24.py

    diff --git a/trac/upgrades/db24.py b/trac/upgrades/db24.py
    new file mode 100644
    - +  
     1from trac.db import Table, Column, Index, DatabaseManager 
     2 
     3def do_upgrade(env, ver, cursor): 
     4    # Change repository key from reponame to a surrogate id 
     5    cursor.execute("SELECT id FROM repository " 
     6                   "UNION SELECT repos AS id FROM revision " 
     7                   "UNION SELECT repos AS id FROM node_change " 
     8                   "ORDER BY id") 
     9    id_name_list = [(i + 1, name) for i, (name,) in enumerate(cursor)] 
     10     
     11    cursor.execute("CREATE TEMPORARY TABLE repo_old " 
     12                   "AS SELECT * FROM repository") 
     13    cursor.execute("DROP TABLE repository") 
     14    cursor.execute("CREATE TEMPORARY TABLE rev_old " 
     15                   "AS SELECT * FROM revision") 
     16    cursor.execute("DROP TABLE revision") 
     17    cursor.execute("CREATE TEMPORARY TABLE nc_old " 
     18                   "AS SELECT * FROM node_change") 
     19    cursor.execute("DROP TABLE node_change") 
     20     
     21    tables = [Table('repository', key=('id', 'name'))[ 
     22                  Column('id', type='int'), 
     23                  Column('name'), 
     24                  Column('value')], 
     25              Table('revision', key=('repos', 'rev'))[ 
     26                  Column('repos', type='int'), 
     27                  Column('rev'), 
     28                  Column('time', type='int'), 
     29                  Column('author'), 
     30                  Column('message'), 
     31                  Index(['repos', 'time'])], 
     32              Table('node_change', key=('repos', 'rev', 'path', 'change_type'))[ 
     33                  Column('repos', type='int'), 
     34                  Column('rev'), 
     35                  Column('path'), 
     36                  Column('node_type', size=1), 
     37                  Column('change_type', size=1), 
     38                  Column('base_path'), 
     39                  Column('base_rev'), 
     40                  Index(['repos', 'rev'])]] 
     41     
     42    db_connector, _ = DatabaseManager(env)._get_connector() 
     43    for table in tables: 
     44        for stmt in db_connector.to_sql(table): 
     45            cursor.execute(stmt) 
     46     
     47    cursor.executemany("INSERT INTO repository (id,name,value) " 
     48                       "VALUES (%s,'name',%s)", id_name_list) 
     49    cursor.executemany("INSERT INTO repository (id,name,value) " 
     50                       "SELECT %s,name,value FROM repo_old WHERE id=%s", 
     51                       id_name_list) 
     52    cursor.execute("DROP TABLE repo_old") 
     53    cursor.executemany("INSERT INTO revision (repos,rev,time,author,message) " 
     54                       "SELECT %s,rev,time,author,message FROM rev_old " 
     55                       "WHERE repos=%s", id_name_list) 
     56    cursor.execute("DROP TABLE rev_old") 
     57    cursor.executemany("INSERT INTO node_change (repos,rev,path,node_type," 
     58                       "  change_type,base_path,base_rev) " 
     59                       "SELECT %s,rev,path,node_type,change_type,base_path," 
     60                       "  base_rev FROM nc_old WHERE repos=%s", id_name_list) 
     61    cursor.execute("DROP TABLE nc_old") 
  • trac/versioncontrol/admin.py

    diff --git a/trac/versioncontrol/admin.py b/trac/versioncontrol/admin.py
    a b  
    121121                return 
    122122            repositories = [repos] 
    123123         
    124         from trac.versioncontrol.cache import CACHE_METADATA_KEYS 
    125124        db = self.env.get_db_cnx() 
    126125        cursor = db.cursor() 
    127126        for repos in sorted(repositories, key=lambda r: r.reponame): 
    128             reponame = repos.reponame 
    129127            printout(_('Resyncing repository history for %(reponame)s... ', 
    130                        reponame=reponame or '(default)')) 
    131             if clean: 
    132                 cursor.execute("DELETE FROM revision WHERE repos=%s", 
    133                                (reponame,)) 
    134                 cursor.execute("DELETE FROM node_change " 
    135                                "WHERE repos=%s", (reponame,)) 
    136                 cursor.executemany("DELETE FROM repository " 
    137                                    "WHERE id=%s AND name=%s", 
    138                                    [(reponame, k) for k in CACHE_METADATA_KEYS]) 
    139                 cursor.executemany("INSERT INTO repository (id, name, value) " 
    140                                    "VALUES (%s, %s, %s)",  
    141                                    [(reponame, k, '')  
    142                                     for k in CACHE_METADATA_KEYS]) 
    143                 db.commit() 
    144             repos.sync(self._sync_feedback) 
     128                       reponame=repos.reponame or '(default)')) 
     129            repos.sync(self._sync_feedback, clean=clean) 
    145130            cursor.execute("SELECT count(rev) FROM revision WHERE repos=%s", 
    146                            (reponame,)) 
     131                           (repos.id,)) 
    147132            for cnt, in cursor: 
    148133                printout(ngettext('%(num)s revision cached.', 
    149134                                  '%(num)s revisions cached.', num=cnt)) 
     
    187172                 
    188173                elif db_provider and req.args.get('save'): 
    189174                    # Modify repository 
    190                     changed = False 
    191175                    changes = {} 
    192176                    for field in db_provider.repository_attrs: 
    193177                        value = normalize_whitespace(req.args.get(field)) 
     
    196180                            changes[field] = value 
    197181                    if changes: 
    198182                        db_provider.modify_repository(reponame, changes) 
    199                         changed = True 
    200                     # Rename repository 
     183                        add_notice(req, _('Your changes have been saved.')) 
    201184                    name = req.args.get('name') 
    202                     if name and name != path_info: 
    203                         db_provider.rename_repository(reponame, name) 
    204                         changed = True 
    205                     if changed: 
    206                         add_notice(req, _('Your changes have been saved.')) 
    207185                    if 'dir' in changes: 
    208186                        msg = _('You should now run "trac-admin $ENV ' 
    209187                                'repository resync %(name)s" to synchronize ' 
  • trac/versioncontrol/api.py

    diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py
    a b  
    9696 
    9797    implements(IRepositoryProvider, IAdminCommandProvider) 
    9898 
    99     repository_attrs = ('alias', 'dir', 'hidden', 'type', 'url') 
     99    repository_attrs = ('alias', 'dir', 'hidden', 'name', 'type', 'url') 
    100100     
    101101    # IRepositoryProvider methods 
    102102 
     
    107107        cursor.execute("SELECT id,name,value FROM repository " 
    108108                       "WHERE name IN (%s)" % ",".join( 
    109109                           "'%s'" % each for each in self.repository_attrs)) 
     110        repos = {} 
     111        for id, name, value in cursor: 
     112            if value is not None: 
     113                repos.setdefault(id, {})[name] = value 
    110114        reponames = {} 
    111         for (id, name, value) in cursor: 
    112             if value is not None: 
    113                 reponames.setdefault(id, {})[name] = value 
     115        for id, info in repos.iteritems(): 
     116            if 'name' in info and ('dir' in info or 'alias' in info): 
     117                info['id'] = id 
     118                reponames[info['name']] = info 
    114119        return reponames.iteritems() 
    115120 
    116121    # IAdminCommandProvider methods 
     
    125130        yield ('repository remove', '<repos>', 
    126131               'Remove a source repository', 
    127132               self._complete_repos, self._do_remove) 
    128         yield ('repository rename', '<repos> <newname>', 
    129                'Rename a source repository', 
    130                self._complete_repos, self._do_rename) 
    131133        yield ('repository set', '<repos> <key> <value>', 
    132134               """Set an attribute of a repository 
    133135                
     
    169171    def _do_remove(self, reponame): 
    170172        self.remove_repository(reponame) 
    171173     
    172     def _do_rename(self, reponame, newname): 
    173         self.rename_repository(reponame, newname) 
    174      
    175174    def _do_set(self, reponame, key, value): 
    176175        if key not in self.repository_attrs: 
    177176            raise AdminCommandError(_('Invalid key "%(key)s"', key=key)) 
     
    194193            raise TracError(_("The repository type '%(type)s' is not " 
    195194                              "supported", type=type_)) 
    196195        db = self.env.get_db_cnx() 
     196        id = rm.get_repository_id(reponame, db) 
    197197        cursor = db.cursor() 
    198198        cursor.executemany("INSERT INTO repository (id, name, value) " 
    199199                           "VALUES (%s, %s, %s)", 
    200                            [(reponame, 'dir', dir), 
    201                             (reponame, 'type', type_ or '')]) 
     200                           [(id, 'dir', dir), 
     201                            (id, 'type', type_ or '')]) 
    202202        db.commit() 
    203203        rm.reload_repositories() 
    204204     
     
    208208            reponame = '' 
    209209        if target == '(default)': 
    210210            target = '' 
     211        rm = RepositoryManager(self.env) 
    211212        db = self.env.get_db_cnx() 
     213        id = rm.get_repository_id(reponame, db) 
    212214        cursor = db.cursor() 
    213215        cursor.executemany("INSERT INTO repository (id, name, value) " 
    214216                           "VALUES (%s, %s, %s)", 
    215                            [(reponame, 'dir', None), 
    216                             (reponame, 'alias', target)]) 
     217                           [(id, 'dir', None), 
     218                            (id, 'alias', target)]) 
    217219        db.commit() 
    218         RepositoryManager(self.env).reload_repositories() 
     220        rm.reload_repositories() 
    219221     
    220222    def remove_repository(self, reponame): 
    221223        """Remove a repository.""" 
    222224        if reponame == '(default)': 
    223225            reponame = '' 
     226        rm = RepositoryManager(self.env) 
    224227        db = self.env.get_db_cnx() 
     228        id = rm.get_repository_id(reponame, db) 
    225229        cursor = db.cursor() 
    226         cursor.execute("DELETE FROM repository WHERE id=%s", (reponame,)) 
    227         cursor.execute("DELETE FROM revision WHERE repos=%s", (reponame,)) 
    228         cursor.execute("DELETE FROM node_change WHERE repos=%s", (reponame,)) 
     230        cursor.execute("DELETE FROM repository WHERE id=%s", (id,)) 
     231        cursor.execute("DELETE FROM revision WHERE repos=%s", (id,)) 
     232        cursor.execute("DELETE FROM node_change WHERE repos=%s", (id,)) 
    229233        db.commit() 
    230         RepositoryManager(self.env).reload_repositories() 
    231      
    232     def rename_repository(self, reponame, newname): 
    233         """Rename a repository.""" 
    234         if reponame == '(default)': 
    235             reponame = '' 
    236         if newname == '(default)': 
    237             newname = '' 
    238         db = self.env.get_db_cnx() 
    239         cursor = db.cursor() 
    240         cursor.execute("UPDATE repository SET id=%s WHERE id=%s", 
    241                        (newname, reponame)) 
    242         cursor.execute("UPDATE revision SET repos=%s WHERE repos=%s", 
    243                        (newname, reponame)) 
    244         cursor.execute("UPDATE node_change SET repos=%s WHERE repos=%s", 
    245                        (newname, reponame)) 
    246         db.commit() 
    247         RepositoryManager(self.env).reload_repositories() 
     234        rm.reload_repositories() 
    248235     
    249236    def modify_repository(self, reponame, changes): 
    250237        """Modify attributes of a repository.""" 
    251238        if reponame == '(default)': 
    252239            reponame = '' 
     240        rm = RepositoryManager(self.env) 
    253241        db = self.env.get_db_cnx() 
     242        id = rm.get_repository_id(reponame, db) 
    254243        cursor = db.cursor() 
    255244        for (k, v) in changes.iteritems(): 
    256245            if k not in self.repository_attrs: 
    257246                continue 
    258             if k == 'alias' and v == '(default)': 
     247            if k in('alias', 'name') and v == '(default)': 
    259248                v = '' 
    260249            cursor.execute("UPDATE repository SET value=%s " 
    261                            "WHERE id=%s AND name=%s", (v, reponame, k)) 
     250                           "WHERE id=%s AND name=%s", (v, id, k)) 
    262251            cursor.execute("SELECT value FROM repository " 
    263                            "WHERE id=%s AND name=%s", (reponame, k)) 
     252                           "WHERE id=%s AND name=%s", (id, k)) 
    264253            if not cursor.fetchone(): 
    265                 cursor.execute("INSERT INTO repository VALUES (%s, %s, %s)", 
    266                                (reponame, k, v)) 
     254                cursor.execute("INSERT INTO repository (id, name, value) " 
     255                               "VALUES (%s, %s, %s)", (id, k, v)) 
    267256        db.commit() 
    268         RepositoryManager(self.env).reload_repositories() 
     257        rm.reload_repositories() 
    269258 
    270259 
    271260class RepositoryManager(Component): 
     
    445434                        repositories.append(repos) 
    446435        return repositories 
    447436 
     437    def get_repository_id(self, reponame, db=None): 
     438        """Return a unique id for the given repository name.""" 
     439        handle_ta = False 
     440        if db is None: 
     441            db = self.env.get_db_cnx() 
     442            handle_ta = True 
     443        cursor = db.cursor() 
     444        cursor.execute("SELECT id FROM repository " 
     445                       "WHERE name='name' AND value=%s", (reponame,)) 
     446        for id, in cursor: 
     447            return id 
     448        cursor.execute("SELECT COALESCE(MAX(id),0) FROM repository") 
     449        id = cursor.fetchone()[0] + 1 
     450        cursor.execute("INSERT INTO repository (id, name, value) " 
     451                       "VALUES (%s,%s,%s)", (id, 'name', reponame)) 
     452        if handle_ta: 
     453            db.commit() 
     454        return id 
     455     
    448456    def get_repository(self, reponame, authname): 
    449457        """Retrieve the appropriate Repository for the given name. 
    450458 
     
    455463           :return: if no corresponding repository was defined,  
    456464                    simply return `None`. 
    457465        """ 
     466        reponame = reponame or '' 
    458467        repoinfo = self.get_all_repositories().get(reponame, {}) 
    459468        if repoinfo and 'alias' in repoinfo: 
    460469            reponame = repoinfo['alias'] 
     
    488497                    rdir = os.path.join(self.env.path, rdir) 
    489498                connector = self._get_connector(rtype) 
    490499                repos = connector.get_repository(rtype, rdir, repoinfo) 
    491                 repos.reponame = reponame 
    492500                repositories[reponame] = repos 
    493501            return repos 
    494502        finally: 
     
    538546                        self.log.warn("Discarding duplicate repository '%s'", 
    539547                                      reponame) 
    540548                    else: 
     549                        info['name'] = reponame 
     550                        if 'id' not in info: 
     551                            info['id'] = self.get_repository_id(reponame) 
    541552                        self._all_repositories[reponame] = info 
    542553        return self._all_repositories 
    543554     
     
    668679class Repository(object): 
    669680    """Base class for a repository provided by a version control system.""" 
    670681 
    671     def __init__(self, name, authz, log): 
     682    def __init__(self, reponame, id, name, authz, log): 
     683        self.reponame = reponame 
     684        self.id = id 
    672685        self.name = name 
    673686        self.authz = authz or Authorizer() 
    674687        self.log = log 
    675         self.reponame = name # overriden by the reponame key used to create it 
    676688 
    677689    def close(self): 
    678690        """Close the connection to the repository.""" 
     
    695707        """ 
    696708        pass 
    697709 
    698     def sync(self, rev_callback=None): 
     710    def sync(self, rev_callback=None, clean=False): 
    699711        """Perform a sync of the repository cache, if relevant. 
    700712         
    701713        If given, `rev_callback` must be a callable taking a `rev` parameter. 
    702714        The backend will call this function for each `rev` it decided to 
    703715        synchronize, once the synchronization changes are committed to the  
    704         cache. 
     716        cache. When `clean` is `True`, the cache is cleaned first. 
    705717        """ 
    706718        pass 
    707719 
  • trac/versioncontrol/cache.py

    diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
    a b  
    4848        self.repos = repos 
    4949        self.metadata = CacheProxy(self.__class__.__module__ + '.' 
    5050                                   + self.__class__.__name__ + '.metadata:' 
    51                                    + self.repos.reponame, self._metadata, 
     51                                   + str(self.repos.id), self._metadata, 
    5252                                   self.env) 
    53         Repository.__init__(self, repos.name, authz, log) 
     53        Repository.__init__(self, repos.reponame, repos.id, repos.name, authz, 
     54                            log) 
    5455 
    55     def _set_reponame(self, value): 
    56         self.repos.reponame = value 
    57         self.metadata.id = self.__class__.__module__ + '.' \ 
    58                            + self.__class__.__name__ + '.metadata:' \ 
    59                            + value 
    60      
    61     reponame = property(fget=lambda self: self.repos.reponame, 
    62                         fset=_set_reponame) 
    63      
    6456    def close(self): 
    6557        self.repos.close() 
    6658 
     
    8678        cursor.execute("SELECT rev FROM revision " 
    8779                       "WHERE repos=%s AND time >= %s AND time < %s " 
    8880                       "ORDER BY time DESC, rev DESC", 
    89                        (self.reponame, to_timestamp(start), 
     81                       (self.id, to_timestamp(start), 
    9082                        to_timestamp(stop))) 
    9183        for rev, in cursor: 
    9284            try: 
     
    10193        cursor = db.cursor() 
    10294        cursor.execute("SELECT time,author,message FROM revision " 
    10395                       "WHERE repos=%s AND rev=%s", 
    104                        (self.reponame, str(cset.rev))) 
     96                       (self.id, str(cset.rev))) 
    10597        old_changeset = None 
    10698        for time, author, message in cursor: 
    10799            date = datetime.fromtimestamp(time, utc) 
     
    110102        cursor.execute("UPDATE revision SET time=%s, author=%s, message=%s " 
    111103                       "WHERE repos=%s AND rev=%s", 
    112104                       (to_timestamp(cset.date), cset.author, cset.message, 
    113                         self.reponame, str(cset.rev))) 
     105                        self.id, str(cset.rev))) 
    114106        db.commit() 
    115107        return old_changeset 
    116108         
     
    120112        cursor.execute("SELECT name, value FROM repository " 
    121113                       "WHERE id=%%s AND name IN (%s)" %  
    122114                       ','.join(['%s'] * len(CACHE_METADATA_KEYS)), 
    123                        (self.reponame,) + CACHE_METADATA_KEYS) 
     115                       (self.id,) + CACHE_METADATA_KEYS) 
    124116        return dict(cursor) 
    125117 
    126     def sync(self, feedback=None): 
     118    def sync(self, feedback=None, clean=False): 
    127119        db = self.env.get_db_cnx() 
    128120        cursor = db.cursor() 
     121        if clean: 
     122            self.log.info('Cleaning cache') 
     123            cursor.execute("DELETE FROM revision WHERE repos=%s", (self.id,)) 
     124            cursor.execute("DELETE FROM node_change WHERE repos=%s", 
     125                           (self.id,)) 
     126            cursor.executemany("DELETE FROM repository " 
     127                               "WHERE id=%s AND name=%s", 
     128                               [(self.id, k) for k in CACHE_METADATA_KEYS]) 
     129            cursor.executemany("INSERT INTO repository (id, name, value) " 
     130                               "VALUES (%s, %s, %s)",  
     131                               [(self.id, k, '') for k in CACHE_METADATA_KEYS]) 
     132            self.metadata.invalidate(db) 
     133            db.commit() 
     134             
    129135        metadata = self.metadata.get(db) 
    130136        do_commit = False 
    131137         
     
    143149            self.log.info('Storing initial "repository_dir": %s' % self.name) 
    144150            cursor.execute("INSERT INTO repository (id,name,value) " 
    145151                           "VALUES (%s,%s,%s)", 
    146                            (self.reponame, CACHE_REPOSITORY_DIR, self.name)) 
     152                           (self.id, CACHE_REPOSITORY_DIR, self.name)) 
    147153            do_commit = True 
    148154        else: # 'repository_dir' cleared by a resync 
    149155            self.log.info('Resetting "repository_dir": %s' % self.name) 
    150156            cursor.execute("UPDATE repository SET value=%s " 
    151157                           "WHERE id=%s AND name=%s", 
    152                            (self.name, self.reponame, CACHE_REPOSITORY_DIR)) 
     158                           (self.name, self.id, CACHE_REPOSITORY_DIR)) 
    153159            do_commit = True 
    154160 
    155161        # -- retrieve the youngest revision in the repository 
     
    161167        if youngest is None: 
    162168            cursor.execute("INSERT INTO repository (id,name,value) " 
    163169                           "VALUES (%s,%s,%s)", 
    164                            (self.reponame, CACHE_YOUNGEST_REV, '')) 
     170                           (self.id, CACHE_YOUNGEST_REV, '')) 
    165171            do_commit = True 
    166172 
    167173        if do_commit: 
     
    205211            # 0. first check if there's no (obvious) resync in progress 
    206212            cursor.execute("SELECT rev FROM revision " 
    207213                           "WHERE repos=%s AND rev=%s", 
    208                            (self.reponame, str(next_youngest))) 
     214                           (self.id, str(next_youngest))) 
    209215            for rev, in cursor: 
    210216                # already there, but in progress, so keep ''previous'' 
    211217                # notion of 'youngest' 
     
    232238                        cursor.execute("INSERT INTO revision " 
    233239                                       " (repos,rev,time,author,message) " 
    234240                                       "VALUES (%s,%s,%s,%s,%s)", 
    235                                        (self.reponame, str(next_youngest), 
     241                                       (self.id, str(next_youngest), 
    236242                                        to_timestamp(cset.date), 
    237243                                        cset.author, cset.message)) 
    238244                    except Exception, e: # *another* 1.1. resync attempt won  
     
    257263                                       " (repos,rev,path,node_type," 
    258264                                       "  change_type,base_path,base_rev) " 
    259265                                       "VALUES (%s,%s,%s,%s,%s,%s,%s)", 
    260                                        (self.reponame, str(next_youngest), 
     266                                       (self.id, str(next_youngest), 
    261267                                        path, kind, action, bpath, brev)) 
    262268 
    263269                    # 1.3. iterate (1.1 should always succeed now) 
     
    268274                    #      (minimize possibility of failures at point 0.) 
    269275                    cursor.execute("UPDATE repository SET value=%s " 
    270276                                   "WHERE id=%s AND name=%s", 
    271                                    (str(youngest), self.reponame, 
     277                                   (str(youngest), self.id, 
    272278                                    CACHE_YOUNGEST_REV)) 
    273279                    self.metadata.invalidate(db) 
    274280                    db.commit() 
     
    336342        # the changeset revs are sequence of ints: 
    337343        sql = "SELECT rev FROM node_change WHERE repos=%s AND " + \ 
    338344              db.cast('rev', 'int') + " " + direction + " %s" 
    339         args = [self.reponame, rev] 
     345        args = [self.id, rev] 
    340346 
    341347        if path: 
    342348            path = path.lstrip('/') 
     
    406412        cursor = db.cursor() 
    407413        cursor.execute("SELECT time,author,message FROM revision " 
    408414                       "WHERE repos=%s AND rev=%s", 
    409                        (self.repos.reponame, str(rev))) 
     415                       (self.repos.id, str(rev))) 
    410416        row = cursor.fetchone() 
    411417        if row: 
    412418            _date, author, message = row 
     
    421427        cursor = db.cursor() 
    422428        cursor.execute("SELECT path,node_type,change_type,base_path,base_rev " 
    423429                       "FROM node_change WHERE repos=%s AND rev=%s " 
    424                        "ORDER BY path", (self.repos.reponame, str(self.rev))) 
     430                       "ORDER BY path", (self.repos.id, str(self.rev))) 
    425431        for path, kind, change, base_path, base_rev in cursor: 
    426432            if not self.authz.has_permission(posixpath.join(self.scope, 
    427433                                                            path.strip('/'))): 
  • trac/versioncontrol/svn_fs.py

    diff --git a/trac/versioncontrol/svn_fs.py b/trac/versioncontrol/svn_fs.py
    a b  
    278278        if not self._version: 
    279279            self._version = self._get_version() 
    280280            self.env.systeminfo.append(('Subversion', self._version)) 
    281         fs_repos = SubversionRepository(dir, None, self.log, 
     281        fs_repos = SubversionRepository(options['name'], options['id'], 
     282                                        dir, None, self.log, 
    282283                                        {'tags': self.tags, 
    283284                                         'branches': self.branches, 
    284285                                         'url': options.get('url') or ''}) 
     
    306307class SubversionRepository(Repository): 
    307308    """Repository implementation based on the svn.fs API.""" 
    308309 
    309     def __init__(self, path, authz, log, options={}): 
     310    def __init__(self, reponame, id, path, authz, log, options={}): 
    310311        self.log = log 
    311312        self.options = options 
    312313        self.pool = Pool() 
     
    337338        self.base = 'svn:%s:%s' % (self.uuid, _from_svn(root_path_utf8)) 
    338339        name = 'svn:%s:%s' % (self.uuid, self.path) 
    339340 
    340         Repository.__init__(self, name, authz, log) 
     341        Repository.__init__(self, reponame, id, name, authz, log) 
    341342 
    342343        # if root_path_utf8 is shorter than the path_utf8, the difference is 
    343344        # this scope (which always starts with a '/') 
  • trac/versioncontrol/templates/admin_repositories.html

    diff --git a/trac/versioncontrol/templates/admin_repositories.html b/trac/versioncontrol/templates/admin_repositories.html
    a b  
    9191          </fieldset> 
    9292        </form> 
    9393 
    94         <form class="addnew" id="trac-addalias" method="post" action=""> 
     94        <form py:if="any(not info.alias for info in repositories.itervalues())" 
     95              class="addnew" id="trac-addalias" method="post" action=""> 
    9596          <fieldset> 
    9697            <legend>Add Alias:</legend> 
    9798            <div class="field"> 
  • trac/versioncontrol/tests/api.py

    diff --git a/trac/versioncontrol/tests/api.py b/trac/versioncontrol/tests/api.py
    a b  
    2121class ApiTestCase(unittest.TestCase): 
    2222 
    2323    def setUp(self): 
    24         self.repo_base = Repository('testrepo', None, None) 
     24        self.repo_base = Repository('testrepo', 1, 'testrepo', None, None) 
    2525 
    2626    def test_raise_NotImplementedError_close(self): 
    2727        self.failUnlessRaises(NotImplementedError, self.repo_base.close) 
  • trac/versioncontrol/tests/cache.py

    diff --git a/trac/versioncontrol/tests/cache.py b/trac/versioncontrol/tests/cache.py
    a b  
    3333        self.db = self.env.get_db_cnx() 
    3434        self.log = self.env.log 
    3535        cursor = self.db.cursor() 
    36         cursor.execute("INSERT INTO repository (id, name, value) " 
    37                        "VALUES (%s,%s,%s)", 
    38                        ('test-repos', 'youngest_rev', '')) 
     36        cursor.executemany("INSERT INTO repository (id,name,value) " 
     37                           "VALUES (%s,%s,%s)", 
     38                           [(1, 'name', 'test-repos'), 
     39                            (1, 'youngest_rev', '')]) 
    3940 
    4041    def tearDown(self): 
    4142        self.env.reset_db() 
     
    4546        def no_changeset(rev): 
    4647            raise NoSuchChangeset(rev) 
    4748             
    48         repos = Mock(Repository, 'test-repos', None, self.log, 
     49        repos = Mock(Repository, 'test-repos', 1, 'test-repos', None, self.log, 
    4950                     get_changeset=no_changeset, 
    5051                     get_oldest_rev=lambda: 1, 
    5152                     get_youngest_rev=lambda: 0, 
     
    6970                           get_changes=lambda: []), 
    7071                      Mock(Changeset, 1, 'Import', 'joe', t2, 
    7172                           get_changes=lambda: iter(changes))] 
    72         repos = Mock(Repository, 'test-repos', None, self.log, 
     73        repos = Mock(Repository, 'test-repos', 1, 'test-repos', None, self.log, 
    7374                     get_changeset=lambda x: changesets[int(x)], 
    7475                     get_oldest_rev=lambda: 0, 
    7576                     get_youngest_rev=lambda: 1, 
     
    9798        t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) 
    9899        cursor = self.db.cursor() 
    99100        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) " 
    100                        "VALUES ('test-repos',0,%s,'','')", 
     101                       "VALUES (1,0,%s,'','')", 
    101102                       (to_timestamp(t1),)) 
    102103        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) " 
    103                        "VALUES ('test-repos',1,%s,'joe','Import')", 
     104                       "VALUES (1,1,%s,'joe','Import')", 
    104105                       (to_timestamp(t2),)) 
    105106        cursor.executemany("INSERT INTO node_change (repos,rev,path," 
    106107                           "node_type,change_type,base_path,base_rev) " 
    107                            "VALUES ('test-repos','1',%s,%s,%s,%s,%s)", 
     108                           "VALUES (1,'1',%s,%s,%s,%s,%s)", 
    108109                           [('trunk', 'D', 'A', None, None), 
    109110                            ('trunk/README', 'F', 'A', None, None)]) 
    110111        cursor.execute("UPDATE repository SET value='1' " 
    111                        "WHERE id='test-repos' AND name='youngest_rev'") 
     112                       "WHERE id=1 AND name='youngest_rev'") 
    112113 
    113114        changes = [('trunk/README', Node.FILE, Changeset.EDIT, 'trunk/README', 1)] 
    114115        changeset = Mock(Changeset, 2, 'Update', 'joe', t3, 
    115116                         get_changes=lambda: iter(changes)) 
    116         repos = Mock(Repository, 'test-repos', None, self.log, 
     117        repos = Mock(Repository, 'test-repos', 1, 'test-repos', None, self.log, 
    117118                     get_changeset=lambda x: changeset, 
    118119                     get_youngest_rev=lambda: 2, 
    119120                     get_oldest_rev=lambda: 0, 
     
    137138        t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) 
    138139        cursor = self.db.cursor() 
    139140        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) " 
    140                        "VALUES ('test-repos',0,%s,'','')", 
     141                       "VALUES (1,0,%s,'','')", 
    141142                       (to_timestamp(t1),)) 
    142143        cursor.execute("INSERT INTO revision (repos,rev,time,author,message) " 
    143                        "VALUES ('test-repos',1,%s,'joe','Import')", 
     144                       "VALUES (1,1,%s,'joe','Import')", 
    144145                       (to_timestamp(t2),)) 
    145146        cursor.executemany("INSERT INTO node_change (repos,rev,path," 
    146147                           "node_type,change_type,base_path,base_rev) " 
    147                            "VALUES ('test-repos','1',%s,%s,%s,%s,%s)", 
     148                           "VALUES (1,'1',%s,%s,%s,%s,%s)", 
    148149                           [('trunk', 'D', 'A', None, None), 
    149150                            ('trunk/README', 'F', 'A', None, None)]) 
    150151        cursor.execute("UPDATE repository SET value='1' " 
    151                        "WHERE id='test-repos' AND name='youngest_rev'") 
     152                       "WHERE id=1 AND name='youngest_rev'") 
    152153 
    153         repos = Mock(Repository, 'test-repos', None, self.log, 
     154        repos = Mock(Repository, 'test-repos', 1, 'test-repos', None, self.log, 
    154155                     get_changeset=lambda x: None, 
    155156                     get_youngest_rev=lambda: 1, 
    156157                     get_oldest_rev=lambda: 0, 
  • trac/versioncontrol/tests/svn_fs.py

    diff --git a/trac/versioncontrol/tests/svn_fs.py b/trac/versioncontrol/tests/svn_fs.py
    a b  
    8686class SubversionRepositoryTestCase(unittest.TestCase): 
    8787 
    8888    def setUp(self): 
    89         self.repos = SubversionRepository(REPOS_PATH, None, 
     89        self.repos = SubversionRepository('repo', 1, REPOS_PATH, None, 
    9090                                          logger_factory('test')) 
    9191 
    9292    def tearDown(self): 
     
    508508class ScopedSubversionRepositoryTestCase(unittest.TestCase): 
    509509 
    510510    def setUp(self): 
    511         self.repos = SubversionRepository(REPOS_PATH + u'/tête', None, 
    512                                           logger_factory('test')) 
     511        self.repos = SubversionRepository('repo', 1, REPOS_PATH + u'/tête', 
     512                                          None, logger_factory('test')) 
    513513 
    514514    def tearDown(self): 
    515515        self.repos = None 
     
    757757class RecentPathScopedRepositoryTestCase(unittest.TestCase): 
    758758 
    759759    def setUp(self): 
    760         self.repos = SubversionRepository(REPOS_PATH + u'/tête/dir1', None, 
     760        self.repos = SubversionRepository('repo', 1, 
     761                                          REPOS_PATH + u'/tête/dir1', None, 
    761762                                          logger_factory('test')) 
    762763 
    763764    def tearDown(self): 
     
    777778class NonSelfContainedScopedTestCase(unittest.TestCase): 
    778779 
    779780    def setUp(self): 
    780         self.repos = SubversionRepository(REPOS_PATH + '/tags/v1', None, 
    781                                           logger_factory('test')) 
     781        self.repos = SubversionRepository('repo', 1, REPOS_PATH + '/tags/v1', 
     782                                          None, logger_factory('test')) 
    782783 
    783784    def tearDown(self): 
    784785        self.repos = None 
     
    795796class AnotherNonSelfContainedScopedTestCase(unittest.TestCase): 
    796797 
    797798    def setUp(self): 
    798         self.repos = SubversionRepository(REPOS_PATH + '/branches', None, 
    799                                           logger_factory('test')) 
     799        self.repos = SubversionRepository('repo', 1, REPOS_PATH + '/branches', 
     800                                          None, logger_factory('test')) 
    800801 
    801802    def tearDown(self): 
    802803        self.repos = None