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
22
22
from unittest import TestSuite
23
from warnings import warn
24
25
from bzrlib import bzrdir, check, delta, gpg, errors, xml5, ui, transactions, osutils
25
26
from bzrlib.decorators import needs_read_lock, needs_write_lock
26
27
from bzrlib.errors import InvalidRevisionId
27
28
from bzrlib.graph import Graph
28
29
from bzrlib.inter import InterObject
29
from bzrlib.inventory import Inventory
30
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
30
31
from bzrlib.knit import KnitVersionedFile, KnitPlainFactory
31
32
from bzrlib.lockable_files import LockableFiles, TransportLock
32
33
from bzrlib.lockdir import LockDir
33
34
from bzrlib.osutils import (safe_unicode, rand_bytes, compact_date,
35
36
from bzrlib.revision import NULL_REVISION, Revision
37
from bzrlib.revisiontree import RevisionTree
36
38
from bzrlib.store.versioned import VersionedFileStore, WeaveStore
37
39
from bzrlib.store.text import TextStore
38
40
from bzrlib.symbol_versioning import (deprecated_method,
41
from bzrlib.trace import mutter, note
42
from bzrlib.tree import RevisionTree, EmptyTree
43
from bzrlib.testament import Testament
44
from bzrlib.trace import mutter, note, warning
43
45
from bzrlib.tsort import topo_sort
44
from bzrlib.testament import Testament
45
from bzrlib.tree import EmptyTree
46
46
from bzrlib.weave import WeaveFile
49
# Old formats display a warning, but only once
50
_deprecation_warning_done = False
49
53
class Repository(object):
50
54
"""Repository holding history for one or more branches.
70
74
assert inv.revision_id is None or inv.revision_id == revid, \
71
75
"Mismatch between inventory revision" \
72
76
" id and insertion revid (%r, %r)" % (inv.revision_id, revid)
77
assert inv.root is not None
73
78
inv_text = xml5.serializer_v5.write_inventory_to_string(inv)
74
79
inv_sha1 = osutils.sha_string(inv_text)
75
80
inv_vf = self.control_weaves.get_weave('inventory',
189
194
self.control_weaves = control_store
190
195
# TODO: make sure to construct the right store classes, etc, depending
191
196
# on whether escaping is required.
197
self._warn_if_deprecated()
193
199
def __repr__(self):
194
200
return '%s(%r)' % (self.__class__.__name__,
256
262
:param revprops: Optional dictionary of revision properties.
257
263
:param revision_id: Optional revision id.
259
return CommitBuilder(self, parents, config, timestamp, timezone,
260
committer, revprops, revision_id)
265
return _CommitBuilder(self, parents, config, timestamp, timezone,
266
committer, revprops, revision_id)
262
268
def unlock(self):
263
269
self.control_files.unlock()
347
353
t in self.revision_trees(required_trees))
348
354
for revision in revisions:
349
355
if not revision.parent_ids:
350
old_tree = EmptyTree()
356
old_tree = self.revision_tree(None)
352
358
old_tree = trees[revision.parent_ids[0]]
353
yield delta.compare_trees(old_tree, trees[revision.revision_id])
359
yield trees[revision.revision_id].changes_from(old_tree)
356
362
def get_revision_delta(self, revision_id):
444
450
:param revision_id: The expected revision id of the inventory.
445
451
:param xml: A serialised inventory.
447
return xml5.serializer_v5.read_inventory_from_string(xml)
453
result = xml5.serializer_v5.read_inventory_from_string(xml)
454
result.root.revision = revision_id
450
458
def get_inventory_xml(self, revision_id):
565
573
def revision_tree(self, revision_id):
566
574
"""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."""
576
`revision_id` may be None for the empty tree revision.
570
578
# TODO: refactor this to use an existing revision object
571
579
# so we don't need to read it in twice.
572
580
if revision_id is None or revision_id == NULL_REVISION:
581
return RevisionTree(self, Inventory(), NULL_REVISION)
575
583
inv = self.get_revision_inventory(revision_id)
576
584
return RevisionTree(self, inv, revision_id)
692
def _warn_if_deprecated(self):
693
global _deprecation_warning_done
694
if _deprecation_warning_done:
696
_deprecation_warning_done = True
697
warning("Format %s for %s is deprecated - please use 'bzr upgrade' to get better performance"
698
% (self._format, self.bzrdir.transport.base))
685
701
class AllInOneRepository(Repository):
686
702
"""Legacy support - the repository behaviour for all-in-one branches."""
746
762
present_parents.append(p_id)
747
763
parent_trees[p_id] = repository.revision_tree(p_id)
749
parent_trees[p_id] = EmptyTree()
765
parent_trees[p_id] = repository.revision_tree(None)
751
767
inv = revision_tree.inventory
769
# backwards compatability hack: skip the root id.
770
entries = inv.iter_entries()
753
772
# Add the texts that are not already present
754
for path, ie in inv.iter_entries():
773
for path, ie in entries:
755
774
w = repository.weave_store.get_weave_or_empty(ie.file_id,
756
775
repository.get_transaction())
757
776
if ie.revision not in w:
825
843
class KnitRepository(MetaDirRepository):
826
844
"""Knit format repository."""
846
def _warn_if_deprecated(self):
847
# This class isn't deprecated
828
850
def _inventory_add_lines(self, inv_vf, revid, parents, lines):
829
851
inv_vf.add_lines_with_ghosts(revid, parents, lines)
1580
1605
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()
1593
1607
@needs_write_lock
1594
1608
def fetch(self, revision_id=None, pb=None):
1595
1609
"""Fetch the content required to construct revision_id.
1614
1628
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)
1632
1630
@needs_read_lock
1633
1631
def missing_revision_ids(self, revision_id=None):
1634
1632
"""Return the revision ids that source has that target does not.
1652
1650
# that we've decided we need.
1653
1651
return [rev_id for rev_id in source_ids if rev_id in result_set]
1656
"""Release the locks on source and target."""
1658
self.target.unlock()
1660
self.source.unlock()
1663
1654
class InterWeaveRepo(InterRepository):
1664
1655
"""Optimised code paths between Weave based repositories."""
1982
1973
This allows describing a tree to be committed without needing to
1983
1974
know the internals of the format of the repository.
1977
record_root_entry = False
1985
1978
def __init__(self, repository, parents, config, timestamp=None,
1986
1979
timezone=None, committer=None, revprops=None,
1987
1980
revision_id=None):
2004
1997
assert isinstance(committer, basestring), type(committer)
2005
1998
self._committer = committer
2007
self.new_inventory = Inventory()
2000
self.new_inventory = Inventory(None)
2008
2001
self._new_revision_id = revision_id
2009
2002
self.parents = parents
2010
2003
self.repository = repository
2014
2007
self._revprops.update(revprops)
2016
2009
if timestamp is None:
2017
self._timestamp = time.time()
2019
self._timestamp = long(timestamp)
2010
timestamp = time.time()
2011
# Restrict resolution to 1ms
2012
self._timestamp = round(timestamp, 3)
2021
2014
if timezone is None:
2022
2015
self._timezone = local_time_offset()
2045
2038
def finish_inventory(self):
2046
2039
"""Tell the builder that the inventory is finished."""
2040
if self.new_inventory.root is None:
2041
warn('Root entry should be supplied to record_entry_contents, as'
2043
DeprecationWarning, stacklevel=2)
2044
self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
2047
2045
self.new_inventory.revision_id = self._new_revision_id
2048
2046
self.inv_sha1 = self.repository.add_inventory(
2049
2047
self._new_revision_id,
2073
2071
def record_entry_contents(self, ie, parent_invs, path, tree):
2074
2072
"""Record the content of ie from tree into the commit if needed.
2074
Side effect: sets ie.revision when unchanged
2076
2076
:param ie: An inventory entry present in the commit.
2077
2077
:param parent_invs: The inventories of the parent revisions of the
2087
2087
# which may be the sole parent if it is untouched.
2088
2088
if ie.revision is not None:
2091
# In this revision format, root entries have no knit or weave
2092
if ie is self.new_inventory.root:
2093
if len(parent_invs):
2094
ie.revision = parent_invs[0].root.revision
2090
2098
previous_entries = ie.find_previous_heads(
2092
2100
self.repository.weave_store,
2115
2123
:param text_sha1: Optional SHA1 of the file contents.
2116
2124
:param text_size: Optional size of the file contents.
2118
mutter('storing text of file {%s} in revision {%s} into %r',
2119
file_id, self._new_revision_id, self.repository.weave_store)
2126
# mutter('storing text of file {%s} in revision {%s} into %r',
2127
# file_id, self._new_revision_id, self.repository.weave_store)
2120
2128
# special case to avoid diffing on renames or
2122
2130
if (len(file_parents) == 1