~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
6225.1.1 by Jelmer Vernooij
Add parent_ids argument to BranchBuilder.build_commit.
89
    def test_build_commit_parent_ids(self):
90
        """build_commit() takes a parent_ids argument."""
91
        builder = BranchBuilder(self.get_transport().clone('foo'))
92
        rev_id1 = builder.build_commit(
93
            parent_ids=["ghost"], allow_leftmost_as_ghost=True)
94
        rev_id2 = builder.build_commit(parent_ids=[])
95
        branch = builder.get_branch()
96
        self.assertEqual((1, rev_id2), branch.last_revision_info())
97
        self.assertEqual(
98
            ["ghost"],
99
            branch.repository.get_revision(rev_id1).parent_ids)
100
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
101
102
class TestBranchBuilderBuildSnapshot(tests.TestCaseWithMemoryTransport):
103
104
    def assertTreeShape(self, expected_shape, tree):
105
        """Check that the tree shape matches expectations."""
106
        tree.lock_read()
107
        try:
108
            entries = [(path, ie.file_id, ie.kind)
109
                       for path, ie in tree.iter_entries_by_dir()]
110
        finally:
111
            tree.unlock()
112
        self.assertEqual(expected_shape, entries)
113
114
    def build_a_rev(self):
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
115
        builder = BranchBuilder(self.get_transport().clone('foo'))
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
116
        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.
117
            [('add', ('', 'a-root-id', 'directory', None)),
118
             ('add', ('a', 'a-id', 'file', 'contents'))])
119
        self.assertEqual('A-id', rev_id1)
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
120
        return builder
121
122
    def test_add_one_file(self):
123
        builder = self.build_a_rev()
3567.4.1 by John Arbash Meinel
Initial work to have BranchBuilder allow us to do tree-shape work.
124
        branch = builder.get_branch()
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
125
        self.assertEqual((1, 'A-id'), branch.last_revision_info())
126
        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.
127
        rev_tree.lock_read()
128
        self.addCleanup(rev_tree.unlock)
3567.4.2 by John Arbash Meinel
test that we can add more files into an existing build
129
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
130
                              (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.
131
        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
132
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
133
    def test_add_second_file(self):
134
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
135
        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
136
            [('add', ('b', 'b-id', 'file', 'content_b'))])
137
        self.assertEqual('B-id', rev_id2)
138
        branch = builder.get_branch()
139
        self.assertEqual((2, rev_id2), branch.last_revision_info())
140
        rev_tree = branch.repository.revision_tree(rev_id2)
141
        rev_tree.lock_read()
142
        self.addCleanup(rev_tree.unlock)
143
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
144
                              (u'a', 'a-id', 'file'),
145
                              (u'b', 'b-id', 'file')], rev_tree)
146
        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
147
3567.4.7 by John Arbash Meinel
Revert back to using MemoryTree.mkdir() rather than creating the directory during add().
148
    def test_add_empty_dir(self):
149
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
150
        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().
151
            [('add', ('b', 'b-id', 'directory', None))])
152
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
153
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
154
                              (u'a', 'a-id', 'file'),
155
                              (u'b', 'b-id', 'directory'),
156
                             ], rev_tree)
157
4070.5.1 by Martin Pool
BranchBuilder now takes a timestamp for commits
158
    def test_commit_timestamp(self):
159
        builder = self.make_branch_builder('foo')
160
        rev_id = builder.build_snapshot(None, None,
161
            [('add', (u'', None, 'directory', None))],
162
            timestamp=1234567890)
163
        rev = builder.get_branch().repository.get_revision(rev_id)
164
        self.assertEqual(
165
            1234567890,
166
            int(rev.timestamp))
167
3567.4.15 by John Arbash Meinel
Allow setting the commit message.
168
    def test_commit_message_default(self):
169
        builder = BranchBuilder(self.get_transport().clone('foo'))
170
        rev_id = builder.build_snapshot(None, None,
171
            [('add', (u'', None, 'directory', None))])
172
        branch = builder.get_branch()
173
        rev = branch.repository.get_revision(rev_id)
174
        self.assertEqual(u'commit 1', rev.message)
175
176
    def test_commit_message_supplied(self):
177
        builder = BranchBuilder(self.get_transport().clone('foo'))
178
        rev_id = builder.build_snapshot(None, None,
179
            [('add', (u'', None, 'directory', None))],
180
            message=u'Foo')
