1
# Copyright (C) 2005, 2006, 2009 Canonical Ltd
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
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
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Serializer factory for reading and writing bundles.
20
from __future__ import absolute_import
22
from bzrlib import errors
26
23
from bzrlib.bundle.serializer import (BundleSerializer,
27
24
_get_bundle_header,
29
26
from bzrlib.bundle.serializer import binary_diff
30
from bzrlib.bundle.bundle_data import (RevisionInfo, BundleInfo)
27
from bzrlib.bundle.bundle_data import (RevisionInfo, BundleInfo, BundleTree)
31
28
from bzrlib.diff import internal_diff
29
from bzrlib.osutils import pathjoin
30
from bzrlib.progress import DummyProgress
32
31
from bzrlib.revision import NULL_REVISION
33
33
from bzrlib.testament import StrictTestament
34
34
from bzrlib.timestamp import (
35
35
format_highres_date,
37
38
from bzrlib.textfile import text_file
38
39
from bzrlib.trace import mutter
144
146
If this parameter is True, and value is the empty string, we will
145
147
write an extra space.
148
raise ValueError('indentation must be greater than 0')
149
assert indent > 0, 'indentation must be greater than 0'
150
151
f.write('#' + (' ' * indent))
151
152
f.write(key.encode('utf-8'))
182
183
i_max = len(self.revision_ids)
183
184
for i, rev_id in enumerate(self.revision_ids):
184
pb.update("Generating revision data", i, i_max)
185
pb.update("Generating revsion data", i, i_max)
185
186
rev = self.source.get_revision(rev_id)
186
187
if rev_id == last_rev_id:
187
188
rev_tree = last_rev_tree
205
206
base_tree = self.source.revision_tree(base_id)
206
207
force_binary = (i != 0)
207
self._write_revision(rev, rev_tree, base_id, base_tree,
208
self._write_revision(rev, rev_tree, base_id, base_tree,
208
209
explicit_base, force_binary)
210
211
last_rev_id = base_id
211
212
last_rev_tree = base_tree
213
214
def _testament_sha1(self, revision_id):
214
return StrictTestament.from_revision(self.source,
215
return StrictTestament.from_revision(self.source,
215
216
revision_id).as_sha1()
217
def _write_revision(self, rev, rev_tree, base_rev, base_tree,
218
def _write_revision(self, rev, rev_tree, base_rev, base_tree,
218
219
explicit_base, force_binary):
219
220
"""Write out the information for a revision."""
220
221
def w(key, value):
239
240
for name, value in sorted(rev.properties.items()):
240
241
self._write(name, value, indent=3,
241
242
trailing_space_when_empty=True)
243
244
# Add an extra blank space at the end
244
245
self.to_file.write('\n')
252
253
self.to_file.write(' // '.join(p_texts).encode('utf-8'))
253
254
self.to_file.write('\n')
255
def _write_delta(self, new_tree, old_tree, default_revision_id,
256
def _write_delta(self, new_tree, old_tree, default_revision_id,
257
258
"""Write out the changes between the trees."""
258
259
DEVNULL = '/dev/null'
262
263
def do_diff(file_id, old_path, new_path, action, force_binary):
263
264
def tree_lines(tree, require_text=False):
264
if tree.has_id(file_id):
265
266
tree_file = tree.get_file(file_id)
266
267
if require_text is True:
267
268
tree_file = text_file(tree_file)
275
276
old_lines = tree_lines(old_tree, require_text=True)
276
277
new_lines = tree_lines(new_tree, require_text=True)
277
278
action.write(self.to_file)
278
internal_diff(old_path, old_lines, new_path, new_lines,
279
internal_diff(old_path, old_lines, new_path, new_lines,
280
281
except errors.BinaryFile:
281
282
old_lines = tree_lines(old_tree, require_text=False)
282
283
new_lines = tree_lines(new_tree, require_text=False)
283
284
action.add_property('encoding', 'base64')
284
285
action.write(self.to_file)
285
binary_diff(old_path, old_lines, new_path, new_lines,
286
binary_diff(old_path, old_lines, new_path, new_lines,
288
289
def finish_action(action, file_id, kind, meta_modified, text_modified,
289
290
old_path, new_path):
290
entry = new_tree.root_inventory[file_id]
291
entry = new_tree.inventory[file_id]
291
292
if entry.revision != default_revision_id:
292
293
action.add_utf8_property('last-changed', entry.revision)
293
294
if meta_modified:
307
308
for path, file_id, kind in delta.added:
308
309
action = Action('added', [kind, path], [('file-id', file_id)])
309
meta_modified = (kind=='file' and
310
meta_modified = (kind=='file' and
310
311
new_tree.is_executable(file_id))
311
312
finish_action(action, file_id, kind, meta_modified, True,
326
327
for path, file_id, kind in delta.unchanged:
327
new_rev = new_tree.get_file_revision(file_id)
328
ie = new_tree.inventory[file_id]
329
new_rev = getattr(ie, 'revision', None)
328
330
if new_rev is None:
330
old_rev = old_tree.get_file_revision(file_id)
332
old_rev = getattr(old_tree.inventory[ie.file_id], 'revision', None)
331
333
if new_rev != old_rev:
332
action = Action('modified', [new_tree.kind(file_id),
333
new_tree.id2path(file_id)])
334
action.add_utf8_property('last-changed', new_rev)
334
action = Action('modified', [ie.kind,
335
new_tree.id2path(ie.file_id)])
336
action.add_utf8_property('last-changed', ie.revision)
335
337
action.write(self.to_file)
347
349
object.__init__(self)
348
350
self.from_file = iter(from_file)
349
351
self._next_line = None
351
353
self.info = self._get_info()
352
354
# We put the actual inventory ids in the footer, so that the patch
353
355
# is easier to read for humans.
459
461
# What do we do with a key we don't recognize
460
462
raise errors.MalformedHeader('Unknown Key: "%s"' % key)
462
464
def _read_many(self, indent):
463
465
"""If a line ends with no entry, that means that it should be
464
466
followed with multiple lines of values.
501
503
elif line.startswith('... '):
502
504
action += line[len('... '):-1].decode('utf-8')
504
if (self._next_line is not None and
506
if (self._next_line is not None and
505
507
self._next_line.startswith('===')):
506
508
return action, lines, True
507
509
elif self._next_line is None or self._next_line.startswith('#'):
521
523
action, lines, do_continue = self._read_one_patch()
522
524
if action is not None:
523
525
revision_actions.append((action, lines))
524
if self.info.revisions[-1].tree_actions is not None:
525
raise AssertionError()
526
assert self.info.revisions[-1].tree_actions is None
526
527
self.info.revisions[-1].tree_actions = revision_actions
528
529
def _read_footer(self):
550
551
testament = StrictTestament.from_revision(repository, revision_id)
551
552
return testament.as_sha1()
553
def _testament_sha1(self, revision, tree):
554
return StrictTestament(revision, tree).as_sha1()
554
def _testament_sha1(self, revision, inventory):
555
return StrictTestament(revision, inventory).as_sha1()