/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2241.1.5 by Martin Pool
Move KnitFormat2 into repofmt
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
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
"""Tests for bzrdir implementations - tests a bzrdir format."""
18
2520.4.54 by Aaron Bentley
Hang a create_bundle method off repository
19
from cStringIO import StringIO
1666.1.6 by Robert Collins
Make knit the default format.
20
import re
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
21
1534.1.31 by Robert Collins
Deprecated fetch.fetch and fetch.greedy_fetch for branch.fetch, and move the Repository.fetch internals to InterRepo and InterWeaveRepo.
22
import bzrlib
1752.2.87 by Andrew Bennetts
Make tests pass.
23
from bzrlib import (
24
    bzrdir,
25
    errors,
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
26
    graph,
1752.2.87 by Andrew Bennetts
Make tests pass.
27
    remote,
28
    repository,
29
    )
1852.10.3 by Robert Collins
Remove all uses of compare_trees and replace with Tree.changes_from throughout bzrlib.
30
from bzrlib.delta import TreeDelta
1731.1.33 by Aaron Bentley
Revert no-special-root changes
31
from bzrlib.inventory import Inventory, InventoryDirectory
3047.1.2 by Andrew Bennetts
Directly test that sprouting branches from a HPSS preserves the repository format.
32
from bzrlib.repofmt.weaverepo import (
33
    RepositoryFormat5,
34
    RepositoryFormat6,
35
    RepositoryFormat7,
36
    )
2996.2.3 by Aaron Bentley
Add tests for install_revisions and add_signature
37
from bzrlib.revision import NULL_REVISION, Revision
3047.1.1 by Andrew Bennetts
Fix for bug 164626, add test that Repository.sprout preserves format.
38
from bzrlib.smart import server
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
39
from bzrlib.symbol_versioning import one_two
3023.2.1 by Martin Pool
test_get_data_stream should indicate NotApplicable rather than silently passing
40
from bzrlib.tests import (
3047.1.1 by Andrew Bennetts
Fix for bug 164626, add test that Repository.sprout preserves format.
41
    KnownFailure,
3023.2.1 by Martin Pool
test_get_data_stream should indicate NotApplicable rather than silently passing
42
    TestCaseWithTransport,
43
    TestNotApplicable,
44
    TestSkipped,
45
    )
2485.7.1 by Robert Collins
Relocate TestCaseWithRepository to be more central.
46
from bzrlib.tests.repository_implementations import TestCaseWithRepository
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
47
from bzrlib.transport import get_transport
48
from bzrlib.upgrade import upgrade
49
from bzrlib.workingtree import WorkingTree
50
51
2018.5.66 by Wouter van Heyst
Fix repository test parameterization for RemoteRepository.
52
class TestRepositoryMakeBranchAndTree(TestCaseWithRepository):
53
54
    def test_repository_format(self):
2018.5.119 by Robert Collins
Unbreak TestRepositoryMakeBranchAndTree.
55
        # make sure the repository on tree.branch is of the desired format,
56
        # because developers use this api to setup the tree, branch and 
57
        # repository for their tests: having it now give the right repository
58
        # type would invalidate the tests.
2018.5.66 by Wouter van Heyst
Fix repository test parameterization for RemoteRepository.
59
        tree = self.make_branch_and_tree('repo')
60
        self.assertIsInstance(tree.branch.repository._format,
61
            self.repository_format.__class__)
2381.1.1 by Robert Collins
Split out hpss test fixes which dont depend on new or altered API's.
62
63
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
64
class TestRepository(TestCaseWithRepository):
65
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
66
    def test_clone_to_default_format(self):
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
67
        #TODO: Test that cloning a repository preserves all the information
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
68
        # such as signatures[not tested yet] etc etc.
69
        # when changing to the current default format.
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
70
        tree_a = self.make_branch_and_tree('a')
2381.1.3 by Robert Collins
Review feedback.
71
        self.build_tree(['a/foo'])
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
72
        tree_a.add('foo', 'file1')
73
        tree_a.commit('rev1', rev_id='rev1')
74
        bzrdirb = self.make_bzrdir('b')
75
        repo_b = tree_a.branch.repository.clone(bzrdirb)
76
        tree_b = repo_b.revision_tree('rev1')
2592.3.214 by Robert Collins
Merge bzr.dev.
77
        tree_b.lock_read()
78
        self.addCleanup(tree_b.unlock)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
79
        tree_b.get_file_text('file1')
80
        rev1 = repo_b.get_revision('rev1')
81
3169.2.1 by Robert Collins
New method ``iter_inventories`` on Repository for access to many
82
    def test_iter_inventories_is_ordered(self):
83
        # just a smoke test
84
        tree = self.make_branch_and_tree('a')
85
        first_revision = tree.commit('')
86
        second_revision = tree.commit('')
87
        tree.lock_read()
88
        self.addCleanup(tree.unlock)
89
        revs = (first_revision, second_revision)
90
        invs = tree.branch.repository.iter_inventories(revs)
91
        for rev_id, inv in zip(revs, invs):
92
            self.assertEqual(rev_id, inv.revision_id)
93
            self.assertIsInstance(inv, Inventory)
94
1910.2.63 by Aaron Bentley
Add supports_rich_root member to repository
95
    def test_supports_rich_root(self):
96
        tree = self.make_branch_and_tree('a')
97
        tree.commit('')
98
        second_revision = tree.commit('')
99
        inv = tree.branch.repository.revision_tree(second_revision).inventory
100
        rich_root = (inv.root.revision != second_revision)
2018.5.113 by Robert Collins
Test only fixes from the ported-to-bzr.dev test-correctness branch.
101
        self.assertEqual(rich_root,
1910.2.63 by Aaron Bentley
Add supports_rich_root member to repository
102
                         tree.branch.repository.supports_rich_root())
103
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
104
    def test_clone_specific_format(self):
105
        """todo"""
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
106
107
    def test_format_initialize_find_open(self):
108
        # loopback test to check the current format initializes to itself.
109
        if not self.repository_format.is_supported():
110
            # unsupported formats are not loopback testable
111
            # because the default open will not open them and
112
            # they may not be initializable.
113
            return
114
        # supported formats must be able to init and open
115
        t = get_transport(self.get_url())
116
        readonly_t = get_transport(self.get_readonly_url())
117
        made_control = self.bzrdir_format.initialize(t.base)
118
        made_repo = self.repository_format.initialize(made_control)
119
        self.assertEqual(made_control, made_repo.bzrdir)
120
121
        # find it via bzrdir opening:
122
        opened_control = bzrdir.BzrDir.open(readonly_t.base)
123
        direct_opened_repo = opened_control.open_repository()
124
        self.assertEqual(direct_opened_repo.__class__, made_repo.__class__)
125
        self.assertEqual(opened_control, direct_opened_repo.bzrdir)
126
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
127
        self.assertIsInstance(direct_opened_repo._format,
128
                              self.repository_format.__class__)
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
129
        # find it via Repository.open
130
        opened_repo = repository.Repository.open(readonly_t.base)
131
        self.failUnless(isinstance(opened_repo, made_repo.__class__))
132
        self.assertEqual(made_repo._format.__class__,
133
                         opened_repo._format.__class__)
134
        # if it has a unique id string, can we probe for it ?
135
        try:
136
            self.repository_format.get_format_string()
137
        except NotImplementedError:
138
            return
139
        self.assertEqual(self.repository_format,
140
                         repository.RepositoryFormat.find_format(opened_control))
141
142
    def test_create_repository(self):
1534.6.1 by Robert Collins
allow API creation of shared repositories
143
        # bzrdir can construct a repository for itself.
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
144
        if not self.bzrdir_format.is_supported():
145
            # unsupported formats are not loopback testable
146
            # because the default open will not open them and
147
            # they may not be initializable.
148
            return
149
        t = get_transport(self.get_url())
150
        made_control = self.bzrdir_format.initialize(t.base)
151
        made_repo = made_control.create_repository()
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
152
        # Check that we have a repository object.
153
        made_repo.has_revision('foo')
