source: tracdarcs/tracdarcs/components.py @ 195

Revision 195, 9.3 KB checked in by lele@…, 3 years ago (diff)

Adapted the wiki link formatter to handle multiple repositories
Given that we are dealing with full darcs hashes, when the context doesn't
identify a particular repository, take the first one containing such changeset.

RevLine 
[126]1# -*- coding: utf-8 -*-
[18]2#
3# Copyright (C) 2005 Edgewall Software
[174]4# Copyright (C) 2005-2010 Lele Gaifax <lele@metapensiero.it>
[18]5#
6# This software is licensed as described in the file COPYING, which
7# you should have received as part of this distribution. The terms
8# are also available at http://trac.edgewall.com/license.html.
9#
10# This software consists of voluntary contributions made by many
11# individuals. For the exact contribution history, see the revision
12# history and logs, available at http://projects.edgewall.com/trac/.
13#
14# Author: Lele Gaifax <lele@metapensiero.it>
15
[120]16from genshi.builder import tag
17
[72]18from trac.config import BoolOption, Option
[18]19from trac.core import Component, implements
[111]20from trac.db import Column, DatabaseManager, Index, Table
[72]21from trac.env import IEnvironmentSetupParticipant
[195]22from trac.util.text import shorten_line, to_unicode
[120]23from trac.versioncontrol import IRepositoryConnector, NoSuchChangeset
[144]24from trac.versioncontrol.web_ui import IPropertyRenderer, RenderedProperty
25
[120]26from trac.wiki import IWikiSyntaxProvider
[18]27
[27]28from tracdarcs.repository import DarcsRepository
29
[18]30class DarcsConnector(Component):
31
[120]32    implements(IRepositoryConnector, IWikiSyntaxProvider)
[18]33
[72]34    dont_escape_8bit = BoolOption('darcs', 'dont_escape_8bit', 'false',
35                                  "Avoid darcs automatic escape of non-7bit chars.")
36
37    darcs_command = Option('darcs', 'command', 'darcs',
38                           "Name of the external darcs binary.")
39
[182]40    max_concurrent_darcses = Option('darcs', 'max_concurrent_darcses', 0,
41                                    "Max number of concurrent darcses running per "
42                                    "repository (0 means unlimited).")
[173]43
[123]44    possible_encodings = Option('darcs', 'possible_encodings', 'utf-8,iso8859-1',
45                                "Specify possible repository encodings.")
46
[185]47    eager_annotations = BoolOption('darcs', 'eager_annotations', 'false',
48                                   "Compute the annotation cache as soon as possible.")
49
[18]50    # IRepositoryConnector methods
51
52    def get_supported_types(self):
53        """Support the `darcs:` scheme"""
54        yield ("darcs", 8)
55
[161]56    def get_repository(self, type, dir, params):
[33]57        """Return a `DarcsRepository`"""
[18]58        db = self.env.get_db_cnx()
[72]59        darcs = self.darcs_command
60        if self.dont_escape_8bit:
61            darcs = "DARCS_DONT_ESCAPE_8BIT=1 " + darcs
[123]62        if self.possible_encodings:
63            possible_encodings = [e.strip()
64                                  for e in self.possible_encodings.split(',')]
[173]65
66        # Setup the semaphore used to limit the number of concurrent running
67        # darcs within a single repository.
68
69        if self.max_concurrent_darcses and int(self.max_concurrent_darcses)>0:
70            from command import DarcsCommand
71            if DarcsCommand.RUNNING_DARCSES is None:
72                from threading import BoundedSemaphore
73                DarcsCommand.RUNNING_DARCSES = BoundedSemaphore(value=int(self.max_concurrent_darcses))
74
[185]75        return DarcsRepository(db, dir, self.env.log, darcs, possible_encodings, params,
76                               self.eager_annotations)
[18]77
[120]78    # IWikiSyntaxProvider methods
79
80    def get_wiki_syntax(self):
[121]81        yield (r'[0-9]{14}-[0-9a-f]{5}-[0-9a-f]{40}(.gz)?',
[120]82               lambda formatter, label, match: self._format_link(formatter, 'cset', label, label))
83
84    def get_link_resolvers(self):
85        yield ('cset', self._format_link)
86
87    def _format_link(self, formatter, ns, rev, label):
[195]88        reponame = None
[120]89
[195]90        # See if the context carries a repository...
91        context = formatter.context
92        while context:
93            if context.resource.realm in ('source', 'changeset'):
94                reponame = context.resource.parent.id
95                break
96            context = context.parent
97
98        # If it does not, take the first repository containing the
99        # specified revision, if any. We can do this assuming that
100        #   a) we are dealing with full darcs hashes and
101        #   b) darcs hashes are globally unique
102        if reponame is None:
103            db = self.env.get_db_cnx()
104            c = db.cursor()
105            c.execute('SELECT r.value '
106                      'FROM darcs_changesets c JOIN repository r ON c.repo_id = r.id '
107                      'WHERE c.hash = %s AND r.name = %s '
108                      'ORDER BY r.id '
109                      'LIMIT 1', (rev, 'name',))
110            row = c.fetchone()
111            reponame = row and row[0] or ''
112
113        repos = self.env.get_repository(reponame)
114        if repos:
115            try:
116                chgset = repos.get_changeset(rev)
117                return tag.a(chgset.rev, class_="changeset",
118                             title=shorten_line(chgset.message),
119                             href=formatter.href.changeset(chgset.rev, reponame))
120            except NoSuchChangeset, e:
121                errmsg = to_unicode(e)
122        else:
123            errmsg = 'Repository "%s" not found' % reponame
124
125        return tag.a(label, class_="missing changeset", title=errmsg, rel="nofollow")
[120]126
[18]127class DarcsSetup(Component):
128    """Darcs customizer.
129
[33]130    The db tables required for the darcs backend are created here.
[18]131    """
132
133    implements(IEnvironmentSetupParticipant)
134
135    def environment_created(self):
[33]136        """After standard environment has been created, add the needed
137        tables."""
[18]138
139        db = self.env.get_db_cnx()
140        self.upgrade_environment(db)
141        db.commit()
142
143    def environment_needs_upgrade(self, db):
[33]144        """Check to see if the darcs tables are already there."""
[18]145
146        c = db.cursor()
147        try:
[132]148            c.execute('SELECT repo_id,rev,hash,name '
149                      'FROM darcs_changesets LIMIT 1')
[131]150            c.execute('SELECT repo_id,node_id,node_type,add_rev,remove_rev '
[88]151                      'FROM darcs_nodes LIMIT 1')
[158]152            c.execute('SELECT repo_id,node_id,rev,path,parent_id,change_kind '
[88]153                      'FROM darcs_node_changes LIMIT 1')
[134]154            c.execute('SELECT repo_id,node_id,rev,content,size '
[88]155                      'FROM darcs_cache LIMIT 1')
[170]156            c.execute('SELECT repo_id,node_id,rev '
157                      'FROM darcs_annotate_cache LIMIT 1')
[18]158            return False
159        except:
[44]160            db.rollback()
[18]161            return True
162
163    def upgrade_environment(self, db):
[33]164        """Actually add the new db tables."""
[18]165
[88]166        def drop_table(table_name):
[34]167            c = db.cursor()
[88]168            try:
169                c.execute('drop table %s' % table_name)
170            except:
[46]171                db.rollback()
[34]172                pass
173
[132]174        drop_table('darcs_revisions')  # until 0.6
175        drop_table('darcs_changesets')
[88]176        drop_table('darcs_nodes')
177        drop_table('darcs_node_changes')
178        drop_table('darcs_cache')
[170]179        drop_table('darcs_annotate_cache')
[34]180
[47]181        connector = DatabaseManager(self.env)._get_connector()[0]
182        if 'postgres' in [supp[0] for supp in connector.get_supported_schemes()]:
183            blobtype = 'bytea'
184        else:
185            blobtype = 'blob'
[132]186        rev_table = Table('darcs_changesets', key=('repo_id','rev'))[
[160]187            Column('repo_id',type='int'),
[88]188            Column('rev',type='int'),
189            Column('hash'),
[111]190            Column('name'),
[139]191            Index(['hash','repo_id'])]
[131]192        node_table = Table('darcs_nodes', key=('repo_id','node_id'))[
[160]193            Column('repo_id',type='int'),
[88]194            Column('node_id',type='int'),
[135]195            Column('node_type',size=1),
[88]196            Column('add_rev',type='int'),
197            Column('remove_rev',type='int')]
[131]198        change_table = Table('darcs_node_changes', key=('repo_id','node_id','rev'))[
[160]199            Column('repo_id',type='int'),
[88]200            Column('node_id',type='int'),
201            Column('rev',type='int'),
202            Column('path'),
203            Column('parent_id',type='int'),
[158]204            Column('change_kind'),
205            Index(['path'])]
[131]206        cache_table = Table('darcs_cache', key=('repo_id','node_id','rev'))[
[160]207            Column('repo_id',type='int'),
[88]208            Column('node_id',type='int'),
209            Column('rev',type='int'),
[134]210            Column('content',type=blobtype),
211            Column('size',type='int')]
[170]212        ann_cache_table = Table('darcs_annotate_cache', key=('repo_id','node_id','rev','up_to_line'))[
213            Column('repo_id',type='int'),
214            Column('node_id',type='int'),
215            Column('rev',type='int'),
216            Column('up_to_line',type='int'),
217            Column('blame_rev',type='int')]
[18]218        c = db.cursor()
[170]219        for t in [rev_table,node_table,change_table,cache_table,ann_cache_table]:
[88]220            for stmt in connector.to_sql(t):
221                c.execute(stmt)
[144]222
223class EquivalentChangesetsRenderer(Component):
224    implements(IPropertyRenderer)
225
226    def match_property(self, name, mode):
227        return (mode == 'revprop' and name == 'EqChangesets') and 5 or 0
228
229    def render_property(self, name, mode, context, props):
230        eqcsets = props[name]
231        eqlinks = [tag.a(repos or '(default)', class_="changeset",
232                         title="Equivalent patch %s in repository %s" % (
233                             rev, repos or '(default)'),
234                         href=context.href.changeset(rev, repos))
235                   for repos, rev in eqcsets]
236        return RenderedProperty(name='Present in:',
237                                name_attributes=[("class", "property")],
238                                content=tag([eqlinks]))
Note: See TracBrowser for help on using the repository browser.