/brz/remove-bazaar

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