~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/pack_repo.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-04-15 12:07:39 UTC
  • mfrom: (5777.5.3 inventoryworkingtree)
  • Revision ID: pqm@pqm.ubuntu.com-20110415120739-7ftrzs0qmz98ar5e
(jelmer) Split inventory-specific implementation out of MutableTree into
 MutableInventoryTree. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2011 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
30
30
    osutils,
31
31
    pack,
32
32
    transactions,
 
33
    tsort,
33
34
    ui,
34
 
    xml5,
35
 
    xml6,
36
 
    xml7,
37
35
    )
38
36
from bzrlib.index import (
39
37
    CombinedGraphIndex,
42
40
from bzrlib.knit import (
43
41
    KnitPlainFactory,
44
42
    KnitVersionedFiles,
45
 
    _KnitGraphIndex,
46
43
    _DirectPackAccess,
47
44
    )
48
 
from bzrlib import tsort
49
45
""")
50
46
from bzrlib import (
51
 
    bzrdir,
52
47
    btree_index,
53
48
    errors,
54
49
    lockable_files,
55
50
    lockdir,
56
 
    revision as _mod_revision,
57
51
    )
58
52
 
59
53
from bzrlib.decorators import needs_write_lock, only_raises
60
 
from bzrlib.index import (
61
 
    GraphIndex,
62
 
    InMemoryGraphIndex,
63
 
    )
64
54
from bzrlib.lock import LogicalLockResult
65
55
from bzrlib.repofmt.knitrepo import KnitRepository
66
56
from bzrlib.repository import (
69
59
    RepositoryFormat,
70
60
    RepositoryWriteLockResult,
71
61
    RootCommitBuilder,
72
 
    StreamSource,
73
62
    )
74
63
from bzrlib.trace import (
75
64
    mutter,
673
662
        # What text keys to copy. None for 'all texts'. This is set by
674
663
        # _copy_inventory_texts
675
664
        self._text_filter = None
676
 
        self._extra_init()
677
 
 
678
 
    def _extra_init(self):
679
 
        """A template hook to allow extending the constructor trivially."""
680
665
 
681
666
    def _pack_map_and_index_list(self, index_attribute):
682
667
        """Convert a list of packs to an index pack map and index list.
1209
1194
        return new_pack
1210
1195
 
1211
1196
 
1212
 
class ReconcilePacker(Packer):
1213
 
    """A packer which regenerates indices etc as it copies.
1214
 
 
1215
 
    This is used by ``bzr reconcile`` to cause parent text pointers to be
1216
 
    regenerated.
1217
 
    """
1218
 
 
1219
 
    def _extra_init(self):
1220
 
        self._data_changed = False
1221
 
 
1222
 
    def _process_inventory_lines(self, inv_lines):
1223
 
        """Generate a text key reference map rather for reconciling with."""
1224
 
        repo = self._pack_collection.repo
1225
 
        refs = repo._find_text_key_references_from_xml_inventory_lines(
1226
 
            inv_lines)
1227
 
        self._text_refs = refs
1228
 
        # during reconcile we:
1229
 
        #  - convert unreferenced texts to full texts
1230
 
        #  - correct texts which reference a text not copied to be full texts
1231
 
        #  - copy all others as-is but with corrected parents.
1232
 
        #  - so at this point we don't know enough to decide what becomes a full
1233
 
        #    text.
1234
 
        self._text_filter = None
1235
 
 
1236
 
    def _copy_text_texts(self):
1237
 
        """generate what texts we should have and then copy."""
1238
 
        self.pb.update("Copying content texts", 3)
1239
 
        # we have three major tasks here:
1240
 
        # 1) generate the ideal index
1241
 
        repo = self._pack_collection.repo
1242
 
        ancestors = dict([(key[0], tuple(ref[0] for ref in refs[0])) for
1243
 
            _1, key, _2, refs in
1244
 
            self.new_pack.revision_index.iter_all_entries()])
1245
 
        ideal_index = repo._generate_text_key_index(self._text_refs, ancestors)
1246
 
        # 2) generate a text_nodes list that contains all the deltas that can
1247
 
        #    be used as-is, with corrected parents.
1248
 
        ok_nodes = []
1249
 
        bad_texts = []
1250
 
        discarded_nodes = []
1251
 
        NULL_REVISION = _mod_revision.NULL_REVISION
1252
 
        text_index_map, text_nodes = self._get_text_nodes()
1253
 
        for node in text_nodes:
1254
 
            # 0 - index
1255
 
            # 1 - key
1256
 
            # 2 - value
1257
 
            # 3 - refs
1258
 
            try:
1259
 
                ideal_parents = tuple(ideal_index[node[1]])
1260
 
            except KeyError:
1261
 
                discarded_nodes.append(node)
1262
 
                self._data_changed = True
1263
 
            else:
1264
 
                if ideal_parents == (NULL_REVISION,):
1265
 
                    ideal_parents = ()
1266
 
                if ideal_parents == node[3][0]:
1267
 
                    # no change needed.
1268
 
                    ok_nodes.append(node)
1269
 
                elif ideal_parents[0:1] == node[3][0][0:1]:
1270
 
                    # the left most parent is the same, or there are no parents
1271
 
                    # today. Either way, we can preserve the representation as
1272
 
                    # long as we change the refs to be inserted.
1273
 
                    self._data_changed = True
1274
 
                    ok_nodes.append((node[0], node[1], node[2],
1275
 
                        (ideal_parents, node[3][1])))
1276
 
                    self._data_changed = True
1277
 
                else:
1278
 
                    # Reinsert this text completely
1279
 
                    bad_texts.append((node[1], ideal_parents))
1280
 
                    self._data_changed = True
1281
 
        # we're finished with some data.
1282
 
        del ideal_index
1283
 
        del text_nodes
1284
 
        # 3) bulk copy the ok data
1285
 
        total_items, readv_group_iter = self._least_readv_node_readv(ok_nodes)
1286
 
        list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
1287
 
            self.new_pack.text_index, readv_group_iter, total_items))
