/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/knitrepo.py

  • Committer: Aaron Bentley
  • Date: 2007-12-21 06:34:27 UTC
  • mto: This revision was merged to the branch mainline in revision 3140.
  • Revision ID: aaron.bentley@utoronto.ca-20071221063427-pavy148wf806gf11
Fix commit for a checkout sharing a repo with its branch (abentley, #177592)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
from bzrlib.lazy_import import lazy_import
 
18
lazy_import(globals(), """
 
19
from bzrlib import (
 
20
    debug,
 
21
    )
 
22
from bzrlib.store import revision
 
23
from bzrlib.store.revision.knit import KnitRevisionStore
 
24
""")
 
25
from bzrlib import (
 
26
    bzrdir,
 
27
    deprecated_graph,
 
28
    errors,
 
29
    knit,
 
30
    lockable_files,
 
31
    lockdir,
 
32
    osutils,
 
33
    symbol_versioning,
 
34
    transactions,
 
35
    xml5,
 
36
    xml6,
 
37
    xml7,
 
38
    )
 
39
 
 
40
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
41
from bzrlib.repository import (
 
42
    CommitBuilder,
 
43
    MetaDirRepository,
 
44
    MetaDirRepositoryFormat,
 
45
    RepositoryFormat,
 
46
    RootCommitBuilder,
 
47
    )
 
48
import bzrlib.revision as _mod_revision
 
49
from bzrlib.store.versioned import VersionedFileStore
 
50
from bzrlib.trace import mutter, mutter_callsite
 
51
from bzrlib.util import bencode
 
52
 
 
53
 
 
54
class _KnitParentsProvider(object):
 
55
 
 
56
    def __init__(self, knit):
 
57
        self._knit = knit
 
58
 
 
59
    def __repr__(self):
 
60
        return 'KnitParentsProvider(%r)' % self._knit
 
61
 
 
62
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
 
63
    def get_parents(self, revision_ids):
 
64
        """See graph._StackedParentsProvider.get_parents"""
 
65
        parent_map = self.get_parent_map(revision_ids)
 
66
        return [parent_map.get(r, None) for r in revision_ids]
 
67
 
 
68
    def get_parent_map(self, keys):
 
69
        """See graph._StackedParentsProvider.get_parent_map"""
 
70
        parent_map = {}
 
71
        for revision_id in keys:
 
72
            if revision_id == _mod_revision.NULL_REVISION:
 
73
                parent_map[revision_id] = []
 
74
            else:
 
75
                try:
 
76
                    parents = self._knit.get_parents_with_ghosts(revision_id)
 
77
                except errors.RevisionNotPresent:
 
78
                    pass
 
79
                else:
 
80
                    if len(parents) == 0:
 
81
                        parents = [_mod_revision.NULL_REVISION]
 
82
                parent_map[revision_id] = parents
 
83
        return parent_map
 
84
 
 
85
 
 
86
class KnitRepository(MetaDirRepository):
 
87
    """Knit format repository."""
 
88
 
 
89
    # These attributes are inherited from the Repository base class. Setting
 
90
    # them to None ensures that if the constructor is changed to not initialize
 
91
    # them, or a subclass fails to call the constructor, that an error will
 
92
    # occur rather than the system working but generating incorrect data.
 
93
    _commit_builder_class = None
 
94
    _serializer = None
 
95
 
 
96
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
 
97
        control_store, text_store, _commit_builder_class, _serializer):
 
98
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files,
 
99
            _revision_store, control_store, text_store)
 
100
        self._commit_builder_class = _commit_builder_class
 
101
        self._serializer = _serializer
 
102
        self._reconcile_fixes_text_parents = True
 
103
 
 
104
    def _warn_if_deprecated(self):
 
105
        # This class isn't deprecated
 
106
        pass
 
107
 
 
108
    def _inventory_add_lines(self, inv_vf, revid, parents, lines, check_content):
 
109
        return inv_vf.add_lines_with_ghosts(revid, parents, lines,
 
110
            check_content=check_content)[0]
 
