/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

  • Committer: Robert Collins
  • Date: 2010-05-04 06:22:51 UTC
  • mto: This revision was merged to the branch mainline in revision 5206.
  • Revision ID: robertc@robertcollins.net-20100504062251-1ocjhrl53mum9ehw
Minor local_abspath docstring improvement.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
24
24
    bzrdir,
25
25
    check,
26
26
    chk_map,
 
27
    config,
27
28
    debug,
28
29
    errors,
29
30
    fetch as _mod_fetch,
39
40
    lru_cache,
40
41
    osutils,
41
42
    revision as _mod_revision,
 
43
    static_tuple,
42
44
    symbol_versioning,
 
45
    trace,
43
46
    tsort,
44
47
    ui,
45
48
    versionedfile,
861
864
# Repositories
862
865
 
863
866
 
864
 
class Repository(_RelockDebugMixin):
 
867
class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
865
868
    """Repository holding history for one or more branches.
866
869
 
867
870
    The repository holds and retrieves historical information including
1025
1028
 
1026
1029
        :seealso: add_inventory, for the contract.
1027
1030
        """
1028
 
        inv_lines = self._serialise_inventory_to_lines(inv)
 
1031
        inv_lines = self._serializer.write_inventory_to_lines(inv)
1029
1032
        return self._inventory_add_lines(revision_id, parents,
1030
1033
            inv_lines, check_content=False)
1031
1034
 
1238
1241
        """Check a single text from this repository."""
1239
1242
        if kind == 'inventories':
1240
1243
            rev_id = record.key[0]
1241
 
            inv = self.deserialise_inventory(rev_id,
 
1244
            inv = self._deserialise_inventory(rev_id,
1242
1245
                record.get_bytes_as('fulltext'))
1243
1246
            if last_object is not None:
1244
1247
                delta = inv._make_delta(last_object)
1288
1291
 
1289
1292
        :param _format: The format of the repository on disk.
1290
1293
        :param a_bzrdir: The BzrDir of the repository.
1291
 
 
1292
 
        In the future we will have a single api for all stores for
1293
 
        getting file texts, inventories and revisions, then
1294
 
        this construct will accept instances of those things.
1295
1294
        """
 
1295
        # In the future we will have a single api for all stores for
 
1296
        # getting file texts, inventories and revisions, then
 
1297
        # this construct will accept instances of those things.
1296
1298
        super(Repository, self).__init__()
1297
1299
        self._format = _format
1298
1300
        # the following are part of the public API for Repository:
1304
1306
        self._reconcile_does_inventory_gc = True
1305
1307
        self._reconcile_fixes_text_parents = False
1306
1308
        self._reconcile_backsup_inventory = True
1307
 
        # not right yet - should be more semantically clear ?
1308
 
        #
1309
 
        # TODO: make sure to construct the right store classes, etc, depending
1310
 
        # on whether escaping is required.
1311
 
        self._warn_if_deprecated()
1312
1309
        self._write_group = None
1313
1310
        # Additional places to query for data.
1314
1311
        self._fallback_repositories = []
1315
1312
        # An InventoryEntry cache, used during deserialization
1316
1313
        self._inventory_entry_cache = fifo_cache.FIFOCache(10*1024)
 
1314
        # Is it safe to return inventory entries directly from the entry cache,
 
1315
        # rather copying them?
 
1316
        self._safe_to_return_from_cache = False
 
1317
 
 
1318
    @property
 
1319
    def user_transport(self):
 
1320
        return self.bzrdir.user_transport
 
1321
 
 
1322
    @property
 
1323
    def control_transport(self):
 
1324
        return self._transport
1317
1325
 
1318
1326
    def __repr__(self):
1319
1327
        if self._fallback_repositories:
1386
1394
        locked = self.is_locked()
1387
1395
        result = self.control_files.lock_write(token=token)
1388
1396
        if not locked:
 
1397
            self._warn_if_deprecated()
1389
1398
            self._note_lock('w')
1390
1399
            for repo in self._fallback_repositories:
1391
1400
                # Writes don't affect fallback repos
1397
1406
        locked = self.is_locked()
1398
1407
        self.control_files.lock_read()
1399
1408
        if not locked:
 
1409
            self._warn_if_deprecated()
1400
1410
            self._note_lock('r')
1401
1411
            for repo in self._fallback_repositories:
1402
1412
                repo.lock_read()
1466
1476
 
1467
1477
        # now gather global repository information
1468
1478
        # XXX: This is available for many repos regardless of listability.
1469
 
        if self.bzrdir.root_transport.listable():
 
1479
        if self.user_transport.listable():
1470
1480
            # XXX: do we want to __define len__() ?
1471
1481
            # Maybe the versionedfiles object should provide a different
1472
1482
            # method to get the number of keys.
1482
1492
        :param using: If True, list only branches using this repository.
1483
1493
        """
1484
1494
        if using and not self.is_shared():
1485
 
            try:
1486
 
                return [self.bzrdir.open_branch()]
1487
 
            except errors.NotBranchError:
1488
 
                return []
 
1495
            return self.bzrdir.list_branches()
1489
1496
        class Evaluator(object):
1490
1497
 
1491
1498
            def __init__(self):
1500
1507
                    except errors.NoRepositoryPresent:
1501
1508
                        pass
1502
1509
                    else:
1503
 
                        return False, (None, repository)
 
1510
                        return False, ([], repository)
1504
1511
                self.first_call = False
1505
 
                try:
1506
 
                    value = (bzrdir.open_branch(), None)
1507
 
                except errors.NotBranchError:
1508
 
                    value = (None, None)
 
1512
                value = (bzrdir.list_branches(), None)
1509
1513
                return True, value
1510
1514
 
1511
 
        branches = []
1512
 
        for branch, repository in bzrdir.BzrDir.find_bzrdirs(
1513
 
                self.bzrdir.root_transport, evaluate=Evaluator()):
1514
 
            if branch is not None:
1515
 
                branches.append(branch)
 
1515
        ret = []
 
1516
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
 
1517
                self.user_transport, evaluate=Evaluator()):
 
