~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
6008.2.1 by Andrew Bennetts
Allow renaming entries out of a dir and unversioning a dir in the one build_snapshot call.
250
    def test_rename_out_of_unversioned_subdir(self):
251
        builder = self.build_a_rev()
252
        builder.build_snapshot('B-id', None,
253
            [('add', ('dir', 'dir-id', 'directory', None)),
254
             ('rename', ('a', 'dir/a'))])
255
        builder.build_snapshot('C-id', None,
256
            [('rename', ('dir/a', 'a')),
257
             ('unversion', 'dir-id')])
258
        rev_tree = builder.get_branch().repository.revision_tree('C-id')
259
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
260
                              (u'a', 'a-id', 'file')], rev_tree)
261
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
262
    def test_set_parent(self):
263
        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.
264
        builder.start_series()
265
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
266
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
267
            [('modify', ('a-id', 'new\ncontent\n'))])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
268
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
269
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
270
        # We should now have a graph:
271
        #   A
272
        #   |\
273
        #   C B
274
        # And not A => B => C
275
        repo = builder.get_branch().repository
276
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',)},
277
                         repo.get_parent_map(['B-id', 'C-id']))
278
        b_tree = repo.revision_tree('B-id')
279
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
280
                              (u'a', 'a-id', 'file'),
281
                             ], b_tree)
282
        self.assertEqual('new\ncontent\n', b_tree.get_file_text('a-id'))
283
284
        # We should still be using the content from A in C, not from B
285
        c_tree = repo.revision_tree('C-id')
286
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
287
                              (u'a', 'a-id', 'file'),
288
                              (u'c', 'c-id', 'file'),
289
                             ], c_tree)
290
        self.assertEqual('contents', c_tree.get_file_text('a-id'))
291
        self.assertEqual('alt\ncontent\n', c_tree.get_file_text('c-id'))
292
293
    def test_set_merge_parent(self):
294
        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.
295
        builder.start_series()
296
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
297
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
298
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
299
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
300
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
301
        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.
302
        repo = builder.get_branch().repository
303
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
304
                          'D-id': ('B-id', 'C-id')},
305
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
306
        d_tree = repo.revision_tree('D-id')
307
        # Note: by default a merge node does *not* pull in the changes from the
308
        #       merged tree, you have to supply it yourself.
309
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
310
                              (u'a', 'a-id', 'file'),
311
                              (u'b', 'b-id', 'file'),
312
                             ], d_tree)
313
314
    def test_set_merge_parent_and_contents(self):
315
        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.
316
        builder.start_series()
317
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
318
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
319
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
320
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
321
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
322
        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.
323
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
324
        repo = builder.get_branch().repository
325
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
326
                          'D-id': ('B-id', 'C-id')},
327
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
328
        d_tree = repo.revision_tree('D-id')
329
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
330
                              (u'a', 'a-id', 'file'),
331
                              (u'b', 'b-id', 'file'),
332
                              (u'c', 'c-id', 'file'),
333
                             ], d_tree)
334
        # Because we copied the exact text into *this* tree, the 'c' file
335
        # should look like it was not modified in the merge
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
336
        self.assertEqual('C-id', d_tree.get_file_revision('c-id'))
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
337
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
338
    def test_set_parent_to_null(self):
339
        builder = self.build_a_rev()
340
        builder.start_series()
341
        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:'].
342
        builder.build_snapshot('B-id', [],
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
343
            [('add', ('', None, 'directory', None))])
344
        # We should now have a graph:
345
        #   A B
346
        # And not A => B
347
        repo = builder.get_branch().repository
348
        self.assertEqual({'A-id': (_mod_revision.NULL_REVISION,),
349
                          'B-id': (_mod_revision.NULL_REVISION,),},
350
                         repo.get_parent_map(['A-id', 'B-id']))
351
352
    
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
353
    def test_start_finish_series(self):
354
        builder = BranchBuilder(self.get_transport().clone('foo'))