181
        branch = builder.get_branch()
182
        rev = branch.repository.get_revision(rev_id)
183
        self.assertEqual(u'Foo', rev.message)
184
5060.1.1 by Robert Collins
``bzrlib.branchbuilder.BranchBuilder.build_snapshot`` now accepts a
185
    def test_commit_message_callback(self):
186
        builder = BranchBuilder(self.get_transport().clone('foo'))
187
        rev_id = builder.build_snapshot(None, None,
188
            [('add', (u'', None, 'directory', None))],
189
            message_callback=lambda x:u'Foo')
190
        branch = builder.get_branch()
191
        rev = branch.repository.get_revision(rev_id)
192
        self.assertEqual(u'Foo', rev.message)
193
3567.4.3 by John Arbash Meinel
Add an action for modifying an existing file
194
    def test_modify_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.3 by John Arbash Meinel
Add an action for modifying an existing file
197
            [('modify', ('a-id', 'new\ncontent\n'))])
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.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.
204
205
    def test_delete_file(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.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
208
            [('unversion', 'a-id')])
209
        self.assertEqual('B-id', rev_id2)
210
        branch = builder.get_branch()
211
        rev_tree = branch.repository.revision_tree(rev_id2)
212
        rev_tree.lock_read()
213
        self.addCleanup(rev_tree.unlock)
214
        self.assertTreeShape([(u'', 'a-root-id', 'directory')], rev_tree)
215
3567.4.5 by John Arbash Meinel
MemoryTree.add(directory) will now create a directory node in the Transport
216
    def test_delete_directory(self):
217
        builder = self.build_a_rev()
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
218
        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
219
            [('add', ('b', 'b-id', 'directory', None)),
220
             ('add', ('b/c', 'c-id', 'file', 'foo\n')),
221
             ('add', ('b/d', 'd-id', 'directory', None)),
222
             ('add', ('b/d/e', 'e-id', 'file', 'eff\n')),
223
            ])
224
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
225
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
226
                              (u'a', 'a-id', 'file'),
227
                              (u'b', 'b-id', 'directory'),
228
                              (u'b/c', 'c-id', 'file'),
229
                              (u'b/d', 'd-id', 'directory'),
230
                              (u'b/d/e', 'e-id', 'file')], rev_tree)
3567.4.6 by John Arbash Meinel
unversioning a directory is recursive.
231
        # Removing a directory removes all child dirs
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
232
        builder.build_snapshot('C-id', None, [('unversion', 'b-id')])
3567.4.6 by John Arbash Meinel
unversioning a directory is recursive.
233
        rev_tree = builder.get_branch().repository.revision_tree('C-id')
234
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
235
                              (u'a', 'a-id', 'file'),
236
                             ], rev_tree)
3567.4.5 by John Arbash Meinel
MemoryTree.add(directory) will now create a directory node in the Transport
237
3567.4.4 by John Arbash Meinel
Add the ability to 'unversion' files, and handle unknown actions.
238
    def test_unknown_action(self):
239
        builder = self.build_a_rev()
3567.4.18 by John Arbash Meinel
Apply the review changes from Martin to the exact patch he approved.
240
        e = self.assertRaises(ValueError,
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
241
            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.
242
        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.
243
3567.5.1 by John Arbash Meinel
Implement rename_one on MemoryTree, and expose that in the Branch Builder
244
    def test_rename(self):
245
        builder = self.build_a_rev()
246
        builder.build_snapshot('B-id', None,
247
            [('rename', ('a', 'b'))])
248
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
249
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
250
                              (u'b', 'a-id', 'file')], rev_tree)
251
252
    def test_rename_into_subdir(self):
253
        builder = self.build_a_rev()
254
        builder.build_snapshot('B-id', None,
255
            [('add', ('dir', 'dir-id', 'directory', None)),
256
             ('rename', ('a', 'dir/a'))])
257
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
258
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
259
                              (u'dir', 'dir-id', 'directory'),