1534.4.40 by Robert Collins
Add RepositoryFormats and allow bzrdir.open or create _repository to be used.
154
        self.assertEqual(made_control, made_repo.bzrdir)
1534.6.1 by Robert Collins
allow API creation of shared repositories
155
        
156
    def test_create_repository_shared(self):
157
        # bzrdir can construct a shared repository.
158
        if not self.bzrdir_format.is_supported():
159
            # unsupported formats are not loopback testable
160
            # because the default open will not open them and
161
            # they may not be initializable.
162
            return
163
        t = get_transport(self.get_url())
164
        made_control = self.bzrdir_format.initialize(t.base)
165
        try:
166
            made_repo = made_control.create_repository(shared=True)
167
        except errors.IncompatibleFormat:
168
            # not all repository formats understand being shared, or
169
            # may only be shared in some circumstances.
170
            return
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
171
        # Check that we have a repository object.
172
        made_repo.has_revision('foo')
1534.6.1 by Robert Collins
allow API creation of shared repositories
173
        self.assertEqual(made_control, made_repo.bzrdir)
1534.6.3 by Robert Collins
find_repository sufficiently robust.
174
        self.assertTrue(made_repo.is_shared())
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
175
176
    def test_revision_tree(self):
177
        wt = self.make_branch_and_tree('.')
1731.1.33 by Aaron Bentley
Revert no-special-root changes
178
        wt.set_root_id('fixed-root')
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
179
        wt.commit('lala!', rev_id='revision-1', allow_pointless=True)
180
        tree = wt.branch.repository.revision_tree('revision-1')
1731.1.45 by Aaron Bentley
Merge bzr.dev
181
        self.assertEqual('revision-1', tree.inventory.root.revision) 
182
        expected = InventoryDirectory('fixed-root', '', None)
183
        expected.revision = 'revision-1'
184
        self.assertEqual([('', 'V', 'directory', 'fixed-root', expected)],
1731.1.54 by Aaron Bentley
Fix revision_tree tests
185
                         list(tree.list_files(include_root=True)))
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
186
        tree = wt.branch.repository.revision_tree(None)
1731.1.54 by Aaron Bentley
Fix revision_tree tests
187
        self.assertEqual([], list(tree.list_files(include_root=True)))
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
188
        tree = wt.branch.repository.revision_tree(NULL_REVISION)
1731.1.54 by Aaron Bentley
Fix revision_tree tests
189
        self.assertEqual([], list(tree.list_files(include_root=True)))
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
190
1770.3.3 by Jelmer Vernooij
Add tests for Branch.get_revision_delta() and Repository.get_revision_delta().
191
    def test_get_revision_delta(self):
192
        tree_a = self.make_branch_and_tree('a')
2381.1.3 by Robert Collins
Review feedback.
193
        self.build_tree(['a/foo'])
1770.3.3 by Jelmer Vernooij
Add tests for Branch.get_revision_delta() and Repository.get_revision_delta().
194
        tree_a.add('foo', 'file1')
195
        tree_a.commit('rev1', rev_id='rev1')
2381.1.3 by Robert Collins
Review feedback.
196
        self.build_tree(['a/vla'])
1770.3.3 by Jelmer Vernooij
Add tests for Branch.get_revision_delta() and Repository.get_revision_delta().
197
        tree_a.add('vla', 'file2')
198
        tree_a.commit('rev2', rev_id='rev2')
199
200
        delta = tree_a.branch.repository.get_revision_delta('rev1')
201
        self.assertIsInstance(delta, TreeDelta)
202
        self.assertEqual([('foo', 'file1', 'file')], delta.added)
203
        delta = tree_a.branch.repository.get_revision_delta('rev2')
204
        self.assertIsInstance(delta, TreeDelta)
205
        self.assertEqual([('vla', 'file2', 'file')], delta.added)
206
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
207
    def test_clone_bzrdir_repository_revision(self):
208
        # make a repository with some revisions,
209
        # and clone it, this should not have unreferenced revisions.
210
        # also: test cloning with a revision id of NULL_REVISION -> empty repo.
211
        raise TestSkipped('revision limiting is not implemented yet.')
212
213
    def test_clone_repository_basis_revision(self):
214
        raise TestSkipped('the use of a basis should not add noise data to the result.')
215
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
216
    def test_clone_shared_no_tree(self):
217
        # cloning a shared repository keeps it shared
218
        # and preserves the make_working_tree setting.
219
        made_control = self.make_bzrdir('source')
220
        try:
221
            made_repo = made_control.create_repository(shared=True)
222
        except errors.IncompatibleFormat:
223
            # not all repository formats understand being shared, or
224
            # may only be shared in some circumstances.
225
            return
2018.14.2 by Andrew Bennetts
All but one repository_implementation tests for RemoteRepository passing.
226
        try:
227
            made_repo.set_make_working_trees(False)
228
        except NotImplementedError:
2018.5.120 by Robert Collins
The Repository API ``make_working_trees`` is now permitted to return
229
            # the repository does not support having its tree-making flag
230
            # toggled.
231
            return
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
232
        result = made_control.clone(self.get_url('target'))
1752.2.55 by Andrew Bennetts
Replace another isinstance(made_repo, Repository) check.
233
        # Check that we have a repository object.
234
        made_repo.has_revision('foo')
235
1534.6.5 by Robert Collins
Cloning of repos preserves shared and make-working-tree attributes.
236
        self.assertEqual(made_control, made_repo.bzrdir)
237
        self.assertTrue(result.open_repository().is_shared())
238
        self.assertFalse(result.open_repository().make_working_trees())
239
2018.5.90 by Andrew Bennetts
Fix test_upgrade_preserves_signatures; it incorrectly assumed that upgrade(wt, ...) would necessarily affect the repository.
240
    def test_upgrade_preserves_signatures(self):
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
241
        wt = self.make_branch_and_tree('source')
242
        wt.commit('A', allow_pointless=True, rev_id='A')
2018.5.90 by Andrew Bennetts
Fix test_upgrade_preserves_signatures; it incorrectly assumed that upgrade(wt, ...) would necessarily affect the repository.
243
        repo = wt.branch.repository
2592.3.39 by Robert Collins
Fugly version to remove signatures.kndx
244
        repo.lock_write()
245
        repo.start_write_group()
2018.5.90 by Andrew Bennetts
Fix test_upgrade_preserves_signatures; it incorrectly assumed that upgrade(wt, ...) would necessarily affect the repository.
246
        repo.sign_revision('A', bzrlib.gpg.LoopbackGPGStrategy(None))
2592.3.39 by Robert Collins
Fugly version to remove signatures.kndx
247
        repo.commit_write_group()
248
        repo.unlock()
2018.5.90 by Andrew Bennetts
Fix test_upgrade_preserves_signatures; it incorrectly assumed that upgrade(wt, ...) would necessarily affect the repository.
249
        old_signature = repo.get_signature_text('A')
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
250
        try:
251
            old_format = bzrdir.BzrDirFormat.get_default_format()
252
            # This gives metadir branches something they can convert to.
253
            # it would be nice to have a 'latest' vs 'default' concept.
2255.2.208 by Robert Collins
Remove more references to 'experimental' formats.
254
            format = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
2018.5.90 by Andrew Bennetts
Fix test_upgrade_preserves_signatures; it incorrectly assumed that upgrade(wt, ...) would necessarily affect the repository.
255
            upgrade(repo.bzrdir.root_transport.base, format=format)
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
256
        except errors.UpToDateFormat:
257
            # this is in the most current format already.
258
            return
1910.2.12 by Aaron Bentley
Implement knit repo format 2
259
        except errors.BadConversionTarget, e:
260
            raise TestSkipped(str(e))
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
261
        wt = WorkingTree.open(wt.basedir)
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
262
        new_signature = wt.branch.repository.get_signature_text('A')
1556.1.4 by Robert Collins
Add a new format for what will become knit, and the surrounding logic to upgrade repositories within metadirs, and tests for the same.
263
        self.assertEqual(old_signature, new_signature)
264
1624.3.19 by Olaf Conradi
New call get_format_description to give a user-friendly description of a
265
    def test_format_description(self):
