bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
1  | 
# Copyright (C) 2008, 2009 Canonical Ltd
 | 
2  | 
#
 | 
|
3  | 
# This program is free software; you can redistribute it and/or modify
 | 
|
4  | 
# it under the terms of the GNU General Public License as published by
 | 
|
5  | 
# the Free Software Foundation; either version 2 of the License, or
 | 
|
6  | 
# (at your option) any later version.
 | 
|
7  | 
#
 | 
|
8  | 
# This program is distributed in the hope that it will be useful,
 | 
|
9  | 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|
10  | 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|
11  | 
# GNU General Public License for more details.
 | 
|
12  | 
#
 | 
|
13  | 
# You should have received a copy of the GNU General Public License
 | 
|
14  | 
# along with this program; if not, write to the Free Software
 | 
|
15  | 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
|
16  | 
||
17  | 
"""Inventory delta serialisation.
 | 
|
18  | 
||
| 
4205.5.6
by Andrew Bennetts
 Garden module docstring a little, cleanup unused imports and inaccurate __all__.  | 
19  | 
See doc/developers/inventory.txt for the description of the format.
 | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
20  | 
|
21  | 
In this module the interesting classes are:
 | 
|
| 
4205.5.6
by Andrew Bennetts
 Garden module docstring a little, cleanup unused imports and inaccurate __all__.  | 
22  | 
 - InventoryDeltaSerializer - object to read/write inventory deltas.
 | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
23  | 
"""
 | 
24  | 
||
| 
4205.5.6
by Andrew Bennetts
 Garden module docstring a little, cleanup unused imports and inaccurate __all__.  | 
25  | 
__all__ = ['InventoryDeltaSerializer']  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
26  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
27  | 
from bzrlib import errors  | 
| 
4205.5.6
by Andrew Bennetts
 Garden module docstring a little, cleanup unused imports and inaccurate __all__.  | 
28  | 
from bzrlib.osutils import basename  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
29  | 
from bzrlib import inventory  | 
30  | 
from bzrlib.revision import NULL_REVISION  | 
|
31  | 
||
32  | 
||
33  | 
def _directory_content(entry):  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
34  | 
"""Serialize the content component of entry which is a directory.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
35  | 
    
 | 
36  | 
    :param entry: An InventoryDirectory.
 | 
|
37  | 
    """
 | 
|
38  | 
return "dir"  | 
|
39  | 
||
40  | 
||
41  | 
def _file_content(entry):  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
42  | 
"""Serialize the content component of entry which is a file.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
43  | 
    
 | 
44  | 
    :param entry: An InventoryFile.
 | 
|
45  | 
    """
 | 
|
46  | 
if entry.executable:  | 
|
47  | 
exec_bytes = 'Y'  | 
|
48  | 
else:  | 
|
49  | 
exec_bytes = ''  | 
|
50  | 
size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1)  | 
|
51  | 
if None in size_exec_sha:  | 
|
52  | 
raise errors.BzrError('Missing size or sha for %s' % entry.file_id)  | 
|
53  | 
return "file\x00%d\x00%s\x00%s" % size_exec_sha  | 
|
54  | 
||
55  | 
||
56  | 
def _link_content(entry):  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
57  | 
"""Serialize the content component of entry which is a symlink.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
58  | 
    
 | 
59  | 
    :param entry: An InventoryLink.
 | 
|
60  | 
    """
 | 
|
61  | 
target = entry.symlink_target  | 
|
62  | 
if target is None:  | 
|
63  | 
raise errors.BzrError('Missing target for %s' % entry.file_id)  | 
|
64  | 
return "link\x00%s" % target.encode('utf8')  | 
|
65  | 
||
66  | 
||
67  | 
def _reference_content(entry):  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
68  | 
"""Serialize the content component of entry which is a tree-reference.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
69  | 
    
 | 
70  | 
    :param entry: A TreeReference.
 | 
|
71  | 
    """
 | 
