~bzr-pqm/bzr/bzr.dev

4070.5.1 by Martin Pool
BranchBuilder now takes a timestamp for commits
1
# Copyright (C) 2007, 2009 Canonical Ltd
2466.7.3 by Robert Collins
Create bzrlib.branchbuilder.
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2466.7.3 by Robert Collins
Create bzrlib.branchbuilder.
16
17
"""Tests for the BranchBuilder class."""
18
2466.7.4 by Robert Collins
Add BranchBuilder.get_branch().
19
from bzrlib import (
20
    branch as _mod_branch,
3567.4.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
21
    errors,
2466.7.4 by Robert Collins
Add BranchBuilder.get_branch().
22
    revision as _mod_revision,
23
    tests,
24
    )
2466.7.3 by Robert Collins
Create bzrlib.branchbuilder.
25
from bzrlib.branchbuilder import BranchBuilder
26
27
28
class TestBranchBuilder(tests.TestCaseWithMemoryTransport):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
29
2466.7.3 by Robert Collins
Create bzrlib.branchbuilder.
30
    def test_create(self):
31
        """Test the constructor api."""
32
        builder = BranchBuilder(self.get_transport().clone('foo'))
33
        # we dont care if the branch has been built or not at this point.
2466.7.4 by Robert Collins
Add BranchBuilder.get_branch().
34
35
    def test_get_branch(self):
36
        """get_branch returns the created branch."""
37
        builder = BranchBuilder(self.get_transport().clone('foo'))
38
        branch = builder.get_branch()
39
        self.assertIsInstance(branch, _mod_branch.Branch)
40
        self.assertEqual(self.get_transport().clone('foo').base,
41
            branch.base)
42
        self.assertEqual(
43
            (0, _mod_revision.NULL_REVISION),
44
            branch.last_revision_info())
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
45
2466.7.10 by Robert Collins
Add a format parameter to BranchBuilder.
46
    def test_format(self):
47
        """Making a BranchBuilder with a format option sets the branch type."""
48
        builder = BranchBuilder(self.get_transport(), format='dirstate-tags')
49
        branch = builder.get_branch()
50
        self.assertIsInstance(branch, _mod_branch.BzrBranch6)
51
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
52
    def test_build_one_commit(self):
53
        """doing build_commit causes a commit to happen."""
54
        builder = BranchBuilder(self.get_transport().clone('foo'))
2466.7.9 by Robert Collins
Return the commited revision id from BranchBuilder.build_commit to save later instrospection.
55
        rev_id = builder.build_commit()
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
56
        branch = builder.get_branch()
2466.7.9 by Robert Collins
Return the commited revision id from BranchBuilder.build_commit to save later instrospection.
57
        self.assertEqual((1, rev_id), branch.last_revision_info())
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
58
        self.assertEqual(
59
            'commit 1',
60
            branch.repository.get_revision(branch.last_revision()).message)
61
4070.5.1 by Martin Pool
BranchBuilder now takes a timestamp for commits
62
    def test_build_commit_timestamp(self):
63
        """You can set a date when committing."""
64
        builder = self.make_branch_builder('foo')
65
        rev_id = builder.build_commit(timestamp=1236043340)
66
        branch = builder.get_branch()
67
        self.assertEqual((1, rev_id), branch.last_revision_info())
68
        rev = branch.repository.get_revision(branch.last_revision())
69
        self.assertEqual(
70
            'commit 1',
71
            rev.message)
72
        self.assertEqual(
73
            1236043340,
74
            int(rev.timestamp))
75
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
76
    def test_build_two_commits(self):
77
        """The second commit has the right parents and message."""
78
        builder = BranchBuilder(self.get_transport().clone('foo'))
2466.7.9 by Robert Collins
Return the commited revision id from BranchBuilder.build_commit to save later instrospection.
79
        rev_id1 = builder.build_commit()
80
        rev_id2 = builder.build_commit()
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
81
        branch = builder.get_branch()
2466.7.9 by Robert Collins
Return the commited revision id from BranchBuilder.build_commit to save later instrospection.
82
        self.assertEqual((2, rev_id2), branch.last_revision_info())
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
83
        self.assertEqual(
84
            'commit 2',
85
            branch.repository.get_revision(branch.last_revision()).message)
86
        self.assertEqual(
2466.7.9 by Robert Collins
Return the commited revision id from BranchBuilder.build_commit to save later instrospection.
87
            [rev_id1],
2466.7.6 by Robert Collins
Add BranchBuilder.build_commit.
88
            branch.repository.get_revision(branch.last_revision()).parent_ids)
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
89
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
90
91
class TestBranchBuilderBuildSnapshot(tests.TestCaseWithMemoryTransport):
92
93
    def assertTreeShape(self, expected_shape, tree):