266
        repo = self.make_repository('.')
267
        text = repo._format.get_format_description()
268
        self.failUnless(len(text))
269
1666.1.6 by Robert Collins
Make knit the default format.
270
    def assertMessageRoundtrips(self, message):
271
        """Assert that message roundtrips to a repository and back intact."""
272
        tree = self.make_branch_and_tree('.')
273
        tree.commit(message, rev_id='a', allow_pointless=True)
274
        rev = tree.branch.repository.get_revision('a')
275
        # we have to manually escape this as we dont try to
276
        # roundtrip xml invalid characters at this point.
277
        # when escaping is moved to the serialiser, this test
278
        # can check against the literal message rather than
279
        # this escaped version.
280
        escaped_message, escape_count = re.subn(
281
            u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
282
            lambda match: match.group(0).encode('unicode_escape'),
283
            message)
284
        escaped_message= re.sub('\r', '\n', escaped_message)
285
        self.assertEqual(rev.message, escaped_message)
286
        # insist the class is unicode no matter what came in for 
287
        # consistency.
288
        self.assertIsInstance(rev.message, unicode)
289
290
    def test_commit_unicode_message(self):
291
        # a siple unicode message should be preserved
292
        self.assertMessageRoundtrips(u'foo bar gamm\xae plop')
293
294
    def test_commit_unicode_control_characters(self):
295
        # a unicode message with control characters should roundtrip too.
