~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-23 19:10:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5390.
  • Revision ID: john@arbash-meinel.com-20100823191035-57bojnmqw54nutsz
switch 'x += 1' to 'x = x + 1' to deal with brain-damaged old versions of pyrex.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
    chk_map,
27
27
    config,
28
28
    debug,
29
 
    errors,
30
29
    fetch as _mod_fetch,
31
30
    fifo_cache,
32
31
    generate_ids,
40
39
    lru_cache,
41
40
    osutils,
42
41
    revision as _mod_revision,
 
42
    static_tuple,
43
43
    symbol_versioning,
44
44
    trace,
45
45
    tsort,
46
 
    ui,
47
46
    versionedfile,
48
47
    )
49
48
from bzrlib.bundle import serializer
52
51
from bzrlib.testament import Testament
53
52
""")
54
53
 
 
54
from bzrlib import (
 
55
    errors,
 
56
    registry,
 
57
    ui,
 
58
    )
55
59
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
56
60
from bzrlib.inter import InterObject
57
61
from bzrlib.inventory import (
60
64
    ROOT_ID,
61
65
    entry_factory,
62
66
    )
63
 
from bzrlib.lock import _RelockDebugMixin
64
 
from bzrlib import registry
 
67
from bzrlib.recordcounter import RecordCounter
 
68
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
65
69
from bzrlib.trace import (
66
70
    log_exception_quietly, note, mutter, mutter_callsite, warning)
67
71
 
70
74
_deprecation_warning_done = False
71
75
 
72
76
 
 
77
class IsInWriteGroupError(errors.InternalBzrError):
 
78
 
 
79
    _fmt = "May not refresh_data of repo %(repo)s while in a write group."
 
80
 
 
81
    def __init__(self, repo):
 
82
        errors.InternalBzrError.__init__(self, repo=repo)
 
83
 
 
84
 
73
85
class CommitBuilder(object):
74
86
    """Provides an interface to build up a commit.
75
87
 
230
242
 
231
243
    def _gen_revision_id(self):
232
244
        """Return new revision-id."""
233
 
        return generate_ids.gen_revision_id(self._config.username(),
234
 
                                            self._timestamp)
 
245
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
235
246
 
236
247
    def _generate_revision_if_needed(self):
237
248
        """Create a revision id if None was supplied.
277
288
 
278
289
        :param tree: The tree which is being committed.
279
290
        """
280
 
        # NB: if there are no parents then this method is not called, so no
281
 
        # need to guard on parents having length.
 
291
        if len(self.parents) == 0:
 
292
            raise errors.RootMissing()
282
293
        entry = entry_factory['directory'](tree.path2id(''), '',
283
294
            None)
284
295
        entry.revision = self._new_revision_id
859
870
        # versioned roots do not change unless the tree found a change.
860
871
 
861
872
 
 
873
class RepositoryWriteLockResult(LogicalLockResult):
 
874
    """The result of write locking a repository.
 
875
 
 
876
    :ivar repository_token: The token obtained from the underlying lock, or
 
877
        None.
 
878
    :ivar unlock: A callable which will unlock the lock.
 
879
    """
 
880
 
 
881
    def __init__(self, unlock, repository_token):
 
882
        LogicalLockResult.__init__(self, unlock)
 
883
        self.repository_token = repository_token
 
884
 
 
885
    def __repr__(self):
 
886
        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
 
887
            self.unlock)
 
888
 
 
889
 
862
890
######################################################################
863
891
# Repositories
864
892
 
865
893
 
866
 
class Repository(_RelockDebugMixin):
 
894
class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
867
895
    """Repository holding history for one or more branches.
868
896
 
869
897
    The repository holds and retrieves historical information including
1017
1045
                " id and insertion revid (%r, %r)"
1018
1046
                % (inv.revision_id, revision_id))
1019
1047
        if inv.root is None:
1020
 
            raise AssertionError()
 
1048
            raise errors.RootMissing()
1021
1049
        return self._add_inventory_checked(revision_id, inv, parents)
1022
1050
 
1023
1051
    def _add_inventory_checked(self, revision_id, inv, parents):
1027
1055
 
1028
1056
        :seealso: add_inventory, for the contract.
1029
1057
        """
1030
 
        inv_lines = self._serialise_inventory_to_lines(inv)
 
1058
        inv_lines = self._serializer.write_inventory_to_lines(inv)
1031
1059
        return self._inventory_add_lines(revision_id, parents,
1032
1060
            inv_lines, check_content=False)
1033
1061
 
1290
1318
 
1291
1319
        :param _format: The format of the repository on disk.
1292
1320
        :param a_bzrdir: The BzrDir of the repository.
1293
 
 
1294
 
        In the future we will have a single api for all stores for
