1
1
# Copyright (C) 2005, 2006 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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
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
from bzrlib.inventory import Inventory
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.store.versioned import VersionedFileStore, WeaveStore
37
from bzrlib.store.text import TextStore
38
from bzrlib.symbol_versioning import (deprecated_method,
60
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
61
from bzrlib.symbol_versioning import (
41
from bzrlib.trace import mutter, note
42
from bzrlib.tree import RevisionTree, EmptyTree
43
from bzrlib.tsort import topo_sort
44
from bzrlib.testament import Testament
45
from bzrlib.tree import EmptyTree
46
from bzrlib.weave import WeaveFile
65
from bzrlib.trace import mutter, note, warning
68
# Old formats display a warning, but only once
69
_deprecation_warning_done = False
49
72
class Repository(object):
256
282
:param revprops: Optional dictionary of revision properties.
257
283
:param revision_id: Optional revision id.
259
return CommitBuilder(self, parents, config, timestamp, timezone,
260
committer, revprops, revision_id)
285
return _CommitBuilder(self, parents, config, timestamp, timezone,
286
committer, revprops, revision_id)
262
288
def unlock(self):
263
289
self.control_files.unlock()
411
435
# revisions. We don't need to see all lines in the inventory because
412
436
# only those added in an inventory in rev X can contain a revision=X
414
for line in w.iter_lines_added_or_present_in_versions(selected_revision_ids):
415
start = line.find('file_id="')+9
416
if start < 9: continue
417
end = line.find('"', start)
419
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])
421
start = line.find('revision="')+10
422
if start < 10: continue
423
end = line.find('"', start)
425
revision_id = _unescape_xml(line[start:end])
426
if revision_id in selected_revision_ids:
427
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)
565
600
def revision_tree(self, revision_id):
566
601
"""Return Tree for a revision on this branch.
568
`revision_id` may be None for the null revision, in which case
569
an `EmptyTree` is returned."""
603
`revision_id` may be None for the empty tree revision.
570
605
# TODO: refactor this to use an existing revision object
571
606
# so we don't need to read it in twice.
572
if revision_id is None or revision_id == NULL_REVISION:
607
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
608
return RevisionTree(self, Inventory(), _mod_revision.NULL_REVISION)
575
610
inv = self.get_revision_inventory(revision_id)
576
611
return RevisionTree(self, inv, revision_id)
746
792
present_parents.append(p_id)
747
793
parent_trees[p_id] = repository.revision_tree(p_id)
749
parent_trees[p_id] = EmptyTree()
795
parent_trees[p_id] = repository.revision_tree(None)
751
797
inv = revision_tree.inventory
798
entries = inv.iter_entries()
799
# backwards compatability hack: skip the root id.
800
if not repository.supports_rich_root():
801
path, root = entries.next()
802
if root.revision != rev.revision_id:
803
raise errors.IncompatibleRevision(repr(repository))
753
804
# Add the texts that are not already present
754
for path, ie in inv.iter_entries():
805
for path, ie in entries:
755
806
w = repository.weave_store.get_weave_or_empty(ie.file_id,
756
807
repository.get_transaction())
757
808
if ie.revision not in w:
889
943
:return: a dictionary of revision_id->revision_parents_list.
891
945
# special case NULL_REVISION
892
if revision_id == NULL_REVISION:
946
if revision_id == _mod_revision.NULL_REVISION:
894
weave = self._get_revision_vf()
895
entire_graph = weave.get_graph()
948
a_weave = self._get_revision_vf()
949
entire_graph = a_weave.get_graph()
896
950
if revision_id is None:
897
return weave.get_graph()
898
elif revision_id not in weave:
951
return a_weave.get_graph()
952
elif revision_id not in a_weave:
899
953
raise errors.NoSuchRevision(self, revision_id)
901
955
# add what can be reached from revision_id
967
1021
return self._get_revision_vf().get_parents(revision_id)
1024
class KnitRepository2(KnitRepository):
1026
def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1027
control_store, text_store):
1028
KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1029
_revision_store, control_store, text_store)
1030
self._serializer = xml6.serializer_v6
1032
def deserialise_inventory(self, revision_id, xml):
1033
"""Transform the xml into an inventory object.
1035
:param revision_id: The expected revision id of the inventory.
1036
:param xml: A serialised inventory.
1038
result = self._serializer.read_inventory_from_string(xml)
1039
assert result.root.revision is not None
1042
def serialise_inventory(self, inv):
1043
"""Transform the inventory object into XML text.
1045
:param revision_id: The expected revision id of the inventory.
1046
:param xml: A serialised inventory.
1048
assert inv.revision_id is not None
1049
assert inv.root.revision is not None
1050
return KnitRepository.serialise_inventory(self, inv)
1052
def get_commit_builder(self, branch, parents, config, timestamp=None,
1053
timezone=None, committer=None, revprops=None,
1055
"""Obtain a CommitBuilder for this repository.
1057
:param branch: Branch to commit to.
1058
:param parents: Revision ids of the parents of the new revision.
1059
:param config: Configuration to use.
1060
:param timestamp: Optional timestamp recorded for commit.
1061
:param timezone: Optional timezone for timestamp.
1062
:param committer: Optional committer to set for commit.
1063
:param revprops: Optional dictionary of revision properties.
1064
:param revision_id: Optional revision id.
1066
return RootCommitBuilder(self, parents, config, timestamp, timezone,
1067
committer, revprops, revision_id)
970
1070
class RepositoryFormat(object):
971
1071
"""A repository format.
1116
1224
class PreSplitOutRepositoryFormat(RepositoryFormat):
1117
1225
"""Base class for the pre split out repository formats."""
1227
rich_root_data = False
1119
1229
def initialize(self, a_bzrdir, shared=False, _internal=False):
1120
1230
"""Create a weave repository.
1122
1232
TODO: when creating split out bzr branch formats, move this to a common
1123
1233
base for Format5, Format6. or something like that.
1125
from bzrlib.weavefile import write_weave_v5
1126
from bzrlib.weave import Weave
1129
1236
raise errors.IncompatibleFormat(self, a_bzrdir._format)
1444
1556
repo_transport,
1445
1557
prefixed=False,
1446
1558
file_mode=control_files._file_mode,
1447
versionedfile_class=KnitVersionedFile,
1448
versionedfile_kwargs={'factory':KnitPlainFactory()},
1559
versionedfile_class=knit.KnitVersionedFile,
1560
versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
1451
def get_format_string(self):
1452
"""See RepositoryFormat.get_format_string()."""
1453
return "Bazaar-NG Knit Repository Format 1"
1455
def get_format_description(self):
1456
"""See RepositoryFormat.get_format_description()."""
1457
return "Knit repository format 1"
1459
1563
def _get_revision_store(self, repo_transport, control_files):
1460
1564
"""See RepositoryFormat._get_revision_store()."""
1461
1565
from bzrlib.store.revision.knit import KnitRevisionStore
1473
1579
def _get_text_store(self, transport, control_files):
1474
1580
"""See RepositoryFormat._get_text_store()."""
1475
1581
return self._get_versioned_file_store('knits',
1478
versionedfile_class=KnitVersionedFile,
1584
versionedfile_class=knit.KnitVersionedFile,
1585
versionedfile_kwargs={
1586
'create_parent_dir':True,
1587
'delay_create':True,
1588
'dir_mode':control_files._dir_mode,
1481
1592
def initialize(self, a_bzrdir, shared=False):
1482
1593
"""Create a knit format 1 repository.
1529
1642
text_store=text_store)
1645
class RepositoryFormatKnit1(RepositoryFormatKnit):
1646
"""Bzr repository knit format 1.
1648
This repository format has:
1649
- knits for file texts and inventory
1650
- hash subdirectory based stores.
1651
- knits for revisions and signatures
1652
- TextStores for revisions and signatures.
1653
- a format marker of its own
1654
- an optional 'shared-storage' flag
1655
- an optional 'no-working-trees' flag
1658
This format was introduced in bzr 0.8.
1660
def get_format_string(self):
1661
"""See RepositoryFormat.get_format_string()."""
1662
return "Bazaar-NG Knit Repository Format 1"
1664
def get_format_description(self):
1665
"""See RepositoryFormat.get_format_description()."""
1666
return "Knit repository format 1"
1668
def check_conversion_target(self, target_format):
1672
class RepositoryFormatKnit2(RepositoryFormatKnit):
1673
"""Bzr repository knit format 2.
1675
THIS FORMAT IS EXPERIMENTAL
1676
This repository format has:
1677
- knits for file texts and inventory
1678
- hash subdirectory based stores.
1679
- knits for revisions and signatures
1680
- TextStores for revisions and signatures.
1681
- a format marker of its own
1682
- an optional 'shared-storage' flag
1683
- an optional 'no-working-trees' flag
1685
- Support for recording full info about the tree root
1689
rich_root_data = True
1691
def get_format_string(self):
1692
"""See RepositoryFormat.get_format_string()."""
1693
return "Bazaar Knit Repository Format 2\n"
1695
def get_format_description(self):
1696
"""See RepositoryFormat.get_format_description()."""
1697
return "Knit repository format 2"
1699
def check_conversion_target(self, target_format):
1700
if not target_format.rich_root_data:
1701
raise errors.BadConversionTarget(
1702
'Does not support rich root data.', target_format)
1704
def open(self, a_bzrdir, _found=False, _override_transport=None):
1705
"""See RepositoryFormat.open().
1707
:param _override_transport: INTERNAL USE ONLY. Allows opening the
1708
repository at a slightly different url
1709
than normal. I.e. during 'upgrade'.
1712
format = RepositoryFormat.find_format(a_bzrdir)
1713
assert format.__class__ == self.__class__
1714
if _override_transport is not None:
1715
repo_transport = _override_transport
1717
repo_transport = a_bzrdir.get_repository_transport(None)
1718
control_files = lockable_files.LockableFiles(repo_transport, 'lock',
1720
text_store = self._get_text_store(repo_transport, control_files)
1721
control_store = self._get_control_store(repo_transport, control_files)
1722
_revision_store = self._get_revision_store(repo_transport, control_files)
1723
return KnitRepository2(_format=self,
1725
control_files=control_files,
1726
_revision_store=_revision_store,
1727
control_store=control_store,
1728
text_store=text_store)
1532
1732
# formats which have no format string are not discoverable
1533
1733
# and not independently creatable, so are not registered.
1534
1734
RepositoryFormat.register_format(RepositoryFormat7())
1535
1735
_default_format = RepositoryFormatKnit1()
1536
1736
RepositoryFormat.register_format(_default_format)
1737
RepositoryFormat.register_format(RepositoryFormatKnit2())
1537
1738
RepositoryFormat.set_default_format(_default_format)
1538
1739
_legacy_formats = [RepositoryFormat4(),
1539
1740
RepositoryFormat5(),
1552
1753
InterRepository.get(other).method_name(parameters).
1556
1757
"""The available optimised InterRepository types."""
1559
1759
def copy_content(self, revision_id=None, basis=None):
1560
"""Make a complete copy of the content in self into destination.
1562
This is a destructive operation! Do not use it on existing
1565
:param revision_id: Only copy the content needed to construct
1566
revision_id and its parents.
1567
:param basis: Copy the needed data preferentially from basis.
1570
self.target.set_make_working_trees(self.source.make_working_trees())
1571
except NotImplementedError:
1573
# grab the basis available data
1574
if basis is not None:
1575
self.target.fetch(basis, revision_id=revision_id)
1576
# but don't bother fetching if we have the needed data now.
1577
if (revision_id not in (None, NULL_REVISION) and
1578
self.target.has_revision(revision_id)):
1580
self.target.fetch(self.source, revision_id=revision_id)
1582
def _double_lock(self, lock_source, lock_target):
1583
"""Take out too locks, rolling back the first if the second throws."""
1588
# we want to ensure that we don't leave source locked by mistake.
1589
# and any error on target should not confuse source.
1590
self.source.unlock()
1760
raise NotImplementedError(self.copy_content)
1594
1762
def fetch(self, revision_id=None, pb=None):
1595
1763
"""Fetch the content required to construct revision_id.
1597
The content is copied from source to target.
1765
The content is copied from self.source to self.target.
1599
1767
:param revision_id: if None all content is copied, if NULL_REVISION no
1600
1768
content is copied.
1604
1772
Returns the copied revision count and the failed revisions in a tuple:
1605
1773
(copied, failures).
1607
from bzrlib.fetch import GenericRepoFetcher
1608
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1609
self.source, self.source._format, self.target, self.target._format)
1610
f = GenericRepoFetcher(to_repository=self.target,
1611
from_repository=self.source,
1612
last_revision=revision_id,
1614
return f.count_copied, f.failed_revisions
1616
def lock_read(self):
1617
"""Take out a logical read lock.
1619
This will lock the source branch and the target branch. The source gets
1620
a read lock and the target a read lock.
1622
self._double_lock(self.source.lock_read, self.target.lock_read)
1624
def lock_write(self):
1625
"""Take out a logical write lock.
1627
This will lock the source branch and the target branch. The source gets
1628
a read lock and the target a write lock.
1630
self._double_lock(self.source.lock_read, self.target.lock_write)
1775
raise NotImplementedError(self.fetch)
1632
1777
@needs_read_lock
1633
1778
def missing_revision_ids(self, revision_id=None):
1634
1779
"""Return the revision ids that source has that target does not.
1652
1797
# that we've decided we need.
1653
1798
return [rev_id for rev_id in source_ids if rev_id in result_set]
1656
"""Release the locks on source and target."""
1801
class InterSameDataRepository(InterRepository):
1802
"""Code for converting between repositories that represent the same data.
1804
Data format and model must match for this to work.
1807
_matching_repo_format = RepositoryFormat4()
1808
"""Repository format for testing with."""
1811
def is_compatible(source, target):
1812
if not isinstance(source, Repository):
1814
if not isinstance(target, Repository):
1816
if source._format.rich_root_data == target._format.rich_root_data:
1822
def copy_content(self, revision_id=None, basis=None):
1823
"""Make a complete copy of the content in self into destination.
1825
This is a destructive operation! Do not use it on existing
1828
:param revision_id: Only copy the content needed to construct
1829
revision_id and its parents.
1830
:param basis: Copy the needed data preferentially from basis.
1658
self.target.unlock()
1660
self.source.unlock()
1663
class InterWeaveRepo(InterRepository):
1833
self.target.set_make_working_trees(self.source.make_working_trees())
1834
except NotImplementedError:
1836
# grab the basis available data
1837
if basis is not None:
1838
self.target.fetch(basis, revision_id=revision_id)
1839
# but don't bother fetching if we have the needed data now.
1840
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
1841
self.target.has_revision(revision_id)):
1843
self.target.fetch(self.source, revision_id=revision_id)
1846
def fetch(self, revision_id=None, pb=None):
1847
"""See InterRepository.fetch()."""
1848
from bzrlib.fetch import GenericRepoFetcher
1849
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
1850
self.source, self.source._format, self.target,
1851
self.target._format)
1852
f = GenericRepoFetcher(to_repository=self.target,
1853
from_repository=self.source,
1854
last_revision=revision_id,
1856
return f.count_copied, f.failed_revisions
1859
class InterWeaveRepo(InterSameDataRepository):
1664
1860
"""Optimised code paths between Weave based repositories."""
1666
1862
_matching_repo_format = RepositoryFormat7()
1840
2036
# that against the revision records.
1841
2037
return self.source._eliminate_revisions_not_present(required_topo_revisions)
2040
class InterModel1and2(InterRepository):
2042
_matching_repo_format = None
2045
def is_compatible(source, target):
2046
if not isinstance(source, Repository):
2048
if not isinstance(target, Repository):
2050
if not source._format.rich_root_data and target._format.rich_root_data:
2056
def fetch(self, revision_id=None, pb=None):
2057
"""See InterRepository.fetch()."""
2058
from bzrlib.fetch import Model1toKnit2Fetcher
2059
f = Model1toKnit2Fetcher(to_repository=self.target,
2060
from_repository=self.source,
2061
last_revision=revision_id,
2063
return f.count_copied, f.failed_revisions
2066
def copy_content(self, revision_id=None, basis=None):
2067
"""Make a complete copy of the content in self into destination.
2069
This is a destructive operation! Do not use it on existing
2072
:param revision_id: Only copy the content needed to construct
2073
revision_id and its parents.
2074
:param basis: Copy the needed data preferentially from basis.
2077
self.target.set_make_working_trees(self.source.make_working_trees())
2078
except NotImplementedError:
2080
# grab the basis available data
2081
if basis is not None:
2082
self.target.fetch(basis, revision_id=revision_id)
2083
# but don't bother fetching if we have the needed data now.
2084
if (revision_id not in (None, _mod_revision.NULL_REVISION) and
2085
self.target.has_revision(revision_id)):
2087
self.target.fetch(self.source, revision_id=revision_id)
2090
class InterKnit1and2(InterKnitRepo):
2092
_matching_repo_format = None
2095
def is_compatible(source, target):
2096
"""Be compatible with Knit1 source and Knit2 target"""
2098
return (isinstance(source._format, (RepositoryFormatKnit1)) and
2099
isinstance(target._format, (RepositoryFormatKnit2)))
2100
except AttributeError:
2104
def fetch(self, revision_id=None, pb=None):
2105
"""See InterRepository.fetch()."""
2106
from bzrlib.fetch import Knit1to2Fetcher
2107
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2108
self.source, self.source._format, self.target,
2109
self.target._format)
2110
f = Knit1to2Fetcher(to_repository=self.target,
2111
from_repository=self.source,
2112
last_revision=revision_id,
2114
return f.count_copied, f.failed_revisions
2117
InterRepository.register_optimiser(InterSameDataRepository)
1843
2118
InterRepository.register_optimiser(InterWeaveRepo)
1844
2119
InterRepository.register_optimiser(InterKnitRepo)
2120
InterRepository.register_optimiser(InterModel1and2)
2121
InterRepository.register_optimiser(InterKnit1and2)
1847
2124
class RepositoryTestProviderAdapter(object):
1912
2189
# default format.
1913
2190
# XXX: robertc 20060220 reinstate this when there are two supported
1914
2191
# formats which do not have an optimal code path between them.
1915
result.append((InterRepository,
1916
RepositoryFormat6(),
1917
RepositoryFormatKnit1()))
2192
#result.append((InterRepository,
2193
# RepositoryFormat6(),
2194
# RepositoryFormatKnit1()))
1918
2195
for optimiser in InterRepository._optimisers:
1919
result.append((optimiser,
1920
optimiser._matching_repo_format,
1921
optimiser._matching_repo_format
2196
if optimiser._matching_repo_format is not None:
2197
result.append((optimiser,
2198
optimiser._matching_repo_format,
2199
optimiser._matching_repo_format
1923
2201
# if there are specific combinations we want to use, we can add them
2203
result.append((InterModel1and2, RepositoryFormat5(),
2204
RepositoryFormatKnit2()))
2205
result.append((InterKnit1and2, RepositoryFormatKnit1(),
2206
RepositoryFormatKnit2()))
2042
2328
self.new_inventory, self._config)
2043
2329
return self._new_revision_id
2331
def revision_tree(self):
2332
"""Return the tree that was just committed.
2334
After calling commit() this can be called to get a RevisionTree
2335
representing the newly committed tree. This is preferred to
2336
calling Repository.revision_tree() because that may require
2337
deserializing the inventory, while we already have a copy in
2340
return RevisionTree(self.repository, self.new_inventory,
2341
self._new_revision_id)
2045
2343
def finish_inventory(self):
2046
2344
"""Tell the builder that the inventory is finished."""
2345
if self.new_inventory.root is None:
2346
symbol_versioning.warn('Root entry should be supplied to'
2347
' record_entry_contents, as of bzr 0.10.',
2348
DeprecationWarning, stacklevel=2)
2349
self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2047
2350
self.new_inventory.revision_id = self._new_revision_id
2048
2351
self.inv_sha1 = self.repository.add_inventory(
2049
2352
self._new_revision_id,
2152
2470
versionedfile.clear_cache()
2473
class _CommitBuilder(CommitBuilder):
2474
"""Temporary class so old CommitBuilders are detected properly
2476
Note: CommitBuilder works whether or not root entry is recorded.
2479
record_root_entry = True
2482
class RootCommitBuilder(CommitBuilder):
2483
"""This commitbuilder actually records the root id"""
2485
record_root_entry = True
2487
def record_entry_contents(self, ie, parent_invs, path, tree):
2488
"""Record the content of ie from tree into the commit if needed.
2490
Side effect: sets ie.revision when unchanged
2492
:param ie: An inventory entry present in the commit.
2493
:param parent_invs: The inventories of the parent revisions of the
2495
:param path: The path the entry is at in the tree.
2496
:param tree: The tree which contains this entry and should be used to
2499
assert self.new_inventory.root is not None or ie.parent_id is None
2500
self.new_inventory.add(ie)
2502
# ie.revision is always None if the InventoryEntry is considered
2503
# for committing. ie.snapshot will record the correct revision
2504
# which may be the sole parent if it is untouched.
2505
if ie.revision is not None:
2508
previous_entries = ie.find_previous_heads(
2510
self.repository.weave_store,
2511
self.repository.get_transaction())
2512
# we are creating a new revision for ie in the history store
2514
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
2155
2517
_unescape_map = {