source: tailor/vcpx/bzr.py @ 1164

Revision 1164, 10.6 KB checked in by lele@…, 7 years ago (diff)

Use normalized path for comparing with paths from bzrlib
This is the patch attached to ticket #59, thank you luks.

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