1553
1570
"""Release the mutex around the pack-names index."""
1554
1571
self.repo.control_files.unlock()
1556
def _save_pack_names(self, clear_obsolete_packs=False):
1557
"""Save the list of packs.
1559
This will take out the mutex around the pack names list for the
1560
duration of the method call. If concurrent updates have been made, a
1561
three-way merge between the current list and the current in memory list
1564
:param clear_obsolete_packs: If True, clear out the contents of the
1565
obsolete_packs directory.
1569
builder = GraphIndexBuilder()
1570
# load the disk nodes across
1572
for index, key, value in self._iter_disk_pack_index():
1573
disk_nodes.add((key, value))
1574
# do a two-way diff against our original content
1575
current_nodes = set()
1576
for name, sizes in self._names.iteritems():
1578
((name, ), ' '.join(str(size) for size in sizes)))
1579
deleted_nodes = self._packs_at_load - current_nodes
1580
new_nodes = current_nodes - self._packs_at_load
1581
disk_nodes.difference_update(deleted_nodes)
1582
disk_nodes.update(new_nodes)
1583
# TODO: handle same-name, index-size-changes here -
1584
# e.g. use the value from disk, not ours, *unless* we're the one
1586
for key, value in disk_nodes:
1587
builder.add_node(key, value)
1588
self.transport.put_file('pack-names', builder.finish(),
1589
mode=self.repo.bzrdir._get_file_mode())
1590
# move the baseline forward
1591
self._packs_at_load = disk_nodes
1592
if clear_obsolete_packs:
1593
self._clear_obsolete_packs()
1595
self._unlock_names()
1596
# synchronise the memory packs list with what we just wrote:
1573
def _diff_pack_names(self):
1574
"""Read the pack names from disk, and compare it to the one in memory.
1576
:return: (disk_nodes, deleted_nodes, new_nodes)
1577
disk_nodes The final set of nodes that should be referenced
1578
deleted_nodes Nodes which have been removed from when we started
1579
new_nodes Nodes that are newly introduced
1581
# load the disk nodes across
1583
for index, key, value in self._iter_disk_pack_index():
1584
disk_nodes.add((key, value))
1586
# do a two-way diff against our original content
1587
current_nodes = set()
1588
for name, sizes in self._names.iteritems():
1590
((name, ), ' '.join(str(size) for size in sizes)))
1592
# Packs no longer present in the repository, which were present when we
1593
# locked the repository
1594
deleted_nodes = self._packs_at_load - current_nodes
1595
# Packs which this process is adding
1596
new_nodes = current_nodes - self._packs_at_load
1598
# Update the disk_nodes set to include the ones we are adding, and
1599
# remove the ones which were removed by someone else
1600
disk_nodes.difference_update(deleted_nodes)
1601
disk_nodes.update(new_nodes)
1603
return disk_nodes, deleted_nodes, new_nodes
1605
def _syncronize_pack_names_from_disk_nodes(self, disk_nodes):
1606
"""Given the correct set of pack files, update our saved info.
1608
:return: (removed, added, modified)
1609
removed pack names removed from self._names
1610
added pack names added to self._names
1611
modified pack names that had changed value
1616
## self._packs_at_load = disk_nodes
1597
1617
new_names = dict(disk_nodes)
1598
1618
# drop no longer present nodes
1599
1619
for pack in self.all_packs():
1600
1620
if (pack.name,) not in new_names:
1621
removed.append(pack.name)
1601
1622
self._remove_pack_from_memory(pack)
1602
1623
# add new nodes/refresh existing ones
1603
1624
for key, value in disk_nodes:
1617
1638
self._remove_pack_from_memory(self.get_pack_by_name(name))
1618
1639
self._names[name] = sizes
1619
1640
self.get_pack_by_name(name)
1641
modified.append(name)
1622
1644
self._names[name] = sizes
1623
1645
self.get_pack_by_name(name)
1647
return removed, added, modified
1649
def _save_pack_names(self, clear_obsolete_packs=False):
1650
"""Save the list of packs.
1652
This will take out the mutex around the pack names list for the
1653
duration of the method call. If concurrent updates have been made, a
1654
three-way merge between the current list and the current in memory list
1657
:param clear_obsolete_packs: If True, clear out the contents of the
1658
obsolete_packs directory.
1662
builder = self._index_builder_class()
1663
disk_nodes, deleted_nodes, new_nodes = self._diff_pack_names()
1664
# TODO: handle same-name, index-size-changes here -
1665
# e.g. use the value from disk, not ours, *unless* we're the one
1667
for key, value in disk_nodes:
1668
builder.add_node(key, value)
1669
self.transport.put_file('pack-names', builder.finish(),
1670
mode=self.repo.bzrdir._get_file_mode())
1671
# move the baseline forward
1672
self._packs_at_load = disk_nodes
1673
if clear_obsolete_packs:
1674
self._clear_obsolete_packs()
1676
self._unlock_names()
1677
# synchronise the memory packs list with what we just wrote:
1678
self._syncronize_pack_names_from_disk_nodes(disk_nodes)
1680
def reload_pack_names(self):
1681
"""Sync our pack listing with what is present in the repository.
1683
This should be called when we find out that something we thought was
1684
present is now missing. This happens when another process re-packs the
1687
# This is functionally similar to _save_pack_names, but we don't write
1688
# out the new value.
1689
disk_nodes, _, _ = self._diff_pack_names()
1690
self._packs_at_load = disk_nodes
1692
modified) = self._syncronize_pack_names_from_disk_nodes(disk_nodes)
1693
if removed or added or modified:
1625
1697
def _clear_obsolete_packs(self):
1626
1698
"""Delete everything from the obsolete-packs directory.
1689
1754
self.repo._text_knit = None
1692
class KnitPackRevisionStore(KnitRevisionStore):
1693
"""An object to adapt access from RevisionStore's to use KnitPacks.
1695
This class works by replacing the original RevisionStore.
1696
We need to do this because the KnitPackRevisionStore is less
1697
isolated in its layering - it uses services from the repo.
1700
def __init__(self, repo, transport, revisionstore):
1701
"""Create a KnitPackRevisionStore on repo with revisionstore.
1703
This will store its state in the Repository, use the
1704
indices to provide a KnitGraphIndex,
1705
and at the end of transactions write new indices.
1707
KnitRevisionStore.__init__(self, revisionstore.versioned_file_store)
1709
self._serializer = revisionstore._serializer
1710
self.transport = transport
1712
def get_revision_file(self, transaction):
1713
"""Get the revision versioned file object."""
1714
if getattr(self.repo, '_revision_knit', None) is not None:
1715
return self.repo._revision_knit
1716
self.repo._pack_collection.ensure_loaded()
1717
add_callback = self.repo._pack_collection.revision_index.add_callback
1718
# setup knit specific objects
1719
knit_index = KnitGraphIndex(
1720
self.repo._pack_collection.revision_index.combined_index,
1721
add_callback=add_callback)
1722
self.repo._revision_knit = knit.KnitVersionedFile(
1723
'revisions', self.transport.clone('..'),
1724
self.repo.bzrdir._get_file_mode(),
1726
index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1727
access_method=self.repo._pack_collection.revision_index.knit_access)
1728
return self.repo._revision_knit
1730
def get_signature_file(self, transaction):
1731
"""Get the signature versioned file object."""
1732
if getattr(self.repo, '_signature_knit', None) is not None:
1733
return self.repo._signature_knit
1734
self.repo._pack_collection.ensure_loaded()
1735
add_callback = self.repo._pack_collection.signature_index.add_callback
1736
# setup knit specific objects
1737
knit_index = KnitGraphIndex(
1738
self.repo._pack_collection.signature_index.combined_index,
1739
add_callback=add_callback, parents=False)
1740
self.repo._signature_knit = knit.KnitVersionedFile(
1741
'signatures', self.transport.clone('..'),
1742
self.repo.bzrdir._get_file_mode(),
1744
index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1745
access_method=self.repo._pack_collection.signature_index.knit_access)
1746
return self.repo._signature_knit
1749
class KnitPackTextStore(VersionedFileStore):
1750
"""Presents a TextStore abstraction on top of packs.
1752
This class works by replacing the original VersionedFileStore.
1753
We need to do this because the KnitPackRevisionStore is less
1754
isolated in its layering - it uses services from the repo and shares them
1755
with all the data written in a single write group.
1758
def __init__(self, repo, transport, weavestore):
1759
"""Create a KnitPackTextStore on repo with weavestore.
1761
This will store its state in the Repository, use the
1762
indices FileNames to provide a KnitGraphIndex,
1763
and at the end of transactions write new indices.
1765
# don't call base class constructor - it's not suitable.
1766
# no transient data stored in the transaction
1768
self._precious = False
1770
self.transport = transport
1771
self.weavestore = weavestore
1772
# XXX for check() which isn't updated yet
1773
self._transport = weavestore._transport
1775
def get_weave_or_empty(self, file_id, transaction):
1776
"""Get a 'Knit' backed by the .tix indices.
1778
The transaction parameter is ignored.
1780
self.repo._pack_collection.ensure_loaded()
1781
add_callback = self.repo._pack_collection.text_index.add_callback
1782
# setup knit specific objects
1783
file_id_index = GraphIndexPrefixAdapter(
1784
self.repo._pack_collection.text_index.combined_index,
1785
(file_id, ), 1, add_nodes_callback=add_callback)
1786
knit_index = KnitGraphIndex(file_id_index,
1787
add_callback=file_id_index.add_nodes,
1788
deltas=True, parents=True)
1789
return knit.KnitVersionedFile('text:' + file_id,
1790
self.transport.clone('..'),
1793
access_method=self.repo._pack_collection.text_index.knit_access,
1794
factory=knit.KnitPlainFactory())
1796
get_weave = get_weave_or_empty
1799
"""Generate a list of the fileids inserted, for use by check."""
1800
self.repo._pack_collection.ensure_loaded()
1802
for index, key, value, refs in \
1803
self.repo._pack_collection.text_index.combined_index.iter_all_entries():
1808
class InventoryKnitThunk(object):
1809
"""An object to manage thunking get_inventory_weave to pack based knits."""
1811
def __init__(self, repo, transport):
1812
"""Create an InventoryKnitThunk for repo at transport.
1814
This will store its state in the Repository, use the
1815
indices FileNames to provide a KnitGraphIndex,
1816
and at the end of transactions write a new index..
1819
self.transport = transport
1821
def get_weave(self):
1822
"""Get a 'Knit' that contains inventory data."""
1823
self.repo._pack_collection.ensure_loaded()
1824
add_callback = self.repo._pack_collection.inventory_index.add_callback
1825
# setup knit specific objects
1826
knit_index = KnitGraphIndex(
1827
self.repo._pack_collection.inventory_index.combined_index,
1828
add_callback=add_callback, deltas=True, parents=True)
1829
return knit.KnitVersionedFile(
1830
'inventory', self.transport.clone('..'),
1831
self.repo.bzrdir._get_file_mode(),
1833
index=knit_index, delta=True, factory=knit.KnitPlainFactory(),
1834
access_method=self.repo._pack_collection.inventory_index.knit_access)
1837
1757
class KnitPackRepository(KnitRepository):
1838
"""Repository with knit objects stored inside pack containers."""
1840
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1841
control_store, text_store, _commit_builder_class, _serializer):
1758
"""Repository with knit objects stored inside pack containers.
1760
The layering for a KnitPackRepository is:
1762
Graph | HPSS | Repository public layer |
1763
===================================================
1764
Tuple based apis below, string based, and key based apis above
1765
---------------------------------------------------
1767
Provides .texts, .revisions etc
1768
This adapts the N-tuple keys to physical knit records which only have a
1769
single string identifier (for historical reasons), which in older formats
1770
was always the revision_id, and in the mapped code for packs is always
1771
the last element of key tuples.
1772
---------------------------------------------------
1774
A separate GraphIndex is used for each of the
1775
texts/inventories/revisions/signatures contained within each individual
1776
pack file. The GraphIndex layer works in N-tuples and is unaware of any
1778
===================================================
1782
def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
1842
1784
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1843
_revision_store, control_store, text_store, _commit_builder_class,
1785
_commit_builder_class, _serializer)
1845
1786
index_transport = self._transport.clone('indices')
1846
self._pack_collection = RepositoryPackCollection(self,
1787
self._pack_collection = RepositoryPackCollection(self, self._transport,
1848
1788
index_transport,
1849
1789
self._transport.clone('upload'),
1850
self._transport.clone('packs'))
1851
self._revision_store = KnitPackRevisionStore(self, index_transport, self._revision_store)
1852
self.weave_store = KnitPackTextStore(self, index_transport, self.weave_store)
1853
self._inv_thunk = InventoryKnitThunk(self, index_transport)
1790
self._transport.clone('packs'),
1791
_format.index_builder_class,
1792
_format.index_class)
1793
self.inventories = KnitVersionedFiles(
1794
_KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
1795
add_callback=self._pack_collection.inventory_index.add_callback,
1796
deltas=True, parents=True, is_locked=self.is_locked),
1797
data_access=self._pack_collection.inventory_index.data_access,
1798
max_delta_chain=200)
1799
self.revisions = KnitVersionedFiles(
1800
_KnitGraphIndex(self._pack_collection.revision_index.combined_index,
1801
add_callback=self._pack_collection.revision_index.add_callback,
1802
deltas=False, parents=True, is_locked=self.is_locked),
1803
data_access=self._pack_collection.revision_index.data_access,
1805
self.signatures = KnitVersionedFiles(
1806
_KnitGraphIndex(self._pack_collection.signature_index.combined_index,
1807
add_callback=self._pack_collection.signature_index.add_callback,
1808
deltas=False, parents=False, is_locked=self.is_locked),
1809
data_access=self._pack_collection.signature_index.data_access,
1811
self.texts = KnitVersionedFiles(
1812
_KnitGraphIndex(self._pack_collection.text_index.combined_index,
1813
add_callback=self._pack_collection.text_index.add_callback,
1814
deltas=True, parents=True, is_locked=self.is_locked),
1815
data_access=self._pack_collection.text_index.data_access,
1816
max_delta_chain=200)
1854
1817
# True when the repository object is 'write locked' (as opposed to the
1855
1818
# physical lock only taken out around changes to the pack-names list.)
1856
1819
# Another way to represent this would be a decorator around the control
2264
2182
return "Packs containing knits with rich root support\n"
2267
class RepositoryFormatPackDevelopment0(RepositoryFormatPack):
2185
class RepositoryFormatKnitPack5(RepositoryFormatPack):
2186
"""Repository that supports external references to allow stacking.
2190
Supports external lookups, which results in non-truncated ghosts after
2191
reconcile compared to pack-0.92 formats.
2194
repository_class = KnitPackRepository
2195
_commit_builder_class = PackCommitBuilder
2196
supports_external_lookups = True
2197
# What index classes to use
2198
index_builder_class = InMemoryGraphIndex
2199
index_class = GraphIndex
2202
def _serializer(self):
2203
return xml5.serializer_v5
2205
def _get_matching_bzrdir(self):
2206
return bzrdir.format_registry.make_bzrdir('1.6')
2208
def _ignore_setting_bzrdir(self, format):
2211
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2213
def get_format_string(self):
2214
"""See RepositoryFormat.get_format_string()."""
2215
return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
2217
def get_format_description(self):
2218
"""See RepositoryFormat.get_format_description()."""
2219
return "Packs 5 (adds stacking support, requires bzr 1.6)"
2221
def check_conversion_target(self, target_format):
2225
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
2226
"""A repository with rich roots and stacking.
2228
New in release 1.6.1.
2230
Supports stacking on other repositories, allowing data to be accessed
2231
without being stored locally.
2234
repository_class = KnitPackRepository
2235
_commit_builder_class = PackRootCommitBuilder
2236
rich_root_data = True
2237
supports_tree_reference = False # no subtrees
2238
supports_external_lookups = True
2239
# What index classes to use
2240
index_builder_class = InMemoryGraphIndex
2241
index_class = GraphIndex
2244
def _serializer(self):
2245
return xml6.serializer_v6
2247
def _get_matching_bzrdir(self):
2248
return bzrdir.format_registry.make_bzrdir(
2251
def _ignore_setting_bzrdir(self, format):
2254
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2256
def check_conversion_target(self, target_format):
2257
if not target_format.rich_root_data:
2258
raise errors.BadConversionTarget(
2259
'Does not support rich root data.', target_format)
2261
def get_format_string(self):
2262
"""See RepositoryFormat.get_format_string()."""
2263
return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
2265
def get_format_description(self):
2266
return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
2269
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
2270
"""A repository with rich roots and external references.
2274
Supports external lookups, which results in non-truncated ghosts after
2275
reconcile compared to pack-0.92 formats.
2277
This format was deprecated because the serializer it uses accidentally
2278
supported subtrees, when the format was not intended to. This meant that
2279
someone could accidentally fetch from an incorrect repository.
2282
repository_class = KnitPackRepository
2283
_commit_builder_class = PackRootCommitBuilder
2284
rich_root_data = True
2285
supports_tree_reference = False # no subtrees
2287
supports_external_lookups = True
2288
# What index classes to use
2289
index_builder_class = InMemoryGraphIndex
2290
index_class = GraphIndex
2293
def _serializer(self):
2294
return xml7.serializer_v7
2296
def _get_matching_bzrdir(self):
2297
return bzrdir.format_registry.make_bzrdir(
2300
def _ignore_setting_bzrdir(self, format):
2303
_matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2305
def check_conversion_target(self, target_format):
2306
if not target_format.rich_root_data:
2307
raise errors.BadConversionTarget(
2308
'Does not support rich root data.', target_format)
2310
def get_format_string(self):
2311
"""See RepositoryFormat.get_format_string()."""
2312
return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
2314
def get_format_description(self):
2315
return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
2319
class RepositoryFormatPackDevelopment2(RepositoryFormatPack):
2268
2320
"""A no-subtrees development repository.
2270
This format should be retained until the second release after bzr 1.0.
2322
This format should be retained until the second release after bzr 1.7.
2272
No changes to the disk behaviour from pack-0.92.
2324
This is pack-1.6.1 with B+Tree indices.
2275
2327
repository_class = KnitPackRepository
2276
2328
_commit_builder_class = PackCommitBuilder
2277
_serializer = xml5.serializer_v5
2329
supports_external_lookups = True
2330
# What index classes to use
2331
index_builder_class = BTreeBuilder
2332
index_class = BTreeGraphIndex
2335
def _serializer(self):
2336
return xml5.serializer_v5
2279
2338
def _get_matching_bzrdir(self):
2280
return bzrdir.format_registry.make_bzrdir('development0')
2339
return bzrdir.format_registry.make_bzrdir('development2')
2282
2341
def _ignore_setting_bzrdir(self, format):