source: tracdarcs/tracdarcs/components.py @ 174

Revision 174, 7.9 KB checked in by lele@…, 3 years ago (diff)

Update copyright year

Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (C) 2005 Edgewall Software
4# Copyright (C) 2005-2010 Lele Gaifax <lele@metapensiero.it>
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
16from genshi.builder import tag
17
18from trac.config import BoolOption, Option
19from trac.core import Component, implements
20from trac.db import Column, DatabaseManager, Index, Table
21from trac.env import IEnvironmentSetupParticipant
22from trac.util.text import shorten_line
23from trac.versioncontrol import IRepositoryConnector, NoSuchChangeset
24from trac.versioncontrol.web_ui import IPropertyRenderer, RenderedProperty
25
26from trac.wiki import IWikiSyntaxProvider
27
28from tracdarcs.repository import DarcsRepository
29
30class DarcsConnector(Component):
31
32    implements(IRepositoryConnector, IWikiSyntaxProvider)
33
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
40    max_concurrent_darcses = Option('darcs', 'max_concurrent_darcses', 'false',
41                                    "Max number of concurrent darcses running per repository.")
42
43    possible_encodings = Option('darcs', 'possible_encodings', 'utf-8,iso8859-1',
44                                "Specify possible repository encodings.")
45
46    # IRepositoryConnector methods
47
48    def get_supported_types(self):
49        """Support the `darcs:` scheme"""
50        yield ("darcs", 8)
51
52    def get_repository(self, type, dir, params):
53        """Return a `DarcsRepository`"""
54        db = self.env.get_db_cnx()
55        darcs = self.darcs_command
56        if self.dont_escape_8bit:
57            darcs = "DARCS_DONT_ESCAPE_8BIT=1 " + darcs
58        if self.possible_encodings:
59            possible_encodings = [e.strip()
60                                  for e in self.possible_encodings.split(',')]
61
62        # Setup the semaphore used to limit the number of concurrent running
63        # darcs within a single repository.
64
65        if self.max_concurrent_darcses and int(self.max_concurrent_darcses)>0:
66            from command import DarcsCommand
67            if DarcsCommand.RUNNING_DARCSES is None:
68                from threading import BoundedSemaphore
69                DarcsCommand.RUNNING_DARCSES = BoundedSemaphore(value=int(self.max_concurrent_darcses))
70
71        return DarcsRepository(db, dir, self.env.log, darcs, possible_encodings, params)
72
73    # IWikiSyntaxProvider methods
74
75    def get_wiki_syntax(self):
76        yield (r'[0-9]{14}-[0-9a-f]{5}-[0-9a-f]{40}(.gz)?',
77               lambda formatter, label, match: self._format_link(formatter, 'cset', label, label))
78
79    def get_link_resolvers(self):
80        yield ('cset', self._format_link)
81
82    def _format_link(self, formatter, ns, rev, label):
83
84        repos = self.env.get_repository()
85        try:
86            chgset = repos.get_changeset(rev)
87            return tag.a(chgset.rev, class_="changeset",
88                         title=shorten_line(chgset.message),
89                         href=formatter.href.changeset(chgset.rev))
90        except NoSuchChangeset, e:
91            return tag.a(label, class_="missing changeset",
92                         title="No changeset for %s" % rev, rel="nofollow")
93
94class DarcsSetup(Component):
95    """Darcs customizer.
96
97    The db tables required for the darcs backend are created here.
98    """
99
100    implements(IEnvironmentSetupParticipant)
101
102    def environment_created(self):
103        """After standard environment has been created, add the needed
104        tables."""
105
106        db = self.env.get_db_cnx()
107        self.upgrade_environment(db)
108        db.commit()
109
110    def environment_needs_upgrade(self, db):
111        """Check to see if the darcs tables are already there."""
112
113        c = db.cursor()
114        try:
115            c.execute('SELECT repo_id,rev,hash,name '
116                      'FROM darcs_changesets LIMIT 1')
117            c.execute('SELECT repo_id,node_id,node_type,add_rev,remove_rev '
118                      'FROM darcs_nodes LIMIT 1')
119            c.execute('SELECT repo_id,node_id,rev,path,parent_id,change_kind '
120                      'FROM darcs_node_changes LIMIT 1')
121            c.execute('SELECT repo_id,node_id,rev,content,size '
122                      'FROM darcs_cache LIMIT 1')
123            c.execute('SELECT repo_id,node_id,rev '
124                      'FROM darcs_annotate_cache LIMIT 1')
125            return False
126        except:
127            db.rollback()
128            return True
129
130    def upgrade_environment(self, db):
131        """Actually add the new db tables."""
132
133        def drop_table(table_name):
134            c = db.cursor()
135            try:
136                c.execute('drop table %s' % table_name)
137            except:
138                db.rollback()
139                pass
140
141        drop_table('darcs_revisions')  # until 0.6
142        drop_table('darcs_changesets')
143        drop_table('darcs_nodes')
144        drop_table('darcs_node_changes')
145        drop_table('darcs_cache')
146        drop_table('darcs_annotate_cache')
147
148        connector = DatabaseManager(self.env)._get_connector()[0]
149        if 'postgres' in [supp[0] for supp in connector.get_supported_schemes()]:
150            blobtype = 'bytea'
151        else:
152            blobtype = 'blob'
153        rev_table = Table('darcs_changesets', key=('repo_id','rev'))[
154            Column('repo_id',type='int'),
155            Column('rev',type='int'),
156            Column('hash'),
157            Column('name'),
158            Index(['hash','repo_id'])]
159        node_table = Table('darcs_nodes', key=('repo_id','node_id'))[
160            Column('repo_id',type='int'),
161            Column('node_id',type='int'),
162            Column('node_type',size=1),
163            Column('add_rev',type='int'),
164            Column('remove_rev',type='int')]
165        change_table = Table('darcs_node_changes', key=('repo_id','node_id','rev'))[
166            Column('repo_id',type='int'),
167            Column('node_id',type='int'),
168            Column('rev',type='int'),
169            Column('path'),
170            Column('parent_id',type='int'),
171            Column('change_kind'),
172            Index(['path'])]
173        cache_table = Table('darcs_cache', key=('repo_id','node_id','rev'))[
174            Column('repo_id',type='int'),
175            Column('node_id',type='int'),
176            Column('rev',type='int'),
177            Column('content',type=blobtype),
178            Column('size',type='int')]
179        ann_cache_table = Table('darcs_annotate_cache', key=('repo_id','node_id','rev','up_to_line'))[
180            Column('repo_id',type='int'),
181            Column('node_id',type='int'),
182            Column('rev',type='int'),
183            Column('up_to_line',type='int'),
184            Column('blame_rev',type='int')]
185        c = db.cursor()
186        for t in [rev_table,node_table,change_table,cache_table,ann_cache_table]:
187            for stmt in connector.to_sql(t):
188                c.execute(stmt)
189
190class EquivalentChangesetsRenderer(Component):
191    implements(IPropertyRenderer)
192
193    def match_property(self, name, mode):
194        return (mode == 'revprop' and name == 'EqChangesets') and 5 or 0
195
196    def render_property(self, name, mode, context, props):
197        eqcsets = props[name]
198        eqlinks = [tag.a(repos or '(default)', class_="changeset",
199                         title="Equivalent patch %s in repository %s" % (
200                             rev, repos or '(default)'),
201                         href=context.href.changeset(rev, repos))
202                   for repos, rev in eqcsets]
203        return RenderedProperty(name='Present in:',
204                                name_attributes=[("class", "property")],
205                                content=tag([eqlinks]))
Note: See TracBrowser for help on using the repository browser.