~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/vf_repository.py

(gz) Fix deprecations of win32utils path function unicode wrappers (Martin
 Packman)

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
 
19
21
 
20
22
from bzrlib.lazy_import import lazy_import
21
23
lazy_import(globals(), """
23
25
 
24
26
from bzrlib import (
25
27
    check,
 
28
    config as _mod_config,
26
29
    debug,
27
30
    fetch as _mod_fetch,
28
31
    fifo_cache,
38
41
    tsort,
39
42
    ui,
40
43
    versionedfile,
 
44
    vf_search,
41
45
    )
42
46
 
43
47
from bzrlib.recordcounter import RecordCounter
44
48
from bzrlib.revisiontree import InventoryRevisionTree
45
49
from bzrlib.testament import Testament
 
50
from bzrlib.i18n import gettext
46
51
""")
47
52
 
48
53
from bzrlib import (
64
69
    CommitBuilder,
65
70
    InterRepository,
66
71
    MetaDirRepository,
67
 
    MetaDirRepositoryFormat,
 
72
    RepositoryFormatMetaDir,
68
73
    Repository,
69
74
    RepositoryFormat,
70
75
    )
71
76
 
72
77
from bzrlib.trace import (
73
 
    mutter,
 
78
    mutter
74
79
    )
75
80
 
76
81
 
79
84
 
80
85
    supports_full_versioned_files = True
81
86
    supports_versioned_directories = True
 
87
    supports_unreferenced_revisions = True
82
88
 
83
89
    # Should commit add an inventory, or an inventory delta to the repository.
84
90
    _commit_inv_deltas = True
103
109
    # the default CommitBuilder does not manage trees whose root is versioned.
104
110
    _versioned_root = False
105
111
 
106
 
    def __init__(self, repository, parents, config, timestamp=None,
 
112
    def __init__(self, repository, parents, config_stack, timestamp=None,
107
113
                 timezone=None, committer=None, revprops=None,
108
114
                 revision_id=None, lossy=False):
109
115
        super(VersionedFileCommitBuilder, self).__init__(repository,
110
 
            parents, config, timestamp, timezone, committer, revprops,
 
116
            parents, config_stack, timestamp, timezone, committer, revprops,
111
117
            revision_id, lossy)
112
118
        try:
113
119
            basis_id = self.parents[0]
195
201
                       properties=self._revprops)
196
202
        rev.parent_ids = self.parents
197
203
        self.repository.add_revision(self._new_revision_id, rev,
198
 
            self.new_inventory, self._config)
 
204
            self.new_inventory, self._config_stack)
199
205
        self._ensure_fallback_inventories()
200
206
        self.repository.commit_write_group()
201
207
        return self._new_revision_id
419
425
                return None, False, None
420
426
        # XXX: Friction: parent_candidates should return a list not a dict
421
427
        #      so that we don't have to walk the inventories again.
422
 
        parent_candiate_entries = ie.parent_candidates(parent_invs)
423
 
        head_set = self._heads(ie.file_id, parent_candiate_entries.keys())
 
428
        parent_candidate_entries = ie.parent_candidates(parent_invs)
 
429
        head_set = self._heads(ie.file_id, parent_candidate_entries.keys())
424
430
        heads = []
425
431
        for inv in parent_invs:
426
432
            if inv.has_id(ie.file_id):
441
447
            store = True
442
448
        if not store:
443
449
            # There is a single head, look it up for comparison
444
 
            parent_entry = parent_candiate_entries[heads[0]]
 
450
            parent_entry = parent_candidate_entries[heads[0]]
445
451
            # if the non-content specific data has changed, we'll be writing a
446
452
            # node:
447
453
            if (parent_entry.parent_id != ie.parent_id or
559
565
        :param iter_changes: An iter_changes iterator with the changes to apply
560
566
            to basis_revision_id. The iterator must not include any items with
561
567
            a current kind of None - missing items must be either filtered out
562
 
            or errored-on beefore record_iter_changes sees the item.
 
568
            or errored-on before record_iter_changes sees the item.
563
569
        :param _entry_factory: Private method to bind entry_factory locally for
564
570
            performance.
565
571
        :return: A generator of (file_id, relpath, fs_hash) tuples for use with
918
924
        """
919
925
        if not self._format.supports_external_lookups:
920
926
            raise errors.UnstackableRepositoryFormat(self._format, self.base)
 
927
        # This can raise an exception, so should be done before we lock the
 
928
        # fallback repository.
 
929
        self._check_fallback_repository(repository)
921
930
        if self.is_locked():
922
931
            # This repository will call fallback.unlock() when we transition to
923
932
            # the unlocked state, so we make sure to increment the lock count
924
933
            repository.lock_read()
925
 
        self._check_fallback_repository(repository)
926
934
        self._fallback_repositories.append(repository)
927
935
        self.texts.add_fallback_versioned_files(repository.texts)
928
936
        self.inventories.add_fallback_versioned_files(repository.inventories)
1039
1047
        # TODO: jam 20070210 Shouldn't we check rev.revision_id and
1040
1048
        #       rev.parent_ids?
1041
1049
        _mod_revision.check_not_reserved_id(revision_id)
1042
 
        if config is not None and config.signature_needed():
 
1050
        if (config is not None and
 
1051
            config.get('create_signatures') == _mod_config.SIGN_ALWAYS):
1043
1052
            if inv is None:
1044
1053
                inv = self.get_inventory(revision_id)
1045
1054
            tree = InventoryRevisionTree(self, inv, revision_id)
1085
1094
        keys = {'chk_bytes':set(), 'inventories':set(), 'texts':set()}
1086
1095
        kinds = ['chk_bytes', 'texts']
1087
1096
        count = len(checker.pending_keys)
1088
 
        bar.update("inventories", 0, 2)
 
1097
        bar.update(gettext("inventories"), 0, 2)
1089
1098
        current_keys = checker.pending_keys
1090
1099
        checker.pending_keys = {}
1091
1100
        # Accumulate current checks.
1111
1120
            del keys['inventories']
1112
1121
        else:
1113
1122
            return
1114
 
        bar.update("texts", 1)
 
1123
        bar.update(gettext("texts"), 1)
1115
1124
        while (checker.pending_keys or keys['chk_bytes']
1116
1125
            or keys['texts']):
1117
1126
            # Something to check.
1180
1189
                'sha1 mismatch: %s has sha1 %s expected %s referenced by %s' %
1181
1190
                (record.key, sha1, item_data[1], item_data[2]))
1182
1191
 
 
1192
    @needs_read_lock
 
1193
    def _eliminate_revisions_not_present(self, revision_ids):
 
1194
        """Check every revision id in revision_ids to see if we have it.
 
1195
 
 
1196
        Returns a set of the present revisions.
 
1197
        """
 
1198
        result = []
 
1199
        graph = self.get_graph()
 
1200
        parent_map = graph.get_parent_map(revision_ids)
 
1201
        # The old API returned a list, should this actually be a set?
 
1202
        return parent_map.keys()
 
1203
 
1183
1204
    def __init__(self, _format, a_bzrdir, control_files):
1184
1205
        """Instantiate a VersionedFileRepository.
1185
1206
 
1186
1207
        :param _format: The format of the repository on disk.
1187
 
        :param a_bzrdir: The BzrDir of the repository.
 
1208
        :param controldir: The ControlDir of the repository.
1188
1209
        :param control_files: Control files to use for locking, etc.
1189
1210
        """
1190
1211
        # In the future we will have a single api for all stores for
1192
1213
        # this construct will accept instances of those things.
1193
1214
        super(VersionedFileRepository, self).__init__(_format, a_bzrdir,
1194
1215
            control_files)
 
1216
        self._transport = control_files._transport
 
1217
        self.base = self._transport.base
1195
1218
        # for tests
1196
1219
        self._reconcile_does_inventory_gc = True
1197
1220
        self._reconcile_fixes_text_parents = False
1202
1225
        # rather copying them?
1203
1226
        self._safe_to_return_from_cache = False
1204
1227
 
 
1228
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1229
            fetch_spec=None):
 
1230
        """Fetch the content required to construct revision_id from source.
 
1231
 
 
1232
        If revision_id is None and fetch_spec is None, then all content is
 
1233
        copied.
 
1234
 
 
1235
        fetch() may not be used when the repository is in a write group -
 
1236
        either finish the current write group before using fetch, or use
 
1237
        fetch before starting the write group.
 
1238
 
 
1239
        :param find_ghosts: Find and copy revisions in the source that are
 
1240
            ghosts in the target (and not reachable directly by walking out to
 
1241
            the first-present revision in target from revision_id).
 
1242
        :param revision_id: If specified, all the content needed for this
 
1243
            revision ID will be copied to the target.  Fetch will determine for
 
1244
            itself which content needs to be copied.
 
1245
        :param fetch_spec: If specified, a SearchResult or
 
1246
            PendingAncestryResult that describes which revisions to copy.  This
 
1247
            allows copying multiple heads at once.  Mutually exclusive with
 
1248
            revision_id.
 
1249
        """
 
1250
        if fetch_spec is not None and revision_id is not None:
 
1251
            raise AssertionError(
 
1252
                "fetch_spec and revision_id are mutually exclusive.")
 
1253
        if self.is_in_write_group():
 
1254
            raise errors.InternalBzrError(
 
1255
                "May not fetch while in a write group.")
 
1256
        # fast path same-url fetch operations
 
1257
        # TODO: lift out to somewhere common with RemoteRepository
 
1258
        # <https://bugs.launchpad.net/bzr/+bug/401646>
 
1259
        if (self.has_same_location(source)
 
1260
            and fetch_spec is None
 
1261
            and self._has_same_fallbacks(source)):
 
1262
            # check that last_revision is in 'from' and then return a
 
1263
            # no-operation.
 
1264
            if (revision_id is not None and
 
1265
                not _mod_revision.is_null(revision_id)):
 
1266
                self.get_revision(revision_id)
 
1267
            return 0, []
 
1268
        inter = InterRepository.get(source, self)
 
1269
        if (fetch_spec is not None and
 
1270
            not getattr(inter, "supports_fetch_spec", False)):
 
1271
            raise errors.UnsupportedOperation(
 
1272
                "fetch_spec not supported for %r" % inter)
 
1273
        return inter.fetch(revision_id=revision_id,
 
1274
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
 
1275
 
1205
1276
    @needs_read_lock
1206
1277
    def gather_stats(self, revid=None, committers=None):
1207
1278
        """See Repository.gather_stats()."""
1216
1287
            # result['size'] = t
1217
1288
        return result
1218
1289
 
1219
 
    def get_commit_builder(self, branch, parents, config, timestamp=None,
 
1290
    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
1220
1291
                           timezone=None, committer=None, revprops=None,
1221
1292
                           revision_id=None, lossy=False):
1222
1293
        """Obtain a CommitBuilder for this repository.
1223
1294
 
1224
1295
        :param branch: Branch to commit to.
1225
1296
        :param parents: Revision ids of the parents of the new revision.
1226
 
        :param config: Configuration to use.
 
1297
        :param config_stack: Configuration stack to use.
1227
1298
        :param timestamp: Optional timestamp recorded for commit.
1228
1299
        :param timezone: Optional timezone for timestamp.
1229
1300
        :param committer: Optional committer to set for commit.
1236
1307
            raise errors.BzrError("Cannot commit directly to a stacked branch"
1237
1308
                " in pre-2a formats. See "
1238
1309
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1239
 
        result = self._commit_builder_class(self, parents, config,
 
1310
        result = self._commit_builder_class(self, parents, config_stack,
1240
1311
            timestamp, timezone, committer, revprops, revision_id,
1241
1312
            lossy)
1242
1313
        self.start_write_group()
1498
1569
            text_keys[(file_id, revision_id)] = callable_data
1499
1570
        for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1500
1571
            if record.storage_kind == 'absent':
1501
 
                raise errors.RevisionNotPresent(record.key, self)
 
1572
                raise errors.RevisionNotPresent(record.key[1], record.key[0])
1502
1573
            yield text_keys[record.key], record.get_bytes_as('chunked')
1503
1574
 
1504
1575
    def _generate_text_key_index(self, text_key_references=None,
1554
1625
        batch_size = 10 # should be ~150MB on a 55K path tree
1555
1626
        batch_count = len(revision_order) / batch_size + 1
1556
1627
        processed_texts = 0
1557
 
        pb.update("Calculating text parents", processed_texts, text_count)
 
1628
        pb.update(gettext("Calculating text parents"), processed_texts, text_count)
1558
1629
        for offset in xrange(batch_count):
1559
1630
            to_query = revision_order[offset * batch_size:(offset + 1) *
1560
1631
                batch_size]
1563
1634
            for revision_id in to_query:
1564
1635
                parent_ids = ancestors[revision_id]
1565
1636
                for text_key in revision_keys[revision_id]:
1566
 
                    pb.update("Calculating text parents", processed_texts)
 
1637
                    pb.update(gettext("Calculating text parents"), processed_texts)
1567
1638
                    processed_texts += 1
1568
1639
                    candidate_parents = []
1569
1640
                    for parent_id in parent_ids:
1639
1710
        num_file_ids = len(file_ids)
1640
1711
        for file_id, altered_versions in file_ids.iteritems():
1641
1712
            if pb is not None:
1642
 
                pb.update("Fetch texts", count, num_file_ids)
 
1713
                pb.update(gettext("Fetch texts"), count, num_file_ids)
1643
1714
            count += 1
1644
1715
            yield ("file", file_id, altered_versions)
1645
1716
 
1682
1753
        if ((None in revision_ids)
1683
1754
            or (_mod_revision.NULL_REVISION in revision_ids)):
1684
1755
            raise ValueError('cannot get null revision inventory')
1685
 
        return self._iter_inventories(revision_ids, ordering)
 
1756
        for inv, revid in self._iter_inventories(revision_ids, ordering):
 
1757
            if inv is None:
 
1758
                raise errors.NoSuchRevision(self, revid)
 
1759
            yield inv
1686
1760
 
1687
1761
    def _iter_inventories(self, revision_ids, ordering):
1688
1762
        """single-document based inventory iteration."""
1689
1763
        inv_xmls = self._iter_inventory_xmls(revision_ids, ordering)
1690
1764
        for text, revision_id in inv_xmls:
1691
 
            yield self._deserialise_inventory(revision_id, text)
 
1765
            if text is None:
 
1766
                yield None, revision_id
 
1767
            else:
 
1768
                yield self._deserialise_inventory(revision_id, text), revision_id
1692
1769
 
1693
1770
    def _iter_inventory_xmls(self, revision_ids, ordering):
1694
1771
        if ordering is None:
1712
1789
                else:
1713
1790
                    yield ''.join(chunks), record.key[-1]
1714
1791
            else:
1715
 
                raise errors.NoSuchRevision(self, record.key)
 
1792
                yield None, record.key[-1]
1716
1793
            if order_as_requested:
1717
1794
                # Yield as many results as we can while preserving order.
1718
1795
                while next_key in text_chunks:
1747
1824
    def _get_inventory_xml(self, revision_id):
1748
1825
        """Get serialized inventory as a string."""
1749
1826
        texts = self._iter_inventory_xmls([revision_id], 'unordered')
1750
 
        try:
1751
 
            text, revision_id = texts.next()
1752
 
        except StopIteration:
1753
 
            raise errors.HistoryMissing(self, 'inventory', revision_id)
 
1827
        text, revision_id = texts.next()
 
1828
        if text is None:
 
1829
            raise errors.NoSuchRevision(self, revision_id)
1754
1830
        return text
1755
1831
 
1756
1832
    @needs_read_lock
1831
1907
        """Return the graph walker for text revisions."""
1832
1908
        return graph.Graph(self.texts)
1833
1909
 
 
1910
    def revision_ids_to_search_result(self, result_set):
 
1911
        """Convert a set of revision ids to a graph SearchResult."""
 
1912
        result_parents = set()
 
1913
        for parents in self.get_graph().get_parent_map(
 
1914
            result_set).itervalues():
 
1915
            result_parents.update(parents)
 
1916
        included_keys = result_set.intersection(result_parents)
 
1917
        start_keys = result_set.difference(included_keys)
 
1918
        exclude_keys = result_parents.difference(result_set)
 
1919
        result = vf_search.SearchResult(start_keys, exclude_keys,
 
1920
            len(result_set), result_set)
 
1921
        return result
 
1922
 
1834
1923
    def _get_versioned_file_checker(self, text_key_references=None,
1835
1924
        ancestors=None):
1836
1925
        """Return an object suitable for checking versioned files.
1921
2010
            control_files)
1922
2011
 
1923
2012
 
1924
 
class MetaDirVersionedFileRepositoryFormat(MetaDirRepositoryFormat,
 
2013
class MetaDirVersionedFileRepositoryFormat(RepositoryFormatMetaDir,
1925
2014
        VersionedFileRepositoryFormat):
1926
2015
    """Base class for repository formats using versioned files in metadirs."""
1927
2016
 
2450
2539
            self.text_index.iterkeys()])
2451
2540
        # text keys is now grouped by file_id
2452
2541
        n_versions = len(self.text_index)
2453
 
        progress_bar.update('loading text store', 0, n_versions)
 
2542
        progress_bar.update(gettext('loading text store'), 0, n_versions)
2454
2543
        parent_map = self.repository.texts.get_parent_map(self.text_index)
2455
2544
        # On unlistable transports this could well be empty/error...
2456
2545
        text_keys = self.repository.texts.keys()
2457
2546
        unused_keys = frozenset(text_keys) - set(self.text_index)
2458
2547
        for num, key in enumerate(self.text_index.iterkeys()):
2459
 
            progress_bar.update('checking text graph', num, n_versions)
 
2548
            progress_bar.update(gettext('checking text graph'), num, n_versions)
2460
2549
            correct_parents = self.calculate_file_version_parents(key)
2461
2550
            try:
2462
2551
                knit_parents = parent_map[key]
2472
2561
 
2473
2562
    _walk_to_common_revisions_batch_size = 50
2474
2563
 
 
2564
    supports_fetch_spec = True
 
2565
 
2475
2566
    @needs_write_lock
2476
2567
    def fetch(self, revision_id=None, find_ghosts=False,
2477
2568
            fetch_spec=None):
2483
2574
                            content is copied.
2484
2575
        :return: None.
2485
2576
        """
2486
 
        ui.ui_factory.warn_experimental_format_fetch(self)
 
2577
        if self.target._format.experimental:
 
2578
            ui.ui_factory.show_user_warning('experimental_format_fetch',
 
2579
                from_format=self.source._format,
 
2580
                to_format=self.target._format)
2487
2581
        from bzrlib.fetch import RepoFetcher
2488
2582
        # See <https://launchpad.net/bugs/456077> asking for a warning here
2489
2583
        if self.source._format.network_name() != self.target._format.network_name():
2550
2644
                searcher.stop_searching_any(stop_revs)
2551
2645
            if searcher_exhausted:
2552
2646
                break
2553
 
        return searcher.get_result()
 
2647
        (started_keys, excludes, included_keys) = searcher.get_state()
 
2648
        return vf_search.SearchResult(started_keys, excludes,
 
2649
            len(included_keys), included_keys)
2554
2650
 
2555
2651
    @needs_read_lock
2556
2652
    def search_missing_revision_ids(self,
2903
2999
        for offset in range(0, len(revision_ids), batch_size):
2904
3000
            self.target.start_write_group()
2905
3001
            try:
2906
 
                pb.update('Transferring revisions', offset,
 
3002
                pb.update(gettext('Transferring revisions'), offset,
2907
3003
                          len(revision_ids))
2908
3004
                batch = revision_ids[offset:offset+batch_size]
2909
3005
                basis_id = self._fetch_batch(batch, basis_id, cache)
2917
3013
                    hints.extend(hint)
2918
3014
        if hints and self.target._format.pack_compresses:
2919
3015
            self.target.pack(hint=hints)
2920
 
        pb.update('Transferring revisions', len(revision_ids),
 
3016
        pb.update(gettext('Transferring revisions'), len(revision_ids),
2921
3017
                  len(revision_ids))
2922
3018
 
2923
3019
    @needs_write_lock
2928
3024
            revision_ids = fetch_spec.get_keys()
2929
3025
        else:
2930
3026
            revision_ids = None
2931
 
        ui.ui_factory.warn_experimental_format_fetch(self)
 
3027
        if self.source._format.experimental:
 
3028
            ui.ui_factory.show_user_warning('experimental_format_fetch',
 
3029
                from_format=self.source._format,
 
3030
                to_format=self.target._format)
2932
3031
        if (not self.source.supports_rich_root()
2933
3032
            and self.target.supports_rich_root()):
2934
3033
            self._converting_to_rich_root = True
3029
3128
            _install_revision(repository, revision, revision_tree, signature,
3030
3129
                inventory_cache)
3031
3130
            if pb is not None:
3032
 
                pb.update('Transferring revisions', n + 1, num_revisions)
 
3131
                pb.update(gettext('Transferring revisions'), n + 1, num_revisions)
3033
3132
    except:
3034
3133
        repository.abort_write_group()
3035
3134
        raise