|
72  | 
tree_revision = entry.reference_revision  | 
|
73  | 
if tree_revision is None:  | 
|
74  | 
raise errors.BzrError('Missing reference revision for %s' % entry.file_id)  | 
|
75  | 
return "tree\x00%s" % tree_revision  | 
|
76  | 
||
77  | 
||
78  | 
def _dir_to_entry(content, name, parent_id, file_id, last_modified,  | 
|
79  | 
_type=inventory.InventoryDirectory):  | 
|
80  | 
"""Convert a dir content record to an InventoryDirectory."""  | 
|
81  | 
result = _type(file_id, name, parent_id)  | 
|
82  | 
result.revision = last_modified  | 
|
83  | 
return result  | 
|
84  | 
||
85  | 
||
86  | 
def _file_to_entry(content, name, parent_id, file_id, last_modified,  | 
|
87  | 
_type=inventory.InventoryFile):  | 
|
88  | 
"""Convert a dir content record to an InventoryFile."""  | 
|
89  | 
result = _type(file_id, name, parent_id)  | 
|
90  | 
result.revision = last_modified  | 
|
91  | 
result.text_size = int(content[1])  | 
|
92  | 
result.text_sha1 = content[3]  | 
|
93  | 
if content[2]:  | 
|
94  | 
result.executable = True  | 
|
95  | 
else:  | 
|
96  | 
result.executable = False  | 
|
97  | 
return result  | 
|
98  | 
||
99  | 
||
100  | 
def _link_to_entry(content, name, parent_id, file_id, last_modified,  | 
|
101  | 
_type=inventory.InventoryLink):  | 
|
102  | 
"""Convert a link content record to an InventoryLink."""  | 
|
103  | 
result = _type(file_id, name, parent_id)  | 
|
104  | 
result.revision = last_modified  | 
|
105  | 
result.symlink_target = content[1].decode('utf8')  | 
|
106  | 
return result  | 
|
107  | 
||
108  | 
||
109  | 
def _tree_to_entry(content, name, parent_id, file_id, last_modified,  | 
|
110  | 
_type=inventory.TreeReference):  | 
|
111  | 
"""Convert a tree content record to a TreeReference."""  | 
|
112  | 
result = _type(file_id, name, parent_id)  | 
|
113  | 
result.revision = last_modified  | 
|
114  | 
result.reference_revision = content[1]  | 
|
115  | 
return result  | 
|
116  | 
||
117  | 
||
118  | 
||
119  | 
class InventoryDeltaSerializer(object):  | 
|
120  | 
"""Serialize and deserialize inventory deltas."""  | 
|
121  | 
||
122  | 
FORMAT_1 = 'bzr inventory delta v1 (bzr 1.14)'  | 
|
123  | 
||
124  | 
def __init__(self, versioned_root, tree_references):  | 
|
| 
4205.5.5
by Andrew Bennetts
 Reorganise tests a little.  | 
125  | 
"""Create an InventoryDeltaSerializer.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
126  | 
|
127  | 
        :param versioned_root: If True, any root entry that is seen is expected
 | 
|
128  | 
            to be versioned, and root entries can have any fileid.
 | 
|
129  | 
        :param tree_references: If True support tree-reference entries.
 | 
|
130  | 
        """
 | 
|
131  | 
self._versioned_root = versioned_root  | 
|
132  | 
self._tree_references = tree_references  | 
|
133  | 
self._entry_to_content = {  | 
|
134  | 
'directory': _directory_content,  | 
|
135  | 
'file': _file_content,  | 
|
136  | 
'symlink': _link_content,  | 
|
137  | 
        }
 | 
|
138  | 
if tree_references:  | 
|
139  | 
self._entry_to_content['tree-reference'] = _reference_content  | 
|
140  | 
||
141  | 
def delta_to_lines(self, old_name, new_name, delta_to_new):  | 
|
142  | 
"""Return a line sequence for delta_to_new.  | 
|
143  | 
||
144  | 
        :param old_name: A UTF8 revision id for the old inventory.  May be
 | 
|
145  | 
            NULL_REVISION if there is no older inventory and delta_to_new
 | 
|
146  | 
            includes the entire inventory contents.
 | 
|
147  | 
        :param new_name: The version name of the inventory we create with this
 | 
|
148  | 
            delta.
 | 
|
149  | 
        :param delta_to_new: An inventory delta such as Inventory.apply_delta
 | 
|
150  | 
            takes.
 | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
151  | 
        :return: The serialized delta as lines.
 | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
152  | 
        """
 | 