111
 
 
112
    @needs_read_lock
 
113
    def _all_revision_ids(self):
 
114
        """See Repository.all_revision_ids()."""
 
115
        # Knits get the revision graph from the index of the revision knit, so
 
116
        # it's always possible even if they're on an unlistable transport.
 
117
        return self._revision_store.all_revision_ids(self.get_transaction())
 
118
 
 
119
    def fileid_involved_between_revs(self, from_revid, to_revid):
 
120
        """Find file_id(s) which are involved in the changes between revisions.
 
121
 
 
122
        This determines the set of revisions which are involved, and then
 
123
        finds all file ids affected by those revisions.
 
124
        """
 
125
        vf = self._get_revision_vf()
 
126
        from_set = set(vf.get_ancestry(from_revid))
 
127
        to_set = set(vf.get_ancestry(to_revid))
 
128
        changed = to_set.difference(from_set)
 
129
        return self._fileid_involved_by_set(changed)
 
130
 
 
131
    def fileid_involved(self, last_revid=None):
 
132
        """Find all file_ids modified in the ancestry of last_revid.
 
133
 
 
134
        :param last_revid: If None, last_revision() will be used.
 
135
        """
 
136
        if not last_revid:
 
137
            changed = set(self.all_revision_ids())
 
138
        else:
 
139
            changed = set(self.get_ancestry(last_revid))
 
140
        if None in changed:
 
141
            changed.remove(None)
 
142
        return self._fileid_involved_by_set(changed)
 
143
 
 
144
    @needs_read_lock
 
145
    def get_ancestry(self, revision_id, topo_sorted=True):
 
146
        """Return a list of revision-ids integrated by a revision.
 
147
        
 
148
        This is topologically sorted, unless 'topo_sorted' is specified as
 
149
        False.
 
150
        """
 
151
        if _mod_revision.is_null(revision_id):
 
152
            return [None]
 
153
        vf = self._get_revision_vf()
 
154
        try:
 
155
            return [None] + vf.get_ancestry(revision_id, topo_sorted)
 
156
        except errors.RevisionNotPresent:
 
157
            raise errors.NoSuchRevision(self, revision_id)
 
158
 
 
159
    @needs_read_lock
 
160
    def get_data_stream(self, revision_ids):
 
161
        """See Repository.get_data_stream."""
 
162
        item_keys = self.item_keys_introduced_by(revision_ids)
 
163
        for knit_kind, file_id, versions in item_keys:
 
164
            name = (knit_kind,)
 
165
            if knit_kind == 'file':
 
166
                name = ('file', file_id)
 
167
                knit = self.weave_store.get_weave_or_empty(
 
168
                    file_id, self.get_transaction())
 
169
            elif knit_kind == 'inventory':
 
170
                knit = self.get_inventory_weave()
 
171
            elif knit_kind == 'revisions':
 
172
                knit = self._revision_store.get_revision_file(
 
173
                    self.get_transaction())
 
174
            elif knit_kind == 'signatures':
 
175
                knit = self._revision_store.get_signature_file(
 
176
                    self.get_transaction())
 
177
            else:
 
178
                raise AssertionError('Unknown knit kind %r' % (knit_kind,))
 
179
            yield name, _get_stream_as_bytes(knit, versions)
 
180
 
 
181
    @needs_read_lock
 
182
    def get_revision(self, revision_id):
 
183
        """Return the Revision object for a named revision"""
 
184
        revision_id = osutils.safe_revision_id(revision_id)
 
185
        return self.get_revision_reconcile(revision_id)
 
186
 
 
187
    @needs_read_lock
 
188
    def get_revision_graph(self, revision_id=None):
 
189
        """Return a dictionary containing the revision graph.
 
190
 
 
191
        :param revision_id: The revision_id to get a graph from. If None, then
 
192
        the entire revision graph is returned. This is a deprecated mode of
 
193
        operation and will be removed in the future.
 
194
        :return: a dictionary of revision_id->revision_parents_list.
 
195
        """
 
196
        if 'evil' in debug.debug_flags:
 
