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

Revision 1445, 11.4 KB checked in by Ori Avtalion <ori@…>, 6 years ago (diff)

Resolved bzrlib deprecation warnings. Have bazaar generate the revision-ids.
1) add.smart_add_tree was deprecated in version 0.18.
2) Unicode revision ids were deprecated in bzr 0.15. Revision id generators should be creating utf8 revision ids.

Instead of encoding the revision id in utf8, it's simpler to have bzrlib generate it.

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