source: tracdarcs/tracdarcs/repository.py @ 31

Revision 31, 10.7 KB checked in by sreeram@…, 7 years ago (diff)

Added implementations of Repository.get_path_history and .get_changes

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