94
        """Check that the tree shape matches expectations."""
95
        tree.lock_read()
96
        try:
97
            entries = [(path, ie.file_id, ie.kind)
98
                       for path, ie in tree.iter_entries_by_dir()]
99
        finally:
100
            tree.unlock()
101
        self.assertEqual(expected_shape, entries)
102
103
    def build_a_rev(self):
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
104
        builder = BranchBuilder(self.get_transport().clone('foo'))
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
105
        rev_id1 = builder.build_snapshot('A-id', None,
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
106
            [('add', ('', 'a-root-id', 'directory', None)),
107
             ('add', ('a', 'a-id', 'file', 'contents'))])
108
        self.assertEqual('A-id', rev_id1)
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
109
        return builder
110
111
    def test_add_one_file(self):
112
        builder = self.build_a_rev()
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
113
        branch = builder.get_branch()
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
114
        self.assertEqual((1, 'A-id'), branch.last_revision_info())
115
        rev_tree = branch.repository.revision_tree('A-id')
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
116
        rev_tree.lock_read()
117
        self.addCleanup(rev_tree.unlock)
3567.4.2 by John Arbash Meinel
test that we can add more files into an existing build
118
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
119
                              (u'a', 'a-id', 'file')], rev_tree)
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
120
        self.assertEqual('contents', rev_tree.get_file_text('a-id'))
3567.4.2 by John Arbash Meinel
test that we can add more files into an existing build
121
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
122
    def test_add_second_file(self):
123
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
124
        rev_id2 = builder.build_snapshot('B-id', None,
3567.4.2 by John Arbash Meinel
test that we can add more files into an existing build
125
            [('add', ('b', 'b-id', 'file', 'content_b'))])
126
        self.assertEqual('B-id', rev_id2)
127
        branch = builder.get_branch()
128
        self.assertEqual((2, rev_id2), branch.last_revision_info())
129
        rev_tree = branch.repository.revision_tree(rev_id2)
130
        rev_tree.lock_read()
131
        self.addCleanup(rev_tree.unlock)
132
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
133
                              (u'a', 'a-id', 'file'),
134
                              (u'b', 'b-id', 'file')], rev_tree)
135
        self.assertEqual('content_b', rev_tree.get_file_text('b-id'))
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
136
3567.4.7 by John Arbash Meinel
Revert back to using MemoryTree.mkdir() rather than creating the directory during add().
137
    def test_add_empty_dir(self):
138
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
139
        rev_id2 = builder.build_snapshot('B-id', None,
3567.4.7 by John Arbash Meinel
Revert back to using MemoryTree.mkdir() rather than creating the directory during add().
140
            [('add', ('b', 'b-id', 'directory', None))])
141
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
142
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
143
                              (u'a', 'a-id', 'file'),
144
                              (u'b', 'b-id', 'directory'),
145
                             ], rev_tree)
146
4070.5.1 by Martin Pool
BranchBuilder now takes a timestamp for commits
147
    def test_commit_timestamp(self):
148
        builder = self.make_branch_builder('foo')
149
        rev_id = builder.build_snapshot(None, None,
150
            [('add', (u'', None, 'directory', None))],
151
            timestamp=1234567890)
152
        rev = builder.get_branch().repository.get_revision(rev_id)
153
        self.assertEqual(
154
            1234567890,
155
            int(rev.timestamp))
156
3567.4.15 by John Arbash Meinel
Allow setting the commit message.
157
    def test_commit_message_default(self):
158
        builder = BranchBuilder(self.get_transport().clone('foo'))
159
        rev_id = builder.build_snapshot(None, None,
160
            [('add', (u'', None, 'directory', None))])
161
        branch = builder.get_branch()
162
        rev = branch.repository.get_revision(rev_id)
163
        self.assertEqual(u'commit 1', rev.message)
164
165
    def test_commit_message_supplied(self):
166
        builder = BranchBuilder(self.get_transport().clone('foo'))
167
        rev_id = builder.build_snapshot(None, None,
168
            [('add', (u'', None, 'directory', None))],
169
            message=u'Foo')
170
        branch = builder.get_branch()
171
        rev = branch.repository.get_revision(rev_id)
172
        self.assertEqual(u'Foo', rev.message)