1295
 
        getting file texts, inventories and revisions, then
1296
 
        this construct will accept instances of those things.
1297
1321
        """
 
1322
        # In the future we will have a single api for all stores for
 
1323
        # getting file texts, inventories and revisions, then
 
1324
        # this construct will accept instances of those things.
1298
1325
        super(Repository, self).__init__()
1299
1326
        self._format = _format
1300
1327
        # the following are part of the public API for Repository:
1315
1342
        # rather copying them?
1316
1343
        self._safe_to_return_from_cache = False
1317
1344
 
 
1345
    @property
 
1346
    def user_transport(self):
 
1347
        return self.bzrdir.user_transport
 
1348
 
 
1349
    @property
 
1350
    def control_transport(self):
 
1351
        return self._transport
 
1352
 
1318
1353
    def __repr__(self):
1319
1354
        if self._fallback_repositories:
1320
1355
            return '%s(%r, fallback_repositories=%r)' % (
1368
1403
        data during reads, and allows a 'write_group' to be obtained. Write
1369
1404
        groups must be used for actual data insertion.
1370
1405
 
 
1406
        A token should be passed in if you know that you have locked the object
 
1407
        some other way, and need to synchronise this object's state with that
 
1408
        fact.
 
1409
 
 
1410
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
1411
 
1371
1412
        :param token: if this is already locked, then lock_write will fail
1372
1413
            unless the token matches the existing lock.
1373
1414
        :returns: a token if this instance supports tokens, otherwise None.
1376
1417
        :raises MismatchedToken: if the specified token doesn't match the token
1377
1418
            of the existing lock.
1378
1419
        :seealso: start_write_group.
1379
 
 
1380
 
        A token should be passed in if you know that you have locked the object
1381
 
        some other way, and need to synchronise this object's state with that
1382
 
        fact.
1383
 
 
1384
 
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
1420
        :return: A RepositoryWriteLockResult.
1385
1421
        """
1386
1422
        locked = self.is_locked()
1387
 
        result = self.control_files.lock_write(token=token)
 
1423
        token = self.control_files.lock_write(token=token)
1388
1424
        if not locked:
1389
1425
            self._warn_if_deprecated()
1390
1426
            self._note_lock('w')
1392
1428
                # Writes don't affect fallback repos
1393
1429
                repo.lock_read()
1394
1430
            self._refresh_data()
1395
 
        return result
 
1431
        return RepositoryWriteLockResult(self.unlock, token)
1396
1432
 
1397
1433
    def lock_read(self):
 
1434
        """Lock the repository for read operations.
 
1435
 
 
1436
        :return: An object with an unlock method which will release the lock
 
1437
            obtained.
 
1438
        """
1398
1439
        locked = self.is_locked()
1399
1440
        self.control_files.lock_read()
1400
1441
        if not locked:
1403
1444
            for repo in self._fallback_repositories:
1404
1445
                repo.lock_read()
1405
1446
            self._refresh_data()
 
1447
        return LogicalLockResult(self.unlock)
1406
1448
 
1407
1449
    def get_physical_lock_status(self):
1408
1450
        return self.control_files.get_physical_lock_status()
1468
1510
 
1469
1511
        # now gather global repository information
1470
1512
        # XXX: This is available for many repos regardless of listability.
1471
 
        if self.bzrdir.root_transport.listable():
 
1513
        if self.user_transport.listable():
1472
1514
            # XXX: do we want to __define len__() ?
1473
1515
            # Maybe the versionedfiles object should provide a different
1474
1516
            # method to get the number of keys.
1506
1548
 
1507
1549
        ret = []
1508
1550
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
1509
 
                self.bzrdir.root_transport, evaluate=Evaluator()):
 
1551
                self.user_transport, evaluate=Evaluator()):
1510
1552
            if branches is not None:
1511
1553
                ret.extend(branches)
1512
1554
            if not using and repository is not None:
1626
1668
        return missing_keys
1627
1669
 
1628
1670
    def refresh_data(self):
1629
 
        """Re-read any data needed to to synchronise with disk.
 
1671
        """Re-read any data needed to synchronise with disk.
1630
1672
 
1631
1673
        This method is intended to be called after another repository instance
1632
1674
        (such as one used by a smart server) has inserted data into the
1633
 
        repository. It may not be called during a write group, but may be
1634
 
        called at any other time.
 
1675
        repository. On all repositories this will work outside of write groups.
 
1676
        Some repository formats (pack and newer for bzrlib native formats)
 
1677
        support refresh_data inside write groups. If called inside a write
 
1678
        group on a repository that does not support refreshing in a write group
 
1679
        IsInWriteGroupError will be raised.
