14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from cStringIO import StringIO
17
from __future__ import absolute_import
29
29
revision as _mod_revision,
34
from bzrlib.bundle import bundle_data, serializer as bundle_serializer
35
from bzrlib import bencode
36
versionedfile as _mod_versionedfile,
38
from ...bundle import bundle_data, serializer as bundle_serializer
39
from ...i18n import ngettext
40
from ...sixish import (
46
class _MPDiffInventoryGenerator(_mod_versionedfile._MPDiffGenerator):
47
"""Generate Inventory diffs serialized inventories."""
49
def __init__(self, repo, inventory_keys):
50
super(_MPDiffInventoryGenerator, self).__init__(repo.inventories,
56
"""Compute the diffs one at a time."""
57
# This is instead of compute_diffs() since we guarantee our ordering of
58
# inventories, we don't have to do any buffering
59
self._find_needed_keys()
60
# We actually use a slightly different ordering. We grab all of the
61
# parents first, and then grab the ordered requests.
62
needed_ids = [k[-1] for k in self.present_parents]
63
needed_ids.extend([k[-1] for k in self.ordered_keys])
64
inv_to_str = self.repo._serializer.write_inventory_to_string
65
for inv in self.repo.iter_inventories(needed_ids):
66
revision_id = inv.revision_id
68
if key in self.present_parents:
69
# Not a key we will transmit, which is a shame, since because
70
# of that bundles don't work with stacked branches
73
parent_ids = [k[-1] for k in self.parent_map[key]]
74
as_bytes = inv_to_str(inv)
75
self._process_one_record(key, (as_bytes,))
76
if parent_ids is None:
78
diff = self.diffs.pop(key)
79
sha1 = osutils.sha_string(as_bytes)
80
yield revision_id, parent_ids, sha1, diff
38
83
class BundleWriter(object):
281
326
parents = graph.get_parent_map(revision_ids)
282
327
self.revision_ids = [r for r in revision_ids if r in parents]
283
self.revision_keys = set([(revid,) for revid in self.revision_ids])
328
self.revision_keys = {(revid,) for revid in self.revision_ids}
285
330
def do_write(self):
286
331
"""Write all data to the bundle"""
287
trace.note('Bundling %d revision(s).', len(self.revision_ids))
288
self.repository.lock_read()
332
trace.note(ngettext('Bundling %d revision.', 'Bundling %d revisions.',
333
len(self.revision_ids)), len(self.revision_ids))
334
with self.repository.lock_read():
290
335
self.bundle.begin()
291
336
self.write_info()
292
337
self.write_files()
293
338
self.write_revisions()
294
339
self.bundle.end()
296
self.repository.unlock()
297
340
return self.revision_ids
299
342
def write_info(self):
310
353
altered_fileids = self.repository.fileids_altered_by_revision_ids(
311
354
self.revision_ids)
312
for file_id, revision_ids in altered_fileids.iteritems():
355
for file_id, revision_ids in viewitems(altered_fileids):
313
356
for revision_id in revision_ids:
314
357
text_keys.append((file_id, revision_id))
315
358
self._add_mp_records_keys('file', self.repository.texts, text_keys)
350
393
inventory_key_order = [(r,) for r in revision_order]
351
parent_map = self.repository.inventories.get_parent_map(
353
missing_keys = set(inventory_key_order).difference(parent_map)
355
raise errors.RevisionNotPresent(list(missing_keys)[0],
356
self.repository.inventories)
357
inv_to_str = self.repository._serializer.write_inventory_to_string
358
# Make sure that we grab the parent texts first
360
map(just_parents.update, parent_map.itervalues())
361
just_parents.difference_update(parent_map)
362
# Ignore ghost parents
363
present_parents = self.repository.inventories.get_parent_map(
365
ghost_keys = just_parents.difference(present_parents)
366
needed_inventories = list(present_parents) + inventory_key_order
367
needed_inventories = [k[-1] for k in needed_inventories]
369
for inv in self.repository.iter_inventories(needed_inventories):
370
revision_id = inv.revision_id
372
as_bytes = inv_to_str(inv)
373
# The sha1 is validated as the xml/textual form, not as the
374
# form-in-the-repository
375
sha1 = osutils.sha_string(as_bytes)
376
as_lines = osutils.split_lines(as_bytes)
378
all_lines[key] = as_lines
379
if key in just_parents:
380
# We don't transmit those entries
382
# Create an mpdiff for this text, and add it to the output
383
parent_keys = parent_map[key]
384
# See the comment in VF.make_mpdiffs about how this effects
385
# ordering when there are ghosts present. I think we have a latent
387
parent_lines = [all_lines[p_key] for p_key in parent_keys
388
if p_key not in ghost_keys]
389
diff = multiparent.MultiParent.from_lines(
390
as_lines, parent_lines)
394
generator = _MPDiffInventoryGenerator(self.repository,
396
for revision_id, parent_ids, sha1, diff in generator.iter_diffs():
391
397
text = ''.join(diff.to_patch())
392
parent_ids = [k[-1] for k in parent_keys]
393
398
self.bundle.add_multiparent_record(text, sha1, parent_ids,
394
399
'inventory', revision_id, None)