173
5060.1.1 by Robert Collins
``bzrlib.branchbuilder.BranchBuilder.build_snapshot`` now accepts a
174
    def test_commit_message_callback(self):
175
        builder = BranchBuilder(self.get_transport().clone('foo'))
176
        rev_id = builder.build_snapshot(None, None,
177
            [('add', (u'', None, 'directory', None))],
178
            message_callback=lambda x:u'Foo')
179
        branch = builder.get_branch()
180
        rev = branch.repository.get_revision(rev_id)
181
        self.assertEqual(u'Foo', rev.message)
182
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
183
    def test_modify_file(self):
184
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
185
        rev_id2 = builder.build_snapshot('B-id', None,
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
186
            [('modify', ('a-id', 'new\ncontent\n'))])
187
        self.assertEqual('B-id', rev_id2)
188
        branch = builder.get_branch()
189
        rev_tree = branch.repository.revision_tree(rev_id2)
190
        rev_tree.lock_read()
191
        self.addCleanup(rev_tree.unlock)
192
        self.assertEqual('new\ncontent\n', rev_tree.get_file_text('a-id'))
3567.4.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
193
194
    def test_delete_file(self):
195
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
196
        rev_id2 = builder.build_snapshot('B-id', None,
3567.4.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
197
            [('unversion', 'a-id')])
198
        self.assertEqual('B-id', rev_id2)
199
        branch = builder.get_branch()
200
        rev_tree = branch.repository.revision_tree(rev_id2)
201
        rev_tree.lock_read()
202
        self.addCleanup(rev_tree.unlock)
203
        self.assertTreeShape([(u'', 'a-root-id', 'directory')], rev_tree)
204
3567.4.5 by John Arbash Meinel
MemoryTree.add(directory) will now create a directory node in the Transport
205
    def test_delete_directory(self):
206
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
207
        rev_id2 = builder.build_snapshot('B-id', None,
3567.4.5 by John Arbash Meinel
MemoryTree.add(directory) will now create a directory node in the Transport
208
            [('add', ('b', 'b-id', 'directory', None)),
209
             ('add', ('b/c', 'c-id', 'file', 'foo\n')),
210
             ('add', ('b/d', 'd-id', 'directory', None)),
211
             ('add', ('b/d/e', 'e-id', 'file', 'eff\n')),
212
            ])
213
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
214
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
215
                              (u'a', 'a-id', 'file'),
216
                              (u'b', 'b-id', 'directory'),
217
                              (u'b/c', 'c-id', 'file'),
218
                              (u'b/d', 'd-id', 'directory'),
219
                              (u'b/d/e', 'e-id', 'file')], rev_tree)
3567.4.6 by John Arbash Meinel
unversioning a directory is recursive.
220
        # Removing a directory removes all child dirs
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
221
        builder.build_snapshot('C-id', None, [('unversion', 'b-id')])
3567.4.6 by John Arbash Meinel
unversioning a directory is recursive.
222
        rev_tree = builder.get_branch().repository.revision_tree('C-id')
223
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
224
                              (u'a', 'a-id', 'file'),
225
                             ], rev_tree)
3567.4.5 by John Arbash Meinel
MemoryTree.add(directory) will now create a directory node in the Transport
226
3567.4.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
227
    def test_unknown_action(self):
228
        builder = self.build_a_rev()
3567.4.18 by John Arbash Meinel
Apply the review changes from Martin to the exact patch he approved.
229
        e = self.assertRaises(ValueError,
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
230
            builder.build_snapshot, 'B-id', None, [('weirdo', ('foo',))])
3567.4.18 by John Arbash Meinel
Apply the review changes from Martin to the exact patch he approved.
231
        self.assertEqual('Unknown build action: "weirdo"', str(e))
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
232
3567.5.1 by John Arbash Meinel
Implement rename_one on MemoryTree, and expose that in the Branch Builder
233
    def test_rename(self):
234
        builder = self.build_a_rev()
235
        builder.build_snapshot('B-id', None,
236
            [('rename', ('a', 'b'))])
237
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
238
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
239
                              (u'b', 'a-id', 'file')], rev_tree)
240
241
    def test_rename_into_subdir(self):
242
        builder = self.build_a_rev()
243
        builder.build_snapshot('B-id', None,
244
            [('add', ('dir', 'dir-id', 'directory', None)),
245
             ('rename', ('a', 'dir/a'))])
246
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
247
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
248
                              (u'dir', 'dir-id', 'directory'),
249
                              (u'dir/a', 'a-id', 'file')], rev_tree)
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
250
251
    def test_set_parent(self):