1288
 
        # 4) adhoc copy all the other texts.
1289
 
        # We have to topologically insert all texts otherwise we can fail to
1290
 
        # reconcile when parts of a single delta chain are preserved intact,
1291
 
        # and other parts are not. E.g. Discarded->d1->d2->d3. d1 will be
1292
 
        # reinserted, and if d3 has incorrect parents it will also be
1293
 
        # reinserted. If we insert d3 first, d2 is present (as it was bulk
1294
 
        # copied), so we will try to delta, but d2 is not currently able to be
1295
 
        # extracted because its basis d1 is not present. Topologically sorting
1296
 
        # addresses this. The following generates a sort for all the texts that
1297
 
        # are being inserted without having to reference the entire text key
1298
 
        # space (we only topo sort the revisions, which is smaller).
1299
 
        topo_order = tsort.topo_sort(ancestors)
1300
 
        rev_order = dict(zip(topo_order, range(len(topo_order))))
1301
 
        bad_texts.sort(key=lambda key:rev_order.get(key[0][1], 0))
1302
 
        transaction = repo.get_transaction()
1303
 
        file_id_index = GraphIndexPrefixAdapter(
1304
 
            self.new_pack.text_index,
1305
 
            ('blank', ), 1,
1306
 
            add_nodes_callback=self.new_pack.text_index.add_nodes)
1307
 
        data_access = _DirectPackAccess(
1308
 
                {self.new_pack.text_index:self.new_pack.access_tuple()})
1309
 
        data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
1310
 
            self.new_pack.access_tuple())
1311
 
        output_texts = KnitVersionedFiles(
1312
 
            _KnitGraphIndex(self.new_pack.text_index,
1313
 
                add_callback=self.new_pack.text_index.add_nodes,
1314
 
                deltas=True, parents=True, is_locked=repo.is_locked),
1315
 
            data_access=data_access, max_delta_chain=200)
1316
 
        for key, parent_keys in bad_texts:
1317
 
            # We refer to the new pack to delta data being output.
1318
 
            # A possible improvement would be to catch errors on short reads
1319
 
            # and only flush then.
1320
 
            self.new_pack.flush()
1321
 
            parents = []
1322
 
            for parent_key in parent_keys:
1323
 
                if parent_key[0] != key[0]:
1324
 
                    # Graph parents must match the fileid
1325
 
                    raise errors.BzrError('Mismatched key parent %r:%r' %
1326
 
                        (key, parent_keys))
1327
 
                parents.append(parent_key[1])
1328
 
            text_lines = osutils.split_lines(repo.texts.get_record_stream(
1329
 
                [key], 'unordered', True).next().get_bytes_as('fulltext'))
1330
 
            output_texts.add_lines(key, parent_keys, text_lines,
1331
 
                random_id=True, check_content=False)
1332
 
        # 5) check that nothing inserted has a reference outside the keyspace.
1333
 
        missing_text_keys = self.new_pack.text_index._external_references()
1334
 
        if missing_text_keys:
1335
 
            raise errors.BzrCheckError('Reference to missing compression parents %r'
1336
 
                % (missing_text_keys,))
1337
 
        self._log_copied_texts()
1338
 
 
1339
 
    def _use_pack(self, new_pack):
1340
 
        """Override _use_pack to check for reconcile having changed content."""
1341
 
        # XXX: we might be better checking this at the copy time.
1342
 
        original_inventory_keys = set()
1343
 
        inv_index = self._pack_collection.inventory_index.combined_index
1344
 
        for entry in inv_index.iter_all_entries():
1345
 
            original_inventory_keys.add(entry[1])
1346
 
        new_inventory_keys = set()
1347
 
        for entry in new_pack.inventory_index.iter_all_entries():
1348
 
            new_inventory_keys.add(entry[1])
1349
 
        if new_inventory_keys != original_inventory_keys:
1350
 
            self._data_changed = True
1351
 
        return new_pack.data_inserted() and self._data_changed
1352
 
 
1353
 
 
1354
1197
class RepositoryPackCollection(object):
1355
1198
    """Management of packs within a repository.
1356
1199
 
1573
1416
        mutter('Packing repository %s, which has %d pack files, '
1574
1417
            'containing %d revisions with hint %r.', self, total_packs,
1575
1418
            total_revisions, hint)
 
1419
        while True:
 
1420
            try:
 
1421
                self._try_pack_operations(hint)
 
1422
            except RetryPackOperations:
 
1423
                continue
 
1424
            break
 
1425
 
 
1426
        if clean_obsolete_packs:
 
1427
            self._clear_obsolete_packs()
 
1428
 
 
1429
    def _try_pack_operations(self, hint):
 
1430
        """Calculate the pack operations based on the hint (if any), and
 
