~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-24 13:14:06 UTC
  • mto: (6445.4.5 nested-trees-spec)
  • mto: This revision was merged to the branch mainline in revision 6518.
  • Revision ID: jelmer@samba.org-20120124131406-wedftkorbpv37bm0
Import nested tree doc from devnotes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
2
 
#
3
 
# Authors:
4
 
#   Johan Rydberg <jrydberg@gnu.org>
 
1
# Copyright (C) 2006-2011 Canonical Ltd
5
2
#
6
3
# This program is free software; you can redistribute it and/or modify
7
4
# it under the terms of the GNU General Public License as published by
19
16
 
20
17
"""Versioned text file storage api."""
21
18
 
 
19
from __future__ import absolute_import
 
20
 
22
21
from copy import copy
23
22
from cStringIO import StringIO
24
23
import os
27
26
 
28
27
from bzrlib.lazy_import import lazy_import
29
28
lazy_import(globals(), """
30
 
import urllib
31
 
 
32
29
from bzrlib import (
33
30
    annotate,
 
31
    bencode,
34
32
    errors,
35
33
    graph as _mod_graph,
36
34
    groupcompress,
40
38
    multiparent,
41
39
    tsort,
42
40
    revision,
43
 
    ui,
 
41
    urlutils,
44
42
    )
45
 
from bzrlib.graph import DictParentsProvider, Graph, StackedParentsProvider
46
 
from bzrlib.transport.memory import MemoryTransport
47
43
""")
48
44
from bzrlib.registry import Registry
49
45
from bzrlib.textmerge import TextMerge
50
 
from bzrlib import bencode
51
46
 
52
47
 
53
48
adapter_registry = Registry()
827
822
 
828
823
    def map(self, key):
829
824
        """See KeyMapper.map()."""
830
 
        return urllib.quote(self._map(key))
 
825
        return urlutils.quote(self._map(key))
831
826
 
832
827
    def unmap(self, partition_id):
833
828
        """See KeyMapper.unmap()."""
834
 
        return self._unmap(urllib.unquote(partition_id))
 
829
        return self._unmap(urlutils.unquote(partition_id))
835
830
 
836
831
 
837
832
class PrefixMapper(URLEscapeMapper):
884
879
    def _escape(self, prefix):
885
880
        """Turn a key element into a filesystem safe string.
886
881
 
887
 
        This is similar to a plain urllib.quote, except
 
882
        This is similar to a plain urlutils.quote, except
888
883
        it uses specific safe characters, so that it doesn't
889
884
        have to translate a lot of valid file ids.
890
885
        """
897
892
 
898
893
    def _unescape(self, basename):
899
894
        """Escaped names are easily unescaped by urlutils."""
900
 
        return urllib.unquote(basename)
 
895
        return urlutils.unquote(basename)
901
896
 
902
897
 
903
898
def make_versioned_files_factory(versioned_file_factory, mapper):
930
925
 
931
926
    The use of tuples allows a single code base to support several different
932
927
    uses with only the mapping logic changing from instance to instance.
 
928
 
 
929
    :ivar _immediate_fallback_vfs: For subclasses that support stacking,
 
930
        this is a list of other VersionedFiles immediately underneath this
 
931
        one.  They may in turn each have further fallbacks.
933
932
    """
934
933
 
935
934
    def add_lines(self, key, parents, lines, parent_texts=None,
974
973
    def _add_text(self, key, parents, text, nostore_sha=None, random_id=False):
975
974
        """Add a text to the store.
976
975
 
977
 
        This is a private function for use by CommitBuilder.
 
976
        This is a private function for use by VersionedFileCommitBuilder.
978
977
 
979
978
        :param key: The key tuple of the text to add. If the last element is
980
979
            None, a CHK string will be generated during the addition.
1193
1192
    def _extract_blocks(self, version_id, source, target):
1194
1193
        return None
1195
1194
 
 
1195
    def _transitive_fallbacks(self):
 
1196
        """Return the whole stack of fallback versionedfiles.
 
1197
 
 
1198
        This VersionedFiles may have a list of fallbacks, but it doesn't
 
1199
        necessarily know about the whole stack going down, and it can't know
 
1200
        at open time because they may change after the objects are opened.
 
1201
        """
 
1202
        all_fallbacks = []
 
1203
        for a_vfs in self._immediate_fallback_vfs:
 
1204
            all_fallbacks.append(a_vfs)
 
1205
            all_fallbacks.extend(a_vfs._transitive_fallbacks())
 
1206
        return all_fallbacks
 
1207
 
1196
1208
 
1197
1209
class ThunkedVersionedFiles(VersionedFiles):
1198
1210
    """Storage for many versioned files thunked onto a 'VersionedFile' class.
1411
1423
        return result
1412
1424
 
1413
1425
 
 
1426
class VersionedFilesWithFallbacks(VersionedFiles):
 
1427
 
 
1428
    def without_fallbacks(self):
 
1429
        """Return a clone of this object without any fallbacks configured."""
 
1430
        raise NotImplementedError(self.without_fallbacks)
 
1431
 
 
1432
    def add_fallback_versioned_files(self, a_versioned_files):
 
1433
        """Add a source of texts for texts not present in this knit.
 