197
            mutter_callsite(3,
 
198
                "get_revision_graph scales with size of history.")
 
199
        # special case NULL_REVISION
 
200
        if revision_id == _mod_revision.NULL_REVISION:
 
201
            return {}
 
202
        a_weave = self._get_revision_vf()
 
203
        if revision_id is None:
 
204
            return a_weave.get_graph()
 
205
        if revision_id not in a_weave:
 
206
            raise errors.NoSuchRevision(self, revision_id)
 
207
        else:
 
208
            # add what can be reached from revision_id
 
209
            return a_weave.get_graph([revision_id])
 
210
 
 
211
    @needs_read_lock
 
212
    def get_revision_graph_with_ghosts(self, revision_ids=None):
 
213
        """Return a graph of the revisions with ghosts marked as applicable.
 
214
 
 
215
        :param revision_ids: an iterable of revisions to graph or None for all.
 
216
        :return: a Graph object with the graph reachable from revision_ids.
 
217
        """
 
218
        if 'evil' in debug.debug_flags:
 
219
            mutter_callsite(3,
 
220
                "get_revision_graph_with_ghosts scales with size of history.")
 
221
        result = deprecated_graph.Graph()
 
222
        vf = self._get_revision_vf()
 
223
        versions = set(vf.versions())
 
224
        if not revision_ids:
 
225
            pending = set(self.all_revision_ids())
 
226
            required = set([])
 
227
        else:
 
228
            pending = set(revision_ids)
 
229
            # special case NULL_REVISION
 
230
            if _mod_revision.NULL_REVISION in pending:
 
231
                pending.remove(_mod_revision.NULL_REVISION)
 
232
            required = set(pending)
 
233
        done = set([])
 
234
        while len(pending):
 
235
            revision_id = pending.pop()
 
236
            if not revision_id in versions:
 
237
                if revision_id in required:
 
238
                    raise errors.NoSuchRevision(self, revision_id)
 
239
                # a ghost
 
240
                result.add_ghost(revision_id)
 
241
                # mark it as done so we don't try for it again.
 
242
                done.add(revision_id)
 
243
                continue
 
244
            parent_ids = vf.get_parents_with_ghosts(revision_id)
 
245
            for parent_id in parent_ids:
 
246
                # is this queued or done ?
 
247
                if (parent_id not in pending and
 
248
                    parent_id not in done):
 
249
                    # no, queue it.
 
250
                    pending.add(parent_id)
 
251
            result.add_node(revision_id, parent_ids)
 
252
            done.add(revision_id)
 
253
        return result
 
254
 
 
255
    def _get_revision_vf(self):
 
256
        """:return: a versioned file containing the revisions."""
 
257
        vf = self._revision_store.get_revision_file(self.get_transaction())
 
258
        return vf
 
259
 
 
260
    def _get_history_vf(self):
 
261
        """Get a versionedfile whose history graph reflects all revisions.
 
262
 
 
263
        For knit repositories, this is the revision knit.
 
264
        """
 
265
        return self._get_revision_vf()
 
266
 
 
267
    @needs_write_lock
 
268
    def reconcile(self, other=None, thorough=False):
 
269
        """Reconcile this repository."""
 
270
        from bzrlib.reconcile import KnitReconciler
 
271
        reconciler = KnitReconciler(self, thorough=thorough)
 
272
        reconciler.reconcile()
 
273
        return reconciler
 
274
    
 
275
    def revision_parents(self, revision_id):
 
276
        return self._get_revision_vf().get_parents(revision_id)
 
277
 
 
278
    def _make_parents_provider(self):
 
279
        return _KnitParentsProvider(self._get_revision_vf())
 
280
 
 
281
    def _find_inconsistent_revision_parents(self):
 
282
        """Find revisions with different parent lists in the revision object
 
283
        and in the index graph.
 
284
 
 
285
        :returns: an iterator yielding tuples of (revison-id, parents-in-index,
 
286
            parents-in-revision).
 
287
        """
 
288
        assert self.is_locked()
 
289
        vf = self._get_revision_vf()
 
