source: tailor/vcpx/darcs.py @ 98

Revision 98, 9.7 KB checked in by daniele@…, 9 years ago (diff)

Sort changesets by date in _getUpstreamChangesets() in darcs.py

Line 
1#! /usr/bin/python
2# -*- mode: python; coding: utf-8 -*-
3# :Progetto: vcpx -- Darcs details
4# :Creato:   ven 18 giu 2004 14:45:28 CEST
5# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
6#
7
8"""
9This module contains supporting classes for the `darcs` versioning system.
10"""
11
12__docformat__ = 'reStructuredText'
13
14from shwrap import SystemCommand
15from source import UpdatableSourceWorkingDir
16from target import SyncronizableTargetWorkingDir, TargetInitializationFailure
17
18
19class DarcsInitialize(SystemCommand):
20    COMMAND = "darcs initialize"
21
22
23class DarcsRecord(SystemCommand):
24    COMMAND = "darcs record --all --pipe --look-for-adds %(entries)s"
25
26    def __call__(self, output=None, dry_run=False, **kwargs):
27        date = kwargs.get('date').strftime('%Y/%m/%d %H:%M:%S')
28        author = kwargs.get('author')
29        patchname = kwargs.get('patchname')
30        logmessage = kwargs.get('logmessage')
31        if not logmessage:
32            logmessage = ''
33
34        input = "%s\n%s\n%s\n%s\n" % (date, author, patchname, logmessage)
35       
36        return SystemCommand.__call__(self, output=output, input=input,
37                                      dry_run=dry_run, 
38                                      **kwargs)
39
40
41class DarcsMv(SystemCommand):
42    COMMAND = "darcs mv --standard-verbosity %(old)s %(new)s"
43
44
45class DarcsRemove(SystemCommand):
46    COMMAND = "darcs remove --standard-verbosity %(entry)s"
47
48
49class DarcsAdd(SystemCommand):
50    COMMAND = "darcs add --not-recursive --standard-verbosity %(entry)s"
51
52
53class DarcsTag(SystemCommand):
54    COMMAND = "darcs tag --standard-verbosity --patch-name='%(tagname)s'"
55
56
57class DarcsChanges(SystemCommand):
58    COMMAND = "darcs changes --from-tag=tagname --xml-output --summary"
59
60
61class DarcsAnnotate(SystemCommand):
62    COMMAND = "darcs annotate --standard-verbosity %(entry)s >/dev/null 2>&1"
63   
64class DarcsWorkingDir(UpdatableSourceWorkingDir,SyncronizableTargetWorkingDir):
65    """
66    A working directory under ``darcs``.
67    """
68
69    def __init__(self):
70        self.__visitedDirs = {}
71       
72    ## UpdatableSourceWorkingDir
73   
74    def _getUpstreamChangesets(self, root, sincerev=None):
75        """
76        Do the actual work of fetching the upstream changeset.
77       
78        This is different from the other VC mechanisms: here we want
79        register with the target the changes we submitted to this
80        repository to be sent back to upstream. Since we may want to
81        regroup the various patchsets into a single one, we first
82        manually pull here what we wanna send, then the sync will replay
83        the changes of all new changesets.
84       
85        So, here we actually list the changes after the last tag, not
86        those pending on the other side.
87        """
88
89        tagname = self._getLastTag(root)
90       
91        c = DarcsChanges(working_dir=root)
92        changes = c(output=True)
93
94        changesets = self.__parseDarcsChanges(changes)
95
96        # sort changeset by date
97        changesets.sort(lambda x, y: cmp(x.date, y.date))
98
99        return changesets
100   
101    def __parseDarcsChanges(self, changes):
102        from xml.sax import parseString
103        from xml.sax.handler import ContentHandler
104        from changes import ChangesetEntry, Changeset
105        from datetime import datetime
106       
107        class DarcsXMLChangesHandler(ContentHandler):
108            def __init__(self):
109                self.changesets = []
110                self.current = None
111                self.current_field = []
112
113            def startElement(self, name, attributes):
114                if name == 'patch':
115                    self.current = {}
116                    self.current['author'] = attributes['author']
117                    date = attributes['date']
118                    # 20040619130027
119                    y = int(date[:4])
120                    m = int(date[4:6])
121                    d = int(date[6:8])
122                    hh = int(date[8:10])
123                    mm = int(date[10:12])
124                    ss = int(date[12:14])
125                    timestamp = datetime(y, m, d, hh, mm, ss)
126                    self.current['date'] = timestamp
127                    self.current['revision'] = attributes['revision']
128                    self.current['entries'] = []
129                elif name in ['name', 'comment',
130                              'add_file', 'add_directory',
131                              'modify_file', 'remove_file']:
132                    self.current_field = []
133                elif name == 'path':
134                    self.current_field = []
135                    if attributes.has_key('copyfrom-path'):
136                        self.current_path_action = (
137                            attributes['action'],
138                            attributes['copyfrom-path'][1:], # make it relative
139                            attributes['copyfrom-rev'])
140                    else:
141                        self.current_path_action = attributes['action']
142                elif name == 'move':
143                    self.old_name = attributes['from']
144                    self.new_name = attributes['to']
145                   
146            def endElement(self, name):
147                if name == 'patch':
148                    # Sort the paths to make tests easier
149                    self.current['entries'].sort()
150                    self.changesets.append(Changeset(self.current['name'],
151                                                     self.current['date'],
152                                                     self.current['author'],
153                                                     self.current['comment'],
154                                                     self.current['entries']))
155                    self.current = None
156                elif name in ['name', 'comment']:
157                    self.current[name] = ''.join(self.current_field)
158                elif name == 'move':
159                    entry = ChangesetEntry(self.new_name)
160                    entry.action_kind = entry.RENAMED
161                    entry.old_name = self.old_name
162                    self.current['entries'].append(entry)
163                elif name in ['add_file', 'add_directory',
164                              'modify_file', 'remove_file']:
165                    entry = ChangesetEntry(''.join(self.current_field))
166                    entry.action_kind = { 'add_file': entry.ADDED,
167                                          'add_directory': entry.ADDED,
168                                          'modify_file': entry.UPDATED,
169                                          'remove_file': entry.DELETED,
170                                          'rename_file': entry.RENAMED
171                                        }[name]
172
173                    self.current['entries'].append(entry)
174
175            def characters(self, data):
176                self.current_field.append(data)
177       
178        handler = DarcsXMLChangesHandler()
179        parseString(changes.getvalue(), handler)
180        return handler.changesets
181       
182    def _applyChangeset(self, root, changeset, logger=None):
183        """
184        Do the actual work of applying the changeset to the working copy.
185
186        The changeset is already applied, so this is a do nothing method.
187        """
188
189        return
190   
191    ## SyncronizableTargetWorkingDir
192
193    def _addEntry(self, root, entry):
194        """
195        Add a new entry, maybe registering the directory as well.
196        """
197
198        from os.path import split, join, exists
199
200        basedir = split(entry)[0]
201
202        if basedir:
203            if not self.__visitedDirs.get(basedir):
204                # This is ugly, but I didn't find a better way to test
205                # whether a particular directory is already version
206                # controlled by darcs.
207       
208                dannot = DarcsAnnotate(working_dir=root)
209       
210                dannot(entry=basedir)
211                if dannot.exit_status:
212                    self._addEntry(root, basedir)
213
214                self.__visitedDirs[basedir] = True
215               
216        c = DarcsAdd(working_dir=root)
217        c(entry=entry)
218
219       
220    def _commit(self,root, date, author, remark, changelog=None, entries=None):
221        """
222        Commit the changeset.
223        """
224
225        c = DarcsRecord(working_dir=root)
226
227        if entries:
228            entries = ' '.join(entries)
229        else:
230            entries = '.'
231           
232        c(output=True, date=date, patchname=remark,
233          logmessage=changelog, author=author, entries=entries)
234       
235    def _removeEntry(self, root, entry):
236        """
237        Remove an entry.
238        """
239
240        c = DarcsRemove(working_dir=root)
241        c(entry=entry)
242
243    def _renameEntry(self, root, oldentry, newentry):
244        """
245        Rename an entry.
246        """
247
248        c = DarcsMv(working_dir=root)
249        c(old=oldentry, new=newentry)
250
251    def _createTag(self, root, tagname):
252        """
253        Tag the current situation and remember this as the *last tag*.
254        """
255
256        from os.path import join, exists
257       
258        c = DarcsTag(working_dir=root)
259        c(output=True, tagname=tagname)
260       
261        fname = join(root, '_darcs', 'last-sync-tag')
262        f = open(fname, 'w')
263        f.write(tagname)
264        f.close()
265       
266    def _getLastTag(self, root):
267        """
268        Return the name of the last emitted tag, if any, otherwise None.
269        """
270       
271        from os.path import join, exists
272       
273        fname = join(root, '_darcs', 'last-sync-tag')
274        if exists(fname):
275            f = open(fname)
276            tagname = f.read()
277            f.close()
278           
279            return tagname
280
281    def _initializeWorkingDir(self, root, module):
282        """
283        Execute `darcs initialize`.
284        """
285       
286        c = DarcsInitialize(working_dir=root)
287        c(output=True)
288
289        if c.exit_status:
290            raise TargetInitializationFailure(
291                "'darcs initialize' returned status %s" % c.exit_status)
292
Note: See TracBrowser for help on using the repository browser.