~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/vf_repository.py

(jelmer) Add HPSS call for ``Repository.get_signature_text``. (Jelmer
 Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Repository formats built around versioned files."""
18
18
 
19
 
from __future__ import absolute_import
20
 
 
21
19
 
22
20
from bzrlib.lazy_import import lazy_import
23
21
lazy_import(globals(), """
25
23
 
26
24
from bzrlib import (
27
25
    check,
28
 
    config as _mod_config,
29
26
    debug,
30
27
    fetch as _mod_fetch,
31
28
    fifo_cache,
41
38
    tsort,
42
39
    ui,
43
40
    versionedfile,
44
 
    vf_search,
45
41
    )
46
42
 
47
43
from bzrlib.recordcounter import RecordCounter
69
65
    CommitBuilder,
70
66
    InterRepository,
71
67
    MetaDirRepository,
72
 
    RepositoryFormatMetaDir,
 
68
    MetaDirRepositoryFormat,
73
69
    Repository,
74
70
    RepositoryFormat,
75
71
    )
109
105
    # the default CommitBuilder does not manage trees whose root is versioned.
110
106
    _versioned_root = False
111
107
 
112
 
    def __init__(self, repository, parents, config_stack, timestamp=None,
 
108
    def __init__(self, repository, parents, config, timestamp=None,
113
109
                 timezone=None, committer=None, revprops=None,
114
110
                 revision_id=None, lossy=False):
115
111
        super(VersionedFileCommitBuilder, self).__init__(repository,
116
 
            parents, config_stack, timestamp, timezone, committer, revprops,
 
112
            parents, config, timestamp, timezone, committer, revprops,
117
113
            revision_id, lossy)
118
114
        try:
119
115
            basis_id = self.parents[0]
200
196
                       revision_id=self._new_revision_id,
201
197
                       properties=self._revprops)
202
198
        rev.parent_ids = self.parents
203
 
        if self._config_stack.get('create_signatures') == _mod_config.SIGN_ALWAYS:
204
 
            testament = Testament(rev, self.revision_tree())
205
 
            plaintext = testament.as_short_text()
206
 
            self.repository.store_revision_signature(
207
 
                gpg.GPGStrategy(self._config_stack), plaintext,
208
 
                self._new_revision_id)
209
 
        self.repository._add_revision(rev)
 
199
        self.repository.add_revision(self._new_revision_id, rev,
 
200
            self.new_inventory, self._config)
210
201
        self._ensure_fallback_inventories()
211
202
        self.repository.commit_write_group()
212
203
        return self._new_revision_id
604
595
                        _mod_revision.NULL_REVISION))
605
596
        # The basis inventory from a repository 
606
597
        if revtrees:
607
 
            basis_tree = revtrees[0]
 
598
            basis_inv = revtrees[0].inventory
608
599
        else:
609
 
            basis_tree = self.repository.revision_tree(
610
 
                _mod_revision.NULL_REVISION)
611
 
        basis_inv = basis_tree.root_inventory
 
600
            basis_inv = self.repository.revision_tree(
 
601
                _mod_revision.NULL_REVISION).inventory
612
602
        if len(self.parents) > 0:
613
603
            if basis_revision_id != self.parents[0] and not ghost_basis:
614
604
                raise Exception(
615
605
                    "arbitrary basis parents not yet supported with merges")
616
606
            for revtree in revtrees[1:]:
617
 
                for change in revtree.root_inventory._make_delta(basis_inv):
 
607
                for change in revtree.inventory._make_delta(basis_inv):
618
608
                    if change[1] is None:
619
609
                        # Not present in this parent.
620
610
                        continue
1022
1012
            # return a new inventory, but as there is no revision tree cache in
1023
1013
            # repository this is safe for now - RBC 20081013
1024
1014
            if basis_inv is None:
1025
 
                basis_inv = basis_tree.root_inventory
 
1015
                basis_inv = basis_tree.inventory
1026
1016
            basis_inv.apply_delta(delta)
1027
1017
            basis_inv.revision_id = new_revision_id