1431
        execute them.
 
1432
        """
1576
1433
        # determine which packs need changing
1577
1434
        pack_operations = [[0, []]]
1578
1435
        for pack in self.all_packs():
1581
1438
                # or this pack was included in the hint.
1582
1439
                pack_operations[-1][0] += pack.get_revision_count()
1583
1440
                pack_operations[-1][1].append(pack)
1584
 
        self._execute_pack_operations(pack_operations, OptimisingPacker)
1585
 
 
1586
 
        if clean_obsolete_packs:
1587
 
            self._clear_obsolete_packs()
 
1441
        self._execute_pack_operations(pack_operations, OptimisingPacker,
 
1442
            reload_func=self._restart_pack_operations)
1588
1443
 
1589
1444
    def plan_autopack_combinations(self, existing_packs, pack_distribution):
1590
1445
        """Plan a pack operation.
2044
1899
            raise
2045
1900
        raise errors.RetryAutopack(self.repo, False, sys.exc_info())
2046
1901
 
 
1902
    def _restart_pack_operations(self):
 
1903
        """Reload the pack names list, and restart the autopack code."""
 
1904
        if not self.reload_pack_names():
 
1905
            # Re-raise the original exception, because something went missing
 
1906
            # and a restart didn't find it
 
1907
            raise
 
1908
        raise RetryPackOperations(self.repo, False, sys.exc_info())
 
1909
 
2047
1910
    def _clear_obsolete_packs(self, preserve=None):
2048
1911
        """Delete everything from the obsolete-packs directory.
2049
1912
 
2197
2060
            self._resume_pack(token)
2198
2061
 
2199
2062
 
2200
 
class KnitPackRepository(KnitRepository):
 
2063
class PackRepository(KnitRepository):
2201
2064
    """Repository with knit objects stored inside pack containers.
2202
2065
 
2203
2066
    The layering for a KnitPackRepository is:
2206
2069
    ===================================================
2207
2070
    Tuple based apis below, string based, and key based apis above
2208
2071
    ---------------------------------------------------
2209
 
    KnitVersionedFiles
 
2072
    VersionedFiles
2210
2073
      Provides .texts, .revisions etc
2211
2074
      This adapts the N-tuple keys to physical knit records which only have a
2212
2075
      single string identifier (for historical reasons), which in older formats
2222
2085
 
2223
2086
    """
2224
2087
 
2225
 
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
2226
 
        _serializer):
2227
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
2228
 
            _commit_builder_class, _serializer)
2229
 
        index_transport = self._transport.clone('indices')
2230
 
        self._pack_collection = RepositoryPackCollection(self, self._transport,
2231
 
            index_transport,
2232
 
            self._transport.clone('upload'),
2233
 
            self._transport.clone('packs'),
2234
 
            _format.index_builder_class,
2235
 
            _format.index_class,
2236
 
            use_chk_index=self._format.supports_chks,
2237
 
            )
2238
 
        self.inventories = KnitVersionedFiles(
2239
 
            _KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
2240
 
                add_callback=self._pack_collection.inventory_index.add_callback,
2241
 
                deltas=True, parents=True, is_locked=self.is_locked),
2242
 
            data_access=self._pack_collection.inventory_index.data_access,
2243
 
            max_delta_chain=200)
2244
 
        self.revisions = KnitVersionedFiles(
2245
 
            _KnitGraphIndex(self._pack_collection.revision_index.combined_index,
2246
 
                add_callback=self._pack_collection.revision_index.add_callback,
2247
 
                deltas=False, parents=True, is_locked=self.is_locked,
2248
 
                track_external_parent_refs=True),
2249
 
            data_access=self._pack_collection.revision_index.data_access,
2250
 
            max_delta_chain=0)
2251
 
        self.signatures = KnitVersionedFiles(
2252
 
            _KnitGraphIndex(self._pack_collection.signature_index.combined_index,
2253
 
                add_callback=self._pack_collection.signature_index.add_callback,
2254
 
                deltas=False, parents=False, is_locked=self.is_locked),
2255
 
            data_access=self._pack_collection.signature_index.data_access,
2256
 
            max_delta_chain=0)
2257
 
        self.texts = KnitVersionedFiles(
2258
 
            _KnitGraphIndex(self._pack_collection.text_index.combined_index,
2259
 
                add_callback=self._pack_collection.text_index.add_callback,
2260
 
                deltas=True, parents=True, is_locked=self.is_locked),
2261
 
            data_access=self._pack_collection.text_index.data_access,
2262
 
            max_delta_chain=200)
2263
 
        if _format.supports_chks:
2264
 
            # No graph, no compression:- references from chks are between
2265
 
            # different objects not temporal versions of the same; and without
2266
 
            # some sort of temporal structure knit compression will just fail.
2267
 
            self.chk_bytes = KnitVersionedFiles(
2268
 
                _KnitGraphIndex(self._pack_collection.chk_index.combined_index,
2269
 
                    add_callback=self._pack_collection.chk_index.add_callback,
2270
 
                    deltas=False, parents=False, is_locked=self.is_locked),
2271
 
                data_access=self._pack_collection.chk_index.data_access,
2272
 
                max_delta_chain=0)
2273
 
        else:
2274
 
            self.chk_bytes = None
2275
 
        # True when the repository object is 'write locked' (as opposed to the
2276
 
        # physical lock only taken out around changes to the pack-names list.)
2277
 
        # Another way to represent this would be a decorator around the control
2278
 
        # files object that presents logical locks as physical ones - if this
2279
 
        # gets ugly consider that alternative design. RBC 20071011
2280
 
        self._write_lock_count = 0
2281
 
        self._transaction = None
2282
 
        # for tests
2283
 
        self._reconcile_does_inventory_gc = True
2284
 
        self._reconcile_fixes_text_parents = True
2285
 
        self._reconcile_backsup_inventory = False
2286
 
 
2287
 
    def _warn_if_deprecated(self, branch=None):
2288
 
        # This class isn't deprecated, but one sub-format is
2289
 
        if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
2290
 
            super(KnitPackRepository, self)._warn_if_deprecated(branch)
2291
 
 
2292
2088
    def _abort_write_group(self):
2293
2089
        self.revisions._index._key_dependencies.clear()
2294
2090
        self._pack_collection._abort_write_group()
2295
2091
 
2296
 
    def _get_source(self, to_format):
2297
 
        if to_format.network_name() == self._format.network_name():
2298
 
            return KnitPackStreamSource(self, to_format)
2299
 
        return super(KnitPackRepository, self)._get_source(to_format)
2300
 
 
2301
2092
    def _make_parents_provider(self):
2302
2093
        return graph.CachingParentsProvider(self)
2303
2094
 
2409
2200
        return reconciler
2410
2201
 
2411
2202
    def _reconcile_pack(self, collection, packs, extension, revs, pb):
2412
 
        packer = ReconcilePacker(collection, packs, extension, revs)
2413
 
        return packer.pack(pb)
 
2203
        raise NotImplementedError(self._reconcile_pack)
2414
2204
 
2415
2205
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2416
2206
    def unlock(self):
2435
2225
                repo.unlock()
2436
2226
 
2437
2227
 
2438
 
class KnitPackStreamSource(StreamSource):
2439
 
    """A StreamSource used to transfer data between same-format KnitPack repos.
