22
22
from unittest import TestSuite
24
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
36
25
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
26
from bzrlib.errors import InvalidRevisionId
38
27
from bzrlib.graph import Graph
39
28
from bzrlib.inter import InterObject
40
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
29
from bzrlib.inventory import Inventory
41
30
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
42
31
from bzrlib.lockable_files import LockableFiles, TransportLock
43
32
from bzrlib.lockdir import LockDir
47
36
from bzrlib.revisiontree import RevisionTree
48
37
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
49
38
from bzrlib.store.text import TextStore
50
from bzrlib import symbol_versioning
51
39
from bzrlib.symbol_versioning import (deprecated_method,
54
42
from bzrlib.testament import Testament
55
from bzrlib.trace import mutter, note, warning
43
from bzrlib.trace import mutter, note
56
44
from bzrlib.tsort import topo_sort
57
45
from bzrlib.weave import WeaveFile
60
# Old formats display a warning, but only once
61
_deprecation_warning_done = False
64
48
class Repository(object):
65
49
"""Repository holding history for one or more branches.
274
255
:param revprops: Optional dictionary of revision properties.
275
256
:param revision_id: Optional revision id.
277
return _CommitBuilder(self, parents, config, timestamp, timezone,
278
committer, revprops, revision_id)
258
return CommitBuilder(self, parents, config, timestamp, timezone,
259
committer, revprops, revision_id)
280
261
def unlock(self):
281
262
self.control_files.unlock()
426
410
# revisions. We don't need to see all lines in the inventory because
427
411
# only those added in an inventory in rev X can contain a revision=X
429
pb = ui.ui_factory.nested_progress_bar()
431
for line in w.iter_lines_added_or_present_in_versions(
432
selected_revision_ids, pb=pb):
433
start = line.find('file_id="')+9
434
if start < 9: continue
435
end = line.find('"', start)
437
file_id = _unescape_xml(line[start:end])
413
for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
414
start = line.find('file_id="')+9
415
if start < 9: continue
416
end = line.find('"', start)
418
file_id = _unescape_xml(line[start:end])
439
start = line.find('revision="')+10
440
if start < 10: continue
441
end = line.find('"', start)
443
revision_id = _unescape_xml(line[start:end])
444
if revision_id in selected_revision_ids:
445
result.setdefault(file_id, set()).add(revision_id)
420
start = line.find('revision="')+10
421
if start < 10: continue
422
end = line.find('"', start)
424
revision_id = _unescape_xml(line[start:end])
425
if revision_id in selected_revision_ids:
426
result.setdefault(file_id, set()).add(revision_id)
785
748
parent_trees[p_id] = repository.revision_tree(None)
787
750
inv = revision_tree.inventory
752
# backwards compatability hack: skip the root id.
788
753
entries = inv.iter_entries()
789
# backwards compatability hack: skip the root id.
790
if not repository.supports_rich_root():
791
path, root = entries.next()
792
if root.revision != rev.revision_id:
793
raise errors.IncompatibleRevision(repr(repository))
794
755
# Add the texts that are not already present
795
756
for path, ie in entries:
796
757
w = repository.weave_store.get_weave_or_empty(ie.file_id,
1011
969
return self._get_revision_vf().get_parents(revision_id)
1014
class KnitRepository2(KnitRepository):
1016
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1017
control_store, text_store):
1018
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1019
_revision_store, control_store, text_store)
1020
self._serializer = xml6.serializer_v6
1022
def deserialise_inventory(self, revision_id, xml):
1023
"""Transform the xml into an inventory object.
1025
:param revision_id: The expected revision id of the inventory.
1026
:param xml: A serialised inventory.
1028
result = self._serializer.read_inventory_from_string(xml)
1029
assert result.root.revision is not None
1032
def serialise_inventory(self, inv):
1033
"""Transform the inventory object into XML text.
1035
:param revision_id: The expected revision id of the inventory.
1036
:param xml: A serialised inventory.
1038
assert inv.revision_id is not None
1039
assert inv.root.revision is not None
1040
return KnitRepository.serialise_inventory(self, inv)
1042
def get_commit_builder(self, branch, parents, config, timestamp=None,
1043
timezone=None, committer=None, revprops=None,
1045
"""Obtain a CommitBuilder for this repository.
1047
:param branch: Branch to commit to.
1048
:param parents: Revision ids of the parents of the new revision.
1049
:param config: Configuration to use.
1050
:param timestamp: Optional timestamp recorded for commit.
1051
:param timezone: Optional timezone for timestamp.
1052
:param committer: Optional committer to set for commit.
1053
:param revprops: Optional dictionary of revision properties.
1054
:param revision_id: Optional revision id.
1056
return RootCommitBuilder(self, parents, config, timestamp, timezone,
1057
committer, revprops, revision_id)
1060
972
class RepositoryFormat(object):
1061
973
"""A repository format.
1554
1450
versionedfile_kwargs={'factory':KnitPlainFactory()},
1453
def get_format_string(self):
1454
"""See RepositoryFormat.get_format_string()."""
1455
return "Bazaar-NG Knit Repository Format 1"
1457
def get_format_description(self):
1458
"""See RepositoryFormat.get_format_description()."""
1459
return "Knit repository format 1"
1557
1461
def _get_revision_store(self, repo_transport, control_files):
1558
1462
"""See RepositoryFormat._get_revision_store()."""
1559
1463
from bzrlib.store.revision.knit import KnitRevisionStore
1632
1531
text_store=text_store)
1635
class RepositoryFormatKnit1(RepositoryFormatKnit):
1636
"""Bzr repository knit format 1.
1638
This repository format has:
1639
- knits for file texts and inventory
1640
- hash subdirectory based stores.
1641
- knits for revisions and signatures
1642
- TextStores for revisions and signatures.
1643
- a format marker of its own
1644
- an optional 'shared-storage' flag
1645
- an optional 'no-working-trees' flag
1648
This format was introduced in bzr 0.8.
1650
def get_format_string(self):
1651
"""See RepositoryFormat.get_format_string()."""
1652
return "Bazaar-NG Knit Repository Format 1"
1654
def get_format_description(self):
1655
"""See RepositoryFormat.get_format_description()."""
1656
return "Knit repository format 1"
1658
def check_conversion_target(self, target_format):
1662
class RepositoryFormatKnit2(RepositoryFormatKnit):
1663
"""Bzr repository knit format 2.
1665
THIS FORMAT IS EXPERIMENTAL
1666
This repository format has:
1667
- knits for file texts and inventory
1668
- hash subdirectory based stores.
1669
- knits for revisions and signatures
1670
- TextStores for revisions and signatures.
1671
- a format marker of its own
1672
- an optional 'shared-storage' flag
1673
- an optional 'no-working-trees' flag
1675
- Support for recording full info about the tree root
1679
rich_root_data = True
1681
def get_format_string(self):
1682
"""See RepositoryFormat.get_format_string()."""
1683
return "Bazaar Knit Repository Format 2\n"
1685
def get_format_description(self):
1686
"""See RepositoryFormat.get_format_description()."""
1687
return "Knit repository format 2"
1689
def check_conversion_target(self, target_format):
1690
if not target_format.rich_root_data:
1691
raise errors.BadConversionTarget(
1692
'Does not support rich root data.', target_format)
1694
def open(self, a_bzrdir, _found=False, _override_transport=None):
1695
"""See RepositoryFormat.open().
1697
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1698
repository at a slightly different url
1699
than normal. I.e. during 'upgrade'.
1702
format = RepositoryFormat.find_format(a_bzrdir)
1703
assert format.__class__ == self.__class__
1704
if _override_transport is not None:
1705
repo_transport = _override_transport
1707
repo_transport = a_bzrdir.get_repository_transport(None)
1708
control_files = LockableFiles(repo_transport, 'lock', LockDir)
1709
text_store = self._get_text_store(repo_transport, control_files)
1710
control_store = self._get_control_store(repo_transport, control_files)
1711
_revision_store = self._get_revision_store(repo_transport, control_files)
1712
return KnitRepository2(_format=self,
1714
control_files=control_files,
1715
_revision_store=_revision_store,
1716
control_store=control_store,
1717
text_store=text_store)
1721
1534
# formats which have no format string are not discoverable
1722
1535
# and not independently creatable, so are not registered.
1723
1536
RepositoryFormat.register_format(RepositoryFormat7())
1724
1537
_default_format = RepositoryFormatKnit1()
1725
1538
RepositoryFormat.register_format(_default_format)
1726
RepositoryFormat.register_format(RepositoryFormatKnit2())
1727
1539
RepositoryFormat.set_default_format(_default_format)
1728
1540
_legacy_formats = [RepositoryFormat4(),
1729
1541
RepositoryFormat5(),
1742
1554
InterRepository.get(other).method_name(parameters).
1746
1558
"""The available optimised InterRepository types."""
1748
1561
def copy_content(self, revision_id=None, basis=None):
1749
raise NotImplementedError(self.copy_content)
1562
"""Make a complete copy of the content in self into destination.
1564
This is a destructive operation! Do not use it on existing
1567
:param revision_id: Only copy the content needed to construct
1568
revision_id and its parents.
1569
:param basis: Copy the needed data preferentially from basis.
1572
self.target.set_make_working_trees(self.source.make_working_trees())
1573
except NotImplementedError:
1575
# grab the basis available data
1576
if basis is not None:
1577
self.target.fetch(basis, revision_id=revision_id)
1578
# but don't bother fetching if we have the needed data now.
1579
if (revision_id not in (None, NULL_REVISION) and
1580
self.target.has_revision(revision_id)):
1582
self.target.fetch(self.source, revision_id=revision_id)
1751
1585
def fetch(self, revision_id=None, pb=None):
1752
1586
"""Fetch the content required to construct revision_id.
1754
The content is copied from self.source to self.target.
1588
The content is copied from source to target.
1756
1590
:param revision_id: if None all content is copied, if NULL_REVISION no
1757
1591
content is copied.
1761
1595
Returns the copied revision count and the failed revisions in a tuple:
1762
1596
(copied, failures).
1764
raise NotImplementedError(self.fetch)
1598
from bzrlib.fetch import GenericRepoFetcher
1599
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1600
self.source, self.source._format, self.target, self.target._format)
1601
f = GenericRepoFetcher(to_repository=self.target,
1602
from_repository=self.source,
1603
last_revision=revision_id,
1605
return f.count_copied, f.failed_revisions
1766
1607
@needs_read_lock
1767
1608
def missing_revision_ids(self, revision_id=None):
1768
1609
"""Return the revision ids that source has that target does not.
1787
1628
return [rev_id for rev_id in source_ids if rev_id in result_set]
1790
class InterSameDataRepository(InterRepository):
1791
"""Code for converting between repositories that represent the same data.
1793
Data format and model must match for this to work.
1796
_matching_repo_format = RepositoryFormat4()
1797
"""Repository format for testing with."""
1800
def is_compatible(source, target):
1801
if not isinstance(source, Repository):
1803
if not isinstance(target, Repository):
1805
if source._format.rich_root_data == target._format.rich_root_data:
1811
def copy_content(self, revision_id=None, basis=None):
1812
"""Make a complete copy of the content in self into destination.
1814
This is a destructive operation! Do not use it on existing
1817
:param revision_id: Only copy the content needed to construct
1818
revision_id and its parents.
1819
:param basis: Copy the needed data preferentially from basis.
1822
self.target.set_make_working_trees(self.source.make_working_trees())
1823
except NotImplementedError:
1825
# grab the basis available data
1826
if basis is not None:
1827
self.target.fetch(basis, revision_id=revision_id)
1828
# but don't bother fetching if we have the needed data now.
1829
if (revision_id not in (None, NULL_REVISION) and
1830
self.target.has_revision(revision_id)):
1832
self.target.fetch(self.source, revision_id=revision_id)
1835
def fetch(self, revision_id=None, pb=None):
1836
"""See InterRepository.fetch()."""
1837
from bzrlib.fetch import GenericRepoFetcher
1838
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1839
self.source, self.source._format, self.target,
1840
self.target._format)
1841
f = GenericRepoFetcher(to_repository=self.target,
1842
from_repository=self.source,
1843
last_revision=revision_id,
1845
return f.count_copied, f.failed_revisions
1848
class InterWeaveRepo(InterSameDataRepository):
1631
class InterWeaveRepo(InterRepository):
1849
1632
"""Optimised code paths between Weave based repositories."""
1851
1634
_matching_repo_format = RepositoryFormat7()
2025
1808
# that against the revision records.
2026
1809
return self.source._eliminate_revisions_not_present(required_topo_revisions)
2029
class InterModel1and2(InterRepository):
2031
_matching_repo_format = None
2034
def is_compatible(source, target):
2035
if not isinstance(source, Repository):
2037
if not isinstance(target, Repository):
2039
if not source._format.rich_root_data and target._format.rich_root_data:
2045
def fetch(self, revision_id=None, pb=None):
2046
"""See InterRepository.fetch()."""
2047
from bzrlib.fetch import Model1toKnit2Fetcher
2048
f = Model1toKnit2Fetcher(to_repository=self.target,
2049
from_repository=self.source,
2050
last_revision=revision_id,
2052
return f.count_copied, f.failed_revisions
2055
def copy_content(self, revision_id=None, basis=None):
2056
"""Make a complete copy of the content in self into destination.
2058
This is a destructive operation! Do not use it on existing
2061
:param revision_id: Only copy the content needed to construct
2062
revision_id and its parents.
2063
:param basis: Copy the needed data preferentially from basis.
2066
self.target.set_make_working_trees(self.source.make_working_trees())
2067
except NotImplementedError:
2069
# grab the basis available data
2070
if basis is not None:
2071
self.target.fetch(basis, revision_id=revision_id)
2072
# but don't bother fetching if we have the needed data now.
2073
if (revision_id not in (None, NULL_REVISION) and
2074
self.target.has_revision(revision_id)):
2076
self.target.fetch(self.source, revision_id=revision_id)
2079
class InterKnit1and2(InterKnitRepo):
2081
_matching_repo_format = None
2084
def is_compatible(source, target):
2085
"""Be compatible with Knit1 source and Knit2 target"""
2087
return (isinstance(source._format, (RepositoryFormatKnit1)) and
2088
isinstance(target._format, (RepositoryFormatKnit2)))
2089
except AttributeError:
2093
def fetch(self, revision_id=None, pb=None):
2094
"""See InterRepository.fetch()."""
2095
from bzrlib.fetch import Knit1to2Fetcher
2096
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2097
self.source, self.source._format, self.target,
2098
self.target._format)
2099
f = Knit1to2Fetcher(to_repository=self.target,
2100
from_repository=self.source,
2101
last_revision=revision_id,
2103
return f.count_copied, f.failed_revisions
2106
InterRepository.register_optimiser(InterSameDataRepository)
2107
1811
InterRepository.register_optimiser(InterWeaveRepo)
2108
1812
InterRepository.register_optimiser(InterKnitRepo)
2109
InterRepository.register_optimiser(InterModel1and2)
2110
InterRepository.register_optimiser(InterKnit1and2)
2113
1815
class RepositoryTestProviderAdapter(object):
2178
1880
# default format.
2179
1881
# XXX: robertc 20060220 reinstate this when there are two supported
2180
1882
# formats which do not have an optimal code path between them.
2181
#result.append((InterRepository,
2182
# RepositoryFormat6(),
2183
# RepositoryFormatKnit1()))
1883
result.append((InterRepository,
1884
RepositoryFormat6(),
1885
RepositoryFormatKnit1()))
2184
1886
for optimiser in InterRepository._optimisers:
2185
if optimiser._matching_repo_format is not None:
2186
result.append((optimiser,
2187
optimiser._matching_repo_format,
2188
optimiser._matching_repo_format
1887
result.append((optimiser,
1888
optimiser._matching_repo_format,
1889
optimiser._matching_repo_format
2190
1891
# if there are specific combinations we want to use, we can add them
2192
result.append((InterModel1and2, RepositoryFormat5(),
2193
RepositoryFormatKnit2()))
2194
result.append((InterKnit1and2, RepositoryFormatKnit1(),
2195
RepositoryFormatKnit2()))
2316
2010
self.new_inventory, self._config)
2317
2011
return self._new_revision_id
2319
def revision_tree(self):
2320
"""Return the tree that was just committed.
2322
After calling commit() this can be called to get a RevisionTree
2323
representing the newly committed tree. This is preferred to
2324
calling Repository.revision_tree() because that may require
2325
deserializing the inventory, while we already have a copy in
2328
return RevisionTree(self.repository, self.new_inventory,
2329
self._new_revision_id)
2331
2013
def finish_inventory(self):
2332
2014
"""Tell the builder that the inventory is finished."""
2333
if self.new_inventory.root is None:
2334
symbol_versioning.warn('Root entry should be supplied to'
2335
' record_entry_contents, as of bzr 0.10.',
2336
DeprecationWarning, stacklevel=2)
2337
self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2338
2015
self.new_inventory.revision_id = self._new_revision_id
2339
2016
self.inv_sha1 = self.repository.add_inventory(
2340
2017
self._new_revision_id,
2458
2120
versionedfile.clear_cache()
2461
class _CommitBuilder(CommitBuilder):
2462
"""Temporary class so old CommitBuilders are detected properly
2464
Note: CommitBuilder works whether or not root entry is recorded.
2467
record_root_entry = True
2470
class RootCommitBuilder(CommitBuilder):
2471
"""This commitbuilder actually records the root id"""
2473
record_root_entry = True
2475
def record_entry_contents(self, ie, parent_invs, path, tree):
2476
"""Record the content of ie from tree into the commit if needed.
2478
Side effect: sets ie.revision when unchanged
2480
:param ie: An inventory entry present in the commit.
2481
:param parent_invs: The inventories of the parent revisions of the
2483
:param path: The path the entry is at in the tree.
2484
:param tree: The tree which contains this entry and should be used to
2487
assert self.new_inventory.root is not None or ie.parent_id is None
2488
self.new_inventory.add(ie)
2490
# ie.revision is always None if the InventoryEntry is considered
2491
# for committing. ie.snapshot will record the correct revision
2492
# which may be the sole parent if it is untouched.
2493
if ie.revision is not None:
2496
previous_entries = ie.find_previous_heads(
2498
self.repository.weave_store,
2499
self.repository.get_transaction())
2500
# we are creating a new revision for ie in the history store
2502
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2505
2123
_unescape_map = {