/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Branch now uses BzrDir reasonably sanely.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
from copy import deepcopy
18
18
from cStringIO import StringIO
19
19
from unittest import TestSuite
 
20
import xml.sax.saxutils
 
21
 
20
22
 
21
23
import bzrlib.bzrdir as bzrdir
22
24
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
25
import bzrlib.errors as errors
23
26
from bzrlib.errors import InvalidRevisionId
24
27
from bzrlib.lockable_files import LockableFiles
25
28
from bzrlib.osutils import safe_unicode
34
37
import bzrlib.xml5
35
38
 
36
39
 
37
 
 
38
40
class Repository(object):
39
41
    """Repository holding history for one or more branches.
40
42
 
47
49
    remote) disk.
48
50
    """
49
51
 
 
52
    @needs_read_lock
 
53
    def all_revision_ids(self):
 
54
        """Returns a list of all the revision ids in the repository.
 
55
 
 
56
        It would be nice to have this topologically sorted, but its not yet.
 
57
        """
 
58
        possible_ids = self.get_inventory_weave().names()
 
59
        result = []
 
60
        for id in possible_ids:
 
61
            if self.has_revision(id):
 
62
               result.append(id)
 
63
        return result
 
64
 
50
65
    @staticmethod
51
66
    def create(a_bzrdir):
52
67
        """Construct the current default format repository in a_bzrdir."""
145
160
        control = bzrdir.BzrDir.open(base)
146
161
        return control.open_repository()
147
162
 
 
163
    def push_stores(self, to, revision=NULL_REVISION):
 
164
        """FIXME: document and find a consistent name with other classes."""
 
165
        if (not isinstance(self._format, RepositoryFormat4) or
 
166
            self._format != to._format):
 
167
            from bzrlib.fetch import RepoFetcher
 
168
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
 
169
                   self, self._format, to, to._format)
 
170
            RepoFetcher(to_repository=to, from_repository=self,
 
171
                         last_revision=revision)
 
172
            return
 
173
 
 
174
        # format 4 to format 4 logic only.
 
175
        store_pairs = ((self.text_store,      to.text_store),
 
176
                       (self.inventory_store, to.inventory_store),
 
177
                       (self.revision_store,  to.revision_store))
 
178
        try:
 
179
            for from_store, to_store in store_pairs: 
 
180
                copy_all(from_store, to_store)
 
181
        except UnlistableStore:
 
182
            raise UnlistableBranch(from_store)
 
183
 
148
184
    def unlock(self):
149
185
        self.control_files.unlock()
150
186
 
151
187
    @needs_read_lock
 
188
    def clone(self, a_bzrdir):
 
189
        """Clone this repository into a_bzrdir using the current format.
 
190
 
 
191
        Currently no check is made that the format of this repository and
 
192
        the bzrdir format are compatible. FIXME RBC 20060201.
 
193
        """
 
194
        result = self._format.initialize(a_bzrdir)
 
195
        self.copy(result)
 
196
        return result
 
197
 
 
198
    @needs_read_lock
152
199
    def copy(self, destination):
153
200
        destination.lock_write()
154
201
        try:
212
259
        self.revision_store.add(StringIO(gpg_strategy.sign(plaintext)), 
213
260
                                revision_id, "sig")
214
261
 
 
262
    def fileid_involved_between_revs(self, from_revid, to_revid):
 
263
        """Find file_id(s) which are involved in the changes between revisions.
 
264
 
 
265
        This determines the set of revisions which are involved, and then
 
266
        finds all file ids affected by those revisions.
 
267
        """
 
268
        # TODO: jam 20060119 This code assumes that w.inclusions will
 
269
        #       always be correct. But because of the presence of ghosts
 
270
        #       it is possible to be wrong.
 
271
        #       One specific example from Robert Collins:
 
272
        #       Two branches, with revisions ABC, and AD
 
273
        #       C is a ghost merge of D.
 
274
        #       Inclusions doesn't recognize D as an ancestor.
 
275
        #       If D is ever merged in the future, the weave
 
276
        #       won't be fixed, because AD never saw revision C
 
277
        #       to cause a conflict which would force a reweave.
 
278
        w = self.get_inventory_weave()
 
279
        from_set = set(w.inclusions([w.lookup(from_revid)]))
 
280
        to_set = set(w.inclusions([w.lookup(to_revid)]))
 
281
        included = to_set.difference(from_set)
 
