source: tracdarcs/tracdarcs/repository.py @ 33

Revision 33, 11.3 KB checked in by sreeram@…, 7 years ago (diff)

Added a whole bunch of docstrings and comments

Line 
1import os, StringIO, mimetypes
2from trac.versioncontrol import Repository, Node, Changeset, \
3        NoSuchChangeset, NoSuchNode
4
5from command import DarcsCommand
6from updatedb import update_darcsdb
7from dbutil import NODE_FILE_TYPE, NODE_DIR_TYPE
8from dbutil import CHANGE_ADDED, CHANGE_REMOVED, CHANGE_MOVED, \
9        CHANGE_EDITED, CHANGE_MOVED_EDITED
10from dbutil import query_nodes_for_revision
11import ann2ascii
12
13'''
14This module implements the trac versioncontrol backend API.
15The API consists of 3 classes: DarcsRepository, DarcsNode
16and DarcsChangeset.
17
18Please see the docs in trac.versioncontrol.api for the interface
19which is implemented by this module.
20'''
21
22# mapping from node types used by the darcs backend and the types
23# used by the trac api
24_node_type_map = {
25        NODE_FILE_TYPE : Node.FILE,
26        NODE_DIR_TYPE : Node.DIRECTORY
27        }
28
29# mapping from change types used by the darcs backend and the types
30# used by the trac api
31_change_map = {
32        CHANGE_ADDED : Changeset.ADD,
33        CHANGE_REMOVED : Changeset.DELETE,
34        CHANGE_MOVED : Changeset.MOVE,
35        CHANGE_EDITED : Changeset.EDIT,
36        #FIXME: treat moved&edited as just moved?
37        CHANGE_MOVED_EDITED : Changeset.MOVE
38        }
39
40def get_node_type( db, node_id ) :
41    c = db.cursor()
42    c.execute( 'SELECT node_type FROM darcs_nodes ' +
43            'WHERE node_id = %s', (node_id,) )
44    return c.fetchone()[0]
45
46def get_prev_path_rev( db, node_id, rev ) :
47    c = db.cursor()
48    c.execute( 'SELECT path,rev FROM darcs_node_changes ' +
49            'WHERE node_id = %s AND rev < %s ' +
50            'ORDER BY rev DESC LIMIT 1', (node_id,rev) )
51    path,rev = c.fetchone()
52    return path,rev
53
54class DarcsRepository( Repository ) :
55    def __init__( self, db, path, log, config ) :
56        Repository.__init__( self, path, None, log )
57        self.db = db
58        self.path = path
59        self.log = log
60        self.config = config
61        self.__cmd = DarcsCommand( 'darcs', path, log )
62        # import any new changesets, if any
63        update_darcsdb( db, self.__cmd )
64
65    def close( self ) :
66        pass
67
68    def get_changeset( self, rev ) :
69        rev = self.normalize_rev( rev )
70        return DarcsChangeset( self.db, rev )
71
72    def get_node( self, path, rev=None ) :
73        path = self.normalize_path( path )
74        rev = self.normalize_rev( rev )
75        # compute node_id, node_type and last_rev and then
76        # create a DarcsNode object.
77        # 'last_rev' is the last revision <= rev where this
78        # node was modified.
79        if path == '/' :
80            node_id = None
81            node_type = NODE_DIR_TYPE
82            last_rev = rev
83        else :
84            c = self.db.cursor()
85            q = query_nodes_for_revision( rev )
86            q += ' AND dnc.path = %s'
87            c.execute( q, (path,) )
88            row = c.fetchone()
89            if row is None :
90                raise NoSuchNode( path, rev )
91            node_id,last_rev = row[:2]
92            node_type = get_node_type( self.db, node_id )
93        return DarcsNode( node_id, node_type, path, last_rev,
94                self.db, self.__cmd )
95
96    def get_oldest_rev( self ) :
97        if self.get_youngest_rev() is None :
98            return None
99        return 1
100
101    def get_youngest_rev( self ) :
102        c = self.db.cursor()
103        c.execute( 'SELECT rev FROM darcs_revisions ' +
104                'ORDER BY rev DESC LIMIT 1' )
105        row = c.fetchone()
106        return row and row[0] or None
107
108    def previous_rev( self, rev ) :
109        rev = self.normalize_rev( rev )
110        if rev > 1 :
111            return rev-1
112        return None
113
114    def next_rev( self, rev, path='' ) :
115        rev = self.normalize_rev( rev )
116        if rev < self.get_youngest_rev() :
117            return rev+1
118        return None
119
120    def rev_older_than( self, rev1, rev2 ) :
121        return self.normalize_rev(rev1) < self.normalize_rev(rev2)
122
123    def get_path_history( self, path, rev=None, limit=None ) :
124        # FIXME: this is not correct
125        return self.get_node( path, rev ).get_history( limit )
126
127    def normalize_path( self, path ) :
128        return path and path.strip('/') or '/'
129
130    def normalize_rev( self, rev ) :
131        youngest = self.get_youngest_rev()
132        if rev is None :
133            return youngest
134        rev = int( rev )
135        if rev > youngest :
136            rev = youngest
137        return rev
138
139    def get_changes( self, old_path, old_rev, new_path, new_rev,
140            ignore_ancestry=1 ) :
141        old_path = self.normalize_path( old_path )
142        old_rev = self.normalize_rev( old_rev )
143        new_path = self.normalize_path( new_path )
144        new_rev = self.normalize_rev( new_rev )
145        old_node = self.get_node( old_path, old_rev )
146        new_node = self.get_node( new_path, new_rev )
147
148        node_id = old_node._get_node_id()
149        if node_id != new_node._get_node_id() :
150            raise TracError( 'Node mismatch: base is %s in rev %d '
151                    'and target is %s in rev %d' % (old_path,old_rev,
152                        new_path,new_rev) )
153
154        if old_node.kind == Node.FILE :
155            if old_node.rev != new_node.rev :
156                yield (old_node,new_node,Node.FILE,Changeset.EDIT)
157            return
158
159        c = self.db.cursor()
160        c.execute( 'SELECT rev,path FROM darcs_node_changes '
161                'WHERE node_id = %s AND rev >= %s AND rev <= %s',
162                (node_id,old_rev,new_rev) )
163        node_set = dict()
164        node_list = []
165        c1 = self.db.cursor()
166        for rev,path in c :
167            c1.execute( 'SELECT node_id FROM darcs_node_changes '
168                    'WHERE rev = %s AND path GLOB %s',
169                    (rev,path+'/*') )
170            for nid, in c1 :
171                if nid not in node_set :
172                    node_set[nid] = 1
173                    node_list.append( nid )
174        for nid in node_list :
175            old_node = new_node = None
176            c1.execute( 'SELECT rev,path FROM darcs_node_changes '
177                    'WHERE node_id = %s AND rev < %s '
178                    'ORDER BY rev DESC LIMIT 1',
179                    (nid,old_rev) )
180            row = c1.fetchone()
181            if row is not None :
182                rev,path = row
183                old_node = self.get_node( path, rev )
184            c1.execute( 'SELECT rev,path,change FROM darcs_node_changes '
185                    'WHERE node_id = %s AND rev >= %s AND rev <= %s '
186                    'ORDER BY rev DESC LIMIT 1',
187                    (nid,old_rev,new_rev) )
188            rev,path,change = c1.fetchone()
189            if change != CHANGE_REMOVED :
190                new_node = self.get_node( path, rev )
191            assert (old_node is not None) or (new_node is not None)
192            kind = old_node and old_node.kind or new_node.kind
193            if old_node is None :
194                change = Changeset.ADD
195            elif new_node is None :
196                change = Changeset.DELETE
197            elif old_node.path != new_node.path :
198                change = Changeset.MOVE
199            else :
200                change = Changeset.EDIT
201            yield (old_node,new_node,kind,change)
202
203class DarcsNode( Node ) :
204    def __init__( self, node_id, node_type, path, rev,
205            db, cmd ) :
206        kind = _node_type_map[node_type]
207        Node.__init__( self, path, rev, kind )
208        self.__node_id = node_id
209        self.__node_type = node_type
210        self.__db = db
211        self.__cmd = cmd
212        self.created_path = path
213        self.created_rev = rev
214
215    def _get_node_id( self ) :
216        return self.__node_id
217
218    def get_content( self ) :
219        if self.__node_type == NODE_DIR_TYPE :
220            return None
221        c = self.__db.cursor()
222        c.execute( 'SELECT hash FROM darcs_revisions ' +
223                'WHERE rev = %s', (self.rev,) )
224        hash = c.fetchone()[0]
225        annotate = self.__cmd.annotate( hash, self.path )
226        ann = ann2ascii.parse_annotate( StringIO.StringIO(annotate) )
227        out = StringIO.StringIO()
228        ann.write( out )
229        return StringIO.StringIO( out.getvalue() )
230
231    def get_entries( self ) :
232        if self.__node_type == NODE_FILE_TYPE :
233            return
234        q = query_nodes_for_revision( self.rev )
235        if self.__node_id is None :
236            q += ' AND dnc.parent_id IS NULL'
237        else :
238            q += ' AND dnc.parent_id = %d' % self.__node_id
239        c = self.__db.cursor()
240        c.execute( q )
241        for node_id,rev,path,_ in c :
242            node_type = get_node_type( self.__db, node_id )
243            yield DarcsNode( node_id, node_type, path, rev,
244                    self.__db, self.__cmd )
245
246    def get_history( self, limit=None ) :
247        if self.path == '/' :
248            for i in range(self.rev,0,-1) :
249                yield ( self.path, i, Changeset.EDIT )
250            return
251        c = self.__db.cursor()
252        q = 'SELECT path,rev,change FROM darcs_node_changes ' + \
253                'WHERE node_id = %s AND rev <= %s ' + \
254                'ORDER BY rev DESC'
255        if limit is not None :
256            q += ' LIMIT %d' % limit
257        c.execute( q, (self.__node_id,self.rev) )
258        for path,rev,change in c :
259            yield ( path, rev, _change_map[change] )
260
261    def get_properties( self ) :
262        return {}
263
264    def get_content_length( self ) :
265        if self.isdir :
266            return None
267        return len(self.get_content().read())
268
269    def get_content_type( self ) :
270        if self.isdir :
271            return None
272        return mimetypes.guess_type( self.path )[0]
273
274    def get_name( self ) :
275        return os.path.split( self.path )[1]
276
277    def get_last_modified( self ) :
278        if self.__node_id is None :
279            return 0
280        c = self.__db.cursor()
281        c.execute( 'SELECT rev FROM darcs_node_changes ' +
282                'WHERE node_id = %s AND rev = %s',
283                (self.__node_id,self.rev) )
284        rev = c.fetchone()[0]
285        c.execute( 'SELECT time FROM darcs_revisions ' +
286                'WHERE rev = %s', (rev,) )
287        return c.fetchone()[0]
288
289class DarcsChangeset( Changeset ) :
290    def __init__( self, db, rev ) :
291        c = db.cursor()
292        c.execute( 'SELECT author,time,name,comment ' +
293                'FROM darcs_revisions WHERE rev = %s', (rev,) )
294        row = c.fetchone()
295        if row is None :
296            raise NoSuchChangeset( rev )
297        author,date,name,comment = row
298        msg = name
299        if comment :
300            msg += '\n' + comment
301        Changeset.__init__( self, rev, msg, author, date )
302        self.__db = db
303
304    def get_changes( self ) :
305        c = self.__db.cursor()
306        c.execute( 'SELECT node_id,path,change FROM darcs_node_changes ' +
307                'WHERE rev = %s', (self.rev,) )
308        for node_id,path,change in c :
309            node_type = get_node_type( self.__db, node_id )
310            kind = _node_type_map[node_type]
311            if change == CHANGE_ADDED :
312                prev_path = prev_rev = None
313            else :
314                prev_path,prev_rev = get_prev_path_rev( self.__db,
315                        node_id, self.rev )
316            change = _change_map[change]
317            yield (path,kind,change,prev_path,prev_rev)
318
319    def get_properties( self ) :
320        return []
Note: See TracBrowser for help on using the repository browser.