1028
1018
            return (self.add_inventory(new_revision_id, basis_inv, parents),
1039
1029
        self.inventories._access.flush()
1040
1030
        return result
1041
1031
 
1042
 
    def add_revision(self, revision_id, rev, inv=None):
 
1032
    def add_revision(self, revision_id, rev, inv=None, config=None):
1043
1033
        """Add rev to the revision store as revision_id.
1044
1034
 
1045
1035
        :param revision_id: the revision id to use.
1046
1036
        :param rev: The revision object.
1047
1037
        :param inv: The inventory for the revision. if None, it will be looked
1048
1038
                    up in the inventory storer
 
1039
        :param config: If None no digital signature will be created.
 
1040
                       If supplied its signature_needed method will be used
 
1041
                       to determine if a signature should be made.
1049
1042
        """
1050
1043
        # TODO: jam 20070210 Shouldn't we check rev.revision_id and
1051
1044
        #       rev.parent_ids?
1052
1045
        _mod_revision.check_not_reserved_id(revision_id)
 
1046
        if config is not None and config.signature_needed():
 
1047
            if inv is None:
 
1048
                inv = self.get_inventory(revision_id)
 
1049
            tree = InventoryRevisionTree(self, inv, revision_id)
 
1050
            testament = Testament(rev, tree)
 
1051
            plaintext = testament.as_short_text()
 
1052
            self.store_revision_signature(
 
1053
                gpg.GPGStrategy(config), plaintext, revision_id)
1053
1054
        # check inventory present
1054
1055
        if not self.inventories.get_parent_map([(revision_id,)]):
1055
1056
            if inv is None:
1219
1220
        # rather copying them?
1220
1221
        self._safe_to_return_from_cache = False
1221
1222
 
1222
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
1223
 
            fetch_spec=None):
1224
 
        """Fetch the content required to construct revision_id from source.
1225
 
 
1226
 
        If revision_id is None and fetch_spec is None, then all content is
1227
 
        copied.
1228
 
 
1229
 
        fetch() may not be used when the repository is in a write group -
1230
 
        either finish the current write group before using fetch, or use
1231
 
        fetch before starting the write group.
1232
 
 
1233
 
        :param find_ghosts: Find and copy revisions in the source that are
1234
 
            ghosts in the target (and not reachable directly by walking out to
1235
 
            the first-present revision in target from revision_id).
1236
 
        :param revision_id: If specified, all the content needed for this
1237
 
            revision ID will be copied to the target.  Fetch will determine for
1238
 
            itself which content needs to be copied.
1239
 
        :param fetch_spec: If specified, a SearchResult or
1240
 
            PendingAncestryResult that describes which revisions to copy.  This
1241
 
            allows copying multiple heads at once.  Mutually exclusive with
1242
 
            revision_id.
1243
 
        """
1244
 
        if fetch_spec is not None and revision_id is not None:
1245
 
            raise AssertionError(
1246
 
                "fetch_spec and revision_id are mutually exclusive.")
1247
 
        if self.is_in_write_group():
1248
 
            raise errors.InternalBzrError(
1249
 
                "May not fetch while in a write group.")
1250
 
        # fast path same-url fetch operations
1251
 
        # TODO: lift out to somewhere common with RemoteRepository
1252
 
        # <https://bugs.launchpad.net/bzr/+bug/401646>
1253
 
        if (self.has_same_location(source)
1254
 
            and fetch_spec is None
1255
 
            and self._has_same_fallbacks(source)):
1256
 
            # check that last_revision is in 'from' and then return a
1257
 
            # no-operation.
1258
 
            if (revision_id is not None and
1259
 
                not _mod_revision.is_null(revision_id)):
1260
 
                self.get_revision(revision_id)
1261
 
            return 0, []
1262
 
        inter = InterRepository.get(source, self)
1263
 
        if (fetch_spec is not None and
1264
 
            not getattr(inter, "supports_fetch_spec", False)):
1265
 
            raise errors.UnsupportedOperation(
1266
 
                "fetch_spec not supported for %r" % inter)
1267
 
        return inter.fetch(revision_id=revision_id,
1268
 
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1269
 
 
1270
1223
    @needs_read_lock
1271
1224
    def gather_stats(self, revid=None, committers=None):
1272
1225
        """See Repository.gather_stats()."""
1281
1234
            # result['size'] = t
1282
1235
        return result
1283
1236
 
1284
 
    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
 
1237
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1285
1238
                           timezone=None, committer=None, revprops=None,
1286
1239
                           revision_id=None, lossy=False):
