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 __future__ import absolute_import
 
17
19
from cStringIO import StringIO
 
21
23
from bzrlib import (
 
 
 
34
    versionedfile as _mod_versionedfile,
 
34
36
from bzrlib.bundle import bundle_data, serializer as bundle_serializer
 
 
37
from bzrlib.i18n import ngettext
 
35
38
from bzrlib import bencode
 
 
41
class _MPDiffInventoryGenerator(_mod_versionedfile._MPDiffGenerator):
 
 
42
    """Generate Inventory diffs serialized inventories."""
 
 
44
    def __init__(self, repo, inventory_keys):
 
 
45
        super(_MPDiffInventoryGenerator, self).__init__(repo.inventories,
 
 
51
        """Compute the diffs one at a time."""
 
 
52
        # This is instead of compute_diffs() since we guarantee our ordering of
 
 
53
        # inventories, we don't have to do any buffering
 
 
54
        self._find_needed_keys()
 
 
55
        # We actually use a slightly different ordering. We grab all of the
 
 
56
        # parents first, and then grab the ordered requests.
 
 
57
        needed_ids = [k[-1] for k in self.present_parents]
 
 
58
        needed_ids.extend([k[-1] for k in self.ordered_keys])
 
 
59
        inv_to_str = self.repo._serializer.write_inventory_to_string
 
 
60
        for inv in self.repo.iter_inventories(needed_ids):
 
 
61
            revision_id = inv.revision_id
 
 
63
            if key in self.present_parents:
 
 
64
                # Not a key we will transmit, which is a shame, since because
 
 
65
                # of that bundles don't work with stacked branches
 
 
68
                parent_ids = [k[-1] for k in self.parent_map[key]]
 
 
69
            as_bytes = inv_to_str(inv)
 
 
70
            self._process_one_record(key, (as_bytes,))
 
 
71
            if parent_ids is None:
 
 
73
            diff = self.diffs.pop(key)
 
 
74
            sha1 = osutils.sha_string(as_bytes)
 
 
75
            yield revision_id, parent_ids, sha1, diff
 
38
78
class BundleWriter(object):
 
39
79
    """Writer for bundle-format files.
 
 
285
325
    def do_write(self):
 
286
326
        """Write all data to the bundle"""
 
287
 
        trace.note('Bundling %d revision(s).', len(self.revision_ids))
 
 
327
        trace.note(ngettext('Bundling %d revision.', 'Bundling %d revisions.',
 
 
328
                            len(self.revision_ids)), len(self.revision_ids))
 
288
329
        self.repository.lock_read()
 
290
331
            self.bundle.begin()
 
 
350
391
        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)
 
 
392
        generator = _MPDiffInventoryGenerator(self.repository,
 
 
394
        for revision_id, parent_ids, sha1, diff in generator.iter_diffs():
 
391
395
            text = ''.join(diff.to_patch())
 
392
 
            parent_ids = [k[-1] for k in parent_keys]
 
393
396
            self.bundle.add_multiparent_record(text, sha1, parent_ids,
 
394
397
                                               'inventory', revision_id, None)