1045
1100
if not self.get_parent_map([key]):
1046
1101
raise RevisionNotPresent(key, self)
1047
1102
return cached_version
1048
text_map, contents_map = self._get_content_maps([key])
1049
return contents_map[key]
1051
def _get_content_maps(self, keys, nonlocal_keys=None):
1052
"""Produce maps of text and KnitContents
1054
:param keys: The keys to produce content maps for.
1055
:param nonlocal_keys: An iterable of keys(possibly intersecting keys)
1056
which are known to not be in this knit, but rather in one of the
1058
:return: (text_map, content_map) where text_map contains the texts for
1059
the requested versions and content_map contains the KnitContents.
1061
# FUTURE: This function could be improved for the 'extract many' case
1062
# by tracking each component and only doing the copy when the number of
1063
# children than need to apply delta's to it is > 1 or it is part of the
1066
multiple_versions = len(keys) != 1
1067
record_map = self._get_record_map(keys, allow_missing=True)
1072
if nonlocal_keys is None:
1073
nonlocal_keys = set()
1075
nonlocal_keys = frozenset(nonlocal_keys)
1076
missing_keys = set(nonlocal_keys)
1077
for source in self._fallback_vfs:
1078
if not missing_keys:
1080
for record in source.get_record_stream(missing_keys,
1082
if record.storage_kind == 'absent':
1084
missing_keys.remove(record.key)
1085
lines = osutils.chunks_to_lines(record.get_bytes_as('chunked'))
1086
text_map[record.key] = lines
1087
content_map[record.key] = PlainKnitContent(lines, record.key)
1088
if record.key in keys:
1089
final_content[record.key] = content_map[record.key]
1091
if key in nonlocal_keys:
1096
while cursor is not None:
1098
record, record_details, digest, next = record_map[cursor]
1100
raise RevisionNotPresent(cursor, self)
1101
components.append((cursor, record, record_details, digest))
1103
if cursor in content_map:
1104
# no need to plan further back
1105
components.append((cursor, None, None, None))
1109
for (component_id, record, record_details,
1110
digest) in reversed(components):
1111
if component_id in content_map:
1112
content = content_map[component_id]
1114
content, delta = self._factory.parse_record(key[-1],
1115
record, record_details, content,
1116
copy_base_content=multiple_versions)
1117
if multiple_versions:
1118
content_map[component_id] = content
1120
final_content[key] = content
1122
# digest here is the digest from the last applied component.
1123
text = content.text()
1124
actual_sha = sha_strings(text)
1125
if actual_sha != digest:
1126
raise SHA1KnitCorrupt(self, actual_sha, digest, key, text)
1127
text_map[key] = text
1128
return text_map, final_content
1103
generator = _VFContentMapGenerator(self, [key])
1104
return generator._get_content(key)
1130
1106
def get_parent_map(self, keys):
1131
1107
"""Get a map of the graph parents of keys.
1838
class _ContentMapGenerator(object):
1839
"""Generate texts or expose raw deltas for a set of texts."""
1841
def _get_content(self, key):
1842
"""Get the content object for key."""
1843
if key in self.nonlocal_keys:
1844
record = self.get_record_stream().next()
1845
# Create a content object on the fly
1846
lines = osutils.chunks_to_lines(record.get_bytes_as('chunked'))
1847
return PlainKnitContent(lines, record.key)
1849
# local keys we can ask for directly
1850
return self._get_one_work(key)
1852
def get_record_stream(self):
1853
"""Get a record stream for the keys requested during __init__."""
1854
for record in self._work():
1858
"""Produce maps of text and KnitContents as dicts.
1860
:return: (text_map, content_map) where text_map contains the texts for
1861
the requested versions and content_map contains the KnitContents.
1863
# NB: By definition we never need to read remote sources unless texts
1864
# are requested from them: we don't delta across stores - and we
1865
# explicitly do not want to to prevent data loss situations.
1866
if self.global_map is None:
1867
self.global_map = self.vf.get_parent_map(self.keys)
1868
nonlocal_keys = self.nonlocal_keys
1870
missing_keys = set(nonlocal_keys)
1871
# Read from remote versioned file instances and provide to our caller.
1872
for source in self.vf._fallback_vfs:
1873
if not missing_keys:
1875
# Loop over fallback repositories asking them for texts - ignore
1876
# any missing from a particular fallback.
1877
for record in source.get_record_stream(missing_keys,
1879
if record.storage_kind == 'absent':
1880
# Not in thie particular stream, may be in one of the
1881
# other fallback vfs objects.
1883
missing_keys.remove(record.key)
1886
self._raw_record_map = self.vf._get_record_map_unparsed(self.keys,
1889
for key in self.keys:
1890
if key in self.nonlocal_keys:
1892
yield LazyKnitContentFactory(key, self.global_map[key], self, first)
1895
def _get_one_work(self, requested_key):
1896
# Now, if we have calculated everything already, just return the
1898
if requested_key in self._contents_map:
1899
return self._contents_map[requested_key]
1900
# To simply things, parse everything at once - code that wants one text
1901
# probably wants them all.
1902
# FUTURE: This function could be improved for the 'extract many' case
1903
# by tracking each component and only doing the copy when the number of
1904
# children than need to apply delta's to it is > 1 or it is part of the
1906
multiple_versions = len(self.keys) != 1
1907
if self._record_map is None:
1908
self._record_map = self.vf._raw_map_to_record_map(
1909
self._raw_record_map)
1910
record_map = self._record_map
1911
# raw_record_map is key:
1912
# Have read and parsed records at this point.
1913
for key in self.keys:
1914
if key in self.nonlocal_keys:
1919
while cursor is not None:
1921
record, record_details, digest, next = record_map[cursor]
1923
raise RevisionNotPresent(cursor, self)
1924
components.append((cursor, record, record_details, digest))
1926
if cursor in self._contents_map:
1927
# no need to plan further back
1928
components.append((cursor, None, None, None))
1932
for (component_id, record, record_details,
1933
digest) in reversed(components):
1934
if component_id in self._contents_map:
1935
content = self._contents_map[component_id]
1937
content, delta = self._factory.parse_record(key[-1],
1938
record, record_details, content,
1939
copy_base_content=multiple_versions)
1940
if multiple_versions:
1941
self._contents_map[component_id] = content
1943
# digest here is the digest from the last applied component.
1944
text = content.text()
1945
actual_sha = sha_strings(text)
1946
if actual_sha != digest:
1947
raise SHA1KnitCorrupt(self, actual_sha, digest, key, text)
1948
if multiple_versions:
1949
return self._contents_map[requested_key]
1953
def _wire_bytes(self):
1954
"""Get the bytes to put on the wire for 'key'.
1956
The first collection of bytes asked for returns the serialised
1957
raw_record_map and the additional details (key, parent) for key.
1958
Subsequent calls return just the additional details (key, parent).
1959
The wire storage_kind given for the first key is 'knit-delta-closure',
1960
For subsequent keys it is 'knit-delta-closure-ref'.
1962
:param key: A key from the content generator.
1963
:return: Bytes to put on the wire.
1966
# kind marker for dispatch on the far side,
1967
lines.append('knit-delta-closure')
1969
if self.vf._factory.annotated:
1970
lines.append('annotated')
1973
# then the list of keys
1974
lines.append('\t'.join(['\x00'.join(key) for key in self.keys
1975
if key not in self.nonlocal_keys]))
1976
# then the _raw_record_map in serialised form:
1978
# for each item in the map:
1980
# 1 line with parents if the key is to be yielded (None: for None, '' for ())
1981
# one line with method
1982
# one line with noeol
1983
# one line with next ('' for None)
1984
# one line with byte count of the record bytes
1986
for key, (record_bytes, (method, noeol), next) in \
1987
self._raw_record_map.iteritems():
1988
key_bytes = '\x00'.join(key)
1989
parents = self.global_map.get(key, None)
1991
parent_bytes = 'None:'
1993
parent_bytes = '\t'.join('\x00'.join(key) for key in parents)
1994
method_bytes = method
2000
next_bytes = '\x00'.join(next)
2003
map_byte_list.append('%s\n%s\n%s\n%s\n%s\n%d\n%s' % (
2004
key_bytes, parent_bytes, method_bytes, noeol_bytes, next_bytes,
2005
len(record_bytes), record_bytes))
2006
map_bytes = ''.join(map_byte_list)
2007
lines.append(map_bytes)
2008
bytes = '\n'.join(lines)
2012
class _VFContentMapGenerator(_ContentMapGenerator):
2013
"""Content map generator reading from a VersionedFiles object."""
2015
def __init__(self, versioned_files, keys, nonlocal_keys=None,
2016
global_map=None, raw_record_map=None):
2017
"""Create a _ContentMapGenerator.
2019
:param versioned_files: The versioned files that the texts are being
2021
:param keys: The keys to produce content maps for.
2022
:param nonlocal_keys: An iterable of keys(possibly intersecting keys)
2023
which are known to not be in this knit, but rather in one of the
2025
:param global_map: The result of get_parent_map(keys) (or a supermap).
2026
This is required if get_record_stream() is to be used.
2027
:param raw_record_map: A unparsed raw record map to use for answering
2030
# The vf to source data from
2031
self.vf = versioned_files
2033
self.keys = list(keys)
2034
# Keys known to be in fallback vfs objects
2035
if nonlocal_keys is None:
2036
self.nonlocal_keys = set()
2038
self.nonlocal_keys = frozenset(nonlocal_keys)
2039
# Parents data for keys to be returned in get_record_stream
2040
self.global_map = global_map
2041
# The chunked lists for self.keys in text form
2043
# A cache of KnitContent objects used in extracting texts.
2044
self._contents_map = {}
2045
# All the knit records needed to assemble the requested keys as full
2047
self._record_map = None
2048
if raw_record_map is None:
2049
self._raw_record_map = self.vf._get_record_map_unparsed(keys,
2052
self._raw_record_map = raw_record_map
2053
# the factory for parsing records
2054
self._factory = self.vf._factory
2057
class _NetworkContentMapGenerator(_ContentMapGenerator):
2058
"""Content map generator sourced from a network stream."""
2060
def __init__(self, bytes, line_end):
2061
"""Construct a _NetworkContentMapGenerator from a bytes block."""
2063
self.global_map = {}
2064
self._raw_record_map = {}
2065
self._contents_map = {}
2066
self._record_map = None
2067
self.nonlocal_keys = []
2068
# Get access to record parsing facilities
2069
self.vf = KnitVersionedFiles(None, None)
2072
line_end = bytes.find('\n', start)
2073
line = bytes[start:line_end]
2074
start = line_end + 1
2075
if line == 'annotated':
2076
self._factory = KnitAnnotateFactory()
2078
self._factory = KnitPlainFactory()
2079
# list of keys to emit in get_record_stream
2080
line_end = bytes.find('\n', start)
2081
line = bytes[start:line_end]
2082
start = line_end + 1
2084
tuple(segment.split('\x00')) for segment in line.split('\t')
2086
# now a loop until the end. XXX: It would be nice if this was just a
2087
# bunch of the same records as get_record_stream(..., False) gives, but
2088
# there is a decent sized gap stopping that at the moment.
2092
line_end = bytes.find('\n', start)
2093
key = tuple(bytes[start:line_end].split('\x00'))
2094
start = line_end + 1
2095
# 1 line with parents (None: for None, '' for ())
2096
line_end = bytes.find('\n', start)
2097
line = bytes[start:line_end]
2102
[tuple(segment.split('\x00')) for segment in line.split('\t')
2104
self.global_map[key] = parents
2105
start = line_end + 1
2106
# one line with method
2107
line_end = bytes.find('\n', start)
2108
line = bytes[start:line_end]
2110
start = line_end + 1
2111
# one line with noeol
2112
line_end = bytes.find('\n', start)
2113
line = bytes[start:line_end]
2115
start = line_end + 1
2116
# one line with next ('' for None)
2117
line_end = bytes.find('\n', start)
2118
line = bytes[start:line_end]
2122
next = tuple(bytes[start:line_end].split('\x00'))
2123
start = line_end + 1
2124
# one line with byte count of the record bytes
2125
line_end = bytes.find('\n', start)
2126
line = bytes[start:line_end]
2128
start = line_end + 1
2130
record_bytes = bytes[start:start+count]
2131
start = start + count
2133
self._raw_record_map[key] = (record_bytes, (method, noeol), next)
2135
def get_record_stream(self):
2136
"""Get a record stream for for keys requested by the bytestream."""
2138
for key in self.keys:
2139
yield LazyKnitContentFactory(key, self.global_map[key], self, first)
2142
def _wire_bytes(self):
1828
2146
class _KndxIndex(object):
1829
2147
"""Manages knit index files