1287
1240
        """Obtain a CommitBuilder for this repository.
1288
1241
 
1289
1242
        :param branch: Branch to commit to.
1290
1243
        :param parents: Revision ids of the parents of the new revision.
1291
 
        :param config_stack: Configuration stack to use.
 
1244
        :param config: Configuration to use.
1292
1245
        :param timestamp: Optional timestamp recorded for commit.
1293
1246
        :param timezone: Optional timezone for timestamp.
1294
1247
        :param committer: Optional committer to set for commit.
1301
1254
            raise errors.BzrError("Cannot commit directly to a stacked branch"
1302
1255
                " in pre-2a formats. See "
1303
1256
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1304
 
        result = self._commit_builder_class(self, parents, config_stack,
 
1257
        result = self._commit_builder_class(self, parents, config,
1305
1258
            timestamp, timezone, committer, revprops, revision_id,
1306
1259
            lossy)
1307
1260
        self.start_write_group()
1563
1516
            text_keys[(file_id, revision_id)] = callable_data
1564
1517
        for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1565
1518
            if record.storage_kind == 'absent':
1566
 
                raise errors.RevisionNotPresent(record.key[1], record.key[0])
 
1519
                raise errors.RevisionNotPresent(record.key, self)
1567
1520
            yield text_keys[record.key], record.get_bytes_as('chunked')
1568
1521
 
1569
1522
    def _generate_text_key_index(self, text_key_references=None,
1648
1601
                            try:
1649
1602
                                inv = inventory_cache[parent_id]
1650
1603
                            except KeyError:
1651
 
                                inv = self.revision_tree(parent_id).root_inventory
 
1604
                                inv = self.revision_tree(parent_id).inventory
1652
1605
                                inventory_cache[parent_id] = inv
1653
1606
                            try:
1654
1607
                                parent_entry = inv[text_key[0]]
1747
1700
        if ((None in revision_ids)
1748
1701
            or (_mod_revision.NULL_REVISION in revision_ids)):
1749
1702
            raise ValueError('cannot get null revision inventory')
1750
 
        for inv, revid in self._iter_inventories(revision_ids, ordering):
1751
 
            if inv is None:
1752
 
                raise errors.NoSuchRevision(self, revid)
1753
 
            yield inv
 
1703
        return self._iter_inventories(revision_ids, ordering)
1754
1704
 
1755
1705
    def _iter_inventories(self, revision_ids, ordering):
1756
1706
        """single-document based inventory iteration."""
1757
1707
        inv_xmls = self._iter_inventory_xmls(revision_ids, ordering)
1758
1708
        for text, revision_id in inv_xmls:
1759
 
            if text is None:
1760
 
                yield None, revision_id
1761
 
            else:
1762
 
                yield self._deserialise_inventory(revision_id, text), revision_id
 
1709
            yield self._deserialise_inventory(revision_id, text)
1763
1710
 
1764
1711
    def _iter_inventory_xmls(self, revision_ids, ordering):
1765
1712
        if ordering is None:
1783
1730
                else:
1784
1731
                    yield ''.join(chunks), record.key[-1]
1785
1732
            else:
1786
 
                yield None, record.key[-1]
 
1733
                raise errors.NoSuchRevision(self, record.key)
1787
1734
            if order_as_requested:
1788
1735
                # Yield as many results as we can while preserving order.
1789
1736
                while next_key in text_chunks:
1818
1765
    def _get_inventory_xml(self, revision_id):
1819
1766
        """Get serialized inventory as a string."""
1820
1767
        texts = self._iter_inventory_xmls([revision_id], 'unordered')
1821
 
        text, revision_id = texts.next()
1822
 
        if text is None:
1823
 
            raise errors.NoSuchRevision(self, revision_id)
 
1768
        try:
 
1769
            text, revision_id = texts.next()
 
1770
        except StopIteration:
 
1771
            raise errors.HistoryMissing(self, 'inventory', revision_id)
1824
1772
        return text
1825
1773
 
1826
1774
    @needs_read_lock
1901
1849
        """Return the graph walker for text revisions."""
1902
1850
        return graph.Graph(self.texts)
1903
1851
 
1904
 
    def revision_ids_to_search_result(self, result_set):
1905
 
        """Convert a set of revision ids to a graph SearchResult."""
1906
 
        result_parents = set()
1907
 
        for parents in self.get_graph().get_parent_map(
1908
 
            result_set).itervalues():
1909
 
            result_parents.update(parents)
1910
 
        included_keys = result_set.intersection(result_parents)
1911
 
        start_keys = result_set.difference(included_keys)
1912
 
        exclude_keys = result_parents.difference(result_set)
1913
 
        result = vf_search.SearchResult(start_keys, exclude_keys,
1914
 
            len(result_set), result_set)
1915
 
        return result
1916
 
 
1917
1852
    def _get_versioned_file_checker(self, text_key_references=None,
1918
1853
        ancestors=None):
1919
1854
        """Return an object suitable for checking versioned files.