1635
1680
        """
1636
 
        if self.is_in_write_group():
1637
 
            raise errors.InternalBzrError(
1638
 
                "May not refresh_data while in a write group.")
1639
1681
        self._refresh_data()
1640
1682
 
1641
1683
    def resume_write_group(self, tokens):
1680
1722
                "May not fetch while in a write group.")
1681
1723
        # fast path same-url fetch operations
1682
1724
        # TODO: lift out to somewhere common with RemoteRepository
1683
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401646>
 
1725
        # <https://bugs.launchpad.net/bzr/+bug/401646>
1684
1726
        if (self.has_same_location(source)
1685
1727
            and fetch_spec is None
1686
1728
            and self._has_same_fallbacks(source)):
1895
1937
                rev = self._serializer.read_revision_from_string(text)
1896
1938
                yield (revid, rev)
1897
1939
 
1898
 
    @needs_read_lock
1899
 
    def get_revision_xml(self, revision_id):
1900
 
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
1901
 
        #       would have already do it.
1902
 
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
1903
 
        # TODO: this can't just be replaced by:
1904
 
        # return self._serializer.write_revision_to_string(
1905
 
        #     self.get_revision(revision_id))
1906
 
        # as cStringIO preservers the encoding unlike write_revision_to_string
1907
 
        # or some other call down the path.
1908
 
        rev = self.get_revision(revision_id)
1909
 
        rev_tmp = cStringIO.StringIO()
1910
 
        # the current serializer..
1911
 
        self._serializer.write_revision(rev, rev_tmp)
1912
 
        rev_tmp.seek(0)
1913
 
        return rev_tmp.getvalue()
1914
 
 
1915
1940
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1916
1941
        """Produce a generator of revision deltas.
1917
1942
 
2430
2455
                result.revision_id, revision_id))
2431
2456
        return result
2432
2457
 
2433
 
    def _serialise_inventory(self, inv):
2434
 
        return self._serializer.write_inventory_to_string(inv)
2435
 
 
2436
 
    def _serialise_inventory_to_lines(self, inv):
2437
 
        return self._serializer.write_inventory_to_lines(inv)
2438
 
 
2439
2458
    def get_serializer_format(self):
2440
2459
        return self._serializer.format_num
2441
2460
 
2505
2524
            else:
2506
2525
                next_id = parents[0]
2507
2526
 
2508
 
    @needs_read_lock
2509
 
    def get_revision_inventory(self, revision_id):
2510
 
        """Return inventory of a past revision."""
2511
 
        # TODO: Unify this with get_inventory()
2512
 
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
2513
 
        # must be the same as its revision, so this is trivial.
2514
 
        if revision_id is None:
2515
 
            # This does not make sense: if there is no revision,
2516
 
            # then it is the current tree inventory surely ?!
2517
 
            # and thus get_root_id() is something that looks at the last
2518
 
            # commit on the branch, and the get_root_id is an inventory check.
2519
 
            raise NotImplementedError
2520
 
            # return Inventory(self.get_root_id())
2521
 
        else:
2522
 
            return self.get_inventory(revision_id)
2523
 
 
2524
2527
    def is_shared(self):
2525
2528
        """Return True if this repository is flagged as a shared repository."""
2526
2529
        raise NotImplementedError(self.is_shared)
2560
2563
            return RevisionTree(self, Inventory(root_id=None),
2561
2564
                                _mod_revision.NULL_REVISION)
2562
2565
        else:
2563
 
            inv = self.get_revision_inventory(revision_id)
 
2566
            inv = self.get_inventory(revision_id)
2564
2567
            return RevisionTree(self, inv, revision_id)
2565
2568
 
2566
2569
    def revision_trees(self, revision_ids):
2619
2622
            keys = tsort.topo_sort(parent_map)
2620
2623
        return [None] + list(keys)
2621
2624
 
2622
 
    def pack(self, hint=None):
 
2625
    def pack(self, hint=None, clean_obsolete_packs=False):
2623
2626
        """Compress the data within the repository.
2624
2627
 
2625
2628
        This operation only makes sense for some repository types. For other
2635
2638
            obtained from the result of commit_write_group(). Out of
2636
2639
            date hints are simply ignored, because concurrent operations
2637
2640
            can obsolete them rapidly.
 
2641
 
 
2642
        :param clean_obsolete_packs: Clean obsolete packs immediately after
 
2643
            the pack operation.
2638
2644
        """
2639
2645
 
2640
2646
    def get_transaction(self):
2665
2671
    def _make_parents_provider(self):
2666
2672
        return self
2667
2673
 
 
2674
    @needs_read_lock
 
2675
    def get_known_graph_ancestry(self, revision_ids):
 
2676
        """Return the known graph for a set of revision ids and their ancestors.
 
2677
        """
 
2678
        st = static_tuple.StaticTuple
 