153  | 
lines = ['', '', '', '', '']  | 
|
154  | 
to_line = self._delta_item_to_line  | 
|
155  | 
for delta_item in delta_to_new:  | 
|
156  | 
lines.append(to_line(delta_item))  | 
|
157  | 
if lines[-1].__class__ != str:  | 
|
158  | 
raise errors.BzrError(  | 
|
159  | 
'to_line generated non-str output %r' % lines[-1])  | 
|
160  | 
lines.sort()  | 
|
161  | 
lines[0] = "format: %s\n" % InventoryDeltaSerializer.FORMAT_1  | 
|
162  | 
lines[1] = "parent: %s\n" % old_name  | 
|
163  | 
lines[2] = "version: %s\n" % new_name  | 
|
164  | 
lines[3] = "versioned_root: %s\n" % self._serialize_bool(  | 
|
165  | 
self._versioned_root)  | 
|
166  | 
lines[4] = "tree_references: %s\n" % self._serialize_bool(  | 
|
167  | 
self._tree_references)  | 
|
168  | 
return lines  | 
|
169  | 
||
170  | 
def _serialize_bool(self, value):  | 
|
171  | 
if value:  | 
|
172  | 
return "true"  | 
|
173  | 
else:  | 
|
174  | 
return "false"  | 
|
175  | 
||
176  | 
def _delta_item_to_line(self, delta_item):  | 
|
177  | 
"""Convert delta_item to a line."""  | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
178  | 
oldpath, newpath, file_id, entry = delta_item  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
179  | 
if newpath is None:  | 
180  | 
            # delete
 | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
181  | 
oldpath_utf8 = '/' + oldpath.encode('utf8')  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
182  | 
newpath_utf8 = 'None'  | 
183  | 
parent_id = ''  | 
|
184  | 
last_modified = NULL_REVISION  | 
|
185  | 
content = 'deleted\x00\x00'  | 
|
186  | 
else:  | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
187  | 
if oldpath is None:  | 
188  | 
oldpath_utf8 = 'None'  | 
|
189  | 
else:  | 
|
190  | 
oldpath_utf8 = '/' + oldpath.encode('utf8')  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
191  | 
            # TODO: Test real-world utf8 cache hit rate. It may be a win.
 | 
192  | 
newpath_utf8 = '/' + newpath.encode('utf8')  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
193  | 
            # Serialize None as ''
 | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
194  | 
parent_id = entry.parent_id or ''  | 
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
195  | 
            # Serialize unknown revisions as NULL_REVISION
 | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
196  | 
last_modified = entry.revision  | 
197  | 
            # special cases for /
 | 
|
198  | 
if newpath_utf8 == '/' and not self._versioned_root:  | 
|
199  | 
if file_id != 'TREE_ROOT':  | 
|
200  | 
raise errors.BzrError(  | 
|
201  | 
'file_id %s is not TREE_ROOT for /' % file_id)  | 
|
202  | 
if last_modified is not None:  | 
|
203  | 
raise errors.BzrError(  | 
|
204  | 
'Version present for / in %s' % file_id)  | 
|
205  | 
last_modified = NULL_REVISION  | 
|
206  | 
if last_modified is None:  | 
|
207  | 
raise errors.BzrError("no version for fileid %s" % file_id)  | 
|
208  | 
content = self._entry_to_content[entry.kind](entry)  | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
209  | 
return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" %  | 
210  | 
(oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,  | 
|
211  | 
content))  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
212  | 
|
213  | 
def _deserialize_bool(self, value):  | 
|
214  | 
if value == "true":  | 
|
215  | 
return True  | 
|
216  | 
elif value == "false":  | 
|
217  | 
return False  | 
|
218  | 
else:  | 
|
219  | 
raise errors.BzrError("value %r is not a bool" % (value,))  | 
|
220  | 
||
221  | 
def parse_text_bytes(self, bytes):  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
222  | 
"""Parse the text bytes of a serialized inventory delta.  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
223  | 
|
224  | 
        :param bytes: The bytes to parse. This can be obtained by calling
 | 
|
225  | 
            delta_to_lines and then doing ''.join(delta_lines).
 | 
|
226  | 
        :return: (parent_id, new_id, inventory_delta)
 | 
|
227  | 
        """
 | 