2004
1939
            control_files)
2005
1940
 
2006
1941
 
2007
 
class MetaDirVersionedFileRepositoryFormat(RepositoryFormatMetaDir,
 
1942
class MetaDirVersionedFileRepositoryFormat(MetaDirRepositoryFormat,
2008
1943
        VersionedFileRepositoryFormat):
2009
1944
    """Base class for repository formats using versioned files in metadirs."""
2010
1945
 
2442
2377
        invs_sent_so_far = set([_mod_revision.NULL_REVISION])
2443
2378
        inventory_cache = lru_cache.LRUCache(50)
2444
2379
        null_inventory = from_repo.revision_tree(
2445
 
            _mod_revision.NULL_REVISION).root_inventory
 
2380
            _mod_revision.NULL_REVISION).inventory
2446
2381
        # XXX: ideally the rich-root/tree-refs flags would be per-revision, not
2447
2382
        # per-repo (e.g.  streaming a non-rich-root revision out of a rich-root
2448
2383
        # repo back into a non-rich-root repo ought to be allowed)
2555
2490
 
2556
2491
    _walk_to_common_revisions_batch_size = 50
2557
2492
 
2558
 
    supports_fetch_spec = True
2559
 
 
2560
2493
    @needs_write_lock
2561
2494
    def fetch(self, revision_id=None, find_ghosts=False,
2562
2495
            fetch_spec=None):
2638
2571
                searcher.stop_searching_any(stop_revs)
2639
2572
            if searcher_exhausted:
2640
2573
                break
2641
 
        (started_keys, excludes, included_keys) = searcher.get_state()
2642
 
        return vf_search.SearchResult(started_keys, excludes,
2643
 
            len(included_keys), included_keys)
 
2574
        return searcher.get_result()
2644
2575
 
2645
2576
    @needs_read_lock
2646
2577
    def search_missing_revision_ids(self,
2795
2726
        """
2796
2727
        deltas = []
2797
2728
        # Generate deltas against each tree, to find the shortest.
2798
 
        # FIXME: Support nested trees
2799
2729
        texts_possibly_new_in_tree = set()
2800
2730
        for basis_id, basis_tree in possible_trees:
2801
 
            delta = tree.root_inventory._make_delta(basis_tree.root_inventory)
 
2731
            delta = tree.inventory._make_delta(basis_tree.inventory)
2802
2732
            for old_path, new_path, file_id, new_entry in delta:
2803
2733
                if new_path is None:
2804
2734
                    # This file_id isn't present in the new rev, so we don't
2841
2771
            parents_parents = [key[-1] for key in parents_parents_keys]
2842
2772
            basis_id = _mod_revision.NULL_REVISION
2843
2773
            basis_tree = self.source.revision_tree(basis_id)
2844
 
            delta = parent_tree.root_inventory._make_delta(
2845
 
                basis_tree.root_inventory)
 
2774
            delta = parent_tree.inventory._make_delta(basis_tree.inventory)
2846
2775
            self.target.add_inventory_by_delta(
2847
2776
                basis_id, delta, current_revision_id, parents_parents)
2848
2777
            cache[current_revision_id] = parent_tree
2907
2836
                kind = entry.kind
2908
2837
                texts_possibly_new_in_tree.add((file_id, entry.revision))
2909
2838
            for basis_id, basis_tree in possible_trees:
2910
 
                basis_inv = basis_tree.root_inventory
 
2839
                basis_inv = basis_tree.inventory
2911
2840
                for file_key in list(texts_possibly_new_in_tree):
2912
2841
                    file_id, file_revision = file_key
2913
2842
                    try:
3145
3074
            parent_trees[p_id] = repository.revision_tree(
3146
3075
                                     _mod_revision.NULL_REVISION)
3147
3076
 
3148
 
    # FIXME: Support nested trees
3149
 
    inv = revision_tree.root_inventory
 
3077
    inv = revision_tree.inventory
3150
3078
    entries = inv.iter_entries()
3151
3079
    # backwards compatibility hack: skip the root id.
3152
3080
    if not repository.supports_rich_root():