~bzr-pqm/bzr/bzr.dev

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