1518
            if branches is not None:
 
1519
                ret.extend(branches)
1516
1520
            if not using and repository is not None:
1517
 
                branches.extend(repository.find_branches())
1518
 
        return branches
 
1521
                ret.extend(repository.find_branches())
 
1522
        return ret
1519
1523
 
1520
1524
    @needs_read_lock
1521
1525
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1899
1903
                rev = self._serializer.read_revision_from_string(text)
1900
1904
                yield (revid, rev)
1901
1905
 
1902
 
    @needs_read_lock
1903
 
    def get_revision_xml(self, revision_id):
1904
 
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
1905
 
        #       would have already do it.
1906
 
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
1907
 
        # TODO: this can't just be replaced by:
1908
 
        # return self._serializer.write_revision_to_string(
1909
 
        #     self.get_revision(revision_id))
1910
 
        # as cStringIO preservers the encoding unlike write_revision_to_string
1911
 
        # or some other call down the path.
1912
 
        rev = self.get_revision(revision_id)
1913
 
        rev_tmp = cStringIO.StringIO()
1914
 
        # the current serializer..
1915
 
        self._serializer.write_revision(rev, rev_tmp)
1916
 
        rev_tmp.seek(0)
1917
 
        return rev_tmp.getvalue()
1918
 
 
1919
1906
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1920
1907
        """Produce a generator of revision deltas.
1921
1908
 
2163
2150
        """
2164
2151
        selected_keys = set((revid,) for revid in revision_ids)
2165
2152
        w = _inv_weave or self.inventories
2166
 
        pb = ui.ui_factory.nested_progress_bar()
2167
 
        try:
2168
 
            return self._find_file_ids_from_xml_inventory_lines(
2169
 
                w.iter_lines_added_or_present_in_keys(
2170
 
                    selected_keys, pb=pb),
2171
 
                selected_keys)
2172
 
        finally:
2173
 
            pb.finished()
 
2153
        return self._find_file_ids_from_xml_inventory_lines(
 
2154
            w.iter_lines_added_or_present_in_keys(
 
2155
                selected_keys, pb=None),
 
2156
            selected_keys)
2174
2157
 
2175
2158
    def iter_files_bytes(self, desired_files):
2176
2159
        """Iterate through file versions.
2386
2369
        """single-document based inventory iteration."""
2387
2370
        inv_xmls = self._iter_inventory_xmls(revision_ids, ordering)
2388
2371
        for text, revision_id in inv_xmls:
2389
 
            yield self.deserialise_inventory(revision_id, text)
 
2372
            yield self._deserialise_inventory(revision_id, text)
2390
2373
 
2391
2374
    def _iter_inventory_xmls(self, revision_ids, ordering):
2392
2375
        if ordering is None:
2424
2407
                        next_key = None
2425
2408
                        break
2426
2409
 
2427
 
    def deserialise_inventory(self, revision_id, xml):
 
2410
    def _deserialise_inventory(self, revision_id, xml):
2428
2411
        """Transform the xml into an inventory object.
