1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
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
63
62
MetaDirRepositoryFormat,
66
66
import bzrlib.revision as _mod_revision
67
67
from bzrlib.store.revision.knit import KnitRevisionStore
68
68
from bzrlib.store.versioned import VersionedFileStore
69
from bzrlib.trace import mutter, note, warning
69
from bzrlib.trace import (
72
77
class PackCommitBuilder(CommitBuilder):
200
205
self.pack_transport = pack_transport
201
assert None not in (revision_index, inventory_index, text_index,
202
signature_index, name, pack_transport)
206
if None in (revision_index, inventory_index, text_index,
207
signature_index, name, pack_transport):
208
raise AssertionError()
204
210
def __eq__(self, other):
205
211
return self.__dict__ == other.__dict__
323
329
def access_tuple(self):
324
330
"""Return a tuple (transport, name) for the pack content."""
325
assert self._state in ('open', 'finished')
326
331
if self._state == 'finished':
327
332
return Pack.access_tuple(self)
333
elif self._state == 'open':
329
334
return self.upload_transport, self.random_name
336
raise AssertionError(self._state)
331
338
def data_inserted(self):
332
339
"""True if data has been added to this pack."""
495
502
:param index: An index from the pack parameter.
496
503
:param pack: A Pack instance.
498
assert self.add_callback is None, \
499
"%s already has a writable index through %s" % \
500
(self, self.add_callback)
505
if self.add_callback is not None:
506
raise AssertionError(
507
"%s already has a writable index through %s" % \
508
(self, self.add_callback))
501
509
# allow writing: queue writes to a new index
502
510
self.add_index(index, pack)
503
511
# Updates the index to packs mapping as a side effect,
596
604
return NewPack(self._pack_collection._upload_transport,
597
605
self._pack_collection._index_transport,
598
606
self._pack_collection._pack_transport, upload_suffix=self.suffix,
599
file_mode=self._pack_collection.repo.control_files._file_mode)
607
file_mode=self._pack_collection.repo.bzrdir._get_file_mode())
601
609
def _copy_revision_texts(self):
602
610
"""Copy revision data to the new pack."""
1150
1158
:param pack: A Pack object.
1152
assert pack.name not in self._packs_by_name
1160
if pack.name in self._packs_by_name:
1161
raise AssertionError()
1153
1162
self.packs.append(pack)
1154
1163
self._packs_by_name[pack.name] = pack
1155
1164
self.revision_index.add_index(pack.revision_index, pack)
1577
1586
for key, value in disk_nodes:
1578
1587
builder.add_node(key, value)
1579
1588
self.transport.put_file('pack-names', builder.finish(),
1580
mode=self.repo.control_files._file_mode)
1589
mode=self.repo.bzrdir._get_file_mode())
1581
1590
# move the baseline forward
1582
1591
self._packs_at_load = disk_nodes
1583
# now clear out the obsolete packs directory
1584
1592
if clear_obsolete_packs:
1585
self.transport.clone('obsolete_packs').delete_multi(
1586
self.transport.list_dir('obsolete_packs'))
1593
self._clear_obsolete_packs()
1588
1595
self._unlock_names()
1589
1596
# synchronise the memory packs list with what we just wrote:
1615
1622
self._names[name] = sizes
1616
1623
self.get_pack_by_name(name)
1625
def _clear_obsolete_packs(self):
1626
"""Delete everything from the obsolete-packs directory.
1628
obsolete_pack_transport = self.transport.clone('obsolete_packs')
1629
for filename in obsolete_pack_transport.list_dir('.'):
1631
obsolete_pack_transport.delete(filename)
1632
except (errors.PathError, errors.TransportError), e:
1633
warning("couldn't delete obsolete pack, skipping it:\n%s" % (e,))
1618
1635
def _start_write_group(self):
1619
1636
# Do not permit preparation for writing if we're not in a 'write lock'.
1620
1637
if not self.repo.is_write_locked():
1621
1638
raise errors.NotWriteLocked(self)
1622
1639
self._new_pack = NewPack(self._upload_transport, self._index_transport,
1623
1640
self._pack_transport, upload_suffix='.pack',
1624
file_mode=self.repo.control_files._file_mode)
1641
file_mode=self.repo.bzrdir._get_file_mode())
1625
1642
# allow writing: queue writes to a new index
1626
1643
self.revision_index.add_writable_index(self._new_pack.revision_index,
1627
1644
self._new_pack)
1704
1721
add_callback=add_callback)
1705
1722
self.repo._revision_knit = knit.KnitVersionedFile(
1706
1723
'revisions', self.transport.clone('..'),
1707
self.repo.control_files._file_mode,
1724
self.repo.bzrdir._get_file_mode(),
1709
1726
index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1710
1727
access_method=self.repo._pack_collection.revision_index.knit_access)
1722
1739
add_callback=add_callback, parents=False)
1723
1740
self.repo._signature_knit = knit.KnitVersionedFile(
1724
1741
'signatures', self.transport.clone('..'),
1725
self.repo.control_files._file_mode,
1742
self.repo.bzrdir._get_file_mode(),
1727
1744
index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1728
1745
access_method=self.repo._pack_collection.signature_index.knit_access)
1811
1828
add_callback=add_callback, deltas=True, parents=True)
1812
1829
return knit.KnitVersionedFile(
1813
1830
'inventory', self.transport.clone('..'),
1814
self.repo.control_files._file_mode,
1831
self.repo.bzrdir._get_file_mode(),
1816
1833
index=knit_index, delta=True, factory=knit.KnitPlainFactory(),
1817
1834
access_method=self.repo._pack_collection.inventory_index.knit_access)
1820
1837
class KnitPackRepository(KnitRepository):
1821
"""Experimental graph-knit using repository."""
1838
"""Repository with knit objects stored inside pack containers."""
1823
1840
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1824
1841
control_store, text_store, _commit_builder_class, _serializer):
1825
1842
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1826
1843
_revision_store, control_store, text_store, _commit_builder_class,
1828
index_transport = control_files._transport.clone('indices')
1829
self._pack_collection = RepositoryPackCollection(self, control_files._transport,
1845
index_transport = self._transport.clone('indices')
1846
self._pack_collection = RepositoryPackCollection(self,
1830
1848
index_transport,
1831
control_files._transport.clone('upload'),
1832
control_files._transport.clone('packs'))
1849
self._transport.clone('upload'),
1850
self._transport.clone('packs'))
1833
1851
self._revision_store = KnitPackRevisionStore(self, index_transport, self._revision_store)
1834
1852
self.weave_store = KnitPackTextStore(self, index_transport, self.weave_store)
1835
1853
self._inv_thunk = InventoryKnitThunk(self, index_transport)
1903
1921
self._pack_collection.ensure_loaded()
1904
1922
index = self._pack_collection.revision_index.combined_index
1905
1923
keys = set(keys)
1925
raise ValueError('get_parent_map(None) is not valid')
1906
1926
if _mod_revision.NULL_REVISION in keys:
1907
1927
keys.discard(_mod_revision.NULL_REVISION)
1908
1928
found_parents = {_mod_revision.NULL_REVISION:()}
1918
1938
found_parents[key[0]] = parents
1919
1939
return found_parents
1921
@symbol_versioning.deprecated_method(symbol_versioning.one_four)
1923
def get_revision_graph(self, revision_id=None):
1924
"""Return a dictionary containing the revision graph.
1926
:param revision_id: The revision_id to get a graph from. If None, then
1927
the entire revision graph is returned. This is a deprecated mode of
1928
operation and will be removed in the future.
1929
:return: a dictionary of revision_id->revision_parents_list.
1931
if 'evil' in debug.debug_flags:
1933
"get_revision_graph scales with size of history.")
1934
# special case NULL_REVISION
1935
if revision_id == _mod_revision.NULL_REVISION:
1937
if revision_id is None:
1938
revision_vf = self._get_revision_vf()
1939
return revision_vf.get_graph()
1940
g = self.get_graph()
1941
first = g.get_parent_map([revision_id])
1942
if revision_id not in first:
1943
raise errors.NoSuchRevision(self, revision_id)
1947
NULL_REVISION = _mod_revision.NULL_REVISION
1948
ghosts = set([NULL_REVISION])
1949
for rev_id, parent_ids in g.iter_ancestry([revision_id]):
1950
if parent_ids is None: # This is a ghost
1953
ancestry[rev_id] = parent_ids
1954
for p in parent_ids:
1956
children[p].append(rev_id)
1958
children[p] = [rev_id]
1960
if NULL_REVISION in ancestry:
1961
del ancestry[NULL_REVISION]
1963
# Find all nodes that reference a ghost, and filter the ghosts out
1964
# of their parent lists. To preserve the order of parents, and
1965
# avoid double filtering nodes, we just find all children first,
1967
children_of_ghosts = set()
1968
for ghost in ghosts:
1969
children_of_ghosts.update(children[ghost])
1971
for child in children_of_ghosts:
1972
ancestry[child] = tuple(p for p in ancestry[child]
1976
1941
def has_revisions(self, revision_ids):
1977
1942
"""See Repository.has_revisions()."""
1978
1943
revision_ids = set(revision_ids)
2171
2136
format = RepositoryFormat.find_format(a_bzrdir)
2172
assert format.__class__ == self.__class__
2173
2137
if _override_transport is not None:
2174
2138
repo_transport = _override_transport
2176
2140
repo_transport = a_bzrdir.get_repository_transport(None)
2177
2141
control_files = lockable_files.LockableFiles(repo_transport,
2178
'lock', lockdir.LockDir)
2142
'lock', lockdir.LockDir)
2179
2143
text_store = self._get_text_store(repo_transport, control_files)
2180
2144
control_store = self._get_control_store(repo_transport, control_files)
2181
2145
_revision_store = self._get_revision_store(repo_transport, control_files)