252
        builder = self.build_a_rev()
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
253
        builder.start_series()
254
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
255
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
256
            [('modify', ('a-id', 'new\ncontent\n'))])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
257
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
258
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
259
        # We should now have a graph:
260
        #   A
261
        #   |\
262
        #   C B
263
        # And not A => B => C
264
        repo = builder.get_branch().repository
265
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',)},
266
                         repo.get_parent_map(['B-id', 'C-id']))
267
        b_tree = repo.revision_tree('B-id')
268
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
269
                              (u'a', 'a-id', 'file'),
270
                             ], b_tree)
271
        self.assertEqual('new\ncontent\n', b_tree.get_file_text('a-id'))
272
273
        # We should still be using the content from A in C, not from B
274
        c_tree = repo.revision_tree('C-id')
275
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
276
                              (u'a', 'a-id', 'file'),
277
                              (u'c', 'c-id', 'file'),
278
                             ], c_tree)
279
        self.assertEqual('contents', c_tree.get_file_text('a-id'))
280
        self.assertEqual('alt\ncontent\n', c_tree.get_file_text('c-id'))
281
282
    def test_set_merge_parent(self):
283
        builder = self.build_a_rev()
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
284
        builder.start_series()
285
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
286
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
287
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
288
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
289
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
290
        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
291
        repo = builder.get_branch().repository
292
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
293
                          'D-id': ('B-id', 'C-id')},
294
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
295
        d_tree = repo.revision_tree('D-id')
296
        # Note: by default a merge node does *not* pull in the changes from the
297
        #       merged tree, you have to supply it yourself.
298
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
299
                              (u'a', 'a-id', 'file'),
300
                              (u'b', 'b-id', 'file'),
301
                             ], d_tree)
302
303
    def test_set_merge_parent_and_contents(self):
304
        builder = self.build_a_rev()
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
305
        builder.start_series()
306
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
307
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
308
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
309
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
310
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
311
        builder.build_snapshot('D-id', ['B-id', 'C-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
312
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
313
        repo = builder.get_branch().repository
314
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
315
                          'D-id': ('B-id', 'C-id')},
316
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
317
        d_tree = repo.revision_tree('D-id')
318
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
319
                              (u'a', 'a-id', 'file'),
320
                              (u'b', 'b-id', 'file'),
321
                              (u'c', 'c-id', 'file'),
322
                             ], d_tree)
323
        # Because we copied the exact text into *this* tree, the 'c' file
324
        # should look like it was not modified in the merge
325
        self.assertEqual('C-id', d_tree.inventory['c-id'].revision)
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
326
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
327
    def test_set_parent_to_null(self):
328
        builder = self.build_a_rev()
329
        builder.start_series()
330
        self.addCleanup(builder.finish_series)
5540.2.1 by Gary van der Merwe
Allow for BranchBuilder.build_snapshot to accept parent_ids = [], rather than ['null:'].
331
        builder.build_snapshot('B-id', [],
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
332
            [('add', ('', None, 'directory', None))])
333
        # We should now have a graph:
334
        #   A B
335
        # And not A => B
336
        repo = builder.get_branch().repository
337
        self.assertEqual({'A-id': (_mod_revision.NULL_REVISION,),
338
                          'B-id': (_mod_revision.NULL_REVISION,),},
339
                         repo.get_parent_map(['A-id', 'B-id']))
340
341
    
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
342
    def test_start_finish_series(self):
343
        builder = BranchBuilder(self.get_transport().clone('foo'))
344
        builder.start_series()
345
        try:
346
            self.assertIsNot(None, builder._tree)
347
            self.assertEqual('w', builder._tree._lock_mode)
348
            self.assertTrue(builder._branch.is_locked())
349
        finally:
350
            builder.finish_series()
351
        self.assertIs(None, builder._tree)
352
        self.assertFalse(builder._branch.is_locked())
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
353
354
    def test_ghost_mainline_history(self):
355
        builder = BranchBuilder(self.get_transport().clone('foo'))
356
        builder.start_series()
357
        try:
358
            builder.build_snapshot('tip', ['ghost'],
359
                [('add', ('', 'ROOT_ID', 'directory', ''))],
360
                allow_leftmost_as_ghost=True)
361
        finally:
362
            builder.finish_series()
363
        b = builder.get_branch()
364
        b.lock_read()
365
        self.addCleanup(b.unlock)
366
        self.assertEqual(('ghost',),
367
            b.repository.get_graph().get_parent_map(['tip'])['tip'])