source: tailor/vcpx/repository/bzr.py @ 1209

Revision 1209, 10.4 KB checked in by ydirson@…, 7 years ago (diff)

Move basedir attribute from WorkingDir to Repository

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- bazaar-ng support using the bzrlib instead of the frontend
3# :Creato:   Fri Aug 19 01:06:08 CEST 2005
4# :Autore:   Johan Rydberg <jrydberg@gnu.org>
5#            Jelmer Vernooij <jelmer@samba.org>
6#            Lalo Martins <lalo.martins@gmail.com>
7#            Olaf Conradi <olaf@conradi.org>
8# :Licenza:  GNU General Public License
9#
10
11"""
12This module implements the backends for Bazaar-NG.
13"""
14
15__docformat__ = 'reStructuredText'
16
17
18from sys import version_info
19assert version_info >= (2,4), "Bazaar-NG backend requires Python 2.4"
20del version_info
21
22from bzrlib.osutils import normpath, pathjoin
23from bzrlib.bzrdir import BzrDir
24from bzrlib.delta import compare_trees
25from bzrlib.add import smart_add_tree
26from bzrlib import errors
27from bzrlib import IGNORE_FILENAME, DEFAULT_IGNORE
28
29from vcpx.repository import Repository
30from vcpx.workdir import WorkingDir
31from vcpx.source import UpdatableSourceWorkingDir, ChangesetApplicationFailure
32from vcpx.target import SynchronizableTargetWorkingDir
33
34
35class BzrRepository(Repository):
36    METADIR = '.bzr'
37
38    def _load(self, project):
39        Repository._load(self, project)
40        ppath = project.config.get(self.name, 'python-path')
41        if ppath:
42            from sys import path
43
44            if ppath not in path:
45                path.insert(0, ppath)
46
47
48class BzrWorkingDir(UpdatableSourceWorkingDir, SynchronizableTargetWorkingDir):
49    def __init__(self, repository):
50        WorkingDir.__init__(self, repository)
51        # TODO: check if there is a "repository" in the configuration,
52        # and use it as a bzr repository
53        self.ignored = []
54        self._working_tree = None
55        try:
56            bzrdir = BzrDir.open(self.repository.basedir)
57            wt = self._working_tree = bzrdir.open_workingtree()
58
59            # read .bzrignore for _addSubtree()
60            if wt.has_filename(IGNORE_FILENAME):
61                f = wt.get_file_byname(IGNORE_FILENAME)
62                self.ignored.extend([ line.rstrip("\n\r") for line in f.readlines() ])
63        except errors.NotBranchError, errors.NoWorkingTree:
64            pass
65
66        from os.path import split
67
68        # Omit our own log...
69        logfile = self.repository.projectref().logfile
70        dir, file = split(logfile)
71        if dir == self.repository.basedir:
72            self.ignored.append(file)
73
74        # ... and state file
75        sfname = self.repository.projectref().state_file.filename
76        dir, file = split(sfname)
77        if dir == self.repository.basedir:
78            self.ignored.append(file)
79            self.ignored.append(file+'.old')
80            self.ignored.append(file+'.journal')
81
82        DEFAULT_IGNORE.extend(self.ignored)
83
84
85    #############################
86    ## UpdatableSourceWorkingDir
87
88    def _changesetFromRevision(self, branch, revision_id):
89        """
90        Generate changeset for the given Bzr revision
91        """
92        from datetime import datetime
93        from vcpx.changes import ChangesetEntry, Changeset
94
95        revision = branch.repository.get_revision(revision_id)
96        deltatree = branch.get_revision_delta(branch.revision_id_to_revno(revision_id))
97        entries = []
98
99        for delta in deltatree.added:
100            e = ChangesetEntry(delta[0])
101            e.action_kind = ChangesetEntry.ADDED
102            entries.append(e)
103
104        for delta in deltatree.removed:
105            e = ChangesetEntry(delta[0])
106            e.action_kind = ChangesetEntry.DELETED
107            entries.append(e)
108
109        for delta in deltatree.renamed:
110            e = ChangesetEntry(delta[1])
111            e.action_kind = ChangesetEntry.RENAMED
112            e.old_name = delta[0]
113            entries.append(e)
114
115        for delta in deltatree.modified:
116            e = ChangesetEntry(delta[0])
117            e.action_kind = ChangesetEntry.UPDATED
118            entries.append(e)
119
120        return Changeset(revision.revision_id,
121                         datetime.fromtimestamp(revision.timestamp),
122                         revision.committer,
123                         revision.message,
124                         entries)
125
126    def _getUpstreamChangesets(self, sincerev):
127        """
128        See what other revisions exist upstream and return them
129        """
130        parent_branch = BzrDir.open(self.repository.repository).open_branch()
131        branch = self._working_tree.branch
132        revisions = branch.missing_revisions(parent_branch)
133        branch.fetch(parent_branch)
134
135        for revision_id in revisions:
136            yield self._changesetFromRevision(parent_branch, revision_id)
137
138    def _applyChangeset(self, changeset):
139        """
140        Apply the given changeset to the working tree
141        """
142        parent_branch = BzrDir.open(self.repository.repository).open_branch()
143        self._working_tree.lock_write()
144        self.log.info('Updating to %r', changeset.revision)
145        try:
146            count = self._working_tree.pull(parent_branch,
147                                            stop_revision=changeset.revision)
148            conflicts = self._working_tree.update()
149        finally:
150            self._working_tree.unlock()
151        self.log.debug("%s updated to %s",
152                       ', '.join([e.name for e in changeset.entries]),
153                       changeset.revision)
154        if (count != 1) or conflicts:
155            raise ChangesetApplicationFailure('unknown reason')
156        return [] # No conflict handling yet
157
158    def _checkoutUpstreamRevision(self, revision):
159        """
160        Initial checkout of upstream branch, equivalent of 'bzr branch -r',
161        and return the last changeset.
162        """
163        parent_bzrdir = BzrDir.open(self.repository.repository)
164        parent_branch = parent_bzrdir.open_branch()
165
166        if revision == "INITIAL":
167            revid = parent_branch.get_rev_id(1)
168        elif revision == "HEAD":
169            revid = None
170        else:
171            revid = revision
172
173        self.log.info('Extracting %r out of %r in %r...',
174                      revid, parent_bzrdir.root_transport.base, self.repository.basedir)
175        bzrdir = parent_bzrdir.sprout(self.repository.basedir, revid)
176        self._working_tree = bzrdir.open_workingtree()
177
178        return self._changesetFromRevision(parent_branch, revid)
179
180    #################################
181    ## SynchronizableTargetWorkingDir
182
183    def _addPathnames(self, names):
184        if len(names):
185            names = [ pathjoin(self.repository.basedir, n) for n in names ]
186            smart_add_tree(self._working_tree, names, recurse=False)
187
188    def _addSubtree(self, subdir):
189        subdir = pathjoin(self.repository.basedir, subdir)
190        added, ignored = smart_add_tree(self._working_tree, [subdir], recurse=True)
191
192        from vcpx.dualwd import IGNORED_METADIRS
193
194        for meta in IGNORED_METADIRS + self.ignored:
195            if ignored.has_key(meta):
196                del ignored[meta]
197
198        if len(ignored):
199            f = []
200            map(f.extend, ignored.values())
201            self._addPathnames(f)
202
203    def _commit(self, date, author, patchname, changelog=None, entries=None):
204        """
205        Commit the changeset.
206        """
207        from time import mktime
208        from binascii import hexlify
209        from re import search
210        from bzrlib.osutils import compact_date, rand_bytes
211
212        logmessage = []
213        if patchname:
214            logmessage.append(patchname)
215        if changelog:
216            logmessage.append(changelog)
217        if logmessage:
218            self.log.info('Committing %r...', logmessage[0])
219            logmessage = '\n'.join(logmessage)
220        else:
221            self.log.info('Committing...')
222            logmessage = "Empty changelog"
223        timestamp = int(mktime(date.timetuple()))
224
225        # Guess sane email address
226        email = search("<(.*@.*)>", author)
227        if email:
228            email = email.group(1)
229        else:
230            email = author
231        # Remove whitespace
232        email = ''.join(email.split())
233
234        # Normalize file names
235        if entries:
236            entries = [normpath(entry) for entry in entries]
237       
238        revision_id = "%s-%s-%s" % (email, compact_date(timestamp),
239                                    hexlify(rand_bytes(8)))
240        self._working_tree.commit(logmessage, committer=author,
241                                  specific_files=entries, rev_id=revision_id,
242                                  verbose=self.repository.projectref().verbose,
243                                  timestamp=timestamp)
244
245    def _removePathnames(self, names):
246        """
247        Remove files from the tree.
248        """
249        self.log.info('Removing %s...', ', '.join(names))
250        names.sort(reverse=True) # remove files before the dir they're in
251        self._working_tree.remove(names)
252
253    def _renamePathname(self, oldname, newname):
254        """
255        Rename a file from oldname to newname.
256        """
257        from os import rename
258        from os.path import join, exists
259
260        # bzr does the rename itself as well
261        unmoved = False
262        oldpath = join(self.repository.basedir, oldname)
263        newpath = join(self.repository.basedir, newname)
264        if not exists(oldpath):
265            try:
266                rename(newpath, oldpath)
267            except OSError:
268                self.log.critical('Cannot rename %r back to %r',
269                                  newpath, oldpath)
270                raise
271            unmoved = True
272
273        self.log.info('Renaming %r to %r...', oldname, newname)
274        try:
275            self._working_tree.rename_one(oldname, newname)
276        except:
277            if unmoved:
278                rename(oldpath, newpath)
279            raise
280
281    def _prepareTargetRepository(self):
282        """
283        Create a branch with a working tree at the base directory. If the base
284        directory is inside a Bazaar-NG style "shared repository", it will use
285        that to create a branch and working tree (make sure it allows working
286        trees).
287        """
288        if self._working_tree is None:
289            self.log.info('Initializing new repository in %r...', self.repository.basedir)
290            try:
291                bzrdir = BzrDir.open(self.repository.basedir)
292            except errors.NotBranchError:
293                # really a NotBzrDir error...
294                branch = BzrDir.create_branch_convenience(self.repository.basedir,
295                                                          force_new_tree=True)
296                self._working_tree = branch.bzrdir.open_workingtree()
297            else:
298                bzrdir.create_branch()
299                self._working_tree = bzrdir.create_workingtree()
Note: See TracBrowser for help on using the repository browser.