355
        builder.start_series()
356
        try:
357
            self.assertIsNot(None, builder._tree)
358
            self.assertEqual('w', builder._tree._lock_mode)
359
            self.assertTrue(builder._branch.is_locked())
360
        finally:
361
            builder.finish_series()
362
        self.assertIs(None, builder._tree)
363
        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'.
364
365
    def test_ghost_mainline_history(self):
366
        builder = BranchBuilder(self.get_transport().clone('foo'))
367
        builder.start_series()
368
        try:
369
            builder.build_snapshot('tip', ['ghost'],
370
                [('add', ('', 'ROOT_ID', 'directory', ''))],
371
                allow_leftmost_as_ghost=True)
372
        finally:
373
            builder.finish_series()
374
        b = builder.get_branch()
375
        b.lock_read()
376
        self.addCleanup(b.unlock)
377
        self.assertEqual(('ghost',),
378
            b.repository.get_graph().get_parent_map(['tip'])['tip'])
6008.2.2 by Andrew Bennetts
Add unit test for regression.
379
380
    def test_unversion_root_add_new_root(self):
381
        builder = BranchBuilder(self.get_transport().clone('foo'))
382
        builder.start_series()
383
        builder.build_snapshot('rev-1', None,
384
            [('add', ('', 'TREE_ROOT', 'directory', ''))])
385
        builder.build_snapshot('rev-2', None,
386
            [('unversion', 'TREE_ROOT'),
387
             ('add', ('', 'my-root', 'directory', ''))])
388
        builder.finish_series()
389
        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
390
        self.assertTreeShape([(u'', 'my-root', 'directory')], rev_tree)
391
6008.2.5 by Andrew Bennetts
Rename 'checkpoint' to 'flush', add some unit tests and more comments.
392
    def test_empty_flush(self):
393
        """A flush with no actions before it is a no-op."""
394
        builder = BranchBuilder(self.get_transport().clone('foo'))
395
        builder.start_series()
396
        builder.build_snapshot('rev-1', None,
397
            [('add', ('', 'TREE_ROOT', 'directory', ''))])
398
        builder.build_snapshot('rev-2', None, [('flush', None)])
399
        builder.finish_series()
400
        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
401
        self.assertTreeShape([(u'', 'TREE_ROOT', 'directory')], rev_tree)
402
403
    def test_kind_change(self):
404
        """It's possible to change the kind of an entry in a single snapshot
405
        with a bit of help from the 'flush' action.
406
        """
407
        builder = BranchBuilder(self.get_transport().clone('foo'))
408
        builder.start_series()
409
        builder.build_snapshot('A-id', None,
410
            [('add', (u'', 'a-root-id', 'directory', None)),
411
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
412
        builder.build_snapshot('B-id', None,
413
            [('unversion', 'a-id'),
414
             ('flush', None),
415
             ('add', (u'a', 'a-id', 'directory', None))])
416
        builder.finish_series()
417
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
418
        self.assertTreeShape(
419
            [(u'', 'a-root-id', 'directory'), (u'a', 'a-id', 'directory')],
420
            rev_tree)
421
422
    def test_pivot_root(self):
423
        """It's possible (albeit awkward) to move an existing dir to the root
424
        in a single snapshot by using unversion then flush then add.
425
        """
426
        builder = BranchBuilder(self.get_transport().clone('foo'))
427
        builder.start_series()
428
        builder.build_snapshot('A-id', None,
429
            [('add', (u'', 'orig-root', 'directory', None)),
430
             ('add', (u'dir', 'dir-id', 'directory', None))])
431
        builder.build_snapshot('B-id', None,
432
            [('unversion', 'orig-root'),  # implicitly unversions all children
433
             ('flush', None),
434
             ('add', (u'', 'dir-id', 'directory', None))])
435
        builder.finish_series()
436
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
437
        self.assertTreeShape([(u'', 'dir-id', 'directory')], rev_tree)
438