2440
 
 
2441
 
    This source assumes:
2442
 
        1) Same serialization format for all objects
2443
 
        2) Same root information
2444
 
        3) XML format inventories
2445
 
        4) Atomic inserts (so we can stream inventory texts before text
2446
 
           content)
2447
 
        5) No chk_bytes
2448
 
    """
2449
 
 
2450
 
    def __init__(self, from_repository, to_format):
2451
 
        super(KnitPackStreamSource, self).__init__(from_repository, to_format)
2452
 
        self._text_keys = None
2453
 
        self._text_fetch_order = 'unordered'
2454
 
 
2455
 
    def _get_filtered_inv_stream(self, revision_ids):
2456
 
        from_repo = self.from_repository
2457
 
        parent_ids = from_repo._find_parent_ids_of_revisions(revision_ids)
2458
 
        parent_keys = [(p,) for p in parent_ids]
2459
 
        find_text_keys = from_repo._find_text_key_references_from_xml_inventory_lines
2460
 
        parent_text_keys = set(find_text_keys(
2461
 
            from_repo._inventory_xml_lines_for_keys(parent_keys)))
2462
 
        content_text_keys = set()
2463
 
        knit = KnitVersionedFiles(None, None)
2464
 
        factory = KnitPlainFactory()
2465
 
        def find_text_keys_from_content(record):
2466
 
            if record.storage_kind not in ('knit-delta-gz', 'knit-ft-gz'):
2467
 
                raise ValueError("Unknown content storage kind for"
2468
 
                    " inventory text: %s" % (record.storage_kind,))
2469
 
            # It's a knit record, it has a _raw_record field (even if it was
2470
 
            # reconstituted from a network stream).
2471
 
            raw_data = record._raw_record
2472
 
            # read the entire thing
2473
 
            revision_id = record.key[-1]
2474
 
            content, _ = knit._parse_record(revision_id, raw_data)
2475
 
            if record.storage_kind == 'knit-delta-gz':
2476
 
                line_iterator = factory.get_linedelta_content(content)
2477
 
            elif record.storage_kind == 'knit-ft-gz':
2478
 
                line_iterator = factory.get_fulltext_content(content)
2479
 
            content_text_keys.update(find_text_keys(
2480
 
                [(line, revision_id) for line in line_iterator]))
2481
 
        revision_keys = [(r,) for r in revision_ids]
2482
 
        def _filtered_inv_stream():
2483
 
            source_vf = from_repo.inventories
2484
 
            stream = source_vf.get_record_stream(revision_keys,
2485
 
                                                 'unordered', False)
2486
 
            for record in stream:
2487
 
                if record.storage_kind == 'absent':
2488
 
                    raise errors.NoSuchRevision(from_repo, record.key)
2489
 
                find_text_keys_from_content(record)
2490
 
                yield record
2491
 
            self._text_keys = content_text_keys - parent_text_keys
2492
 
        return ('inventories', _filtered_inv_stream())
2493
 
 
2494
 
    def _get_text_stream(self):
2495
 
        # Note: We know we don't have to handle adding root keys, because both
2496
 
        # the source and target are the identical network name.
2497
 
        text_stream = self.from_repository.texts.get_record_stream(
2498
 
                        self._text_keys, self._text_fetch_order, False)
2499
 
        return ('texts', text_stream)
2500
 
 
2501
 
    def get_stream(self, search):
2502
 
        revision_ids = search.get_keys()
2503
 
        for stream_info in self._fetch_revision_texts(revision_ids):
2504
 
            yield stream_info
2505
 
        self._revision_keys = [(rev_id,) for rev_id in revision_ids]
2506
 
        yield self._get_filtered_inv_stream(revision_ids)
2507
 
        yield self._get_text_stream()
2508
 
 
2509
 
 
2510
 
 
2511
2228
class RepositoryFormatPack(MetaDirRepositoryFormat):
2512
2229
    """Format logic for pack structured repositories.
