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

Revision 1179, 11.0 KB checked in by lele@…, 7 years ago (diff)

Split the monolithic repository.py into smaller units
The repository subclass of each backend is now in the same unit that
implements its working dir, under the vcpx.repository subpackage.
This has several advantages: the obvious of keeping related code closer
and the ability of lazy load only the needed unit, as it was already done
for the working dir subclasses.

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