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