290
        for index_version in vf.versions():
 
291
            parents_according_to_index = tuple(vf.get_parents_with_ghosts(
 
292
                index_version))
 
293
            revision = self.get_revision(index_version)
 
294
            parents_according_to_revision = tuple(revision.parent_ids)
 
295
            if parents_according_to_index != parents_according_to_revision:
 
296
                yield (index_version, parents_according_to_index,
 
297
                    parents_according_to_revision)
 
298
 
 
299
    def _check_for_inconsistent_revision_parents(self):
 
300
        inconsistencies = list(self._find_inconsistent_revision_parents())
 
301
        if inconsistencies:
 
302
            raise errors.BzrCheckError(
 
303
                "Revision knit has inconsistent parents.")
 
304
 
 
305
    def revision_graph_can_have_wrong_parents(self):
 
306
        # The revision.kndx could potentially claim a revision has a different
 
307
        # parent to the revision text.
 
308
        return True
 
309
 
 
310
 
 
311
class RepositoryFormatKnit(MetaDirRepositoryFormat):
 
312
    """Bzr repository knit format (generalized). 
 
313
 
 
314
    This repository format has:
 
315
     - knits for file texts and inventory
 
316
     - hash subdirectory based stores.
 
317
     - knits for revisions and signatures
 
318
     - TextStores for revisions and signatures.
 
319
     - a format marker of its own
 
320
     - an optional 'shared-storage' flag
 
321
     - an optional 'no-working-trees' flag
 
322
     - a LockDir lock
 
323
    """
 
324
 
 
325
    # Set this attribute in derived classes to control the repository class
 
326
    # created by open and initialize.
 
327
    repository_class = None
 
328
    # Set this attribute in derived classes to control the
 
329
    # _commit_builder_class that the repository objects will have passed to
 
330
    # their constructor.
 
331
    _commit_builder_class = None
 
332
    # Set this attribute in derived clases to control the _serializer that the
 
333
    # repository objects will have passed to their constructor.
 
334
    _serializer = xml5.serializer_v5
 
335
    # Knit based repositories handle ghosts reasonably well.
 
336
    supports_ghosts = True
 
337
 
 
338
    def _get_control_store(self, repo_transport, control_files):
 
339
        """Return the control store for this repository."""
 
340
        return VersionedFileStore(
 
341
            repo_transport,
 
342
            prefixed=False,
 
343
            file_mode=control_files._file_mode,
 
344
            versionedfile_class=knit.KnitVersionedFile,
 
345
            versionedfile_kwargs={'factory':knit.KnitPlainFactory()},
 
346
            )
 
347
 
 
348
    def _get_revision_store(self, repo_transport, control_files):
 
349
        """See RepositoryFormat._get_revision_store()."""
 
350
        versioned_file_store = VersionedFileStore(
 
351
            repo_transport,
 
352
            file_mode=control_files._file_mode,
 
353
            prefixed=False,
 
354
            precious=True,
 
355
            versionedfile_class=knit.KnitVersionedFile,
 
356
            versionedfile_kwargs={'delta':False,
 
357
                                  'factory':knit.KnitPlainFactory(),
 
358
                                 },
 
359
            escaped=True,
 
360
            )
 
361
        return KnitRevisionStore(versioned_file_store)
 
362
 
 
363
    def _get_text_store(self, transport, control_files):
 
364
        """See RepositoryFormat._get_text_store()."""
 
365
        return self._get_versioned_file_store('knits',
 
366
                                  transport,
 
367
                                  control_files,
 
368
                                  versionedfile_class=knit.KnitVersionedFile,
 
369
                                  versionedfile_kwargs={
 
370
                                      'create_parent_dir':True,
 
371
                                      'delay_create':True,
 
372
                                      'dir_mode':control_files._dir_mode,
 
373
                                  },
 
374
                                  escaped=True)
 
375
 
 
376
    def initialize(self, a_bzrdir, shared=False):
 
377
        """Create a knit format 1 repository.
 
378
 
 
379
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
380
            be initialized.
 
381
        :param shared: If true the repository will be initialized as a shared
 
382
                       repository.
 
383
        """
 