2513
2230
 
2544
2261
    index_class = None
2545
2262
    _fetch_uses_deltas = True
2546
2263
    fast_deltas = False
 
2264
    supports_full_versioned_files = True
 
2265
    supports_funky_characters = True
 
2266
    revision_graph_can_have_wrong_parents = True
2547
2267
 
2548
2268
    def initialize(self, a_bzrdir, shared=False):
2549
2269
        """Create a pack based repository.
2586
2306
                              _serializer=self._serializer)
2587
2307
 
2588
2308
 
2589
 
class RepositoryFormatKnitPack1(RepositoryFormatPack):
2590
 
    """A no-subtrees parameterized Pack repository.
2591
 
 
2592
 
    This format was introduced in 0.92.
2593
 
    """
2594
 
 
2595
 
    repository_class = KnitPackRepository
2596
 
    _commit_builder_class = PackCommitBuilder
2597
 
    @property
2598
 
    def _serializer(self):
2599
 
        return xml5.serializer_v5
2600
 
    # What index classes to use
2601
 
    index_builder_class = InMemoryGraphIndex
2602
 
    index_class = GraphIndex
2603
 
 
2604
 
    def _get_matching_bzrdir(self):
2605
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2606
 
 
2607
 
    def _ignore_setting_bzrdir(self, format):
2608
 
        pass
2609
 
 
2610
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2611
 
 
2612
 
    def get_format_string(self):
2613
 
        """See RepositoryFormat.get_format_string()."""
2614
 
        return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
2615
 
 
2616
 
    def get_format_description(self):
2617
 
        """See RepositoryFormat.get_format_description()."""
2618
 
        return "Packs containing knits without subtree support"
2619
 
 
2620
 
 
2621
 
class RepositoryFormatKnitPack3(RepositoryFormatPack):
2622
 
    """A subtrees parameterized Pack repository.
2623
 
 
2624
 
    This repository format uses the xml7 serializer to get:
2625
 
     - support for recording full info about the tree root
2626
 
     - support for recording tree-references
2627
 
 
2628
 
    This format was introduced in 0.92.
2629
 
    """
2630
 
 
2631
 
    repository_class = KnitPackRepository
2632
 
    _commit_builder_class = PackRootCommitBuilder
2633
 
    rich_root_data = True
2634
 
    experimental = True
2635
 
    supports_tree_reference = True
2636
 
    @property
2637
 
    def _serializer(self):
2638
 
        return xml7.serializer_v7
2639
 
    # What index classes to use
2640
 
    index_builder_class = InMemoryGraphIndex
2641
 
    index_class = GraphIndex
2642
 
 
2643
 
    def _get_matching_bzrdir(self):
2644
 
        return bzrdir.format_registry.make_bzrdir(
2645
 
            'pack-0.92-subtree')
2646
 
 
2647
 
    def _ignore_setting_bzrdir(self, format):
2648
 
        pass
2649
 
 
2650
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2651
 
 
2652
 
    def get_format_string(self):
2653
 
        """See RepositoryFormat.get_format_string()."""
2654
 
        return "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
2655
 
 
2656
 
    def get_format_description(self):
2657
 
        """See RepositoryFormat.get_format_description()."""
2658
 
        return "Packs containing knits with subtree support\n"
2659
 
 
2660
 
 
2661
 
class RepositoryFormatKnitPack4(RepositoryFormatPack):
2662
 
    """A rich-root, no subtrees parameterized Pack repository.
2663
 
 
2664
 
    This repository format uses the xml6 serializer to get:
2665
 
     - support for recording full info about the tree root
2666
 
 
2667
 
    This format was introduced in 1.0.
2668
 
    """
2669
 
 
2670
 
    repository_class = KnitPackRepository
2671
 
    _commit_builder_class = PackRootCommitBuilder
2672
 
    rich_root_data = True
2673
 
    supports_tree_reference = False
2674
 
    @property
2675
 
    def _serializer(self):
2676
 
        return xml6.serializer_v6
2677
 
    # What index classes to use
2678
 
    index_builder_class = InMemoryGraphIndex
2679
 
    index_class = GraphIndex
2680
 
 
2681
 
    def _get_matching_bzrdir(self):
2682
 
        return bzrdir.format_registry.make_bzrdir(
2683
 
            'rich-root-pack')
2684
 
 
2685
 
    def _ignore_setting_bzrdir(self, format):
2686
 
        pass
2687
 
 
2688
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2689
 
 
2690
 
    def get_format_string(self):
2691
 
        """See RepositoryFormat.get_format_string()."""
2692
 
        return ("Bazaar pack repository format 1 with rich root"
2693
 
                " (needs bzr 1.0)\n")
2694
 
 
2695
 
    def get_format_description(self):
2696
 
        """See RepositoryFormat.get_format_description()."""
2697
 
        return "Packs containing knits with rich root support\n"
2698
 
 
2699
 
 
2700
 
class RepositoryFormatKnitPack5(RepositoryFormatPack):
2701
 
    """Repository that supports external references to allow stacking.
