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

Revision 1215, 10.4 KB checked in by lele@…, 7 years ago (diff)

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