384
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
385
        dirs = ['knits']
 
386
        files = []
 
387
        utf8_files = [('format', self.get_format_string())]
 
388
        
 
389
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
390
        repo_transport = a_bzrdir.get_repository_transport(None)
 
391
        control_files = lockable_files.LockableFiles(repo_transport,
 
392
                                'lock', lockdir.LockDir)
 
393
        control_store = self._get_control_store(repo_transport, control_files)
 
394
        transaction = transactions.WriteTransaction()
 
395
        # trigger a write of the inventory store.
 
396
        control_store.get_weave_or_empty('inventory', transaction)
 
397
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
398
        # the revision id here is irrelevant: it will not be stored, and cannot
 
399
        # already exist.
 
400
        _revision_store.has_revision_id('A', transaction)
 
401
        _revision_store.get_signature_file(transaction)
 
402
        return self.open(a_bzrdir=a_bzrdir, _found=True)
 
403
 
 
404
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
405
        """See RepositoryFormat.open().
 
406
        
 
407
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
408
                                    repository at a slightly different url
 
409
                                    than normal. I.e. during 'upgrade'.
 
410
        """
 
411
        if not _found:
 
412
            format = RepositoryFormat.find_format(a_bzrdir)
 
413
            assert format.__class__ ==  self.__class__
 
414
        if _override_transport is not None:
 
415
            repo_transport = _override_transport
 
416
        else:
 
417
            repo_transport = a_bzrdir.get_repository_transport(None)
 
418
        control_files = lockable_files.LockableFiles(repo_transport,
 
419
                                'lock', lockdir.LockDir)
 
420
        text_store = self._get_text_store(repo_transport, control_files)
 
421
        control_store = self._get_control_store(repo_transport, control_files)
 
422
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
423
        return self.repository_class(_format=self,
 
424
                              a_bzrdir=a_bzrdir,
 
425
                              control_files=control_files,
 
426
                              _revision_store=_revision_store,
 
427
                              control_store=control_store,
 
428
                              text_store=text_store,
 
429
                              _commit_builder_class=self._commit_builder_class,
 
430
                              _serializer=self._serializer)
 
431
 
 
432
 
 
433
class RepositoryFormatKnit1(RepositoryFormatKnit):
 
434
    """Bzr repository knit format 1.
 
435
 
 
436
    This repository format has:
 
437
     - knits for file texts and inventory
 
438
     - hash subdirectory based stores.
 
439
     - knits for revisions and signatures
 
440
     - TextStores for revisions and signatures.
 
441
     - a format marker of its own
 
442
     - an optional 'shared-storage' flag
 
443
     - an optional 'no-working-trees' flag
 
444
     - a LockDir lock
 
445
 
 
446
    This format was introduced in bzr 0.8.
 
447
    """
 
448
 
 
449
    repository_class = KnitRepository
 
450
    _commit_builder_class = CommitBuilder
 
451
    _serializer = xml5.serializer_v5
 
452
 
 
453
    def __ne__(self, other):
 
454
        return self.__class__ is not other.__class__
 
455
 
 
456
    def get_format_string(self):
 
457
        """See RepositoryFormat.get_format_string()."""
 
458
        return "Bazaar-NG Knit Repository Format 1"
 
459
 
 
460
    def get_format_description(self):
 
461
        """See RepositoryFormat.get_format_description()."""
 
462
        return "Knit repository format 1"
 
463
 
 
464
    def check_conversion_target(self, target_format):
 
465
        pass
 
466
 
 
467
 
 
468
class RepositoryFormatKnit3(RepositoryFormatKnit):
 
469
    """Bzr repository knit format 3.
 
470
 
 
471
    This repository format has:
 
472
     - knits for file texts and inventory
 
473
     - hash subdirectory based stores.
 
474
     - knits for revisions and signatures
 
475
     - TextStores for revisions and signatures.
 
476
     - a format marker of its own
 
477
     - an optional 'shared-storage' flag
 
478
     - an optional 'no-working-trees' flag
 
479
     - a LockDir lock
 
480
     - support for recording full info about the tree root
 
481
     - support for recording tree-references
 
482
    """
 