2702
 
 
2703
 
    New in release 1.6.
2704
 
 
2705
 
    Supports external lookups, which results in non-truncated ghosts after
2706
 
    reconcile compared to pack-0.92 formats.
2707
 
    """
2708
 
 
2709
 
    repository_class = KnitPackRepository
2710
 
    _commit_builder_class = PackCommitBuilder
2711
 
    supports_external_lookups = True
2712
 
    # What index classes to use
2713
 
    index_builder_class = InMemoryGraphIndex
2714
 
    index_class = GraphIndex
2715
 
 
2716
 
    @property
2717
 
    def _serializer(self):
2718
 
        return xml5.serializer_v5
2719
 
 
2720
 
    def _get_matching_bzrdir(self):
2721
 
        return bzrdir.format_registry.make_bzrdir('1.6')
2722
 
 
2723
 
    def _ignore_setting_bzrdir(self, format):
2724
 
        pass
2725
 
 
2726
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2727
 
 
2728
 
    def get_format_string(self):
2729
 
        """See RepositoryFormat.get_format_string()."""
2730
 
        return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
2731
 
 
2732
 
    def get_format_description(self):
2733
 
        """See RepositoryFormat.get_format_description()."""
2734
 
        return "Packs 5 (adds stacking support, requires bzr 1.6)"
2735
 
 
2736
 
 
2737
 
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
2738
 
    """A repository with rich roots and stacking.
2739
 
 
2740
 
    New in release 1.6.1.
2741
 
 
2742
 
    Supports stacking on other repositories, allowing data to be accessed
2743
 
    without being stored locally.
2744
 
    """
2745
 
 
2746
 
    repository_class = KnitPackRepository
2747
 
    _commit_builder_class = PackRootCommitBuilder
2748
 
    rich_root_data = True
2749
 
    supports_tree_reference = False # no subtrees
2750
 
    supports_external_lookups = True
2751
 
    # What index classes to use
2752
 
    index_builder_class = InMemoryGraphIndex
2753
 
    index_class = GraphIndex
2754
 
 
2755
 
    @property
2756
 
    def _serializer(self):
2757
 
        return xml6.serializer_v6
2758
 
 
2759
 
    def _get_matching_bzrdir(self):
2760
 
        return bzrdir.format_registry.make_bzrdir(
2761
 
            '1.6.1-rich-root')
2762
 
 
2763
 
    def _ignore_setting_bzrdir(self, format):
2764
 
        pass
2765
 
 
2766
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2767
 
 
2768
 
    def get_format_string(self):
2769
 
        """See RepositoryFormat.get_format_string()."""
2770
 
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
2771
 
 
2772
 
    def get_format_description(self):
2773
 
        return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
2774
 
 
2775
 
 
2776
 
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
2777
 
    """A repository with rich roots and external references.
2778
 
 
2779
 
    New in release 1.6.
2780
 
 
2781
 
    Supports external lookups, which results in non-truncated ghosts after
2782
 
    reconcile compared to pack-0.92 formats.
2783
 
 
2784
 
    This format was deprecated because the serializer it uses accidentally
2785
 
    supported subtrees, when the format was not intended to. This meant that
2786
 
    someone could accidentally fetch from an incorrect repository.
2787
 
    """
2788
 
 
2789
 
    repository_class = KnitPackRepository
2790
 
    _commit_builder_class = PackRootCommitBuilder
2791
 
    rich_root_data = True
2792
 
    supports_tree_reference = False # no subtrees
2793
 
 
2794
 
    supports_external_lookups = True
2795
 
    # What index classes to use
2796
 
    index_builder_class = InMemoryGraphIndex
2797
 
    index_class = GraphIndex
2798
 
 
2799
 
    @property
2800
 
    def _serializer(self):
2801
 
        return xml7.serializer_v7
2802
 
 
2803
 
    def _get_matching_bzrdir(self):
2804
 
        matching = bzrdir.format_registry.make_bzrdir(
2805
 
            '1.6.1-rich-root')
2806
 
        matching.repository_format = self
2807
 
        return matching
2808
 
 
2809
 
    def _ignore_setting_bzrdir(self, format):
2810
 
        pass
2811
 
 
2812
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2813
 
 
2814
 
    def get_format_string(self):
2815
 
        """See RepositoryFormat.get_format_string()."""
2816
 
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
2817
 
 
2818
 
    def get_format_description(self):
2819
 
        return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
2820
 
                " (deprecated)")
2821
 
 
2822
 
 
2823
 
class RepositoryFormatKnitPack6(RepositoryFormatPack):
2824
 
    """A repository with stacking and btree indexes,
2825
 
    without rich roots or subtrees.
2826
 
 
2827
 
    This is equivalent to pack-1.6 with B+Tree indices.