2429
2412
 
2430
2413
        :param revision_id: The expected revision id of the inventory.
2431
2414
        :param xml: A serialised inventory.
2432
2415
        """
2433
2416
        result = self._serializer.read_inventory_from_string(xml, revision_id,
2434
 
                    entry_cache=self._inventory_entry_cache)
 
2417
                    entry_cache=self._inventory_entry_cache,
 
2418
                    return_from_cache=self._safe_to_return_from_cache)
2435
2419
        if result.revision_id != revision_id:
2436
2420
            raise AssertionError('revision id mismatch %s != %s' % (
2437
2421
                result.revision_id, revision_id))
2438
2422
        return result
2439
2423
 
2440
 
    def serialise_inventory(self, inv):
2441
 
        return self._serializer.write_inventory_to_string(inv)
2442
 
 
2443
 
    def _serialise_inventory_to_lines(self, inv):
2444
 
        return self._serializer.write_inventory_to_lines(inv)
2445
 
 
2446
2424
    def get_serializer_format(self):
2447
2425
        return self._serializer.format_num
2448
2426
 
2449
2427
    @needs_read_lock
2450
 
    def get_inventory_xml(self, revision_id):
2451
 
        """Get inventory XML as a file object."""
 
2428
    def _get_inventory_xml(self, revision_id):
 
2429
        """Get serialized inventory as a string."""
2452
2430
        texts = self._iter_inventory_xmls([revision_id], 'unordered')
2453
2431
        try:
2454
2432
            text, revision_id = texts.next()
2456
2434
            raise errors.HistoryMissing(self, 'inventory', revision_id)
2457
2435
        return text
2458
2436
 
2459
 
    @needs_read_lock
2460
 
    def get_inventory_sha1(self, revision_id):
2461
 
        """Return the sha1 hash of the inventory entry
2462
 
        """
2463
 
        return self.get_revision(revision_id).inventory_sha1
2464
 
 
2465
2437
    def get_rev_id_for_revno(self, revno, known_pair):
2466
2438
        """Return the revision id of a revno, given a later (revno, revid)
2467
2439
        pair in the same history.
2518
2490
            else:
2519
2491
                next_id = parents[0]
2520
2492
 
2521
 
    @needs_read_lock
2522
 
    def get_revision_inventory(self, revision_id):
2523
 
        """Return inventory of a past revision."""
2524
 
        # TODO: Unify this with get_inventory()
2525
 
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
2526
 
        # must be the same as its revision, so this is trivial.
2527
 
        if revision_id is None:
2528
 
            # This does not make sense: if there is no revision,
2529
 
            # then it is the current tree inventory surely ?!
2530
 
            # and thus get_root_id() is something that looks at the last
2531
 
            # commit on the branch, and the get_root_id is an inventory check.
2532
 
            raise NotImplementedError
2533
 
            # return Inventory(self.get_root_id())
2534
 
        else:
2535
 
            return self.get_inventory(revision_id)
2536
 
 
2537
2493
    def is_shared(self):
2538
2494
        """Return True if this repository is flagged as a shared repository."""
2539
2495
        raise NotImplementedError(self.is_shared)
2573
2529
            return RevisionTree(self, Inventory(root_id=None),
2574
2530
                                _mod_revision.NULL_REVISION)
2575
2531
        else:
2576
 
            inv = self.get_revision_inventory(revision_id)
 
2532
            inv = self.get_inventory(revision_id)
2577
2533
            return RevisionTree(self, inv, revision_id)
2578
2534
 
2579
2535
    def revision_trees(self, revision_ids):
2632
2588
            keys = tsort.topo_sort(parent_map)
2633
2589
        return [None] + list(keys)
2634
2590
 
2635
 
    def pack(self, hint=None):
 
2591
    def pack(self, hint=None, clean_obsolete_packs=False):
2636
2592
        """Compress the data within the repository.
2637
2593
 
2638
2594
        This operation only makes sense for some repository types. For other
2648
2604
            obtained from the result of commit_write_group(). Out of
2649
2605
            date hints are simply ignored, because concurrent operations
2650
2606
            can obsolete them rapidly.
 
2607
 
 
2608
        :param clean_obsolete_packs: Clean obsolete packs immediately after
 
2609
            the pack operation.
2651
2610
        """
2652
2611
 
2653
2612
    def get_transaction(self):
2678
2637
    def _make_parents_provider(self):
