source: tailor/vcpx/cvs.py @ 22

Revision 22, 8.0 KB checked in by lele@…, 9 years ago (diff)

Apply the refill to every changelog

Line 
1#! /usr/bin/python
2# -*- mode: python; coding: utf-8 -*-
3# :Progetto: vcpx -- CVS details
4# :Creato:   mer 16 giu 2004 00:46:12 CEST
5# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
6#
7
8"""
9This module contains supporting classes for CVS. To get a
10cross-repository revision number ala Subversion, the implementation
11uses `cvsps` to fetch the changes from the upstream repository.
12"""
13
14__docformat__ = 'reStructuredText'
15
16from cvsync.shwrap import SystemCommand
17from source import UpdatableSourceWorkingDir
18from target import SyncronizableTargetWorkingDir
19
20
21class CvsPsLog(SystemCommand):
22    COMMAND = "cvsps %(update)s-b %(branch)s 2>/dev/null"
23
24    def __call__(self, output=None, dry_run=False, **kwargs):
25        update = kwargs.get('update', '')
26        if update:
27            update = '-u '
28        kwargs['update'] = update
29       
30        return SystemCommand.__call__(self, output=output,
31                                      dry_run=dry_run, **kwargs)
32
33   
34class CvsUpdate(SystemCommand):
35    COMMAND = 'cvs -q %(dry)supdate -d -r%(revision)s %(entry)s2>&1'
36   
37    def __call__(self, output=None, dry_run=False, **kwargs):
38        if dry_run:
39            kwargs['dry'] = '-n '
40        else:
41            kwargs['dry'] = ''
42
43        return SystemCommand.__call__(self, output=output,
44                                      dry_run=False, **kwargs)
45
46
47class CvsAdd(SystemCommand):
48    COMMAND = "cvs -q add %(entry)s"
49
50
51class CvsCommit(SystemCommand):
52    COMMAND = "cvs -q ci -F %(logfile)s %(entries)s"
53   
54
55class CvsRemove(SystemCommand):
56    COMMAND = "cvs -q remove %(entry)s"
57
58
59class CvsCheckout(SystemCommand):
60    COMMAND = "cvs -q -d%(repository)s checkout -r %(revision)s %(module)s"
61
62
63class CvsWorkingDir(UpdatableSourceWorkingDir,
64                    SyncronizableTargetWorkingDir):
65
66    """
67    An instance of this class represent a read/write CVS working directory,
68    so that it can be used both as source of patches, or as a target
69    repository.
70
71    They use `cvsps` to actual fetch the changesets metadata from the
72    server, so that we can reasonably group related changes that would
73    otherwise be sparsed, as CVS is file-centric.
74
75    To accomodate this, the last revision (from cvsps point of view)
76    imported in the repository is stored in a file in the `CVS`
77    directory at the root of the working copy. This shouldn't
78    interfere with the normal operations, but since the file isn't
79    versioned you may easily loose it....
80    """
81   
82    ## UpdatableSourceWorkingDir
83   
84    def __getLastUpstreamRevision(self, root):
85        from os.path import join, exists
86       
87        fname = join(root, 'CVS', 'last-synced-revision')
88        if exists(fname):
89            f = open(fname)
90            lastrev = f.read()
91            f.close()
92            return lastrev
93
94    def __setLastUpstreamRevision(self, root, revision):
95        from os.path import join, exists
96       
97        fname = join(root, 'CVS', 'last-synced-revision')
98        f = open(fname, 'w')
99        f.write(revision)
100        f.close()
101
102    def _getUpstreamChangesets(self, root):
103        cvsps = CvsPsLog(working_dir=root)
104
105        startfrom_rev = self.__getLastUpstreamRevision(root)
106        if startfrom_rev:
107            startfrom_rev = int(startfrom_rev)+1
108           
109        from os.path import join, exists
110       
111        fname = join(root, 'CVS', 'Tag')
112        if exists(fname):
113            branch=open(fname).read()[1:-1]
114        else:
115            branch="HEAD"
116
117        changesets = []
118        log = cvsps(output=True, update=True, branch=branch)
119        for cs in self.__enumerateChangesets(log):
120            if not startfrom_rev or (startfrom_rev<=int(cs.revision)):
121                changesets.append(cs)
122
123        return changesets
124   
125    def __enumerateChangesets(self, log):
126        """
127        Parse CVSps log.
128        """
129
130        from changes import Changeset, ChangesetEntry
131
132        # cvsps output sample:
133        ## ---------------------
134        ## PatchSet 1500
135        ## Date: 2004/05/09 17:54:22
136        ## Author: grubert
137        ## Branch: HEAD
138        ## Tag: (none)
139        ## Log:
140        ## Tell the reason for using mbox (not wrapping long lines).
141        ##
142        ## Members:
143        ##         docutils/writers/latex2e.py:1.78->1.79
144       
145        log.seek(0)
146
147        l = None
148        while 1:
149            l = log.readline()
150            if l <> '---------------------\n':
151                break
152
153            l = log.readline()
154            assert l.startswith('PatchSet '), "Parse error: %s"%l
155
156            pset = {}
157            pset['revision'] = l[9:-1]
158            l = log.readline()
159            while not l.startswith('Log:'):
160                field,value = l.split(':',1)
161                pset[field.lower()] = value.strip()
162                l = log.readline()
163
164            msg = []
165            l = log.readline()
166            msg.append(l)
167            l = log.readline()
168            while l <> 'Members: \n':
169                msg.append(l)
170                l = log.readline()
171
172            pset['log'] = ''.join(msg)
173
174            assert l.startswith('Members:'), "Parse error: %s" % l
175
176            pset['entries'] = entries = []
177            l = log.readline()
178
179            while l.startswith('\t'):
180                file,revs = l[1:-1].split(':')
181                fromrev,torev = revs.split('->')
182
183                e = ChangesetEntry(file)
184                e.old_revision = fromrev
185                e.new_revision = torev
186
187                if fromrev=='INITIAL':
188                    e.action_kind = e.ADDED
189                elif "(DEAD)" in torev:
190                    e.action_kind = e.DELETED
191                else:
192                    e.action_kind = e.UPDATED
193
194                entries.append(e)
195                l = log.readline()
196
197            yield Changeset(**pset)
198
199    def _applyChangeset(self, root, changeset):
200        cvsup = CvsUpdate(working_dir=root)
201        for e in changeset.entries:
202            cvsup(entry=e.name, revision=e.new_revision)
203        self.__setLastUpstreamRevision(root, changeset.revision)
204       
205    ## SyncronizableTargetWorkingDir
206
207    def _addEntry(self, root, entry):
208        """
209        Add a new entry, maybe registering the directory as well.
210        """
211
212        from os.path import split, join, exists
213
214        basedir = split(entry)[0]
215        if basedir and not exists(join(root, basedir, 'CVS')):
216            self._addEntry(root, basedir)
217       
218        c = CvsAdd(working_dir=root)
219        c(entry=entry)
220
221    def _checkoutUpstreamRevision(self, basedir, repository, module, revision):
222        """
223        Concretely do the checkout of the upstream sources. Use `revision` as
224        the name of the tag to get.
225        """
226
227        from os.path import join
228       
229        c = CvsCheckout(working_dir=basedir)
230        c(output=True, repository=repository, module=module, revision=revision)
231
232        # update cvsps cache and get its last CVS "revision"
233        wdir = join(basedir, module)
234        csets = self._getUpstreamChangesets(wdir)
235        last = csets[-1]
236        self.__setLastUpstreamRevision(wdir, last.revision)
237
238    def _commit(self, root, author, remark, changelog=None, entries=None):
239        """
240        Commit the changeset.
241        """
242       
243        from tempfile import NamedTemporaryFile
244       
245        log = NamedTemporaryFile(bufsize=0)
246        log.write(remark)
247        log.write('\n')
248        if changelog:
249            log.write(changelog)
250            log.write('\n')
251       
252        c = CvsCommit(working_dir=root)
253        c(entries=entries, logfile=log.name)
254        log.close()
255       
256    def _removeEntry(self, root, entry):
257        """
258        Remove an entry.
259        """
260
261        c = CvsRemove(working_dir=root)
262        c(entry=entry)
263
264    def _renameEntry(self, root, oldentry, newentry):
265        """
266        Rename an entry.
267        """
268
269        self._removeEntry(root, oldentry)
270        self._addEntry(root, newentry)
271
272    def _initializeWorkingDir(self, root, addentry=None):
273        """
274        Add the given directory to an already existing CVS working tree.
275        """
276
277        SyncronizableTargetWorkingDir._initializeWorkingDir(self, root, CvsAdd)
Note: See TracBrowser for help on using the repository browser.