14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from cStringIO import StringIO
19
from bzrlib.lazy_import import lazy_import
20
lazy_import(globals(), """
17
21
from binascii import hexlify
18
22
from copy import deepcopy
19
from cStringIO import StringIO
22
from unittest import TestSuite
24
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
38
revision as _mod_revision,
47
from bzrlib.osutils import (
52
from bzrlib.revisiontree import RevisionTree
53
from bzrlib.store.versioned import VersionedFileStore
54
from bzrlib.store.text import TextStore
55
from bzrlib.testament import Testament
25
58
from bzrlib.decorators import needs_read_lock, needs_write_lock
26
from bzrlib.errors import InvalidRevisionId
27
from bzrlib.graph import Graph
28
59
from bzrlib.inter import InterObject
29
60
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
30
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
31
from bzrlib.lockable_files import LockableFiles, TransportLock
32
from bzrlib.lockdir import LockDir
33
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date,
35
from bzrlib.revision import NULL_REVISION, Revision
36
from bzrlib.revisiontree import RevisionTree
37
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
38
from bzrlib.store.text import TextStore
39
from bzrlib import symbol_versioning
40
from bzrlib.symbol_versioning import (deprecated_method,
61
from bzrlib.symbol_versioning import (
43
from bzrlib.testament import Testament
44
65
from bzrlib.trace import mutter, note, warning
45
from bzrlib.tsort import topo_sort
46
from bzrlib.weave import WeaveFile
49
68
# Old formats display a warning, but only once
417
435
# revisions. We don't need to see all lines in the inventory because
418
436
# only those added in an inventory in rev X can contain a revision=X
420
for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
421
start = line.find('file_id="')+9
422
if start < 9: continue
423
end = line.find('"', start)
425
file_id = _unescape_xml(line[start:end])
438
pb = ui.ui_factory.nested_progress_bar()
440
for line in w.iter_lines_added_or_present_in_versions(
441
selected_revision_ids, pb=pb):
442
start = line.find('file_id="')+9
443
if start < 9: continue
444
end = line.find('"', start)
446
file_id = _unescape_xml(line[start:end])
427
start = line.find('revision="')+10
428
if start < 10: continue
429
end = line.find('"', start)
431
revision_id = _unescape_xml(line[start:end])
432
if revision_id in selected_revision_ids:
433
result.setdefault(file_id, set()).add(revision_id)
448
start = line.find('revision="')+10
449
if start < 10: continue
450
end = line.find('"', start)
452
revision_id = _unescape_xml(line[start:end])
453
if revision_id in selected_revision_ids:
454
result.setdefault(file_id, set()).add(revision_id)
578
605
# TODO: refactor this to use an existing revision object
579
606
# so we don't need to read it in twice.
580
if revision_id is None or revision_id == NULL_REVISION:
581
return RevisionTree(self, Inventory(), NULL_REVISION)
607
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
608
return RevisionTree(self, Inventory(root_id=None),
609
_mod_revision.NULL_REVISION)
583
611
inv = self.get_revision_inventory(revision_id)
584
612
return RevisionTree(self, inv, revision_id)
765
796
parent_trees[p_id] = repository.revision_tree(None)
767
798
inv = revision_tree.inventory
799
entries = inv.iter_entries()
769
800
# backwards compatability hack: skip the root id.
770
entries = inv.iter_entries()
801
if not repository.supports_rich_root():
802
path, root = entries.next()
803
if root.revision != rev.revision_id:
804
raise errors.IncompatibleRevision(repr(repository))
772
805
# Add the texts that are not already present
773
806
for path, ie in entries:
774
807
w = repository.weave_store.get_weave_or_empty(ie.file_id,
911
944
:return: a dictionary of revision_id->revision_parents_list.
913
946
# special case NULL_REVISION
914
if revision_id == NULL_REVISION:
947
if revision_id == _mod_revision.NULL_REVISION:
916
weave = self._get_revision_vf()
917
entire_graph = weave.get_graph()
949
a_weave = self._get_revision_vf()
950
entire_graph = a_weave.get_graph()
918
951
if revision_id is None:
919
return weave.get_graph()
920
elif revision_id not in weave:
952
return a_weave.get_graph()
953
elif revision_id not in a_weave:
921
954
raise errors.NoSuchRevision(self, revision_id)
923
956
# add what can be reached from revision_id
989
1022
return self._get_revision_vf().get_parents(revision_id)
1025
class KnitRepository2(KnitRepository):
1027
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1028
control_store, text_store):
1029
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1030
_revision_store, control_store, text_store)
1031
self._serializer = xml6.serializer_v6
1033
def deserialise_inventory(self, revision_id, xml):
1034
"""Transform the xml into an inventory object.
1036
:param revision_id: The expected revision id of the inventory.
1037
:param xml: A serialised inventory.
1039
result = self._serializer.read_inventory_from_string(xml)
1040
assert result.root.revision is not None
1043
def serialise_inventory(self, inv):
1044
"""Transform the inventory object into XML text.
1046
:param revision_id: The expected revision id of the inventory.
1047
:param xml: A serialised inventory.
1049
assert inv.revision_id is not None
1050
assert inv.root.revision is not None
1051
return KnitRepository.serialise_inventory(self, inv)
1053
def get_commit_builder(self, branch, parents, config, timestamp=None,
1054
timezone=None, committer=None, revprops=None,
1056
"""Obtain a CommitBuilder for this repository.
1058
:param branch: Branch to commit to.
1059
:param parents: Revision ids of the parents of the new revision.
1060
:param config: Configuration to use.
1061
:param timestamp: Optional timestamp recorded for commit.
1062
:param timezone: Optional timezone for timestamp.
1063
:param committer: Optional committer to set for commit.
1064
:param revprops: Optional dictionary of revision properties.
1065
:param revision_id: Optional revision id.
1067
return RootCommitBuilder(self, parents, config, timestamp, timezone,
1068
committer, revprops, revision_id)
992
1071
class RepositoryFormat(object):
993
1072
"""A repository format.
1141
1225
class PreSplitOutRepositoryFormat(RepositoryFormat):
1142
1226
"""Base class for the pre split out repository formats."""
1228
rich_root_data = False
1144
1230
def initialize(self, a_bzrdir, shared=False, _internal=False):
1145
1231
"""Create a weave repository.
1147
1233
TODO: when creating split out bzr branch formats, move this to a common
1148
1234
base for Format5, Format6. or something like that.
1150
from bzrlib.weavefile import write_weave_v5
1151
from bzrlib.weave import Weave
1154
1237
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1469
1557
repo_transport,
1470
1558
prefixed=False,
1471
1559
file_mode=control_files._file_mode,
1472
versionedfile_class=KnitVersionedFile,
1473
versionedfile_kwargs={'factory':KnitPlainFactory()},
1560
versionedfile_class=knit.KnitVersionedFile,
1561
versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1476
def get_format_string(self):
1477
"""See RepositoryFormat.get_format_string()."""
1478
return "Bazaar-NG Knit Repository Format 1"
1480
def get_format_description(self):
1481
"""See RepositoryFormat.get_format_description()."""
1482
return "Knit repository format 1"
1484
1564
def _get_revision_store(self, repo_transport, control_files):
1485
1565
"""See RepositoryFormat._get_revision_store()."""
1486
1566
from bzrlib.store.revision.knit import KnitRevisionStore
1498
1580
def _get_text_store(self, transport, control_files):
1499
1581
"""See RepositoryFormat._get_text_store()."""
1500
1582
return self._get_versioned_file_store('knits',
1503
versionedfile_class=KnitVersionedFile,
1585
versionedfile_class=knit.KnitVersionedFile,
1586
versionedfile_kwargs={
1587
'create_parent_dir':True,
1588
'delay_create':True,
1589
'dir_mode':control_files._dir_mode,
1506
1593
def initialize(self, a_bzrdir, shared=False):
1507
1594
"""Create a knit format 1 repository.
1554
1643
text_store=text_store)
1646
class RepositoryFormatKnit1(RepositoryFormatKnit):
1647
"""Bzr repository knit format 1.
1649
This repository format has:
1650
- knits for file texts and inventory
1651
- hash subdirectory based stores.
1652
- knits for revisions and signatures
1653
- TextStores for revisions and signatures.
1654
- a format marker of its own
1655
- an optional 'shared-storage' flag
1656
- an optional 'no-working-trees' flag
1659
This format was introduced in bzr 0.8.
1661
def get_format_string(self):
1662
"""See RepositoryFormat.get_format_string()."""
1663
return "Bazaar-NG Knit Repository Format 1"
1665
def get_format_description(self):
1666
"""See RepositoryFormat.get_format_description()."""
1667
return "Knit repository format 1"
1669
def check_conversion_target(self, target_format):
1673
class RepositoryFormatKnit2(RepositoryFormatKnit):
1674
"""Bzr repository knit format 2.
1676
THIS FORMAT IS EXPERIMENTAL
1677
This repository format has:
1678
- knits for file texts and inventory
1679
- hash subdirectory based stores.
1680
- knits for revisions and signatures
1681
- TextStores for revisions and signatures.
1682
- a format marker of its own
1683
- an optional 'shared-storage' flag
1684
- an optional 'no-working-trees' flag
1686
- Support for recording full info about the tree root
1690
rich_root_data = True
1692
def get_format_string(self):
1693
"""See RepositoryFormat.get_format_string()."""
1694
return "Bazaar Knit Repository Format 2\n"
1696
def get_format_description(self):
1697
"""See RepositoryFormat.get_format_description()."""
1698
return "Knit repository format 2"
1700
def check_conversion_target(self, target_format):
1701
if not target_format.rich_root_data:
1702
raise errors.BadConversionTarget(
1703
'Does not support rich root data.', target_format)
1705
def open(self, a_bzrdir, _found=False, _override_transport=None):
1706
"""See RepositoryFormat.open().
1708
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1709
repository at a slightly different url
1710
than normal. I.e. during 'upgrade'.
1713
format = RepositoryFormat.find_format(a_bzrdir)
1714
assert format.__class__ == self.__class__
1715
if _override_transport is not None:
1716
repo_transport = _override_transport
1718
repo_transport = a_bzrdir.get_repository_transport(None)
1719
control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1721
text_store = self._get_text_store(repo_transport, control_files)
1722
control_store = self._get_control_store(repo_transport, control_files)
1723
_revision_store = self._get_revision_store(repo_transport, control_files)
1724
return KnitRepository2(_format=self,
1726
control_files=control_files,
1727
_revision_store=_revision_store,
1728
control_store=control_store,
1729
text_store=text_store)
1557
1733
# formats which have no format string are not discoverable
1558
1734
# and not independently creatable, so are not registered.
1559
1735
RepositoryFormat.register_format(RepositoryFormat7())
1560
1736
_default_format = RepositoryFormatKnit1()
1561
1737
RepositoryFormat.register_format(_default_format)
1738
RepositoryFormat.register_format(RepositoryFormatKnit2())
1562
1739
RepositoryFormat.set_default_format(_default_format)
1563
1740
_legacy_formats = [RepositoryFormat4(),
1564
1741
RepositoryFormat5(),
1577
1754
InterRepository.get(other).method_name(parameters).
1581
1758
"""The available optimised InterRepository types."""
1584
1760
def copy_content(self, revision_id=None, basis=None):
1585
"""Make a complete copy of the content in self into destination.
1587
This is a destructive operation! Do not use it on existing
1590
:param revision_id: Only copy the content needed to construct
1591
revision_id and its parents.
1592
:param basis: Copy the needed data preferentially from basis.
1595
self.target.set_make_working_trees(self.source.make_working_trees())
1596
except NotImplementedError:
1598
# grab the basis available data
1599
if basis is not None:
1600
self.target.fetch(basis, revision_id=revision_id)
1601
# but don't bother fetching if we have the needed data now.
1602
if (revision_id not in (None, NULL_REVISION) and
1603
self.target.has_revision(revision_id)):
1605
self.target.fetch(self.source, revision_id=revision_id)
1761
raise NotImplementedError(self.copy_content)
1608
1763
def fetch(self, revision_id=None, pb=None):
1609
1764
"""Fetch the content required to construct revision_id.
1611
The content is copied from source to target.
1766
The content is copied from self.source to self.target.
1613
1768
:param revision_id: if None all content is copied, if NULL_REVISION no
1614
1769
content is copied.
1618
1773
Returns the copied revision count and the failed revisions in a tuple:
1619
1774
(copied, failures).
1621
from bzrlib.fetch import GenericRepoFetcher
1622
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1623
self.source, self.source._format, self.target, self.target._format)
1624
f = GenericRepoFetcher(to_repository=self.target,
1625
from_repository=self.source,
1626
last_revision=revision_id,
1628
return f.count_copied, f.failed_revisions
1776
raise NotImplementedError(self.fetch)
1630
1778
@needs_read_lock
1631
1779
def missing_revision_ids(self, revision_id=None):
1632
1780
"""Return the revision ids that source has that target does not.
1651
1799
return [rev_id for rev_id in source_ids if rev_id in result_set]
1654
class InterWeaveRepo(InterRepository):
1802
class InterSameDataRepository(InterRepository):
1803
"""Code for converting between repositories that represent the same data.
1805
Data format and model must match for this to work.
1808
_matching_repo_format = RepositoryFormat4()
1809
"""Repository format for testing with."""
1812
def is_compatible(source, target):
1813
if not isinstance(source, Repository):
1815
if not isinstance(target, Repository):
1817
if source._format.rich_root_data == target._format.rich_root_data:
1823
def copy_content(self, revision_id=None, basis=None):
1824
"""Make a complete copy of the content in self into destination.
1826
This is a destructive operation! Do not use it on existing
1829
:param revision_id: Only copy the content needed to construct
1830
revision_id and its parents.
1831
:param basis: Copy the needed data preferentially from basis.
1834
self.target.set_make_working_trees(self.source.make_working_trees())
1835
except NotImplementedError:
1837
# grab the basis available data
1838
if basis is not None:
1839
self.target.fetch(basis, revision_id=revision_id)
1840
# but don't bother fetching if we have the needed data now.
1841
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
1842
self.target.has_revision(revision_id)):
1844
self.target.fetch(self.source, revision_id=revision_id)
1847
def fetch(self, revision_id=None, pb=None):
1848
"""See InterRepository.fetch()."""
1849
from bzrlib.fetch import GenericRepoFetcher
1850
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1851
self.source, self.source._format, self.target,
1852
self.target._format)
1853
f = GenericRepoFetcher(to_repository=self.target,
1854
from_repository=self.source,
1855
last_revision=revision_id,
1857
return f.count_copied, f.failed_revisions
1860
class InterWeaveRepo(InterSameDataRepository):
1655
1861
"""Optimised code paths between Weave based repositories."""
1657
1863
_matching_repo_format = RepositoryFormat7()
1831
2037
# that against the revision records.
1832
2038
return self.source._eliminate_revisions_not_present(required_topo_revisions)
2041
class InterModel1and2(InterRepository):
2043
_matching_repo_format = None
2046
def is_compatible(source, target):
2047
if not isinstance(source, Repository):
2049
if not isinstance(target, Repository):
2051
if not source._format.rich_root_data and target._format.rich_root_data:
2057
def fetch(self, revision_id=None, pb=None):
2058
"""See InterRepository.fetch()."""
2059
from bzrlib.fetch import Model1toKnit2Fetcher
2060
f = Model1toKnit2Fetcher(to_repository=self.target,
2061
from_repository=self.source,
2062
last_revision=revision_id,
2064
return f.count_copied, f.failed_revisions
2067
def copy_content(self, revision_id=None, basis=None):
2068
"""Make a complete copy of the content in self into destination.
2070
This is a destructive operation! Do not use it on existing
2073
:param revision_id: Only copy the content needed to construct
2074
revision_id and its parents.
2075
:param basis: Copy the needed data preferentially from basis.
2078
self.target.set_make_working_trees(self.source.make_working_trees())
2079
except NotImplementedError:
2081
# grab the basis available data
2082
if basis is not None:
2083
self.target.fetch(basis, revision_id=revision_id)
2084
# but don't bother fetching if we have the needed data now.
2085
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
2086
self.target.has_revision(revision_id)):
2088
self.target.fetch(self.source, revision_id=revision_id)
2091
class InterKnit1and2(InterKnitRepo):
2093
_matching_repo_format = None
2096
def is_compatible(source, target):
2097
"""Be compatible with Knit1 source and Knit2 target"""
2099
return (isinstance(source._format, (RepositoryFormatKnit1)) and
2100
isinstance(target._format, (RepositoryFormatKnit2)))
2101
except AttributeError:
2105
def fetch(self, revision_id=None, pb=None):
2106
"""See InterRepository.fetch()."""
2107
from bzrlib.fetch import Knit1to2Fetcher
2108
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2109
self.source, self.source._format, self.target,
2110
self.target._format)
2111
f = Knit1to2Fetcher(to_repository=self.target,
2112
from_repository=self.source,
2113
last_revision=revision_id,
2115
return f.count_copied, f.failed_revisions
2118
InterRepository.register_optimiser(InterSameDataRepository)
1834
2119
InterRepository.register_optimiser(InterWeaveRepo)
1835
2120
InterRepository.register_optimiser(InterKnitRepo)
2121
InterRepository.register_optimiser(InterModel1and2)
2122
InterRepository.register_optimiser(InterKnit1and2)
1838
2125
class RepositoryTestProviderAdapter(object):
1903
2190
# default format.
1904
2191
# XXX: robertc 20060220 reinstate this when there are two supported
1905
2192
# formats which do not have an optimal code path between them.
1906
result.append((InterRepository,
1907
RepositoryFormat6(),
1908
RepositoryFormatKnit1()))
2193
#result.append((InterRepository,
2194
# RepositoryFormat6(),
2195
# RepositoryFormatKnit1()))
1909
2196
for optimiser in InterRepository._optimisers:
1910
result.append((optimiser,
1911
optimiser._matching_repo_format,
1912
optimiser._matching_repo_format
2197
if optimiser._matching_repo_format is not None:
2198
result.append((optimiser,
2199
optimiser._matching_repo_format,
2200
optimiser._matching_repo_format
1914
2202
# if there are specific combinations we want to use, we can add them
2204
result.append((InterModel1and2, RepositoryFormat5(),
2205
RepositoryFormatKnit2()))
2206
result.append((InterKnit1and2, RepositoryFormatKnit1(),
2207
RepositoryFormatKnit2()))
2035
2329
self.new_inventory, self._config)
2036
2330
return self._new_revision_id
2332
def revision_tree(self):
2333
"""Return the tree that was just committed.
2335
After calling commit() this can be called to get a RevisionTree
2336
representing the newly committed tree. This is preferred to
2337
calling Repository.revision_tree() because that may require
2338
deserializing the inventory, while we already have a copy in
2341
return RevisionTree(self.repository, self.new_inventory,
2342
self._new_revision_id)
2038
2344
def finish_inventory(self):
2039
2345
"""Tell the builder that the inventory is finished."""
2040
2346
if self.new_inventory.root is None:
2175
2480
record_root_entry = True
2483
class RootCommitBuilder(CommitBuilder):
2484
"""This commitbuilder actually records the root id"""
2486
record_root_entry = True
2488
def record_entry_contents(self, ie, parent_invs, path, tree):
2489
"""Record the content of ie from tree into the commit if needed.
2491
Side effect: sets ie.revision when unchanged
2493
:param ie: An inventory entry present in the commit.
2494
:param parent_invs: The inventories of the parent revisions of the
2496
:param path: The path the entry is at in the tree.
2497
:param tree: The tree which contains this entry and should be used to
2500
assert self.new_inventory.root is not None or ie.parent_id is None
2501
self.new_inventory.add(ie)
2503
# ie.revision is always None if the InventoryEntry is considered
2504
# for committing. ie.snapshot will record the correct revision
2505
# which may be the sole parent if it is untouched.
2506
if ie.revision is not None:
2509
previous_entries = ie.find_previous_heads(
2511
self.repository.weave_store,
2512
self.repository.get_transaction())
2513
# we are creating a new revision for ie in the history store
2515
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2178
2518
_unescape_map = {