2679
2638
        return self
2680
2639
 
 
2640
    @needs_read_lock
 
2641
    def get_known_graph_ancestry(self, revision_ids):
 
2642
        """Return the known graph for a set of revision ids and their ancestors.
 
2643
        """
 
2644
        st = static_tuple.StaticTuple
 
2645
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
2646
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
2647
        return graph.GraphThunkIdsToKeys(known_graph)
 
2648
 
2681
2649
    def get_graph(self, other_repository=None):
2682
2650
        """Return the graph walker for this repository format"""
2683
2651
        parents_provider = self._make_parents_provider()
2778
2746
        result.check(callback_refs)
2779
2747
        return result
2780
2748
 
2781
 
    def _warn_if_deprecated(self):
 
2749
    def _warn_if_deprecated(self, branch=None):
2782
2750
        global _deprecation_warning_done
2783
2751
        if _deprecation_warning_done:
2784
2752
            return
2785
 
        _deprecation_warning_done = True
2786
 
        warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
2787
 
                % (self._format, self.bzrdir.transport.base))
 
2753
        try:
 
2754
            if branch is None:
 
2755
                conf = config.GlobalConfig()
 
2756
            else:
 
2757
                conf = branch.get_config()
 
2758
            if conf.suppress_warning('format_deprecation'):
 
2759
                return
 
2760
            warning("Format %s for %s is deprecated -"
 
2761
                    " please use 'bzr upgrade' to get better performance"
 
2762
                    % (self._format, self.bzrdir.transport.base))
 
2763
        finally:
 
2764
            _deprecation_warning_done = True
2788
2765
 
2789
2766
    def supports_rich_root(self):
2790
2767
        return self._format.rich_root_data
3073
3050
    pack_compresses = False
3074
3051
    # Does the repository inventory storage understand references to trees?
3075
3052
    supports_tree_reference = None
 
3053
    # Is the format experimental ?
 
3054
    experimental = False
3076
3055
 
3077
 
    def __str__(self):
3078
 
        return "<%s>" % self.__class__.__name__
 
3056
    def __repr__(self):
 
3057
        return "%s()" % self.__class__.__name__
3079
3058
 
3080
3059
    def __eq__(self, other):
3081
3060
        # format objects are generally stateless
3199
3178
        """
3200
3179
        raise NotImplementedError(self.open)
3201
3180
 
 
3181
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
 
3182
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
 
3183
        hooks = BzrDir.hooks['post_repo_init']
 
3184
        if not hooks:
 
3185
            return
 
3186
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
 
3187
        for hook in hooks:
 
3188
            hook(params)
 
3189
 
3202
3190
 
3203
3191
class MetaDirRepositoryFormat(RepositoryFormat):
3204
3192
    """Common base class for the new repositories using the metadir layout."""
3409
3397
 
3410
3398
        :param revision_id: if None all content is copied, if NULL_REVISION no
3411
3399
                            content is copied.
3412
 
        :param pb: optional progress bar to use for progress reports. If not
3413
 
                   provided a default one will be created.
 
3400
        :param pb: ignored.
3414
3401
        :return: None.
3415
3402
        """
3416
 
        f = _mod_fetch.RepoFetcher(to_repository=self.target,
 
3403
        ui.ui_factory.warn_experimental_format_fetch(self)
 
3404
        from bzrlib.fetch import RepoFetcher
 
3405
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
3406
        if self.source._format.network_name() != self.target._format.network_name():
 
3407
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
3408
                from_format=self.source._format,
 
3409
                to_format=self.target._format)
 
3410
        f = RepoFetcher(to_repository=self.target,
3417
3411
                               from_repository=self.source,
3418
3412
                               last_revision=revision_id,
3419
3413
                               fetch_spec=fetch_spec,
3420
 
                               pb=pb, find_ghosts=find_ghosts)
 
3414
                               find_ghosts=find_ghosts)
3421
3415
 
3422
3416
    def _walk_to_common_revisions(self, revision_ids):