296
        self.assertMessageRoundtrips(
297
            "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)]))
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
298
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
299
    def test_check_repository(self):
300
        """Check a fairly simple repository's history"""
301
        tree = self.make_branch_and_tree('.')
302
        tree.commit('initial empty commit', rev_id='a-rev',
303
                    allow_pointless=True)
2745.6.39 by Andrew Bennetts
Use scenario in test_check too, and make check actually report inconsistent parents to the end user.
304
        result = tree.branch.repository.check()
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
305
        # writes to log; should accept both verbose or non-verbose
306
        result.report_results(verbose=True)
307
        result.report_results(verbose=False)
308
1756.1.5 by Aaron Bentley
Test get_revisions with all repository types (and fix bug...)
309
    def test_get_revisions(self):
310
        tree = self.make_branch_and_tree('.')
311
        tree.commit('initial empty commit', rev_id='a-rev',
312
                    allow_pointless=True)
313
        tree.commit('second empty commit', rev_id='b-rev',
314
                    allow_pointless=True)
315
        tree.commit('third empty commit', rev_id='c-rev',
316
                    allow_pointless=True)
317
        repo = tree.branch.repository
318
        revision_ids = ['a-rev', 'b-rev', 'c-rev']
319
        revisions = repo.get_revisions(revision_ids)
320
        assert len(revisions) == 3, repr(revisions)
321
        zipped = zip(revisions, revision_ids)
322
        self.assertEqual(len(zipped), 3)
323
        for revision, revision_id in zipped:
324
            self.assertEqual(revision.revision_id, revision_id)
325
            self.assertEqual(revision, repo.get_revision(revision_id))
1732.2.4 by Martin Pool
Split check into Branch.check and Repository.check
326
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
327
    def test_root_entry_has_revision(self):
328
        tree = self.make_branch_and_tree('.')
329
        tree.commit('message', rev_id='rev_id')
2255.7.65 by Robert Collins
Split test_root_revision_entry into tree and repository portions.
330
        rev_tree = tree.branch.repository.revision_tree(tree.last_revision())
1910.2.6 by Aaron Bentley
Update for merge review, handle deprecations
331
        self.assertEqual('rev_id', rev_tree.inventory.root.revision)
1910.2.1 by Aaron Bentley
Ensure root entry always has a revision
332
2255.7.64 by Robert Collins
Disabled test_repository.test_create_basis_inventory, a test that tests tree behaviour in the wrong place - its future is being discussed.
333
    def DISABLED_DELETE_OR_FIX_BEFORE_MERGE_test_create_basis_inventory(self):
1910.2.31 by Aaron Bentley
Fix bugs in basis inventory handling, change filename
334
        # Needs testing here because differences between repo and working tree
335
        # basis inventory formats can lead to bugs.
336
        t = self.make_branch_and_tree('.')
337
        b = t.branch
338
        open('a', 'wb').write('a\n')
339
        t.add('a')
340
        t.commit('a', rev_id='r1')
341
342
        t._control_files.get_utf8('basis-inventory-cache')
343
344
        basis_inv = t.basis_tree().inventory
345
        self.assertEquals('r1', basis_inv.revision_id)
346
        
347
        store_inv = b.repository.get_inventory('r1')
348
        self.assertEquals(store_inv._byid, basis_inv._byid)
349
350
        open('b', 'wb').write('b\n')
351
        t.add('b')
352
        t.commit('b', rev_id='r2')
353
354
        t._control_files.get_utf8('basis-inventory-cache')
355
356
        basis_inv_txt = t.read_basis_inventory()
2100.3.15 by Aaron Bentley
get test suite passing
357
        basis_inv = bzrlib.xml7.serializer_v7.read_inventory_from_string(basis_inv_txt)
1910.2.31 by Aaron Bentley
Fix bugs in basis inventory handling, change filename
358
        self.assertEquals('r2', basis_inv.revision_id)
359
        store_inv = b.repository.get_inventory('r2')
360
361
        self.assertEquals(store_inv._byid, basis_inv._byid)
362
1910.2.36 by Aaron Bentley
Get upgrade from format4 under test and fixed for all formats
363
    def test_upgrade_from_format4(self):
364
        from bzrlib.tests.test_upgrade import _upgrade_dir_template
2241.1.4 by Martin Pool
Moved old weave-based repository formats into bzrlib.repofmt.weaverepo.
365
        if self.repository_format.get_format_description() \
366
            == "Repository format 4":
1910.2.36 by Aaron Bentley
Get upgrade from format4 under test and fixed for all formats
367
            raise TestSkipped('Cannot convert format-4 to itself')
1752.2.89 by Andrew Bennetts
Skip test_upgrade_from_format4 for RemoteRepositoryFormat.
368
        if isinstance(self.repository_format, remote.RemoteRepositoryFormat):
369
            return # local conversion to/from RemoteObjects is irrelevant.
1910.2.36 by Aaron Bentley
Get upgrade from format4 under test and fixed for all formats
370
        self.build_tree_contents(_upgrade_dir_template)
371
        old_repodir = bzrlib.bzrdir.BzrDir.open_unsupported('.')
372
        old_repo_format = old_repodir.open_repository()._format
373
        format = self.repository_format._matchingbzrdir
374
        try:
375
            format.repository_format = self.repository_format
376
        except AttributeError:
377
            pass
378
        upgrade('.', format)
379
1910.2.37 by Aaron Bentley
Handle empty commits, fix test
380
    def test_pointless_commit(self):
381
        tree = self.make_branch_and_tree('.')
382
        self.assertRaises(errors.PointlessCommit, tree.commit, 'pointless',
383
                          allow_pointless=False)
384
        tree.commit('pointless', allow_pointless=True)
385
2323.5.17 by Martin Pool
Add supports_tree_reference to all repo formats (robert)
386
    def test_format_attributes(self):
387
        """All repository formats should have some basic attributes."""
388
        # create a repository to get a real format instance, not the 
3128.1.3 by Vincent Ladeuil
Since we are there s/parameteris.*/parameteriz&/.
389
        # template from the test suite parameterization.
2323.5.17 by Martin Pool
Add supports_tree_reference to all repo formats (robert)
390
        repo = self.make_repository('.')
391
        repo._format.rich_root_data
392
        repo._format.supports_tree_reference
393
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
394
    def test_get_data_stream_deprecated(self):
395
        # If get_data_stream is present it must be deprecated
396
        tree = self.make_branch_and_tree('t')
397
        self.build_tree(['t/foo'])
398
        tree.add('foo', 'file1')
399
        tree.commit('message', rev_id='rev_id')
400
        repo = tree.branch.repository
401
        try:
402
            stream = self.applyDeprecated(one_two, repo.get_data_stream,
403
                ['rev_id'])
404
        except NotImplementedError:
405
            raise TestNotApplicable("%s doesn't support get_data_stream"
406
                % repo._format)
407
        except AttributeError:
408
            pass
409
410
    def test_get_data_stream_for_search(self):
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
411
        # Make a repo with a revision
412
        tree = self.make_branch_and_tree('t')
413
        self.build_tree(['t/foo'])
414
        tree.add('foo', 'file1')
415
        tree.commit('message', rev_id='rev_id')
416
        repo = tree.branch.repository
417
418
        # Get a data stream (a file-like object) for that revision
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
419
        search = graph.SearchResult(set(['rev_id']), set([NULL_REVISION]), 1,
420
            set(['rev_id']))
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
421
        try:
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
422
            stream = repo.get_data_stream_for_search(search)
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
423
        except NotImplementedError:
3023.2.1 by Martin Pool
test_get_data_stream should indicate NotApplicable rather than silently passing
424
            raise TestNotApplicable("%s doesn't support get_data_stream"
425
                % repo._format)
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
426
2535.3.14 by Andrew Bennetts
Move serialising repo data stream to bytes into smart protocol.
427
        # The data stream is a iterator that yields (name, versioned_file)
428
        # pairs for:
2535.3.62 by Andrew Bennetts
Cosmetic changes.
429
        #   * the file knit (or knits; if this repo has rich roots there will
430
        #     be a file knit for that as well as for 'file1').
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
431
        #   * the inventory knit
432
        #   * the revisions knit
433
        # in that order.
2535.3.50 by Andrew Bennetts
Use tuple names in data streams rather than concatenated strings.
434
        expected_record_names = [
435
            ('file', 'file1'),
436
            ('inventory',),
437
            ('signatures',),
438
            ('revisions',)]
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
439
        streamed_names = []
2535.3.15 by Andrew Bennetts
Add KnitVersionedFile.get_stream_as_bytes, start smart implementation of RemoteRepository.get_data_stream.
440
        for name, bytes in stream:
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
441
            streamed_names.append(name)
442
443
        if repo.supports_rich_root():
2535.3.14 by Andrew Bennetts
Move serialising repo data stream to bytes into smart protocol.
444
            # Check for the root versioned file in the stream, then remove it
445
            # from streamed_names so we can compare that with
446
            # expected_record_names.
447
            # Note that the file knits can be in any order, so this test is
448
            # written to allow that.
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
449
            inv = repo.get_inventory('rev_id')
2535.3.50 by Andrew Bennetts
Use tuple names in data streams rather than concatenated strings.
450
            expected_record_name = ('file', inv.root.file_id)
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
451
            self.assertTrue(expected_record_name in streamed_names)
452
            streamed_names.remove(expected_record_name)
453
454
        self.assertEqual(expected_record_names, streamed_names)
455
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
456
    def test_insert_data_stream(self):
457
        tree = self.make_branch_and_tree('source')
458
        self.build_tree(['source/foo'])
459
        tree.add('foo', 'file1')
460
        tree.commit('message', rev_id='rev_id')
461
        source_repo = tree.branch.repository
462
        dest_repo = self.make_repository('dest')
3204.1.2 by Ian Clatworthy
Fix error handling in insert_data_stream (Lukas Lalinsky)
463
        search = dest_repo.search_missing_revision_ids(source_repo,
464
            revision_id='rev_id')
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
465
        try:
3204.1.2 by Ian Clatworthy
Fix error handling in insert_data_stream (Lukas Lalinsky)
466
            stream = source_repo.get_data_stream_for_search(search)
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
467
        except NotImplementedError, e:
468
            # Not all repositories support streaming.
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
469
            self.assertContainsRe(str(e), 'get_data_stream_for_search')
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
470
            raise TestSkipped('This format does not support streaming.')
471
2592.3.214 by Robert Collins
Merge bzr.dev.
472
        dest_repo.lock_write()
473
        try:
474
            dest_repo.start_write_group()
475
            try:
476
                dest_repo.insert_data_stream(stream)
477
            except:
478
                dest_repo.abort_write_group()
479
                raise
480
            else:
481
                dest_repo.commit_write_group()
482
        finally:
483
            dest_repo.unlock()
484
        # reopen to be sure it was added.
485
        dest_repo = dest_repo.bzrdir.open_repository()
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
486
        self.assertTrue(dest_repo.has_revision('rev_id'))
487
3184.5.1 by Lukáš Lalinský
Fix handling of some error cases in insert_data_stream
488
        # insert the same data stream again, should be no-op
3204.1.2 by Ian Clatworthy
Fix error handling in insert_data_stream (Lukas Lalinsky)
489
        stream = source_repo.get_data_stream_for_search(search)
3184.5.1 by Lukáš Lalinský
Fix handling of some error cases in insert_data_stream
490
        dest_repo.lock_write()
491
        try:
492
            dest_repo.start_write_group()
493
            try:
494
                dest_repo.insert_data_stream(stream)
495
            except:
496
                dest_repo.abort_write_group()
497
                raise
498
            else:
499
                dest_repo.commit_write_group()
500
        finally:
501
            dest_repo.unlock()
502
503
        # try to insert data stream with invalid key
504
        stream = [[('bogus-key',), '']]
505
        self.assertRaises(errors.RepositoryDataStreamError,
506
                          dest_repo.insert_data_stream, stream)
507
2520.4.113 by Aaron Bentley
Avoid peeking at Repository._serializer
508
    def test_get_serializer_format(self):
509
        repo = self.make_repository('.')
510
        format = repo.get_serializer_format()
511
        self.assertEqual(repo._serializer.format_num, format)
512
2708.1.7 by Aaron Bentley
Rename extract_files_bytes to iter_files_bytes
513
    def test_iter_files_bytes(self):
2708.1.3 by Aaron Bentley
Implement extract_files_bytes on Repository
514
        tree = self.make_branch_and_tree('tree')
515
        self.build_tree_contents([('tree/file1', 'foo'),
516
                                  ('tree/file2', 'bar')])
517
        tree.add(['file1', 'file2'], ['file1-id', 'file2-id'])
518
        tree.commit('rev1', rev_id='rev1')
519
        self.build_tree_contents([('tree/file1', 'baz')])
520
        tree.commit('rev2', rev_id='rev2')
2708.1.11 by Aaron Bentley
Test and tweak error handling
521
        repository = tree.branch.repository
2592.3.214 by Robert Collins
Merge bzr.dev.
522
        repository.lock_read()
523
        self.addCleanup(repository.unlock)
2708.1.6 by Aaron Bentley
Turn extract_files_bytes into an iterator
524
        extracted = dict((i, ''.join(b)) for i, b in
2708.1.11 by Aaron Bentley
Test and tweak error handling
525
                         repository.iter_files_bytes(
2708.1.6 by Aaron Bentley
Turn extract_files_bytes into an iterator
526
                         [('file1-id', 'rev1', 'file1-old'),
527
                          ('file1-id', 'rev2', 'file1-new'),
528
                          ('file2-id', 'rev1', 'file2'),
529
                         ]))
530
        self.assertEqual('foo', extracted['file1-old'])
531
        self.assertEqual('bar', extracted['file2'])
532
        self.assertEqual('baz', extracted['file1-new'])
2708.1.11 by Aaron Bentley
Test and tweak error handling
533
        self.assertRaises(errors.RevisionNotPresent, list,
534
                          repository.iter_files_bytes(
535
                          [('file1-id', 'rev3', 'file1-notpresent')]))
2592.3.102 by Robert Collins
Change the iter_files_bytes error to allow either RevisionNotPresent or NoSuchId when requesting a file that was not in the repository at all.
536
        self.assertRaises((errors.RevisionNotPresent, errors.NoSuchId), list,
2708.1.11 by Aaron Bentley
Test and tweak error handling
537
                          repository.iter_files_bytes(
538
                          [('file3-id', 'rev3', 'file1-notpresent')]))
2708.1.3 by Aaron Bentley
Implement extract_files_bytes on Repository
539
2535.3.63 by Andrew Bennetts
Add repository implementations test for item_keys_introduced_by.
540
    def test_item_keys_introduced_by(self):
541
        # Make a repo with one revision and one versioned file.
542
        tree = self.make_branch_and_tree('t')
543
        self.build_tree(['t/foo'])
544
        tree.add('foo', 'file1')
545
        tree.commit('message', rev_id='rev_id')
546
        repo = tree.branch.repository
547
548
        # Item keys will be in this order, for maximum convenience for
549
        # generating data to insert into knit repository:
550
        #   * files
551
        #   * inventory
552
        #   * signatures
553
        #   * revisions
554
        expected_item_keys = [
555
            ('file', 'file1', ['rev_id']),
556
            ('inventory', None, ['rev_id']),
557
            ('signatures', None, []),
558
            ('revisions', None, ['rev_id'])]
559
        item_keys = list(repo.item_keys_introduced_by(['rev_id']))
560
        item_keys = [
561
            (kind, file_id, list(versions))
562
            for (kind, file_id, versions) in item_keys]
563
564
        if repo.supports_rich_root():
565
            # Check for the root versioned file in the item_keys, then remove
566
            # it from streamed_names so we can compare that with
567
            # expected_record_names.
568
            # Note that the file keys can be in any order, so this test is
569
            # written to allow that.
570
            inv = repo.get_inventory('rev_id')
571
            root_item_key = ('file', inv.root.file_id, ['rev_id'])
572
            self.assertTrue(root_item_key in item_keys)
573
            item_keys.remove(root_item_key)
574
575
        self.assertEqual(expected_item_keys, item_keys)
576
2850.4.1 by Andrew Bennetts
Add smoketest for repo.get_graph, and fix bug in RemoteRepository.get_graph that it reveals.
577
    def test_get_graph(self):
578
        """Bare-bones smoketest that all repositories implement get_graph."""
579
        repo = self.make_repository('repo')
2592.3.214 by Robert Collins
Merge bzr.dev.
580
        repo.lock_read()
581
        self.addCleanup(repo.unlock)
2850.4.1 by Andrew Bennetts
Add smoketest for repo.get_graph, and fix bug in RemoteRepository.get_graph that it reveals.
582
        repo.get_graph()
583
3146.1.1 by Aaron Bentley
Fix bad ghost handling in KnitParentsProvider
584
    def test_graph_ghost_handling(self):
585
        tree = self.make_branch_and_tree('here')
586
        tree.lock_write()
587
        self.addCleanup(tree.unlock)
588
        tree.commit('initial commit', rev_id='rev1')
589
        tree.add_parent_tree_id('ghost')
590
        tree.commit('commit-with-ghost', rev_id='rev2')
591
        graph = tree.branch.repository.get_graph()
592
        parents = graph.get_parent_map(['ghost', 'rev2'])
593
        self.assertTrue('ghost' not in parents)
3146.1.2 by Aaron Bentley
ParentsProviders now provide tuples of parents, never lists
594
        self.assertEqual(parents['rev2'], ('rev1', 'ghost'))
595
596
    def test_parent_map_type(self):
597
        tree = self.make_branch_and_tree('here')
598
        tree.lock_write()
599
        self.addCleanup(tree.unlock)
600
        tree.commit('initial commit', rev_id='rev1')
601
        tree.commit('next commit', rev_id='rev2')
602
        graph = tree.branch.repository.get_graph()
603
        parents = graph.get_parent_map([NULL_REVISION, 'rev1', 'rev2'])
604
        for value in parents.values():
605
            self.assertIsInstance(value, tuple)
3146.1.1 by Aaron Bentley
Fix bad ghost handling in KnitParentsProvider
606
2819.2.4 by Andrew Bennetts
Add a 'revision_graph_can_have_wrong_parents' method to repository.
607
    def test_implements_revision_graph_can_have_wrong_parents(self):
608
        """All repositories should implement
609
        revision_graph_can_have_wrong_parents, so that check and reconcile can
610
        work correctly.
611
        """
612
        repo = self.make_repository('.')
613
        # This should work, not raise NotImplementedError:
2592.3.214 by Robert Collins
Merge bzr.dev.
614
        if not repo.revision_graph_can_have_wrong_parents():
615
            return
616
        repo.lock_read()
617
        self.addCleanup(repo.unlock)
618
        # This repo must also implement
619
        # _find_inconsistent_revision_parents and
620
        # _check_for_inconsistent_revision_parents.  So calling these
621
        # should not raise NotImplementedError.
622
        list(repo._find_inconsistent_revision_parents())
623
        repo._check_for_inconsistent_revision_parents()
2819.2.4 by Andrew Bennetts
Add a 'revision_graph_can_have_wrong_parents' method to repository.
624
2996.2.4 by Aaron Bentley
Rename function to add_signature_text
625
    def test_add_signature_text(self):
2996.2.3 by Aaron Bentley
Add tests for install_revisions and add_signature
626
        repo = self.make_repository('repo')
627
        repo.lock_write()
628
        self.addCleanup(repo.unlock)
629
        self.addCleanup(repo.commit_write_group)
630
        repo.start_write_group()
631
        inv = Inventory(revision_id='A')
632
        inv.root.revision = 'A'
633
        repo.add_inventory('A', inv, [])
634
        repo.add_revision('A', Revision('A', committer='A', timestamp=0,
635
                          inventory_sha1='', timezone=0, message='A'))
2996.2.4 by Aaron Bentley
Rename function to add_signature_text
636
        repo.add_signature_text('A', 'This might be a signature')
2996.2.3 by Aaron Bentley
Add tests for install_revisions and add_signature
637
        self.assertEqual('This might be a signature',
638
                         repo.get_signature_text('A'))
639
640
    def test_install_revisions(self):
641
        wt = self.make_branch_and_tree('source')
642
        wt.commit('A', allow_pointless=True, rev_id='A')
643
        repo = wt.branch.repository
644
        repo.lock_write()
645
        repo.start_write_group()
646
        repo.sign_revision('A', bzrlib.gpg.LoopbackGPGStrategy(None))
647
        repo.commit_write_group()
648
        repo.unlock()
649
        repo.lock_read()
650
        self.addCleanup(repo.unlock)
651
        repo2 = self.make_repository('repo2')
652
        revision = repo.get_revision('A')
653
        tree = repo.revision_tree('A')
654
        signature = repo.get_signature_text('A')
655
        repo2.lock_write()
656
        self.addCleanup(repo2.unlock)
657
        repository.install_revisions(repo2, [(revision, tree, signature)])
658
        self.assertEqual(revision, repo2.get_revision('A'))
659
        self.assertEqual(signature, repo2.get_signature_text('A'))
1852.9.6 by Robert Collins
Merge the change from Tree.compare to Tree.changes_from.
660
3047.1.1 by Andrew Bennetts
Fix for bug 164626, add test that Repository.sprout preserves format.
661
    # XXX: this helper duplicated from tests.test_repository
662
    def make_remote_repository(self, path):
663
        """Make a RemoteRepository object backed by a real repository that will
664
        be created at the given path."""
665
        repo = self.make_repository(path)
666
        smart_server = server.SmartTCPServer_for_testing()
667
        smart_server.setUp(self.get_server())
668
        remote_transport = get_transport(smart_server.get_url()).clone(path)
669
        self.addCleanup(smart_server.tearDown)
670
        remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
671
        remote_repo = remote_bzrdir.open_repository()
672
        return remote_repo
673
3047.1.2 by Andrew Bennetts
Directly test that sprouting branches from a HPSS preserves the repository format.
674
    def test_sprout_from_hpss_preserves_format(self):
675
        """repo.sprout from a smart server preserves the repository format."""
3047.1.1 by Andrew Bennetts
Fix for bug 164626, add test that Repository.sprout preserves format.
676
        if self.repository_format == RepositoryFormat7():
677
            raise KnownFailure(
678
                "Cannot fetch weaves over smart protocol.")
679
        remote_repo = self.make_remote_repository('remote')
680
        local_bzrdir = self.make_bzrdir('local')
681
        try:
682
            local_repo = remote_repo.sprout(local_bzrdir)
683
        except errors.TransportNotPossible:
684
            raise TestNotApplicable(
685
                "Cannot lock_read old formats like AllInOne over HPSS.")
686
        remote_backing_repo = bzrdir.BzrDir.open(
687
            self.get_vfs_only_url('remote')).open_repository()
688
        self.assertEqual(remote_backing_repo._format, local_repo._format)
689
3047.1.2 by Andrew Bennetts
Directly test that sprouting branches from a HPSS preserves the repository format.
690
    def test_sprout_branch_from_hpss_preserves_repo_format(self):
691
        """branch.sprout from a smart server preserves the repository format.
692
        """
693
        weave_formats = [RepositoryFormat5(), RepositoryFormat6(),
694
                         RepositoryFormat7()]
695
        if self.repository_format in weave_formats:
696
            raise KnownFailure(
697
                "Cannot fetch weaves over smart protocol.")
698
        remote_repo = self.make_remote_repository('remote')
699
        remote_branch = remote_repo.bzrdir.create_branch()
700
        try:
701
            local_bzrdir = remote_branch.bzrdir.sprout('local')
702
        except errors.TransportNotPossible:
703
            raise TestNotApplicable(
704
                "Cannot lock_read old formats like AllInOne over HPSS.")
705
        local_repo = local_bzrdir.open_repository()
706
        remote_backing_repo = bzrdir.BzrDir.open(
707
            self.get_vfs_only_url('remote')).open_repository()
708
        self.assertEqual(remote_backing_repo._format, local_repo._format)
709
3089.2.1 by Andrew Bennetts
Implement RemoteRepository._make_parents_provider.
710
    def test__make_parents_provider(self):
711
        """Repositories must have a _make_parents_provider method that returns
3099.3.5 by John Arbash Meinel
Update the last couple of places that referred to Provider.get_parents() directly.
712
        an object with a get_parent_map method.
3089.2.1 by Andrew Bennetts
Implement RemoteRepository._make_parents_provider.
713
        """
714
        repo = self.make_repository('repo')
3099.3.5 by John Arbash Meinel
Update the last couple of places that referred to Provider.get_parents() directly.
715
        repo._make_parents_provider().get_parent_map
3089.2.1 by Andrew Bennetts
Implement RemoteRepository._make_parents_provider.
716
3140.1.2 by Aaron Bentley
Add ability to find branches inside repositories
717
    def make_repository_and_foo_bar(self, shared):
718
        made_control = self.make_bzrdir('repository')
719
        repo = made_control.create_repository(shared=shared)
720
        bzrdir.BzrDir.create_branch_convenience(self.get_url('repository/foo'),
721
                                                force_new_repo=False)
722
        bzrdir.BzrDir.create_branch_convenience(self.get_url('repository/bar'),
723
                                                force_new_repo=True)
3140.1.3 by Aaron Bentley
Add support for finding branches to BzrDir
724
        baz = self.make_bzrdir('repository/baz')
725
        qux = self.make_branch('repository/baz/qux')
3140.1.6 by Aaron Bentley
Add test that nested branches are returned
726
        quxx = self.make_branch('repository/baz/qux/quxx')
3140.1.2 by Aaron Bentley
Add ability to find branches inside repositories
727
        return repo
728
729
    def test_find_branches(self):
730
        repo = self.make_repository_and_foo_bar(shared=False)
731
        branches = repo.find_branches()
732
        self.assertContainsRe(branches[-1].base, 'repository/foo/$')
3140.1.6 by Aaron Bentley
Add test that nested branches are returned
733
        self.assertContainsRe(branches[-3].base, 'repository/baz/qux/$')
734
        self.assertContainsRe(branches[-2].base, 'repository/baz/qux/quxx/$')
3140.1.2 by Aaron Bentley
Add ability to find branches inside repositories
735
        # in some formats, creating a repo creates a branch
3140.1.6 by Aaron Bentley
Add test that nested branches are returned
736
        if len(branches) == 6:
737
            self.assertContainsRe(branches[-4].base, 'repository/baz/$')
738
            self.assertContainsRe(branches[-5].base, 'repository/bar/$')
739
            self.assertContainsRe(branches[-6].base, 'repository/$')
740
        else:
741
            self.assertEqual(4, len(branches))
3140.1.3 by Aaron Bentley
Add support for finding branches to BzrDir
742
            self.assertContainsRe(branches[-4].base, 'repository/bar/$')
3140.1.2 by Aaron Bentley
Add ability to find branches inside repositories
743
744
    def test_find_branches_using(self):
745
        try:
746
            repo = self.make_repository_and_foo_bar(shared=True)
747
        except errors.IncompatibleFormat:
748
            raise TestNotApplicable
749
        branches = repo.find_branches(using=True)
750
        self.assertContainsRe(branches[-1].base, 'repository/foo/$')
751
        # in some formats, creating a repo creates a branch
752
        if len(branches) == 2:
753
            self.assertContainsRe(branches[-2].base, 'repository/$')
754
        else:
755
            self.assertEqual(1, len(branches))
756
3140.1.9 by Aaron Bentley
Optimize find_branches for standalone repositories
757
    def test_find_branches_using_standalone(self):
758
        branch = self.make_branch('branch')
759
        contained = self.make_branch('branch/contained')
760
        branches = branch.repository.find_branches(using=True)
761
        self.assertEqual([branch.base], [b.base for b in branches])
762
        branches = branch.repository.find_branches(using=False)
763
        self.assertEqual([branch.base, contained.base],
764
                         [b.base for b in branches])
765
766
    def test_find_branches_using_empty_standalone_repo(self):
767
        repo = self.make_repository('repo')
768
        self.assertFalse(repo.is_shared())
769
        try:
770
            repo.bzrdir.open_branch()
771
        except errors.NotBranchError:
772
            self.assertEqual([], repo.find_branches(using=True))
773
        else:
774
            self.assertEqual([repo.bzrdir.root_transport.base],
775
                             [b.base for b in repo.find_branches(using=True)])
776
3047.1.1 by Andrew Bennetts
Fix for bug 164626, add test that Repository.sprout preserves format.
777
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
778
class TestRepositoryLocking(TestCaseWithRepository):
779
780
    def test_leave_lock_in_place(self):
781
        repo = self.make_repository('r')
782
        # Lock the repository, then use leave_lock_in_place so that when we
783
        # unlock the repository the lock is still held on disk.
2018.5.76 by Andrew Bennetts
Testing that repository.{dont_,}leave_lock_in_place raises NotImplementedError if lock_write returns None.
784
        token = repo.lock_write()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
785
        try:
2018.5.76 by Andrew Bennetts
Testing that repository.{dont_,}leave_lock_in_place raises NotImplementedError if lock_write returns None.
786
            if token is None:
787
                # This test does not apply, because this repository refuses lock
788
                # tokens.
789
                self.assertRaises(NotImplementedError, repo.leave_lock_in_place)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
790
                return
2018.5.76 by Andrew Bennetts
Testing that repository.{dont_,}leave_lock_in_place raises NotImplementedError if lock_write returns None.
791
            repo.leave_lock_in_place()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
792
        finally:
793
            repo.unlock()
794
        # We should be unable to relock the repo.
795
        self.assertRaises(errors.LockContention, repo.lock_write)
796
797
    def test_dont_leave_lock_in_place(self):
798
        repo = self.make_repository('r')
799
        # Create a lock on disk.
800
        token = repo.lock_write()
801
        try:
802
            if token is None:
803
                # This test does not apply, because this repository refuses lock
804
                # tokens.
2018.5.76 by Andrew Bennetts
Testing that repository.{dont_,}leave_lock_in_place raises NotImplementedError if lock_write returns None.
805
                self.assertRaises(NotImplementedError,
806
                                  repo.dont_leave_lock_in_place)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
807
                return
808
            try:
809
                repo.leave_lock_in_place()
810
            except NotImplementedError:
811
                # This repository doesn't support this API.
812
                return
813
        finally:
814
            repo.unlock()
815
        # Reacquire the lock (with a different repository object) by using the
816
        # token.
817
        new_repo = repo.bzrdir.open_repository()
818
        new_repo.lock_write(token=token)
819
        # Call dont_leave_lock_in_place, so that the lock will be released by
820
        # this instance, even though the lock wasn't originally acquired by it.
821
        new_repo.dont_leave_lock_in_place()
822
        new_repo.unlock()
823
        # Now the repository is unlocked.  Test this by locking it (without a
824
        # token).
825
        repo.lock_write()
826
        repo.unlock()
827
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
828
    def test_lock_read_then_unlock(self):
829
        # Calling lock_read then unlocking should work without errors.
830
        repo = self.make_repository('r')
831
        repo.lock_read()
832
        repo.unlock()
833
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
834
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
835
class TestCaseWithComplexRepository(TestCaseWithRepository):
836
837
    def setUp(self):
838
        super(TestCaseWithComplexRepository, self).setUp()
839
        tree_a = self.make_branch_and_tree('a')
840
        self.bzrdir = tree_a.branch.bzrdir
841
        # add a corrupt inventory 'orphan'
1534.1.34 by Robert Collins
Move missing_revision_ids from Repository to InterRepository, and eliminate the now unused Repository._compatible_formats method.
842
        # this may need some generalising for knits.
2592.3.74 by Robert Collins
Test setup fix to use get_inventory_weave rather than control_store.get_weave.
843
        tree_a.lock_write()
844
        try:
845
            tree_a.branch.repository.start_write_group()
846
            inv_file = tree_a.branch.repository.get_inventory_weave()
847
            try:
848
                inv_file.add_lines('orphan', [], [])
849
            except:
850
                tree_a.branch.repository.commit_write_group()
851
                raise
852
            else:
853
                tree_a.branch.repository.abort_write_group()
854
        finally:
855
            tree_a.unlock()
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
856
        # add a real revision 'rev1'
857
        tree_a.commit('rev1', rev_id='rev1', allow_pointless=True)
858
        # add a real revision 'rev2' based on rev1
859
        tree_a.commit('rev2', rev_id='rev2', allow_pointless=True)
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
860
        # add a reference to a ghost
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
861
        tree_a.add_parent_tree_id('ghost1')
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
862
        tree_a.commit('rev3', rev_id='rev3', allow_pointless=True)
863
        # add another reference to a ghost, and a second ghost.
1908.6.7 by Robert Collins
Remove all users of set_pending_merges and add_pending_merge except tests that they work correctly.
864
        tree_a.add_parent_tree_id('ghost1')
865
        tree_a.add_parent_tree_id('ghost2')
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
866
        tree_a.commit('rev4', rev_id='rev4', allow_pointless=True)
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
867
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
868
    def test_revision_trees(self):
869
        revision_ids = ['rev1', 'rev2', 'rev3', 'rev4']
870
        repository = self.bzrdir.open_repository()
2592.3.214 by Robert Collins
Merge bzr.dev.
871
        repository.lock_read()
872
        self.addCleanup(repository.unlock)
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
873
        trees1 = list(repository.revision_trees(revision_ids))
874
        trees2 = [repository.revision_tree(t) for t in revision_ids]
875
        assert len(trees1) == len(trees2)
876
        for tree1, tree2 in zip(trees1, trees2):
1852.10.3 by Robert Collins
Remove all uses of compare_trees and replace with Tree.changes_from throughout bzrlib.
877
            assert not tree2.changes_from(tree1).has_changed()
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
878
1756.3.22 by Aaron Bentley
Tweaks from review
879
    def test_get_deltas_for_revisions(self):
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
880
        repository = self.bzrdir.open_repository()
2592.3.214 by Robert Collins
Merge bzr.dev.
881
        repository.lock_read()
882
        self.addCleanup(repository.unlock)
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
883
        revisions = [repository.get_revision(r) for r in 
884
                     ['rev1', 'rev2', 'rev3', 'rev4']]
1756.3.22 by Aaron Bentley
Tweaks from review
885
        deltas1 = list(repository.get_deltas_for_revisions(revisions))
1756.3.20 by Aaron Bentley
Tests for get_revision_deltas and revisions_trees
886
        deltas2 = [repository.get_revision_delta(r.revision_id) for r in
887
                   revisions]
888
        assert deltas1 == deltas2
889
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
890
    def test_all_revision_ids(self):
891
        # all_revision_ids -> all revisions
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
892
        self.assertEqual(['rev1', 'rev2', 'rev3', 'rev4'],
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
893
                         self.bzrdir.open_repository().all_revision_ids())
894
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
895
    def test_get_ancestry_missing_revision(self):
1534.1.34 by Robert Collins
Move missing_revision_ids from Repository to InterRepository, and eliminate the now unused Repository._compatible_formats method.
896
        # get_ancestry(revision that is in some data but not fully installed
897
        # -> NoSuchRevision
1534.4.41 by Robert Collins
Branch now uses BzrDir reasonably sanely.
898
        self.assertRaises(errors.NoSuchRevision,
899
                          self.bzrdir.open_repository().get_ancestry, 'orphan')
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
900
2530.1.1 by Aaron Bentley
Make topological sorting optional for get_ancestry
901
    def test_get_unsorted_ancestry(self):
902
        repo = self.bzrdir.open_repository()
903
        self.assertEqual(set(repo.get_ancestry('rev3')),
904
                         set(repo.get_ancestry('rev3', topo_sorted=False)))
905
1590.1.1 by Robert Collins
Improve common_ancestor performance.
906
    def test_get_revision_graph(self):
907
        # we can get a mapping of id->parents for the entire revision graph or bits thereof.
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
908
        self.assertEqual({'rev1':(),
909
                          'rev2':('rev1', ),
910
                          'rev3':('rev2', ),
911
                          'rev4':('rev3', ),
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
912
                          },
1590.1.1 by Robert Collins
Improve common_ancestor performance.
913
                         self.bzrdir.open_repository().get_revision_graph(None))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
914
        self.assertEqual({'rev1':()},
1590.1.1 by Robert Collins
Improve common_ancestor performance.
915
                         self.bzrdir.open_repository().get_revision_graph('rev1'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
916
        self.assertEqual({'rev1':(),
917
                          'rev2':('rev1', )},
1590.1.1 by Robert Collins
Improve common_ancestor performance.
918
                         self.bzrdir.open_repository().get_revision_graph('rev2'))
2279.7.9 by Andrew Bennetts
Remove some redundant code pointed out by Robert's review, and remove some unused imports while I'm there.
919
        self.assertRaises(errors.NoSuchRevision,
1590.1.1 by Robert Collins
Improve common_ancestor performance.
920
                          self.bzrdir.open_repository().get_revision_graph,
921
                          'orphan')
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
922
        # and ghosts are not mentioned
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
923
        self.assertEqual({'rev1':(),
924
                          'rev2':('rev1', ),
925
                          'rev3':('rev2', ),
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
926
                          },
927
                         self.bzrdir.open_repository().get_revision_graph('rev3'))
1836.3.1 by Robert Collins
(robertc) Teach repository.get_revision_graph, and revision.common_ancestor, about NULL_REVISION.
928
        # and we can ask for the NULLREVISION graph
929
        self.assertEqual({},
930
            self.bzrdir.open_repository().get_revision_graph(NULL_REVISION))
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
931
932
    def test_get_revision_graph_with_ghosts(self):
933
        # we can get a graph object with roots, ghosts, ancestors and
934
        # descendants.
935
        repo = self.bzrdir.open_repository()
936
        graph = repo.get_revision_graph_with_ghosts([])
937
        self.assertEqual(set(['rev1']), graph.roots)
938
        self.assertEqual(set(['ghost1', 'ghost2']), graph.ghosts)
939
        self.assertEqual({'rev1':[],
940
                          'rev2':['rev1'],
941
                          'rev3':['rev2', 'ghost1'],
942
                          'rev4':['rev3', 'ghost1', 'ghost2'],
943
                          },
944
                          graph.get_ancestors())
945
        self.assertEqual({'ghost1':{'rev3':1, 'rev4':1},
946
                          'ghost2':{'rev4':1},
947
                          'rev1':{'rev2':1},
948
                          'rev2':{'rev3':1},
949
                          'rev3':{'rev4':1},
950
                          'rev4':{},
951
                          },
952
                          graph.get_descendants())
1836.3.1 by Robert Collins
(robertc) Teach repository.get_revision_graph, and revision.common_ancestor, about NULL_REVISION.
953
        # and we can ask for the NULLREVISION graph
954
        graph = repo.get_revision_graph_with_ghosts([NULL_REVISION])
955
        self.assertEqual({}, graph.get_ancestors())
956
        self.assertEqual({}, graph.get_descendants())
1590.1.1 by Robert Collins
Improve common_ancestor performance.
957
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
958
    def test_reserved_id(self):
959
        repo = self.make_repository('repository')
2592.3.61 by Robert Collins
Remove inventory.kndx.
960
        repo.lock_write()
961
        repo.start_write_group()
962
        try:
963
            self.assertRaises(errors.ReservedId, repo.add_inventory, 'reserved:',
964
                              None, None)
965
            self.assertRaises(errors.ReservedId, repo.add_revision, 'reserved:',
966
                              None)
967
        finally:
968
            repo.abort_write_group()
969
            repo.unlock()
2229.2.3 by Aaron Bentley
change reserved_id to is_reserved_id, add check_not_reserved for DRY
970
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
971
972
class TestCaseWithCorruptRepository(TestCaseWithRepository):
973
974
    def setUp(self):
975
        super(TestCaseWithCorruptRepository, self).setUp()
976
        # a inventory with no parents and the revision has parents..
977
        # i.e. a ghost.
978
        repo = self.make_repository('inventory_with_unnecessary_ghost')
2592.3.38 by Robert Collins
All experimental format tests passing again.
979
        repo.lock_write()
980
        repo.start_write_group()
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
981
        inv = Inventory(revision_id = 'ghost')
982
        inv.root.revision = 'ghost'
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
983
        sha1 = repo.add_inventory('ghost', inv, [])
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
984
        rev = bzrlib.revision.Revision(timestamp=0,
985
                                       timezone=None,
986
                                       committer="Foo Bar <foo@example.com>",
987
                                       message="Message",
988
                                       inventory_sha1=sha1,
989
                                       revision_id='ghost')
990
        rev.parent_ids = ['the_ghost']
991
        repo.add_revision('ghost', rev)
992
         
1910.2.23 by Aaron Bentley
Fix up test cases that manually construct inventories
993
        inv = Inventory(revision_id = 'the_ghost')
994
        inv.root.revision = 'the_ghost'
1907.1.1 by Aaron Bentley
Unshelved all changes except those related to removing RootEntry
995
        sha1 = repo.add_inventory('the_ghost', inv, [])
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
996
        rev = bzrlib.revision.Revision(timestamp=0,
997
                                       timezone=None,
998
                                       committer="Foo Bar <foo@example.com>",
999
                                       message="Message",
1000
                                       inventory_sha1=sha1,
1001
                                       revision_id='the_ghost')
1002
        rev.parent_ids = []
1003
        repo.add_revision('the_ghost', rev)
1004
        # check its setup usefully
1005
        inv_weave = repo.get_inventory_weave()
1563.2.30 by Robert Collins
Remove all but fetch references to revision_store, making the repository references that are weave specific use the RevisionTextStore.text_store attribute.
1006
        self.assertEqual(['ghost'], inv_weave.get_ancestry(['ghost']))
2592.3.38 by Robert Collins
All experimental format tests passing again.
1007
        repo.commit_write_group()
1008
        repo.unlock()
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
1009
1594.2.10 by Robert Collins
Teach knit fetching and branching to only duplicate relevant data avoiding unnecessary reconciles.
1010
    def test_corrupt_revision_access_asserts_if_reported_wrong(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
1011
        repo_url = self.get_url('inventory_with_unnecessary_ghost')
1012
        repo = repository.Repository.open(repo_url)
1594.2.10 by Robert Collins
Teach knit fetching and branching to only duplicate relevant data avoiding unnecessary reconciles.
1013
        reported_wrong = False
1014
        try:
1015
            if repo.get_ancestry('ghost') != [None, 'the_ghost', 'ghost']:
1016
                reported_wrong = True
1017
        except errors.CorruptRepository:
1018
            # caught the bad data:
1019
            return
1020
        if not reported_wrong:
1021
            return
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
1022
        self.assertRaises(errors.CorruptRepository, repo.get_revision, 'ghost')
1023
1594.2.3 by Robert Collins
bugfix revision.MultipleRevisionSources.get_revision_graph to integrate ghosts between sources. [slow on weaves, fast on knits.
1024
    def test_corrupt_revision_get_revision_reconcile(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
1025
        repo_url = self.get_url('inventory_with_unnecessary_ghost')
1026
        repo = repository.Repository.open(repo_url)
1570.1.13 by Robert Collins
Check for incorrect revision parentage in the weave during revision access.
1027
        repo.get_revision_reconcile('ghost')
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1028
1029
2018.5.29 by Robert Collins
Dont run the vfat repository test on RemoteRepositories as there is no point.
1030
# FIXME: document why this is a TestCaseWithTransport rather than a
1031
#        TestCaseWithRepository
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1032
class TestEscaping(TestCaseWithTransport):
1033
    """Test that repositories can be stored correctly on VFAT transports.
1034
    
1035
    Makes sure we have proper escaping of invalid characters, etc.
1036
1037
    It'd be better to test all operations on the FakeVFATTransportDecorator,
1038
    but working trees go straight to the os not through the Transport layer.
1039
    Therefore we build some history first in the regular way and then 
1040
    check it's safe to access for vfat.
1041
    """
1042
1043
    def test_on_vfat(self):
2018.5.29 by Robert Collins
Dont run the vfat repository test on RemoteRepositories as there is no point.
1044
        # dont bother with remote repository testing, because this test is
1045
        # about local disk layout/support.
1046
        from bzrlib.remote import RemoteRepositoryFormat
1047
        if isinstance(self.repository_format, RemoteRepositoryFormat):
1048
            return
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1049
        FOO_ID = 'foo<:>ID'
2018.5.29 by Robert Collins
Dont run the vfat repository test on RemoteRepositories as there is no point.
1050
        REV_ID = 'revid-1' 
1051
        # this makes a default format repository always, which is wrong: 
1052
        # it should be a TestCaseWithRepository in order to get the 
1053
        # default format.
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1054
        wt = self.make_branch_and_tree('repo')
2052.2.1 by Alexander Belchenko
test_on_vfat win32 fix: use binary line-endings
1055
        self.build_tree(["repo/foo"], line_endings='binary')
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1056
        # add file with id containing wierd characters
1057
        wt.add(['foo'], [FOO_ID])
1058
        wt.commit('this is my new commit', rev_id=REV_ID)
1059
        # now access over vfat; should be safe
1060
        branch = bzrdir.BzrDir.open('vfat+' + self.get_url('repo')).open_branch()
1061
        revtree = branch.repository.revision_tree(REV_ID)
3015.2.13 by Robert Collins
More lock correctness for the use of get_file_text in repository_implementations.
1062
        revtree.lock_read()
1063
        self.addCleanup(revtree.unlock)
1986.1.1 by Robert Collins
Move test_branch_on_vfat into a repository implementation test, to ensure that all repository formats are safe on vfat.
1064
        contents = revtree.get_file_text(FOO_ID)
1065
        self.assertEqual(contents, 'contents of repo/foo\n')
2520.4.54 by Aaron Bentley
Hang a create_bundle method off repository
1066
1067
    def test_create_bundle(self):
1068
        wt = self.make_branch_and_tree('repo')
1069
        self.build_tree(['repo/file1'])
1070
        wt.add('file1')
1071
        wt.commit('file1', rev_id='rev1')
1072
        fileobj = StringIO()
1073
        wt.branch.repository.create_bundle('rev1', NULL_REVISION, fileobj)