~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-16 22:00:19 UTC
  • mto: This revision was merged to the branch mainline in revision 1942.
  • Revision ID: john@arbash-meinel.com-20060816220019-541cb90093258ac3
Using real utf8 and cache_utf8 has similar performance, 272ms, and 363ms

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
 
 
 
2
#
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.
7
 
 
 
7
#
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.
12
 
 
 
12
#
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
20
20
import re
21
21
import time
22
22
from unittest import TestSuite
 
23
from warnings import warn
23
24
 
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, 
34
35
                            local_time_offset)
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,
39
41
        zero_nine, 
40
42
        )
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
47
47
 
48
48
 
 
49
# Old formats display a warning, but only once
 
50
_deprecation_warning_done = False
 
51
 
 
52
 
49
53
class Repository(object):
50
54
    """Repository holding history for one or more branches.
51
55
 
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()
192
198
 
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.
258
264
        """
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)
261
267
 
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)
351
357
            else:
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)
354
360
 
355
361
    @needs_read_lock
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.
446
452
        """
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
 
455
        return result
448
456
 
449
457
    @needs_read_lock
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.
567
575
 
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.
 
577
        """
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:
573
 
            return EmptyTree()
 
581
            return RevisionTree(self, Inventory(), NULL_REVISION)
574
582
        else:
575
583
            inv = self.get_revision_inventory(revision_id)
576
584
            return RevisionTree(self, inv, revision_id)
681
689
        result.check()
682
690
        return result
683
691
 
 
692
    def _warn_if_deprecated(self):
 
693
        global _deprecation_warning_done
 
694
        if _deprecation_warning_done:
 
695
            return
 
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))
 
699
 
684
700
 
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)
748
764
        else:
749
 
            parent_trees[p_id] = EmptyTree()
 
765
            parent_trees[p_id] = repository.revision_tree(None)
750
766
 
751
767
    inv = revision_tree.inventory
752
768
    
 
769
    # backwards compatability hack: skip the root id.
 
770
    entries = inv.iter_entries()
 
771
    entries.next()
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:
790
809
                                                _revision_store,
791
810
                                                control_store,
792
811
                                                text_store)
793
 
 
794
812
        dir_mode = self.control_files._dir_mode
795
813
        file_mode = self.control_files._file_mode
796
814
 
825
843
class KnitRepository(MetaDirRepository):
826
844
    """Knit format repository."""
827
845
 
 
846
    def _warn_if_deprecated(self):
 
847
        # This class isn't deprecated
 
848
        pass
 
849
 
828
850
    def _inventory_add_lines(self, inv_vf, revid, parents, lines):
829
851
        inv_vf.add_lines_with_ghosts(revid, parents, lines)
830
852
 
997
1019
    _formats = {}
998
1020
    """The known formats."""
999
1021
 
 
1022
    def __str__(self):
 
1023
        return "<%s>" % self.__class__.__name__
 
1024
 
1000
1025
    @classmethod
1001
1026
    def find_format(klass, a_bzrdir):
1002
1027
        """Return the format for the repository object in a_bzrdir."""
1579
1604
            return
1580
1605
        self.target.fetch(self.source, revision_id=revision_id)
1581
1606
 
1582
 
    def _double_lock(self, lock_source, lock_target):
1583
 
        """Take out too locks, rolling back the first if the second throws."""
1584
 
        lock_source()
1585
 
        try:
1586
 
            lock_target()
1587
 
        except Exception:
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()
1591
 
            raise
1592
 
 
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.
1613
1627
                               pb=pb)
1614
1628
        return f.count_copied, f.failed_revisions
1615
1629
 
1616
 
    def lock_read(self):
1617
 
        """Take out a logical read lock.
1618
 
 
1619
 
        This will lock the source branch and the target branch. The source gets
1620
 
        a read lock and the target a read lock.
1621
 
        """
1622
 
        self._double_lock(self.source.lock_read, self.target.lock_read)
1623
 
 
1624
 
    def lock_write(self):
1625
 
        """Take out a logical write lock.
1626
 
 
1627
 
        This will lock the source branch and the target branch. The source gets
1628
 
        a read lock and the target a write lock.
1629
 
        """
1630
 
        self._double_lock(self.source.lock_read, self.target.lock_write)
1631
 
 
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]
1654
1652
 
1655
 
    def unlock(self):
1656
 
        """Release the locks on source and target."""
1657
 
        try:
1658
 
            self.target.unlock()
1659
 
        finally:
1660
 
            self.source.unlock()
1661
 
 
1662
1653
 
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.
1984
1975
    """
 
1976
    
 
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
2006
1999
 
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)
2015
2008
 
2016
2009
        if timestamp is None:
2017
 
            self._timestamp = time.time()
2018
 
        else:
2019
 
            self._timestamp = long(timestamp)
 
2010
            timestamp = time.time()
 
2011
        # Restrict resolution to 1ms
 
2012
        self._timestamp = round(timestamp, 3)
2020
2013
 
2021
2014
        if timezone is None:
2022
2015
            self._timezone = local_time_offset()
2044
2037
 
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'
 
2042
                 ' of bzr 0.10.',
 
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.
2075
2073
 
 
2074
        Side effect: sets ie.revision when unchanged
 
2075
 
2076
2076
        :param ie: An inventory entry present in the commit.
2077
2077
        :param parent_invs: The inventories of the parent revisions of the
2078
2078
            commit.
2087
2087
        # which may be the sole parent if it is untouched.
2088
2088
        if ie.revision is not None:
2089
2089
            return
 
2090
 
 
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
 
2095
            else:
 
2096
                ie.revision = None
 
2097
            return
2090
2098
        previous_entries = ie.find_previous_heads(
2091
2099
            parent_invs,
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.
2117
2125
        """
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 
2121
2129
        # reparenting
2122
2130
        if (len(file_parents) == 1
2152
2160
        versionedfile.clear_cache()
2153
2161
 
2154
2162
 
 
2163
class _CommitBuilder(CommitBuilder):
 
2164
    """Temporary class so old CommitBuilders are detected properly
 
2165
    
 
2166
    Note: CommitBuilder works whether or not root entry is recorded.
 
2167
    """
 
2168
 
 
2169
    record_root_entry = True
 
2170
 
 
2171
 
2155
2172
_unescape_map = {
2156
2173
    'apos':"'",
2157
2174
    'quot':'"',