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

Revision 1367, 11.7 KB checked in by Wouter van Heyst <larstiq@…>, 6 years ago (diff)

bazaar-ng-renaming
What was formerly know as Bazaar-NG is no longer the next generation, but the current one.
See  http://bazaar-vcs.org/Baz1x and  http://bazaar-vcs.org/Branding.

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Bazaar 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.
13"""
14
15__docformat__ = 'reStructuredText'
16
17
18from sys import version_info
19assert version_info >= (2,4), "Bazaar backend requires Python 2.4"
20del version_info
21
22from bzrlib import errors
23from bzrlib.add import smart_add_tree
24from bzrlib.bzrdir import BzrDir
25from bzrlib.delta import compare_trees
26from bzrlib.osutils import normpath, pathjoin
27from bzrlib.plugin import load_plugins
28
29from vcpx.repository import Repository
30from vcpx.source import UpdatableSourceWorkingDir, ChangesetApplicationFailure
31from vcpx.target import SynchronizableTargetWorkingDir
32from vcpx.workdir import WorkingDir
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 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        from os.path import split
72        from bzrlib import version_info, IGNORE_FILENAME
73
74        if version_info > (0,9):
75            from bzrlib.ignores import add_runtime_ignores, parse_ignore_file
76        else:
77            from bzrlib import DEFAULT_IGNORE
78
79        WorkingDir.__init__(self, repository)
80        # TODO: check if there is a "repository" in the configuration,
81        # and use it as a bzr repository
82        self.ignored = []
83        self._working_tree = None
84
85        # The bzr repository may have some plugins that needs to be activated
86        load_plugins()
87
88        try:
89            bzrdir = BzrDir.open(self.repository.basedir)
90            wt = self._working_tree = bzrdir.open_workingtree()
91
92            # read .bzrignore for _addSubtree()
93            if wt.has_filename(IGNORE_FILENAME):
94                f = wt.get_file_byname(IGNORE_FILENAME)
95                if version_info > (0,9):
96                    self.ignored.extend(parse_ignore_file(f))
97                else:
98                    self.ignored.extend([ line.rstrip("\n\r") for line in f.readlines() ])
99                f.close()
100        except errors.NotBranchError, errors.NoWorkingTree:
101            pass
102
103        # Omit our own log...
104        logfile = self.repository.projectref().logfile
105        dir, file = split(logfile)
106        if dir == self.repository.basedir:
107            self.ignored.append(file)
108
109        # ... and state file
110        sfname = self.repository.projectref().state_file.filename
111        dir, file = split(sfname)
112        if dir == self.repository.basedir:
113            self.ignored.append(file)
114            self.ignored.append(file+'.old')
115            self.ignored.append(file+'.journal')
116
117        if version_info > (0,9):
118            add_runtime_ignores(self.ignored)
119        else:
120            DEFAULT_IGNORE.extend(self.ignored)
121
122
123    #############################
124    ## UpdatableSourceWorkingDir
125
126    def _changesetFromRevision(self, branch, revision_id):
127        """
128        Generate changeset for the given Bzr revision
129        """
130        from datetime import datetime
131        from vcpx.changes import ChangesetEntry, Changeset
132        from vcpx.tzinfo import FixedOffset, UTC
133
134        revision = branch.repository.get_revision(revision_id)
135        deltatree = branch.get_revision_delta(branch.revision_id_to_revno(revision_id))
136        entries = []
137
138        for delta in deltatree.added:
139            e = ChangesetEntry(delta[0])
140            e.action_kind = ChangesetEntry.ADDED
141            entries.append(e)
142
143        for delta in deltatree.removed:
144            e = ChangesetEntry(delta[0])
145            e.action_kind = ChangesetEntry.DELETED
146            entries.append(e)
147
148        for delta in deltatree.renamed:
149            e = ChangesetEntry(delta[1])
150            e.action_kind = ChangesetEntry.RENAMED
151            e.old_name = delta[0]
152            entries.append(e)
153
154        for delta in deltatree.modified:
155            e = ChangesetEntry(delta[0])
156            e.action_kind = ChangesetEntry.UPDATED
157            entries.append(e)
158
159        if revision.timezone is not None:
160            timezone = FixedOffset(revision.timezone / 60)
161        else:
162            timezone = UTC
163
164        return Changeset(revision.revision_id,
165                         datetime.fromtimestamp(revision.timestamp, timezone),
166                         revision.committer,
167                         revision.message,
168                         entries)
169
170    def _getUpstreamChangesets(self, sincerev):
171        """
172        See what other revisions exist upstream and return them
173        """
174        parent_branch = BzrDir.open(self.repository.repository).open_branch()
175        branch = self._working_tree.branch
176        revisions = branch.missing_revisions(parent_branch)
177        branch.fetch(parent_branch)
178
179        for revision_id in revisions:
180            yield self._changesetFromRevision(parent_branch, revision_id)
181
182    def _applyChangeset(self, changeset):
183        """
184        Apply the given changeset to the working tree
185        """
186        parent_branch = BzrDir.open(self.repository.repository).open_branch()
187        self._working_tree.lock_write()
188        self.log.info('Updating to %r', changeset.revision)
189        try:
190            count = self._working_tree.pull(parent_branch,
191                                            stop_revision=changeset.revision)
192            conflicts = self._working_tree.update()
193        finally:
194            self._working_tree.unlock()
195        self.log.debug("%s updated to %s",
196                       ', '.join([e.name for e in changeset.entries]),
197                       changeset.revision)
198        if (count != 1) or conflicts:
199            raise ChangesetApplicationFailure('unknown reason')
200        return [] # No conflict handling yet
201
202    def _checkoutUpstreamRevision(self, revision):
203        """
204        Initial checkout of upstream branch, equivalent of 'bzr branch -r',
205        and return the last changeset.
206        """
207        parent_bzrdir = BzrDir.open(self.repository.repository)
208        parent_branch = parent_bzrdir.open_branch()
209
210        if revision == "INITIAL":
211            revid = parent_branch.get_rev_id(1)
212        elif revision == "HEAD":
213            revid = None
214        else:
215            revid = revision
216
217        self.log.info('Extracting %r out of %r in %r...',
218                      revid, parent_bzrdir.root_transport.base, self.repository.basedir)
219        bzrdir = parent_bzrdir.sprout(self.repository.basedir, revid)
220        self._working_tree = bzrdir.open_workingtree()
221
222        return self._changesetFromRevision(parent_branch, revid)
223
224    #################################
225    ## SynchronizableTargetWorkingDir
226
227    def _addPathnames(self, names):
228        if len(names):
229            names = [ pathjoin(self.repository.basedir, n) for n in names ]
230            smart_add_tree(self._working_tree, names, recurse=False)
231
232    def _addSubtree(self, subdir):
233        subdir = pathjoin(self.repository.basedir, subdir)
234        added, ignored = smart_add_tree(self._working_tree, [subdir], recurse=True)
235
236        from vcpx.dualwd import IGNORED_METADIRS
237
238        for meta in IGNORED_METADIRS + self.ignored:
239            if ignored.has_key(meta):
240                del ignored[meta]
241
242        if len(ignored):
243            f = []
244            map(f.extend, ignored.values())
245            self._addPathnames(f)
246
247    def _commit(self, date, author, patchname, changelog=None, entries=None,
248                tags = [], isinitialcommit = False):
249        """
250        Commit the changeset.
251        """
252        from calendar import timegm  # like mktime(), but returns UTC timestamp
253        from binascii import hexlify
254        from re import search
255        from bzrlib.osutils import compact_date, rand_bytes
256
257        logmessage = []
258        if patchname:
259            logmessage.append(patchname)
260        if changelog:
261            logmessage.append(changelog)
262        if logmessage:
263            self.log.info('Committing %r...', logmessage[0])
264            logmessage = '\n'.join(logmessage)
265        else:
266            self.log.info('Committing...')
267            logmessage = "Empty changelog"
268
269        timestamp = timegm(date.utctimetuple())
270        timezone  = date.utcoffset().seconds + date.utcoffset().days * 24 * 3600
271
272        # Guess sane email address
273        email = search("<(.*@.*)>", author)
274        if email:
275            email = email.group(1)
276        else:
277            email = author
278        # Remove whitespace
279        email = ''.join(email.split())
280
281        # Normalize file names
282        if entries:
283            entries = [normpath(entry) for entry in entries]
284
285        revision_id = "%s-%s-%s" % (email, compact_date(timestamp),
286                                    hexlify(rand_bytes(8)))
287        self._working_tree.commit(logmessage, committer=author,
288                                  specific_files=entries, rev_id=revision_id,
289                                  verbose=self.repository.projectref().verbose,
290                                  timestamp=timestamp, timezone=timezone)
291
292    def _removePathnames(self, names):
293        """
294        Remove files from the tree.
295        """
296        self.log.info('Removing %s...', ', '.join(names))
297        names.sort(reverse=True) # remove files before the dir they're in
298        self._working_tree.remove(names)
299
300    def _renamePathname(self, oldname, newname):
301        """
302        Rename a file from oldname to newname.
303        """
304        from os import rename
305        from os.path import join, exists
306
307        # bzr does the rename itself as well
308        unmoved = False
309        oldpath = join(self.repository.basedir, oldname)
310        newpath = join(self.repository.basedir, newname)
311        if not exists(oldpath):
312            try:
313                rename(newpath, oldpath)
314            except OSError:
315                self.log.critical('Cannot rename %r back to %r',
316                                  newpath, oldpath)
317                raise
318            unmoved = True
319
320        self.log.info('Renaming %r to %r...', oldname, newname)
321        try:
322            self._working_tree.rename_one(oldname, newname)
323        except:
324            if unmoved:
325                rename(oldpath, newpath)
326            raise
327
328    def _prepareTargetRepository(self):
329        from bzrlib import version_info
330        from vcpx.dualwd import IGNORED_METADIRS
331
332        if self._working_tree is None:
333            self._working_tree = self.repository.create()
334
335        if version_info > (0,9):
336            from bzrlib.ignores import add_runtime_ignores
337            add_runtime_ignores(IGNORED_METADIRS)
338        else:
339            from bzrlib import DEFAULT_IGNORE
340            DEFAULT_IGNORE.extend(IGNORED_METADIRS)
Note: See TracBrowser for help on using the repository browser.