3423
3417
        """Walk out from revision_ids in source to revisions target has.
3840
3834
        pending_revisions = []
3841
3835
        parent_map = self.source.get_parent_map(revision_ids)
3842
3836
        self._fetch_parent_invs_for_stacking(parent_map, cache)
 
3837
        self.source._safe_to_return_from_cache = True
3843
3838
        for tree in self.source.revision_trees(revision_ids):
3844
3839
            # Find a inventory delta for this revision.
3845
3840
            # Find text entries that need to be copied, too.
3893
3888
            pending_revisions.append(revision)
3894
3889
            cache[current_revision_id] = tree
3895
3890
            basis_id = current_revision_id
 
3891
        self.source._safe_to_return_from_cache = False
3896
3892
        # Copy file texts
3897
3893
        from_texts = self.source.texts
3898
3894
        to_texts = self.target.texts
3977
3973
                basis_id = self._fetch_batch(batch, basis_id, cache,
3978
3974
                                             a_graph=a_graph)
3979
3975
            except:
 
3976
                self.source._safe_to_return_from_cache = False
3980
3977
                self.target.abort_write_group()
3981
3978
                raise
3982
3979
            else:
3994
3991
        """See InterRepository.fetch()."""
3995
3992
        if fetch_spec is not None:
3996
3993
            raise AssertionError("Not implemented yet...")
 
3994
        ui.ui_factory.warn_experimental_format_fetch(self)
3997
3995
        if (not self.source.supports_rich_root()
3998
3996
            and self.target.supports_rich_root()):
3999
3997
            self._converting_to_rich_root = True
4000
3998
            self._revision_id_to_root_id = {}
4001
3999
        else:
4002
4000
            self._converting_to_rich_root = False
 
4001
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
4002
        if self.source._format.network_name() != self.target._format.network_name():
 
4003
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
4004
                from_format=self.source._format,
 
4005
                to_format=self.target._format)
4003
4006
        revision_ids = self.target.search_missing_revision_ids(self.source,
4004
4007
            revision_id, find_ghosts=find_ghosts).get_keys()
4005
4008
        if not revision_ids:
4074
4077
        :param to_convert: The disk object to convert.
4075
4078
        :param pb: a progress bar to use for progress information.
4076
4079
        """
4077
 
        self.pb = pb
 
4080
        pb = ui.ui_factory.nested_progress_bar()
4078
4081
        self.count = 0
4079
4082
        self.total = 4
4080
4083
        # this is only useful with metadir layouts - separated repo content.
4081
4084
        # trigger an assertion if not such
4082
4085
        repo._format.get_format_string()
4083
4086
        self.repo_dir = repo.bzrdir
4084
 
        self.step('Moving repository to repository.backup')
 
4087
        pb.update('Moving repository to repository.backup')
4085
4088
        self.repo_dir.transport.move('repository', 'repository.backup')
4086
4089
        backup_transport =  self.repo_dir.transport.clone('repository.backup')
4087
4090
        repo._format.check_conversion_target(self.target_format)
4088
4091
        self.source_repo = repo._format.open(self.repo_dir,
4089
4092
            _found=True,
4090
4093
            _override_transport=backup_transport)
4091
 
        self.step('Creating new repository')
 
4094
        pb.update('Creating new repository')
4092
4095
        converted = self.target_format.initialize(self.repo_dir,
4093
4096
                                                  self.source_repo.is_shared())
4094
4097
        converted.lock_write()
4095
4098
        try:
4096
 
            self.step('Copying content')
 
4099
            pb.update('Copying content')
4097
4100
            self.source_repo.copy_content_into(converted)
4098
4101
        finally:
4099
4102
            converted.unlock()
4100
 
        self.step('Deleting old repository content')
 
4103
        pb.update('Deleting old repository content')
4101
4104
        self.repo_dir.transport.delete_tree('repository.backup')
4102
4105
        ui.ui_factory.note('repository converted')
4103
 
 
4104
 
    def step(self, message):
4105
 
        """Update the pb by a step."""
4106
 
        self.count +=1
4107
 
        self.pb.update(message, self.count, self.total)
 
4106
        pb.finished()
4108
4107
 
4109
4108
 
4110
4109
_unescape_map = {
4602
4601
 
4603
4602
    def _get_convertable_inventory_stream(self, revision_ids,
4604
4603
                                          delta_versus_null=False):
4605
 
        # The source is using CHKs, but the target either doesn't or it has a
4606
 
        # different serializer.  The StreamSink code expects to be able to
 
4604
        # The two formats are sufficiently different that there is no fast
 
4605
        # path, so we need to send just inventorydeltas, which any
 
4606
        # sufficiently modern client can insert into any repository.
 
4607
        # The StreamSink code expects to be able to
4607
4608
        # convert on the target, so we need to put bytes-on-the-wire that can
4608
4609
        # be converted.  That means inventory deltas (if the remote is <1.19,
4609
4610
        # RemoteStreamSink will fallback to VFS to insert the deltas).