282
        changed = map(w.idx_to_name, included)
 
283
        return self._fileid_involved_by_set(changed)
 
284
 
 
285
    def fileid_involved(self, last_revid=None):
 
286
        """Find all file_ids modified in the ancestry of last_revid.
 
287
 
 
288
        :param last_revid: If None, last_revision() will be used.
 
289
        """
 
290
        w = self.get_inventory_weave()
 
291
        if not last_revid:
 
292
            changed = set(w._names)
 
293
        else:
 
294
            included = w.inclusions([w.lookup(last_revid)])
 
295
            changed = map(w.idx_to_name, included)
 
296
        return self._fileid_involved_by_set(changed)
 
297
 
 
298
    def fileid_involved_by_set(self, changes):
 
299
        """Find all file_ids modified by the set of revisions passed in.
 
300
 
 
301
        :param changes: A set() of revision ids
 
302
        """
 
303
        # TODO: jam 20060119 This line does *nothing*, remove it.
 
304
        #       or better yet, change _fileid_involved_by_set so
 
305
        #       that it takes the inventory weave, rather than
 
306
        #       pulling it out by itself.
 
307
        return self._fileid_involved_by_set(changes)
 
308
 
 
309
    def _fileid_involved_by_set(self, changes):
 
310
        """Find the set of file-ids affected by the set of revisions.
 
311
 
 
312
        :param changes: A set() of revision ids.
 
313
        :return: A set() of file ids.
 
314
        
 
315
        This peaks at the Weave, interpreting each line, looking to
 
316
        see if it mentions one of the revisions. And if so, includes
 
317
        the file id mentioned.
 
318
        This expects both the Weave format, and the serialization
 
319
        to have a single line per file/directory, and to have
 
320
        fileid="" and revision="" on that line.
 
321
        """
 
322
        assert (isinstance(self._format, RepositoryFormat5) or
 
323
                isinstance(self._format, RepositoryFormat6)), \
 
324
            "fileid_involved only supported for branches which store inventory as xml"
 
325
 
 
326
        w = self.get_inventory_weave()
 
327
        file_ids = set()
 
328
        for line in w._weave:
 
329
 
 
330
            # it is ugly, but it is due to the weave structure
 
331
            if not isinstance(line, basestring): continue
 
332
 
 
333
            start = line.find('file_id="')+9
 
334
            if start < 9: continue
 
335
            end = line.find('"', start)
 
336
            assert end>= 0
 
337
            file_id = xml.sax.saxutils.unescape(line[start:end])
 
338
 
 
339
            # check if file_id is already present
 
340
            if file_id in file_ids: continue
 
341
 
 
342
            start = line.find('revision="')+10
 
343
            if start < 10: continue
 
344
            end = line.find('"', start)
 
345
            assert end>= 0
 
346
            revision_id = xml.sax.saxutils.unescape(line[start:end])
 
347
 
 
348
            if revision_id in changes:
 
349
                file_ids.add(file_id)
 
350
        return file_ids
 
351
 
215
352
    @needs_read_lock
216
353
    def get_inventory_weave(self):
217
354
        return self.control_weaves.get_weave('inventory',
277
414
        """
278
415
        if revision_id is None:
279
416
            return [None]
 
417
        if not self.has_revision(revision_id):
 
418
            raise errors.NoSuchRevision(self, revision_id)
280
419
        w = self.get_inventory_weave()
281
420
        return [None] + map(w.idx_to_name,
282
421
                            w.inclusions([w.lookup(revision_id)]))
340
479
    """
341
480
 
342
481
    _default_format = None
343
 
    """The default format used for new branches."""
 
482
    """The default format used for new repositories."""
344
483
 
345
484
    _formats = {}
346
485
    """The known formats."""
406
545
        _found is a private parameter, do not use it.
407
546
        """
408
547
        if not _found:
 
548
            # we are being called directly and must probe.
409
549
            raise NotImplementedError
410
550
        return Repository(None, branch_format=None, _format=self, a_bzrdir=a_bzrdir)
411
551
 
490
630
                   RepositoryFormat6()]
491
631
 
492
632
 
 
633
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
 
634
#       make sure that ancestry.weave is deleted (it is never used, but
 
635
#       used to be created)
 
636
 
493
637
class RepositoryTestProviderAdapter(object):
494
638
    """A tool to generate a suite testing multiple repository formats at once.
495
639