2679
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
2680
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
2681
        return graph.GraphThunkIdsToKeys(known_graph)
 
2682
 
2668
2683
    def get_graph(self, other_repository=None):
2669
2684
        """Return the graph walker for this repository format"""
2670
2685
        parents_provider = self._make_parents_provider()
3072
3087
    # Is the format experimental ?
3073
3088
    experimental = False
3074
3089
 
3075
 
    def __str__(self):
3076
 
        return "<%s>" % self.__class__.__name__
 
3090
    def __repr__(self):
 
3091
        return "%s()" % self.__class__.__name__
3077
3092
 
3078
3093
    def __eq__(self, other):
3079
3094
        # format objects are generally stateless
3197
3212
        """
3198
3213
        raise NotImplementedError(self.open)
3199
3214
 
 
3215
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
 
3216
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
 
3217
        hooks = BzrDir.hooks['post_repo_init']
 
3218
        if not hooks:
 
3219
            return
 
3220
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
 
3221
        for hook in hooks:
 
3222
            hook(params)
 
3223
 
3200
3224
 
3201
3225
class MetaDirRepositoryFormat(RepositoryFormat):
3202
3226
    """Common base class for the new repositories using the metadir layout."""
3411
3435
        :return: None.
3412
3436
        """
3413
3437
        ui.ui_factory.warn_experimental_format_fetch(self)
3414
 
        f = _mod_fetch.RepoFetcher(to_repository=self.target,
 
3438
        from bzrlib.fetch import RepoFetcher
 
3439
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
3440
        if self.source._format.network_name() != self.target._format.network_name():
 
3441
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
3442
                from_format=self.source._format,
 
3443
                to_format=self.target._format)
 
3444
        f = RepoFetcher(to_repository=self.target,
3415
3445
                               from_repository=self.source,
3416
3446
                               last_revision=revision_id,
3417
3447
                               fetch_spec=fetch_spec,
3995
4025
        """See InterRepository.fetch()."""
3996
4026
        if fetch_spec is not None:
3997
4027
            raise AssertionError("Not implemented yet...")
3998
 
        # See <https://launchpad.net/bugs/456077> asking for a warning here
3999
 
        #
4000
 
        # nb this is only active for local-local fetches; other things using
4001
 
        # streaming.
4002
 
        ui.ui_factory.warn_cross_format_fetch(self.source._format,
4003
 
            self.target._format)
4004
4028
        ui.ui_factory.warn_experimental_format_fetch(self)
4005
4029
        if (not self.source.supports_rich_root()
4006
4030
            and self.target.supports_rich_root()):
4008
4032
            self._revision_id_to_root_id = {}
4009
4033
        else:
4010
4034
            self._converting_to_rich_root = False
 
4035
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
4036
        if self.source._format.network_name() != self.target._format.network_name():
 
4037
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
4038
                from_format=self.source._format,
 
4039
                to_format=self.target._format)
4011
4040
        revision_ids = self.target.search_missing_revision_ids(self.source,
4012
4041
            revision_id, find_ghosts=find_ghosts).get_keys()
4013
4042
        if not revision_ids:
4254
4283
                is_resume = False
4255
4284
            try:
4256
4285
                # locked_insert_stream performs a commit|suspend.
4257
 
                return self._locked_insert_stream(stream, src_format, is_resume)
 
4286
                return self._locked_insert_stream(stream, src_format,
 
4287
                    is_resume)
4258
4288
            except:
4259
4289
                self.target_repo.abort_write_group(suppress_errors=True)
4260
4290
                raise
4296
4326
                    self._extract_and_insert_inventories(
4297
4327
                        substream, src_serializer)
4298
4328
            elif substream_type == 'inventory-deltas':
4299
 
                ui.ui_factory.warn_cross_format_fetch(src_format,
4300
 
                    self.target_repo._format)
4301
4329
                self._extract_and_insert_inventory_deltas(
4302
4330
                    substream, src_serializer)
4303
4331
            elif substream_type == 'chk_bytes':
4309
4337
                # required if the serializers are different only in terms of
4310
4338
                # the inventory.
4311
4339
                if src_serializer == to_serializer:
4312
 
                    self.target_repo.revisions.insert_record_stream(
4313
 
                        substream)
 
4340
                    self.target_repo.revisions.insert_record_stream(substream)
4314
4341
                else:
4315
4342
                    self._extract_and_insert_revisions(substream,
4316
4343
                        src_serializer)
4424
4451
        """Create a StreamSource streaming from from_repository."""
4425
4452
        self.from_repository = from_repository
4426
4453
        self.to_format = to_format
 
4454
        self._record_counter = RecordCounter()
4427
4455
 
4428
4456
    def delta_on_metadata(self):
4429
4457
        """Return True if delta's are permitted on metadata streams.