|
228  | 
lines = bytes.split('\n')[:-1] # discard the last empty line  | 
|
229  | 
if not lines or lines[0] != 'format: %s' % InventoryDeltaSerializer.FORMAT_1:  | 
|
230  | 
raise errors.BzrError('unknown format %r' % lines[0:1])  | 
|
231  | 
if len(lines) < 2 or not lines[1].startswith('parent: '):  | 
|
232  | 
raise errors.BzrError('missing parent: marker')  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
233  | 
delta_parent_id = lines[1][8:]  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
234  | 
if len(lines) < 3 or not lines[2].startswith('version: '):  | 
235  | 
raise errors.BzrError('missing version: marker')  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
236  | 
delta_version_id = lines[2][9:]  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
237  | 
if len(lines) < 4 or not lines[3].startswith('versioned_root: '):  | 
238  | 
raise errors.BzrError('missing versioned_root: marker')  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
239  | 
delta_versioned_root = self._deserialize_bool(lines[3][16:])  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
240  | 
if len(lines) < 5 or not lines[4].startswith('tree_references: '):  | 
241  | 
raise errors.BzrError('missing tree_references: marker')  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
242  | 
delta_tree_references = self._deserialize_bool(lines[4][17:])  | 
243  | 
if delta_versioned_root != self._versioned_root:  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
244  | 
raise errors.BzrError(  | 
245  | 
"serialized versioned_root flag is wrong: %s" %  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
246  | 
(delta_versioned_root,))  | 
247  | 
if delta_tree_references != self._tree_references:  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
248  | 
raise errors.BzrError(  | 
249  | 
"serialized tree_references flag is wrong: %s" %  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
250  | 
(delta_tree_references,))  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
251  | 
result = []  | 
252  | 
seen_ids = set()  | 
|
253  | 
line_iter = iter(lines)  | 
|
254  | 
for i in range(5):  | 
|
255  | 
line_iter.next()  | 
|
256  | 
for line in line_iter:  | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
257  | 
(oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified,  | 
258  | 
content) = line.split('\x00', 5)  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
259  | 
parent_id = parent_id or None  | 
260  | 
if file_id in seen_ids:  | 
|
261  | 
raise errors.BzrError(  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
262  | 
"duplicate file id in inventory delta %r" % lines)  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
263  | 
seen_ids.add(file_id)  | 
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
264  | 
if newpath_utf8 == '/' and not delta_versioned_root and (  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
265  | 
last_modified != 'null:' or file_id != 'TREE_ROOT'):  | 
266  | 
raise errors.BzrError("Versioned root found: %r" % line)  | 
|
267  | 
elif last_modified[-1] == ':':  | 
|
268  | 
raise errors.BzrError('special revisionid found: %r' % line)  | 
|
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
269  | 
if not delta_tree_references and content.startswith('tree\x00'):  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
270  | 
raise errors.BzrError("Tree reference found: %r" % line)  | 
271  | 
content_tuple = tuple(content.split('\x00'))  | 
|
272  | 
entry = _parse_entry(  | 
|
273  | 
newpath_utf8, file_id, parent_id, last_modified, content_tuple)  | 
|
| 
4205.5.3
by Andrew Bennetts
 Include oldpath in the the serialised delta  | 
274  | 
if oldpath_utf8 == 'None':  | 
275  | 
oldpath = None  | 
|
276  | 
else:  | 
|
277  | 
oldpath = oldpath_utf8.decode('utf8')  | 
|
278  | 
if newpath_utf8 == 'None':  | 
|
279  | 
newpath = None  | 
|
280  | 
else:  | 
|
281  | 
newpath = newpath_utf8.decode('utf8')  | 
|
282  | 
delta_item = (oldpath, newpath, file_id, entry)  | 
|
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
283  | 
result.append(delta_item)  | 
| 
4205.5.7
by Andrew Bennetts
 Fix nits in spelling and naming.  | 
284  | 
return delta_parent_id, delta_version_id, result  | 
| 
4205.5.1
by Andrew Bennetts
 Initial stab at adapting Robert's journalled_inventory serialisation into inventory_delta serialisation.  | 
285  | 
|
286  | 
||
287  | 
def _parse_entry(utf8_path, file_id, parent_id, last_modified, content):  | 
|
288  | 
entry_factory = {  | 
|
289  | 
'dir': _dir_to_entry,  | 
|
290  | 
'file': _file_to_entry,  | 
|
291  | 
'link': _link_to_entry,  | 
|
292  | 
'tree': _tree_to_entry,  | 
|
293  | 
    }
 | 
|
294  | 
kind = content[0]  | 
|
295  | 
path = utf8_path[1:].decode('utf8')  | 
|
296  | 
name = basename(path)  | 
|
297  | 
return entry_factory[content[0]](  | 
|
298  | 
content, name, parent_id, file_id, last_modified)  | 
|
299  |