260
                              (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.
261
6008.2.1 by Andrew Bennetts
Allow renaming entries out of a dir and unversioning a dir in the one build_snapshot call.
262
    def test_rename_out_of_unversioned_subdir(self):
263
        builder = self.build_a_rev()
264
        builder.build_snapshot('B-id', None,
265
            [('add', ('dir', 'dir-id', 'directory', None)),
266
             ('rename', ('a', 'dir/a'))])
267
        builder.build_snapshot('C-id', None,
268
            [('rename', ('dir/a', 'a')),
269
             ('unversion', 'dir-id')])
270
        rev_tree = builder.get_branch().repository.revision_tree('C-id')
271
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
272
                              (u'a', 'a-id', 'file')], rev_tree)
273
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
274
    def test_set_parent(self):
275
        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.
276
        builder.start_series()
277
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
278
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
279
            [('modify', ('a-id', 'new\ncontent\n'))])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
280
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
281
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
282
        # We should now have a graph:
283
        #   A
284
        #   |\
285
        #   C B
286
        # And not A => B => C
287
        repo = builder.get_branch().repository
288
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',)},
289
                         repo.get_parent_map(['B-id', 'C-id']))
290
        b_tree = repo.revision_tree('B-id')
291
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
292
                              (u'a', 'a-id', 'file'),
293
                             ], b_tree)
294
        self.assertEqual('new\ncontent\n', b_tree.get_file_text('a-id'))
295
296
        # We should still be using the content from A in C, not from B
297
        c_tree = repo.revision_tree('C-id')
298
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
299
                              (u'a', 'a-id', 'file'),
300
                              (u'c', 'c-id', 'file'),
301
                             ], c_tree)
302
        self.assertEqual('contents', c_tree.get_file_text('a-id'))
303
        self.assertEqual('alt\ncontent\n', c_tree.get_file_text('c-id'))
304
305
    def test_set_merge_parent(self):
306
        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.
307
        builder.start_series()
308
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
309
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
310
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
311
        builder.build_snapshot('C-id', ['A-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'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
313
        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.
314
        repo = builder.get_branch().repository
315
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
316
                          'D-id': ('B-id', 'C-id')},
317
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
318
        d_tree = repo.revision_tree('D-id')
319
        # Note: by default a merge node does *not* pull in the changes from the
320
        #       merged tree, you have to supply it yourself.
321
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
322
                              (u'a', 'a-id', 'file'),
323
                              (u'b', 'b-id', 'file'),
324
                             ], d_tree)
325
326
    def test_set_merge_parent_and_contents(self):
327
        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.
328
        builder.start_series()