483
 
 
484
    repository_class = KnitRepository
 
485
    _commit_builder_class = RootCommitBuilder
 
486
    rich_root_data = True
 
487
    supports_tree_reference = True
 
488
    _serializer = xml7.serializer_v7
 
489
 
 
490
    def _get_matching_bzrdir(self):
 
491
        return bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
 
492
 
 
493
    def _ignore_setting_bzrdir(self, format):
 
494
        pass
 
495
 
 
496
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
497
 
 
498
    def check_conversion_target(self, target_format):
 
499
        if not target_format.rich_root_data:
 
500
            raise errors.BadConversionTarget(
 
501
                'Does not support rich root data.', target_format)
 
502
        if not getattr(target_format, 'supports_tree_reference', False):
 
503
            raise errors.BadConversionTarget(
 
504
                'Does not support nested trees', target_format)
 
505
            
 
506
    def get_format_string(self):
 
507
        """See RepositoryFormat.get_format_string()."""
 
508
        return "Bazaar Knit Repository Format 3 (bzr 0.15)\n"
 
509
 
 
510
    def get_format_description(self):
 
511
        """See RepositoryFormat.get_format_description()."""
 
512
        return "Knit repository format 3"
 
513
 
 
514
 
 
515
class RepositoryFormatKnit4(RepositoryFormatKnit):
 
516
    """Bzr repository knit format 4.
 
517
 
 
518
    This repository format has everything in format 3, except for
 
519
    tree-references:
 
520
     - knits for file texts and inventory
 
521
     - hash subdirectory based stores.
 
522
     - knits for revisions and signatures
 
523
     - TextStores for revisions and signatures.
 
524
     - a format marker of its own
 
525
     - an optional 'shared-storage' flag
 
526
     - an optional 'no-working-trees' flag
 
527
     - a LockDir lock
 
528
     - support for recording full info about the tree root
 
529
    """
 
530
 
 
531
    repository_class = KnitRepository
 
532
    _commit_builder_class = RootCommitBuilder
 
533
    rich_root_data = True
 
534
    supports_tree_reference = False
 
535
    _serializer = xml6.serializer_v6
 
536
 
 
537
    def _get_matching_bzrdir(self):
 
538
        return bzrdir.format_registry.make_bzrdir('rich-root')
 
539
 
 
540
    def _ignore_setting_bzrdir(self, format):
 
541
        pass
 
542
 
 
543
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
544
 
 
545
    def check_conversion_target(self, target_format):
 
546
        if not target_format.rich_root_data:
 
547
            raise errors.BadConversionTarget(
 
548
                'Does not support rich root data.', target_format)
 
549
 
 
550
    def get_format_string(self):
 
551
        """See RepositoryFormat.get_format_string()."""
 
552
        return 'Bazaar Knit Repository Format 4 (bzr 1.0)\n'
 
553
 
 
554
    def get_format_description(self):
 
555
        """See RepositoryFormat.get_format_description()."""
 
556
        return "Knit repository format 4"
 
557
 
 
558
 
 
559
def _get_stream_as_bytes(knit, required_versions):
 
560
    """Generate a serialised data stream.
 
561
 
 
562
    The format is a bencoding of a list.  The first element of the list is a
 
563
    string of the format signature, then each subsequent element is a list
 
564
    corresponding to a record.  Those lists contain:
 
565
 
 
566
      * a version id
 
567
      * a list of options
 
568
      * a list of parents
 
569
      * the bytes
 
570
 
 
571
    :returns: a bencoded list.
 
572
    """
 
573
    knit_stream = knit.get_data_stream(required_versions)
 
574
    format_signature, data_list, callable = knit_stream
 
575
    data = []
 
576
    data.append(format_signature)
 
577
    for version, options, length, parents in data_list:
 
578
        data.append([version, options, parents, callable(length)])
 
579
    return bencode.bencode(data)