2828
 
    """
2829
 
 
2830
 
    repository_class = KnitPackRepository
2831
 
    _commit_builder_class = PackCommitBuilder
2832
 
    supports_external_lookups = True
2833
 
    # What index classes to use
2834
 
    index_builder_class = btree_index.BTreeBuilder
2835
 
    index_class = btree_index.BTreeGraphIndex
2836
 
 
2837
 
    @property
2838
 
    def _serializer(self):
2839
 
        return xml5.serializer_v5
2840
 
 
2841
 
    def _get_matching_bzrdir(self):
2842
 
        return bzrdir.format_registry.make_bzrdir('1.9')
2843
 
 
2844
 
    def _ignore_setting_bzrdir(self, format):
2845
 
        pass
2846
 
 
2847
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2848
 
 
2849
 
    def get_format_string(self):
2850
 
        """See RepositoryFormat.get_format_string()."""
2851
 
        return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
2852
 
 
2853
 
    def get_format_description(self):
2854
 
        """See RepositoryFormat.get_format_description()."""
2855
 
        return "Packs 6 (uses btree indexes, requires bzr 1.9)"
2856
 
 
2857
 
 
2858
 
class RepositoryFormatKnitPack6RichRoot(RepositoryFormatPack):
2859
 
    """A repository with rich roots, no subtrees, stacking and btree indexes.
2860
 
 
2861
 
    1.6-rich-root with B+Tree indices.
2862
 
    """
2863
 
 
2864
 
    repository_class = KnitPackRepository
2865
 
    _commit_builder_class = PackRootCommitBuilder
2866
 
    rich_root_data = True
2867
 
    supports_tree_reference = False # no subtrees
2868
 
    supports_external_lookups = True
2869
 
    # What index classes to use
2870
 
    index_builder_class = btree_index.BTreeBuilder
2871
 
    index_class = btree_index.BTreeGraphIndex
2872
 
 
2873
 
    @property
2874
 
    def _serializer(self):
2875
 
        return xml6.serializer_v6
2876
 
 
2877
 
    def _get_matching_bzrdir(self):
2878
 
        return bzrdir.format_registry.make_bzrdir(
2879
 
            '1.9-rich-root')
2880
 
 
2881
 
    def _ignore_setting_bzrdir(self, format):
2882
 
        pass
2883
 
 
2884
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2885
 
 
2886
 
    def get_format_string(self):
2887
 
        """See RepositoryFormat.get_format_string()."""
2888
 
        return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
2889
 
 
2890
 
    def get_format_description(self):
2891
 
        return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
2892
 
 
2893
 
 
2894
 
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
2895
 
    """A subtrees development repository.
2896
 
 
2897
 
    This format should be retained until the second release after bzr 1.7.
2898
 
 
2899
 
    1.6.1-subtree[as it might have been] with B+Tree indices.
2900
 
 
2901
 
    This is [now] retained until we have a CHK based subtree format in
2902
 
    development.
2903
 
    """
2904
 
 
2905
 
    repository_class = KnitPackRepository
2906
 
    _commit_builder_class = PackRootCommitBuilder
2907
 
    rich_root_data = True
2908
 
    experimental = True
2909
 
    supports_tree_reference = True
2910
 
    supports_external_lookups = True
2911
 
    # What index classes to use
2912
 
    index_builder_class = btree_index.BTreeBuilder
2913
 
    index_class = btree_index.BTreeGraphIndex
2914
 
 
2915
 
    @property
2916
 
    def _serializer(self):
2917
 
        return xml7.serializer_v7
2918
 
 
2919
 
    def _get_matching_bzrdir(self):
2920
 
        return bzrdir.format_registry.make_bzrdir(
2921
 
            'development5-subtree')
2922
 
 
2923
 
    def _ignore_setting_bzrdir(self, format):
2924
 
        pass
2925
 
 
2926
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2927
 
 
2928
 
    def get_format_string(self):
2929
 
        """See RepositoryFormat.get_format_string()."""
2930
 
        return ("Bazaar development format 2 with subtree support "
2931
 
            "(needs bzr.dev from before 1.8)\n")
2932
 
 
2933
 
    def get_format_description(self):
2934
 
        """See RepositoryFormat.get_format_description()."""
2935
 
        return ("Development repository format, currently the same as "
2936
 
            "1.6.1-subtree with B+Tree indices.\n")
 
2309
class RetryPackOperations(errors.RetryWithNewPacks):
 
2310
    """Raised when we are packing and we find a missing file.
 
2311
 
 
2312
    Meant as a signaling exception, to tell the RepositoryPackCollection.pack
 
2313
    code it should try again.
 
2314
    """
 
2315
 
 
2316
    internal_error = True
 
2317
 
 
2318
    _fmt = ("Pack files have changed, reload and try pack again."
 
2319
            " context: %(context)s %(orig_error)s")
 
2320
 
 
2321
 
 
2322
class _DirectPackAccess(object):
 
2323
    """Access to data in one or more packs with less translation."""
 
2324
 
 
2325
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
 
2326
        """Create a _DirectPackAccess object.
 
2327
 
 
2328
        :param index_to_packs: A dict mapping index objects to the transport
 
2329
            and file names for obtaining data.
 
2330
        :param reload_func: A function to call if we determine that the pack
 
2331
            files have moved and we need to reload our caches. See
 
2332
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
 
2333
        """
 
2334
        self._container_writer = None
 
2335
        self._write_index = None
 
2336
        self._indices = index_to_packs
 
2337
        self._reload_func = reload_func
 
2338
        self._flush_func = flush_func
 
2339
 
 
2340
    def add_raw_records(self, key_sizes, raw_data):
 
2341
        """Add raw knit bytes to a storage area.
 