329
        self.addCleanup(builder.finish_series)
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
330
        builder.build_snapshot('B-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
331
            [('add', ('b', 'b-id', 'file', 'b\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
332
        builder.build_snapshot('C-id', ['A-id'],
3567.4.8 by John Arbash Meinel
Add the ability to force a basis for a revision.
333
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
3567.4.10 by John Arbash Meinel
Clean up the build_snapshot api a bit.
334
        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.
335
            [('add', ('c', 'c-id', 'file', 'alt\ncontent\n'))])
336
        repo = builder.get_branch().repository
337
        self.assertEqual({'B-id': ('A-id',), 'C-id': ('A-id',),
338
                          'D-id': ('B-id', 'C-id')},
339
                         repo.get_parent_map(['B-id', 'C-id', 'D-id']))
340
        d_tree = repo.revision_tree('D-id')
341
        self.assertTreeShape([(u'', 'a-root-id', 'directory'),
342
                              (u'a', 'a-id', 'file'),
343
                              (u'b', 'b-id', 'file'),
344
                              (u'c', 'c-id', 'file'),
345
                             ], d_tree)
346
        # Because we copied the exact text into *this* tree, the 'c' file
347
        # should look like it was not modified in the merge
5793.2.3 by Jelmer Vernooij
Add a RevisionTree.get_file_revision() method.
348
        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.
349
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
350
    def test_set_parent_to_null(self):
351
        builder = self.build_a_rev()
352
        builder.start_series()
353
        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:'].
354
        builder.build_snapshot('B-id', [],
5540.1.1 by Gary van der Merwe
Make ``BranchBuilder.build_snapshot`` accept parent_ids == ['null:'].
355
            [('add', ('', None, 'directory', None))])
356
        # We should now have a graph:
357
        #   A B
358
        # And not A => B
359
        repo = builder.get_branch().repository
360
        self.assertEqual({'A-id': (_mod_revision.NULL_REVISION,),
361
                          'B-id': (_mod_revision.NULL_REVISION,),},
362
                         repo.get_parent_map(['A-id', 'B-id']))
363
364
    
3567.4.17 by John Arbash Meinel
Add the ability to define a series of commits, which allows us to hold open the locks.
365
    def test_start_finish_series(self):
366
        builder = BranchBuilder(self.get_transport().clone('foo'))
367
        builder.start_series()
368
        try:
369
            self.assertIsNot(None, builder._tree)
370
            self.assertEqual('w', builder._tree._lock_mode)
371
            self.assertTrue(builder._branch.is_locked())
372
        finally:
373
            builder.finish_series()
374
        self.assertIs(None, builder._tree)
375
        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'.
376
377
    def test_ghost_mainline_history(self):
378
        builder = BranchBuilder(self.get_transport().clone('foo'))
379
        builder.start_series()
380
        try:
381
            builder.build_snapshot('tip', ['ghost'],
382
                [('add', ('', 'ROOT_ID', 'directory', ''))],
383
                allow_leftmost_as_ghost=True)
384
        finally:
385
            builder.finish_series()
386
        b = builder.get_branch()
387
        b.lock_read()
388
        self.addCleanup(b.unlock)
389
        self.assertEqual(('ghost',),
390
            b.repository.get_graph().get_parent_map(['tip'])['tip'])
6008.2.2 by Andrew Bennetts
Add unit test for regression.
391
392
    def test_unversion_root_add_new_root(self):
393
        builder = BranchBuilder(self.get_transport().clone('foo'))
394
        builder.start_series()
395
        builder.build_snapshot('rev-1', None,
396
            [('add', ('', 'TREE_ROOT', 'directory', ''))])
397
        builder.build_snapshot('rev-2', None,
398
            [('unversion', 'TREE_ROOT'),
399
             ('add', ('', 'my-root', 'directory', ''))])
400
        builder.finish_series()
401
        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
402
        self.assertTreeShape([(u'', 'my-root', 'directory')], rev_tree)
403
6008.2.5 by Andrew Bennetts
Rename 'checkpoint' to 'flush', add some unit tests and more comments.
404
    def test_empty_flush(self):
405
        """A flush with no actions before it is a no-op."""
406
        builder = BranchBuilder(self.get_transport().clone('foo'))
407
        builder.start_series()
408
        builder.build_snapshot('rev-1', None,
409
            [('add', ('', 'TREE_ROOT', 'directory', ''))])
410
        builder.build_snapshot('rev-2', None, [('flush', None)])
411
        builder.finish_series()
412
        rev_tree = builder.get_branch().repository.revision_tree('rev-2')
413
        self.assertTreeShape([(u'', 'TREE_ROOT', 'directory')], rev_tree)
414
415
    def test_kind_change(self):
416
        """It's possible to change the kind of an entry in a single snapshot
417
        with a bit of help from the 'flush' action.
418
        """
419
        builder = BranchBuilder(self.get_transport().clone('foo'))
420
        builder.start_series()
421
        builder.build_snapshot('A-id', None,
422
            [('add', (u'', 'a-root-id', 'directory', None)),
423
             ('add', (u'a', 'a-id', 'file', 'content\n'))])
424
        builder.build_snapshot('B-id', None,
425
            [('unversion', 'a-id'),
426
             ('flush', None),
427
             ('add', (u'a', 'a-id', 'directory', None))])
428
        builder.finish_series()
429
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
430
        self.assertTreeShape(
431
            [(u'', 'a-root-id', 'directory'), (u'a', 'a-id', 'directory')],
432
            rev_tree)
433
434
    def test_pivot_root(self):
435
        """It's possible (albeit awkward) to move an existing dir to the root
436
        in a single snapshot by using unversion then flush then add.
437
        """
438
        builder = BranchBuilder(self.get_transport().clone('foo'))
439
        builder.start_series()
440
        builder.build_snapshot('A-id', None,
441
            [('add', (u'', 'orig-root', 'directory', None)),
442
             ('add', (u'dir', 'dir-id', 'directory', None))])
443
        builder.build_snapshot('B-id', None,
444
            [('unversion', 'orig-root'),  # implicitly unversions all children
445
             ('flush', None),
446
             ('add', (u'', 'dir-id', 'directory', None))])
447
        builder.finish_series()
448
        rev_tree = builder.get_branch().repository.revision_tree('B-id')
449
        self.assertTreeShape([(u'', 'dir-id', 'directory')], rev_tree)
450