1
# (C) 2005 Canonical Development 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
22
22
from bzrlib import errors
23
23
from bzrlib.bundle.serializer import (BundleSerializer,
28
26
from bzrlib.bundle.serializer import binary_diff
29
27
from bzrlib.bundle.bundle_data import (RevisionInfo, BundleInfo, BundleTree)
31
29
from bzrlib.osutils import pathjoin
32
30
from bzrlib.progress import DummyProgress
33
31
from bzrlib.revision import NULL_REVISION
34
from bzrlib.rio import RioWriter, read_stanzas
36
33
from bzrlib.testament import StrictTestament
34
from bzrlib.timestamp import (
37
38
from bzrlib.textfile import text_file
38
39
from bzrlib.trace import mutter
55
56
self.properties = properties
58
def add_utf8_property(self, name, value):
59
"""Add a property whose value is currently utf8 to the action."""
60
self.properties.append((name, value.decode('utf8')))
57
62
def add_property(self, name, value):
58
63
"""Add a property to the action"""
59
64
self.properties.append((name, value))
95
100
return BundleReader(f).info
102
def check_compatible(self):
103
if self.source.supports_rich_root():
104
raise errors.IncompatibleBundleFormat('0.8', repr(self.source))
97
106
def write(self, source, revision_ids, forced_bases, f):
98
107
"""Write the bundless to the supplied files.
131
def write_bundle(self, repository, target, base, fileobj):
132
return self._write_bundle(repository, target, base, fileobj)
121
134
def _write_main_header(self):
122
135
"""Write the header for the changes"""
124
f.write(BUNDLE_HEADER)
137
f.write(_get_bundle_header('0.8'))
128
def _write(self, key, value, indent=1):
129
"""Write out meta information, with proper indenting, etc"""
140
def _write(self, key, value, indent=1, trailing_space_when_empty=False):
141
"""Write out meta information, with proper indenting, etc.
143
:param trailing_space_when_empty: To work around a bug in earlier
144
bundle readers, when writing an empty property, we use "prop: \n"
145
rather than writing "prop:\n".
146
If this parameter is True, and value is the empty string, we will
147
write an extra space.
130
149
assert indent > 0, 'indentation must be greater than 0'
132
151
f.write('#' + (' ' * indent))
133
152
f.write(key.encode('utf-8'))
136
elif isinstance(value, basestring):
154
if trailing_space_when_empty and value == '':
158
elif isinstance(value, str):
162
elif isinstance(value, unicode):
138
164
f.write(value.encode('utf-8'))
142
168
for entry in value:
143
169
f.write('#' + (' ' * (indent+2)))
144
f.write(entry.encode('utf-8'))
170
if isinstance(entry, str):
173
f.write(entry.encode('utf-8'))
147
176
def _write_revisions(self, pb):
151
180
last_rev_id = None
152
181
last_rev_tree = None
154
i_max = len(self.revision_ids)
183
i_max = len(self.revision_ids)
155
184
for i, rev_id in enumerate(self.revision_ids):
156
185
pb.update("Generating revsion data", i, i_max)
157
186
rev = self.source.get_revision(rev_id)
158
187
if rev_id == last_rev_id:
159
188
rev_tree = last_rev_tree
161
base_tree = self.source.revision_tree(rev_id)
162
rev_tree = self.source.revision_tree(rev_id)
190
rev_tree = self.source.revision_tree(rev_id)
163
191
if rev_id in self.forced_bases:
164
192
explicit_base = True
165
193
base_id = self.forced_bases[rev_id]
183
211
last_rev_id = base_id
184
212
last_rev_tree = base_tree
214
def _testament_sha1(self, revision_id):
215
return StrictTestament.from_revision(self.source,
216
revision_id).as_sha1()
186
218
def _write_revision(self, rev, rev_tree, base_rev, base_tree,
187
219
explicit_base, force_binary):
188
220
"""Write out the information for a revision."""
197
229
self._write_delta(rev_tree, base_tree, rev.revision_id, force_binary)
199
231
w('revision id', rev.revision_id)
200
w('sha1', StrictTestament.from_revision(self.source,
201
rev.revision_id).as_sha1())
232
w('sha1', self._testament_sha1(rev.revision_id))
202
233
w('inventory sha1', rev.inventory_sha1)
203
234
if rev.parent_ids:
204
235
w('parent ids', rev.parent_ids)
206
237
w('base id', base_rev)
207
238
if rev.properties:
208
239
self._write('properties', None, indent=1)
209
for name, value in rev.properties.items():
210
self._write(name, value, indent=3)
240
for name, value in sorted(rev.properties.items()):
241
self._write(name, value, indent=3,
242
trailing_space_when_empty=True)
212
244
# Add an extra blank space at the end
213
245
self.to_file.write('\n')
258
290
old_path, new_path):
259
291
entry = new_tree.inventory[file_id]
260
292
if entry.revision != default_revision_id:
261
action.add_property('last-changed', entry.revision)
293
action.add_utf8_property('last-changed', entry.revision)
262
294
if meta_modified:
263
295
action.add_bool_property('executable', entry.executable)
264
296
if text_modified and kind == "symlink":
269
301
action.write(self.to_file)
271
delta = new_tree.changes_from(old_tree, want_unchanged=True)
303
delta = new_tree.changes_from(old_tree, want_unchanged=True,
272
305
for path, file_id, kind in delta.removed:
273
306
action = Action('removed', [kind, path]).write(self.to_file)
300
333
if new_rev != old_rev:
301
334
action = Action('modified', [ie.kind,
302
335
new_tree.id2path(ie.file_id)])
303
action.add_property('last-changed', ie.revision)
336
action.add_utf8_property('last-changed', ie.revision)
304
337
action.write(self.to_file)
317
350
self.from_file = iter(from_file)
318
351
self._next_line = None
320
self.info = BundleInfo08()
353
self.info = self._get_info()
321
354
# We put the actual inventory ids in the footer, so that the patch
322
355
# is easier to read for humans.
323
356
# Unfortunately, that means we need to read everything before we
414
450
revision_info = self.info.revisions[-1]
415
if hasattr(revision_info, key):
451
if key in revision_info.__dict__:
416
452
if getattr(revision_info, key) is None:
453
if key in ('file_id', 'revision_id', 'base_id'):
454
value = value.encode('utf8')
455
elif key in ('parent_ids'):
456
value = [v.encode('utf8') for v in value]
417
457
setattr(revision_info, key, value)
419
459
raise errors.MalformedHeader('Duplicated Key: %s' % key)
501
541
self._next().next()
505
544
class BundleInfo08(BundleInfo):
506
546
def _update_tree(self, bundle_tree, revision_id):
507
547
bundle_tree.note_last_changed('', revision_id)
508
548
BundleInfo._update_tree(self, bundle_tree, revision_id)
550
def _testament_sha1_from_revision(self, repository, revision_id):
551
testament = StrictTestament.from_revision(repository, revision_id)
552
return testament.as_sha1()
554
def _testament_sha1(self, revision, inventory):
555
return StrictTestament(revision, inventory).as_sha1()