2342
 
 
2343
        The data is spooled to the container writer in one bytes-record per
 
2344
        raw data item.
 
2345
 
 
2346
        :param sizes: An iterable of tuples containing the key and size of each
 
2347
            raw data segment.
 
2348
        :param raw_data: A bytestring containing the data.
 
2349
        :return: A list of memos to retrieve the record later. Each memo is an
 
2350
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
 
2351
            length), where the index field is the write_index object supplied
 
2352
            to the PackAccess object.
 
2353
        """
 
2354
        if type(raw_data) is not str:
 
2355
            raise AssertionError(
 
2356
                'data must be plain bytes was %s' % type(raw_data))
 
2357
        result = []
 
2358
        offset = 0
 
2359
        for key, size in key_sizes:
 
2360
            p_offset, p_length = self._container_writer.add_bytes_record(
 
2361
                raw_data[offset:offset+size], [])
 
2362
            offset += size
 
2363
            result.append((self._write_index, p_offset, p_length))
 
2364
        return result
 
2365
 
 
2366
    def flush(self):
 
2367
        """Flush pending writes on this access object.
 
2368
 
 
2369
        This will flush any buffered writes to a NewPack.
 
2370
        """
 
2371
        if self._flush_func is not None:
 
2372
            self._flush_func()
 
2373
 
 
2374
    def get_raw_records(self, memos_for_retrieval):
 
2375
        """Get the raw bytes for a records.
 
2376
 
 
2377
        :param memos_for_retrieval: An iterable containing the (index, pos,
 
2378
            length) memo for retrieving the bytes. The Pack access method
 
2379
            looks up the pack to use for a given record in its index_to_pack
 
2380
            map.
 
2381
        :return: An iterator over the bytes of the records.
 
2382
        """
 
2383
        # first pass, group into same-index requests
 
2384
        request_lists = []
 
2385
        current_index = None
 
2386
        for (index, offset, length) in memos_for_retrieval:
 
2387
            if current_index == index:
 
2388
                current_list.append((offset, length))
 
2389
            else:
 
2390
                if current_index is not None:
 
2391
                    request_lists.append((current_index, current_list))
 
2392
                current_index = index
 
2393
                current_list = [(offset, length)]
 
2394
        # handle the last entry
 
2395
        if current_index is not None:
 
2396
            request_lists.append((current_index, current_list))
 
2397
        for index, offsets in request_lists:
 
2398
            try:
 
2399
                transport, path = self._indices[index]
 
2400
            except KeyError:
 
2401
                # A KeyError here indicates that someone has triggered an index
 
2402
                # reload, and this index has gone missing, we need to start
 
2403
                # over.
 
2404
                if self._reload_func is None:
 
2405
                    # If we don't have a _reload_func there is nothing that can
 
2406
                    # be done
 
2407
                    raise
 
2408
                raise errors.RetryWithNewPacks(index,
 
2409
                                               reload_occurred=True,
 
2410
                                               exc_info=sys.exc_info())
 
2411
            try:
 
2412
                reader = pack.make_readv_reader(transport, path, offsets)
 
2413
                for names, read_func in reader.iter_records():
 
2414
                    yield read_func(None)
 
2415
            except errors.NoSuchFile:
 
2416
                # A NoSuchFile error indicates that a pack file has gone
 
2417
                # missing on disk, we need to trigger a reload, and start over.
 
2418
                if self._reload_func is None:
 
2419
                    raise
 
2420
                raise errors.RetryWithNewPacks(transport.abspath(path),
 
2421
                                               reload_occurred=False,
 
2422
                                               exc_info=sys.exc_info())
 
2423
 
 
2424
    def set_writer(self, writer, index, transport_packname):
 
2425
        """Set a writer to use for adding data."""
 
2426
        if index is not None:
 
2427
            self._indices[index] = transport_packname
 
2428
        self._container_writer = writer
 
2429
        self._write_index = index
 
2430
 
 
2431
    def reload_or_raise(self, retry_exc):
 
2432
        """Try calling the reload function, or re-raise the original exception.
 
2433
 
 
2434
        This should be called after _DirectPackAccess raises a
 
2435
        RetryWithNewPacks exception. This function will handle the common logic
 
2436
        of determining when the error is fatal versus being temporary.
 
2437
        It will also make sure that the original exception is raised, rather
 
2438
        than the RetryWithNewPacks exception.
 
2439
 
 
2440
        If this function returns, then the calling function should retry
 
2441
        whatever operation was being performed. Otherwise an exception will
 
2442
        be raised.
 
2443
 
 
2444
        :param retry_exc: A RetryWithNewPacks exception.
 
2445
        """
 
2446
        is_error = False
 
2447
        if self._reload_func is None:
 
2448
            is_error = True
 
2449
        elif not self._reload_func():
 
2450
            # The reload claimed that nothing changed
 
2451
            if not retry_exc.reload_occurred:
 
2452
                # If there wasn't an earlier reload, then we really were
 
2453
                # expecting to find changes. We didn't find them, so this is a
 
2454
                # hard error
 
2455
                is_error = True
 
2456
        if is_error:
 
2457
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
 
2458
            raise exc_class, exc_value, exc_traceback
 
2459
 
 
2460
 
2937
2461