1434
 
 
1435
        :param a_versioned_files: A VersionedFiles object.
 
1436
        """
 
1437
        raise NotImplementedError(self.add_fallback_versioned_files)
 
1438
 
 
1439
    def get_known_graph_ancestry(self, keys):
 
1440
        """Get a KnownGraph instance with the ancestry of keys."""
 
1441
        parent_map, missing_keys = self._index.find_ancestry(keys)
 
1442
        for fallback in self._transitive_fallbacks():
 
1443
            if not missing_keys:
 
1444
                break
 
1445
            (f_parent_map, f_missing_keys) = fallback._index.find_ancestry(
 
1446
                                                missing_keys)
 
1447
            parent_map.update(f_parent_map)
 
1448
            missing_keys = f_missing_keys
 
1449
        kg = _mod_graph.KnownGraph(parent_map)
 
1450
        return kg
 
1451
 
 
1452
 
1414
1453
class _PlanMergeVersionedFile(VersionedFiles):
1415
1454
    """A VersionedFile for uncommitted and committed texts.
1416
1455
 
1437
1476
        # line data for locally held keys.
1438
1477
        self._lines = {}
1439
1478
        # key lookup providers
1440
 
        self._providers = [DictParentsProvider(self._parents)]
 
1479
        self._providers = [_mod_graph.DictParentsProvider(self._parents)]
1441
1480
 
1442
1481
    def plan_merge(self, ver_a, ver_b, base=None):
1443
1482
        """See VersionedFile.plan_merge"""
1450
1489
 
1451
1490
    def plan_lca_merge(self, ver_a, ver_b, base=None):
1452
1491
        from bzrlib.merge import _PlanLCAMerge
1453
 
        graph = Graph(self)
 
1492
        graph = _mod_graph.Graph(self)
1454
1493
        new_plan = _PlanLCAMerge(ver_a, ver_b, self, (self._file_id,), graph).plan_merge()
1455
1494
        if base is None:
1456
1495
            return new_plan
1508
1547
            result[revision.NULL_REVISION] = ()
1509
1548
        self._providers = self._providers[:1] + self.fallback_versionedfiles
1510
1549
        result.update(
1511
 
            StackedParentsProvider(self._providers).get_parent_map(keys))
 
1550
            _mod_graph.StackedParentsProvider(
 
1551
                self._providers).get_parent_map(keys))
1512
1552
        for key, parents in result.iteritems():
1513
1553
            if parents == ():
1514
1554
                result[key] = (revision.NULL_REVISION,)
1860
1900
    for prefix in sorted(per_prefix_map):
1861
1901
        present_keys.extend(reversed(tsort.topo_sort(per_prefix_map[prefix])))
1862
1902
    return present_keys
 
1903
 
 
1904
 
 
1905
class _KeyRefs(object):
 
1906
 
 
1907
    def __init__(self, track_new_keys=False):
 
1908
        # dict mapping 'key' to 'set of keys referring to that key'
 
1909
        self.refs = {}
 
1910
        if track_new_keys:
 
1911
            # set remembering all new keys
 
1912
            self.new_keys = set()
 
1913
        else:
 
1914
            self.new_keys = None
 
1915
 
 
1916
    def clear(self):
 
1917
        if self.refs:
 
1918
            self.refs.clear()
 
1919
        if self.new_keys:
 
1920
            self.new_keys.clear()
 
1921
 
 
1922
    def add_references(self, key, refs):
 
1923
        # Record the new references
 
1924
        for referenced in refs:
 
1925
            try:
 
1926
                needed_by = self.refs[referenced]
 
1927
            except KeyError:
 
1928
                needed_by = self.refs[referenced] = set()
 
1929
            needed_by.add(key)
 
1930
        # Discard references satisfied by the new key
 
1931
        self.add_key(key)
 
1932
 
 
1933
    def get_new_keys(self):
 
1934
        return self.new_keys
 
1935
    
 
1936
    def get_unsatisfied_refs(self):
 
1937
        return self.refs.iterkeys()
 
1938
 
 
1939
    def _satisfy_refs_for_key(self, key):
 
1940
        try:
 
1941
            del self.refs[key]
 
1942
        except KeyError:
 
1943
            # No keys depended on this key.  That's ok.
 
1944
            pass
 
1945
 
 
1946
    def add_key(self, key):
 
1947
        # satisfy refs for key, and remember that we've seen this key.
 
1948
        self._satisfy_refs_for_key(key)
 
1949
        if self.new_keys is not None:
 
1950
            self.new_keys.add(key)
 
1951
 
 
1952
    def satisfy_refs_for_keys(self, keys):
 
1953
        for key in keys:
 
1954
            self._satisfy_refs_for_key(key)
 
1955
 
 
1956
    def get_referrers(self):
 
1957
        result = set()
 
1958
        for referrers in self.refs.itervalues():
 
1959
            result.update(referrers)
 
1960
        return result
 
1961
 
 
1962
 
 
1963