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

Revision 1201, 10.0 KB checked in by Adeodato Simo <dato@…>, 7 years ago (diff)

[bzr] rewrite _addPathnames and _addSubtree using smart_add_tree

BzrWorkingDir?._addPathnames: make 25 lines shorter by making use of
smart_add_tree, which can deal with already added or renamed files.
This fixes ticket #64, since this function also takes care of adding
missing parent directories.

BzrWorkingDir?._addSubtree: reintroduce, but this time paying attention
to the return value, since it includes a list of ignored files: if any,
call self._addPathnames() on them.

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