/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1908.5.2 by Robert Collins
Create and test set_parent_trees.
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
1908.5.2 by Robert Collins
Create and test set_parent_trees.
16
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
17
"""Tests of the parent related functions of WorkingTrees."""
18
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
19
from cStringIO import StringIO
1908.5.2 by Robert Collins
Create and test set_parent_trees.
20
import os
21
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
22
from bzrlib import (
23
    errors,
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
24
    osutils,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
25
    revision as _mod_revision,
4095.3.4 by Vincent Ladeuil
Some trivial cleanups, related to fix for bug #272444.
26
    tests,
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
27
    )
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
28
from bzrlib.inventory import (
29
    Inventory,
30
    InventoryFile,
31
    InventoryDirectory,
32
    InventoryLink,
33
    )
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
34
from bzrlib.revisiontree import InventoryRevisionTree
4523.1.4 by Martin Pool
Rename remaining *_implementations tests
35
from bzrlib.tests.per_workingtree import TestCaseWithWorkingTree
1908.5.2 by Robert Collins
Create and test set_parent_trees.
36
from bzrlib.uncommit import uncommit
37
38
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
39
class TestParents(TestCaseWithWorkingTree):
40
41
    def assertConsistentParents(self, expected, tree):
1908.7.6 by Robert Collins
Deprecate WorkingTree.last_revision.
42
        """Check that the parents found are as expected.
43
44
        This test helper also checks that they are consistent with
45
        the pre-get_parent_ids() api - which is now deprecated.
46
        """
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
47
        self.assertEqual(expected, tree.get_parent_ids())
48
        if expected == []:
2598.5.7 by Aaron Bentley
Updates from review
49
            self.assertEqual(_mod_revision.NULL_REVISION,
50
                             _mod_revision.ensure_null(tree.last_revision()))
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
51
        else:
1908.7.11 by Robert Collins
Merge bzr.dev and undeprecated WorkingTree.last_revision as per review feedback.
52
            self.assertEqual(expected[0], tree.last_revision())
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
53
54
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
55
class TestGetParents(TestParents):
56
57
    def test_get_parents(self):
58
        t = self.make_branch_and_tree('.')
59
        self.assertEqual([], t.get_parent_ids())
60
61
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
62
class TestSetParents(TestParents):
1908.5.2 by Robert Collins
Create and test set_parent_trees.
63
64
    def test_set_no_parents(self):
65
        t = self.make_branch_and_tree('.')
66
        t.set_parent_trees([])
67
        self.assertEqual([], t.get_parent_ids())
68
        # now give it a real parent, and then set it to no parents again.
69
        t.commit('first post')
70
        t.set_parent_trees([])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
71
        self.assertConsistentParents([], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
72
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
73
    def test_set_null_parent(self):
74
        t = self.make_branch_and_tree('.')
75
        self.assertRaises(errors.ReservedId, t.set_parent_ids, ['null:'],
76
                          allow_leftmost_as_ghost=True)
77
        self.assertRaises(errors.ReservedId, t.set_parent_trees,
78
                          [('null:', None)], allow_leftmost_as_ghost=True)
79
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
80
    def test_set_one_ghost_parent_rejects(self):
81
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
82
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
83
            t.set_parent_trees, [('missing-revision-id', None)])
84
85
    def test_set_one_ghost_parent_force(self):
86
        t = self.make_branch_and_tree('.')
87
        t.set_parent_trees([('missing-revision-id', None)],
88
            allow_leftmost_as_ghost=True)
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
89
        self.assertConsistentParents(['missing-revision-id'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
90
91
    def test_set_two_parents_one_ghost(self):
92
        t = self.make_branch_and_tree('.')
93
        revision_in_repo = t.commit('first post')
94
        # remove the tree's history
95
        uncommit(t.branch, tree=t)
96
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
97
        t.set_parent_trees([(revision_in_repo, rev_tree),
98
            ('another-missing', None)])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
99
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
100
101
    def test_set_three_parents(self):
102
        t = self.make_branch_and_tree('.')
103
        first_revision = t.commit('first post')
104
        uncommit(t.branch, tree=t)
105
        second_revision = t.commit('second post')
106
        uncommit(t.branch, tree=t)
107
        third_revision = t.commit('third post')
108
        uncommit(t.branch, tree=t)
109
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
110
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
111
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
112
        t.set_parent_trees([(first_revision, rev_tree1),
113
            (second_revision, rev_tree2),
114
            (third_revision, rev_tree3)])
1908.5.3 by Robert Collins
Rename the tree.set_parents tests to tree.parents - preparing to add related function tests. Also remove duplication within the tests by factoring out a helper assert.
115
        self.assertConsistentParents(
116
            [first_revision, second_revision, third_revision], t)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
117
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
118
    def test_set_no_parents_ids(self):
119
        t = self.make_branch_and_tree('.')
120
        t.set_parent_ids([])
121
        self.assertEqual([], t.get_parent_ids())
122
        # now give it a real parent, and then set it to no parents again.
123
        t.commit('first post')
124
        t.set_parent_ids([])
125
        self.assertConsistentParents([], t)
126
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
127
    def test_set_one_ghost_parent_ids_rejects(self):
128
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
129
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
130
            t.set_parent_ids, ['missing-revision-id'])
131
132
    def test_set_one_ghost_parent_ids_force(self):
133
        t = self.make_branch_and_tree('.')
134
        t.set_parent_ids(['missing-revision-id'],
135
            allow_leftmost_as_ghost=True)
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
136
        self.assertConsistentParents(['missing-revision-id'], t)
137
138
    def test_set_two_parents_one_ghost_ids(self):
139
        t = self.make_branch_and_tree('.')
140
        revision_in_repo = t.commit('first post')
141
        # remove the tree's history
142
        uncommit(t.branch, tree=t)
143
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
144
        t.set_parent_ids([revision_in_repo, 'another-missing'])
145
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
146
147
    def test_set_three_parents_ids(self):
148
        t = self.make_branch_and_tree('.')
149
        first_revision = t.commit('first post')
150
        uncommit(t.branch, tree=t)
151
        second_revision = t.commit('second post')
152
        uncommit(t.branch, tree=t)
153
        third_revision = t.commit('third post')
154
        uncommit(t.branch, tree=t)
155
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
156
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
157
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
158
        t.set_parent_ids([first_revision, second_revision, third_revision])
159
        self.assertConsistentParents(
160
            [first_revision, second_revision, third_revision], t)
161
3462.1.2 by John Arbash Meinel
Change WT.set_parent_(ids/trees) to filter out ancestors.
162
    def test_set_duplicate_parent_ids(self):
163
        t = self.make_branch_and_tree('.')
164
        rev1 = t.commit('first post')
165
        uncommit(t.branch, tree=t)
166
        rev2 = t.commit('second post')
167
        uncommit(t.branch, tree=t)
168
        rev3 = t.commit('third post')
169
        uncommit(t.branch, tree=t)
170
        t.set_parent_ids([rev1, rev2, rev2, rev3])
171
        # We strip the duplicate, but preserve the ordering
172
        self.assertConsistentParents([rev1, rev2, rev3], t)
173
174
    def test_set_duplicate_parent_trees(self):
175
        t = self.make_branch_and_tree('.')
176
        rev1 = t.commit('first post')
177
        uncommit(t.branch, tree=t)
178
        rev2 = t.commit('second post')
179
        uncommit(t.branch, tree=t)
180
        rev3 = t.commit('third post')
181
        uncommit(t.branch, tree=t)
182
        rev_tree1 = t.branch.repository.revision_tree(rev1)
183
        rev_tree2 = t.branch.repository.revision_tree(rev2)
184
        rev_tree3 = t.branch.repository.revision_tree(rev3)
185
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
186
                            (rev2, rev_tree2), (rev3, rev_tree3)])
187
        # We strip the duplicate, but preserve the ordering
188
        self.assertConsistentParents([rev1, rev2, rev3], t)
189
190
    def test_set_parent_ids_in_ancestry(self):
191
        t = self.make_branch_and_tree('.')
192
        rev1 = t.commit('first post')
193
        rev2 = t.commit('second post')
194
        rev3 = t.commit('third post')
195
        # Reset the tree, back to rev1
196
        t.set_parent_ids([rev1])
197
        t.branch.set_last_revision_info(1, rev1)
198
        self.assertConsistentParents([rev1], t)
199
        t.set_parent_ids([rev1, rev2, rev3])
200
        # rev2 is in the ancestry of rev3, so it will be filtered out
201
        self.assertConsistentParents([rev1, rev3], t)
202
        # Order should be preserved, and the first revision should always be
203
        # kept
204
        t.set_parent_ids([rev2, rev3, rev1])
205
        self.assertConsistentParents([rev2, rev3], t)
206
207
    def test_set_parent_trees_in_ancestry(self):
208
        t = self.make_branch_and_tree('.')
209
        rev1 = t.commit('first post')
210
        rev2 = t.commit('second post')
211
        rev3 = t.commit('third post')
212
        # Reset the tree, back to rev1
213
        t.set_parent_ids([rev1])
214
        t.branch.set_last_revision_info(1, rev1)
215
        self.assertConsistentParents([rev1], t)
216
        rev_tree1 = t.branch.repository.revision_tree(rev1)
217
        rev_tree2 = t.branch.repository.revision_tree(rev2)
218
        rev_tree3 = t.branch.repository.revision_tree(rev3)
219
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
220
                            (rev3, rev_tree3)])
221
        # rev2 is in the ancestry of rev3, so it will be filtered out
222
        self.assertConsistentParents([rev1, rev3], t)
223
        # Order should be preserved, and the first revision should always be
224
        # kept
225
        t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
226
                            (rev3, rev_tree3)])
227
        self.assertConsistentParents([rev2, rev3], t)
228
3763.9.7 by Daniel Clemente
Tested Unicode target rather than always trying to create it in UTF-8. Test renamed
229
    def test_unicode_symlink(self):
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
230
        # this tests bug #272444
4095.3.4 by Vincent Ladeuil
Some trivial cleanups, related to fix for bug #272444.
231
        self.requireFeature(tests.SymlinkFeature)
232
        self.requireFeature(tests.UnicodeFilenameFeature)
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
233
3763.9.3 by Daniel Clemente
Clearer test with better names and just one parent
234
        tree = self.make_branch_and_tree('tree1')
3763.9.2 by Daniel Clemente
Adapted test to use set_parent_ids and raise KnownFailure
235
3763.9.9 by Daniel Clemente
Used a greek omega instead of an accented 'o' to avoid combining characters
236
        # The link points to a file whose name is an omega
237
        # U+03A9 GREEK CAPITAL LETTER OMEGA
238
        # UTF-8: ce a9  UTF-16BE: 03a9  Decimal: Ω
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
239
        target = u'\u03a9'
240
        link_name = u'\N{Euro Sign}link'
241
        os.symlink(target, 'tree1/' + link_name)
242
        tree.add([link_name],['link-id'])
3763.9.2 by Daniel Clemente
Adapted test to use set_parent_ids and raise KnownFailure
243
4095.3.1 by Vincent Ladeuil
Fix #339055 and #277444 by handling non ascii symlink targets.
244
        revision1 = tree.commit('added a link to a Unicode target')
245
        revision2 = tree.commit('this revision will be discarded')
246
        tree.set_parent_ids([revision1])
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
247
        tree.lock_read()
248
        self.addCleanup(tree.unlock)
249
        # Check that the symlink target is safely round-tripped in the trees.
250
        self.assertEqual(target, tree.get_symlink_target('link-id'))
251
        basis = tree.basis_tree()
252
        self.assertEqual(target, basis.get_symlink_target('link-id'))
3763.9.1 by Daniel Clemente
New testcase for bug 272444 (support symlinks to non-ASCII files)
253
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
254
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
255
class TestAddParent(TestParents):
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
256
257
    def test_add_first_parent_id(self):
258
        """Test adding the first parent id"""
259
        tree = self.make_branch_and_tree('.')
260
        first_revision = tree.commit('first post')
261
        uncommit(tree.branch, tree=tree)
262
        tree.add_parent_tree_id(first_revision)
263
        self.assertConsistentParents([first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
264
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
265
    def test_add_first_parent_id_ghost_rejects(self):
266
        """Test adding the first parent id - as a ghost"""
267
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
268
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
269
            tree.add_parent_tree_id, 'first-revision')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
270
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
271
    def test_add_first_parent_id_ghost_force(self):
272
        """Test adding the first parent id - as a ghost"""
273
        tree = self.make_branch_and_tree('.')
274
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
275
        self.assertConsistentParents(['first-revision'], tree)
1908.5.13 by Robert Collins
Adding a parent when the first is a ghost already should not require forcing it.
276
277
    def test_add_second_parent_id_with_ghost_first(self):
278
        """Test adding the second parent when the first is a ghost."""
279
        tree = self.make_branch_and_tree('.')
280
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
281
        tree.add_parent_tree_id('second')
282
        self.assertConsistentParents(['first-revision', 'second'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
283
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
284
    def test_add_second_parent_id(self):
285
        """Test adding the second parent id"""
286
        tree = self.make_branch_and_tree('.')
287
        first_revision = tree.commit('first post')
288
        uncommit(tree.branch, tree=tree)
289
        second_revision = tree.commit('second post')
290
        tree.add_parent_tree_id(first_revision)
291
        self.assertConsistentParents([second_revision, first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
292
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
293
    def test_add_second_parent_id_ghost(self):
294
        """Test adding the second parent id - as a ghost"""
295
        tree = self.make_branch_and_tree('.')
296
        first_revision = tree.commit('first post')
297
        tree.add_parent_tree_id('second')
298
        self.assertConsistentParents([first_revision, 'second'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
299
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
300
    def test_add_first_parent_tree(self):
301
        """Test adding the first parent id"""
302
        tree = self.make_branch_and_tree('.')
303
        first_revision = tree.commit('first post')
304
        uncommit(tree.branch, tree=tree)
305
        tree.add_parent_tree((first_revision,
306
            tree.branch.repository.revision_tree(first_revision)))
307
        self.assertConsistentParents([first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
308
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
309
    def test_add_first_parent_tree_ghost_rejects(self):
310
        """Test adding the first parent id - as a ghost"""
311
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
312
        self.assertRaises(errors.GhostRevisionUnusableHere,
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
313
            tree.add_parent_tree, ('first-revision', None))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
314
1908.5.9 by Robert Collins
Add a guard against setting the tree last-revision value to a ghost in the new tree parent management api.
315
    def test_add_first_parent_tree_ghost_force(self):
316
        """Test adding the first parent id - as a ghost"""
317
        tree = self.make_branch_and_tree('.')
318
        tree.add_parent_tree(('first-revision', None),
319
            allow_leftmost_as_ghost=True)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
320
        self.assertConsistentParents(['first-revision'], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
321
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
322
    def test_add_second_parent_tree(self):
323
        """Test adding the second parent id"""
324
        tree = self.make_branch_and_tree('.')
325
        first_revision = tree.commit('first post')
326
        uncommit(tree.branch, tree=tree)
327
        second_revision = tree.commit('second post')
328
        tree.add_parent_tree((first_revision,
329
            tree.branch.repository.revision_tree(first_revision)))
330
        self.assertConsistentParents([second_revision, first_revision], tree)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
331
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
332
    def test_add_second_parent_tree_ghost(self):
333
        """Test adding the second parent id - as a ghost"""
334
        tree = self.make_branch_and_tree('.')
335
        first_revision = tree.commit('first post')
336
        tree.add_parent_tree(('second', None))
337
        self.assertConsistentParents([first_revision, 'second'], tree)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
338
339
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
340
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
2903.2.7 by Martin Pool
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta
341
    """Tests for the update_basis_by_delta call.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
342
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
343
    This is intuitively defined as 'apply an inventory delta to the basis and
344
    discard other parents', but for trees that have an inventory that is not
345
    managed as a tree-by-id, the implementation requires roughly duplicated
346
    tests with those for apply_inventory_delta on the main tree.
347
    """
348
349
    def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
350
        expected_inventory):
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
351
        tree.lock_write()
352
        try:
353
            tree.update_basis_by_delta(revid, delta)
354
        finally:
355
            tree.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
356
        # check the last revision was adjusted to rev_id
357
        self.assertEqual(revid, tree.last_revision())
358
        # check the parents are what we expect
359
        self.assertEqual([revid], tree.get_parent_ids())
360
        # check that the basis tree has the inventory we expect from applying
361
        # the delta.
362
        result_basis = tree.basis_tree()
363
        result_basis.lock_read()
3619.6.6 by Mark Hammond
eagerly unlock the result_basis to prevent handles staying open.
364
        try:
365
            self.assertEqual(expected_inventory, result_basis.inventory)
366
        finally:
367
            result_basis.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
368
369
    def make_inv_delta(self, old, new):
370
        """Make an inventory delta from two inventories."""
371
        old_ids = set(old._byid.iterkeys())
372
        new_ids = set(new._byid.iterkeys())
373
        adds = new_ids - old_ids
374
        deletes = old_ids - new_ids
375
        common = old_ids.intersection(new_ids)
376
        delta = []
377
        for file_id in deletes:
378
            delta.append((old.id2path(file_id), None, file_id, None))
379
        for file_id in adds:
380
            delta.append((None, new.id2path(file_id), file_id, new[file_id]))
381
        for file_id in common:
382
            if old[file_id] != new[file_id]:
383
                delta.append((old.id2path(file_id), new.id2path(file_id),
384
                    file_id, new[file_id]))
385
        return delta
386
387
    def fake_up_revision(self, tree, revid, shape):
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
388
389
        class ShapeTree(InventoryRevisionTree):
390
391
            def __init__(self, shape):
392
                self._repository = tree.branch.repository
393
                self._inventory = shape
394
395
            def get_file_text(self, file_id, path=None):
396
                ie = self.inventory[file_id]
397
                if ie.kind != "file":
398
                    return ""
399
                return 'a' * ie.text_size
400
401
            def get_file(self, file_id, path=None):
402
                return StringIO(self.get_file_text(file_id))
403
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
404
        tree.lock_write()
405
        try:
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
406
            if shape.root.revision is None:
407
                shape.root.revision = revid
408
            builder = tree.branch.get_commit_builder(
409
                    parents=[],
410
                    timestamp=0,
411
                    timezone=None,
412
                    committer="Foo Bar <foo@example.com>",
413
                    revision_id=revid)
414
            shape_tree = ShapeTree(shape)
5809.4.3 by Jelmer Vernooij
Fix base revid.
415
            base_tree = tree.branch.repository.revision_tree(
416
                    _mod_revision.NULL_REVISION)
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
417
            changes = shape_tree.iter_changes(
5809.4.3 by Jelmer Vernooij
Fix base revid.
418
                base_tree)
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
419
            list(builder.record_iter_changes(shape_tree,
5809.4.3 by Jelmer Vernooij
Fix base revid.
420
                base_tree.get_revision_id(), changes))
5809.4.1 by Jelmer Vernooij
Avoid .texts.add_lines, rather use Repository.get_commit_builder.
421
            builder.finish_inventory()
422
            builder.commit("Message")
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
423
        finally:
424
            tree.unlock()
425
426
    def add_entry(self, inv, rev_id, entry):
427
        entry.revision = rev_id
428
        inv.add(entry)
429
430
    def add_dir(self, inv, rev_id, file_id, parent_id, name):
431
        new_dir = InventoryDirectory(file_id, name, parent_id)
432
        self.add_entry(inv, rev_id, new_dir)
433
434
    def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
435
        new_file = InventoryFile(file_id, name, parent_id)
436
        new_file.text_sha1 = sha
437
        new_file.text_size = size
438
        self.add_entry(inv, rev_id, new_file)
439
440
    def add_link(self, inv, rev_id, file_id, parent_id, name, target):
441
        new_link = InventoryLink(file_id, name, parent_id)
442
        new_link.symlink_target = target
443
        self.add_entry(inv, rev_id, new_link)
444
445
    def add_new_root(self, new_shape, old_revid, new_revid):
446
        if self.bzrdir_format.repository_format.rich_root_data:
447
            self.add_dir(new_shape, old_revid, 'root-id', None, '')
448
        else:
449
            self.add_dir(new_shape, new_revid, 'root-id', None, '')
450
451
    def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
452
        new_shape, new_revid, extra_parent=None):
2889.1.1 by Robert Collins
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
453
        # set the inventory revision ids.
454
        basis_shape.revision_id = basis_revid
455
        new_shape.revision_id = new_revid
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
456
        delta = self.make_inv_delta(basis_shape, new_shape)
457
        tree = self.make_branch_and_tree('tree')
458
        # the shapes need to be in the tree's repository to be able to set them
459
        # as a parent, but the file content is not needed.
460
        if basis_revid is not None:
461
            self.fake_up_revision(tree, basis_revid, basis_shape)
462
            parents = [basis_revid]
463
            if extra_parent is not None:
464
                parents.append(extra_parent)
465
            tree.set_parent_ids(parents)
466
        self.fake_up_revision(tree, new_revid, new_shape)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
467
        # give tree an inventory of new_shape
468
        tree._write_inventory(new_shape)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
469
        self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
470
            delta, new_shape)
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
471
        # The tree should be internally consistent; while this is a moderately
472
        # large hammer, this is a particularly sensitive area of code, so the
473
        # extra assurance is well worth it.
474
        tree._validate()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
475
        osutils.rmtree('tree')
476
477
    def test_no_parents_just_root(self):
478
        """Test doing an empty commit - no parent, set a root only."""
479
        basis_shape = Inventory(root_id=None) # empty tree
480
        new_shape = Inventory() # tree with a root
481
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
482
            'new_parent')
483
484
    def test_no_parents_full_tree(self):
485
        """Test doing a regular initial commit with files and dirs."""
486
        basis_shape = Inventory(root_id=None) # empty tree
487
        revid = 'new-parent'
488
        new_shape = Inventory(root_id=None)
489
        self.add_dir(new_shape, revid, 'root-id', None, '')
490
        self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
491
        self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
492
            12)
493
        self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
494
        self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
495
            '2' * 32, 24)
496
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
497
            revid)
498
499
    def test_file_content_change(self):
500
        old_revid = 'old-parent'
501
        basis_shape = Inventory(root_id=None)
502
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
503
        self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
504
            '1' * 32, 12)
505
        new_revid = 'new-parent'
506
        new_shape = Inventory(root_id=None)
507
        self.add_new_root(new_shape, old_revid, new_revid)
508
        self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
509
            '2' * 32, 24)
510
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
511
            new_shape, new_revid)
512
513
    def test_link_content_change(self):
514
        old_revid = 'old-parent'
515
        basis_shape = Inventory(root_id=None)
516
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
517
        self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
518
            'old-target')
519
        new_revid = 'new-parent'
520
        new_shape = Inventory(root_id=None)
521
        self.add_new_root(new_shape, old_revid, new_revid)
522
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
523
            'new-target')
524
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
525
            new_shape, new_revid)
526
527
    def test_kind_changes(self):
528
        def do_file(inv, revid):
529
            self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
530
                12)
531
        def do_link(inv, revid):
532
            self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
533
        def do_dir(inv, revid):
534
            self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
535
        for old_factory in (do_file, do_link, do_dir):
536
            for new_factory in (do_file, do_link, do_dir):
537
                if old_factory == new_factory:
538
                    continue
539
                old_revid = 'old-parent'
540
                basis_shape = Inventory(root_id=None)
541
                self.add_dir(basis_shape, old_revid, 'root-id', None, '')
542
                old_factory(basis_shape, old_revid)
543
                new_revid = 'new-parent'
544
                new_shape = Inventory(root_id=None)
545
                self.add_new_root(new_shape, old_revid, new_revid)
546
                new_factory(new_shape, new_revid)
547
                self.assertTransitionFromBasisToShape(basis_shape, old_revid,
548
                    new_shape, new_revid)
549
550
    def test_content_from_second_parent_is_dropped(self):
551
        left_revid = 'left-parent'
552
        basis_shape = Inventory(root_id=None)
553
        self.add_dir(basis_shape, left_revid, 'root-id', None, '')
554
        self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
555
            'left-target')
556
        # the right shape has content - file, link, subdir with a child,
557
        # that should all be discarded by the call.
558
        right_revid = 'right-parent'
559
        right_shape = Inventory(root_id=None)
560
        self.add_dir(right_shape, left_revid, 'root-id', None, '')
561
        self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
2865.1.3 by Robert Collins
Review feedback.
562
            'some-target')
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
563
        self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
564
        self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
565
            '2' * 32, 24)
566
        new_revid = 'new-parent'
567
        new_shape = Inventory(root_id=None)
568
        self.add_new_root(new_shape, left_revid, new_revid)
569
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
570
            'new-target')
571
        self.assertTransitionFromBasisToShape(basis_shape, left_revid,
572
            new_shape, new_revid, right_revid)
573
574
    def test_parent_id_changed(self):
2865.1.3 by Robert Collins
Review feedback.
575
        # test that when the only change to an entry is its parent id changing
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
576
        # that it is handled correctly (that is it keeps the same path)
577
        old_revid = 'old-parent'
578
        basis_shape = Inventory(root_id=None)
579
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
580
        self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
581
        self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
582
        new_revid = 'new-parent'
583
        new_shape = Inventory(root_id=None)
584
        self.add_new_root(new_shape, old_revid, new_revid)
585
        self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
586
        self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
587
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
588
            new_shape, new_revid)
589
590
    def test_name_changed(self):
2865.1.3 by Robert Collins
Review feedback.
591
        # test that when the only change to an entry is its name changing that
592
        # it is handled correctly (that is it keeps the same parent id)
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
593
        old_revid = 'old-parent'
594
        basis_shape = Inventory(root_id=None)
595
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
596
        self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
597
        self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
598
        new_revid = 'new-parent'
599
        new_shape = Inventory(root_id=None)
600
        self.add_new_root(new_shape, old_revid, new_revid)
601
        self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
602
        self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
603
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
604
            new_shape, new_revid)
605
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
606
    def test_parent_child_swap(self):
607
        # test a A->A/B and A/B->A path swap.
608
        old_revid = 'old-parent'
609
        basis_shape = Inventory(root_id=None)
610
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
611
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
612
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
613
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
614
        new_revid = 'new-parent'
615
        new_shape = Inventory(root_id=None)
616
        self.add_new_root(new_shape, old_revid, new_revid)
617
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
618
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
619
        self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
620
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
621
            new_shape, new_revid)
622
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
623
    def test_parent_deleted_child_renamed(self):
624
        # test a A->None and A/B->A.
625
        old_revid = 'old-parent'
626
        basis_shape = Inventory(root_id=None)
627
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
628
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
629
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
630
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
631
        new_revid = 'new-parent'
632
        new_shape = Inventory(root_id=None)
633
        self.add_new_root(new_shape, old_revid, new_revid)
634
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
635
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
636
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
637
            new_shape, new_revid)
638
639
    def test_dir_to_root(self):
640
        # test a A->''.
641
        old_revid = 'old-parent'
642
        basis_shape = Inventory(root_id=None)
643
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
644
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
645
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
646
        new_revid = 'new-parent'
647
        new_shape = Inventory(root_id=None)
648
        self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
649
        self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
650
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
651
            new_shape, new_revid)
652
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
653
    def test_path_swap(self):
654
        # test a A->B and B->A path swap.
655
        old_revid = 'old-parent'
656
        basis_shape = Inventory(root_id=None)
657
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
658
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
659
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
660
        self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
661
        self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
662
        self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
663
            '1' * 32, 12)
664
        self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
665
            '2' * 32, 24)
666
        new_revid = 'new-parent'
667
        new_shape = Inventory(root_id=None)
668
        self.add_new_root(new_shape, old_revid, new_revid)
669
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
670
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
671
        self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
672
        self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
673
        self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
674
            '1' * 32, 12)
675
        self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
676
            '2' * 32, 24)
677
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
678
            new_shape, new_revid)
679
680
    def test_adds(self):
681
        # test adding paths and dirs, including adding to a newly added dir.
682
        old_revid = 'old-parent'
683
        basis_shape = Inventory(root_id=None)
684
        # with a root, so its a commit after the first.
685
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
686
        new_revid = 'new-parent'
687
        new_shape = Inventory(root_id=None)
688
        self.add_new_root(new_shape, old_revid, new_revid)
689
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
690
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
691
        self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
692
            '1' * 32, 12)
693
        self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
694
            '2' * 32, 24)
695
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
696
            new_shape, new_revid)
697
698
    def test_removes(self):
699
        # test removing paths, including paths that are within other also
700
        # removed paths.
701
        old_revid = 'old-parent'
702
        basis_shape = Inventory(root_id=None)
703
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
704
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
705
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
706
        self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
707
            '1' * 32, 12)
708
        self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
709
            '2' * 32, 24)
710
        new_revid = 'new-parent'
711
        new_shape = Inventory(root_id=None)
712
        self.add_new_root(new_shape, old_revid, new_revid)
713
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
714
            new_shape, new_revid)
715
716
    def test_move_to_added_dir(self):
717
        old_revid = 'old-parent'
718
        basis_shape = Inventory(root_id=None)
719
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
720
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
721
        new_revid = 'new-parent'
722
        new_shape = Inventory(root_id=None)
723
        self.add_new_root(new_shape, old_revid, new_revid)
724
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
725
        self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
726
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
727
            new_shape, new_revid)
728
729
    def test_move_from_removed_dir(self):
730
        old_revid = 'old-parent'
731
        basis_shape = Inventory(root_id=None)
732
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
733
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
734
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
735
        new_revid = 'new-parent'
736
        new_shape = Inventory(root_id=None)
737
        self.add_new_root(new_shape, old_revid, new_revid)
738
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
739
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
740
            new_shape, new_revid)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
741
742
    def test_move_moves_children_recursively(self):
743
        old_revid = 'old-parent'
744
        basis_shape = Inventory(root_id=None)
745
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
746
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
747
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
748
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
749
        new_revid = 'new-parent'
750
        new_shape = Inventory(root_id=None)
751
        self.add_new_root(new_shape, old_revid, new_revid)
752
        # the moved path:
753
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
754
        # unmoved children.
755
        self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
756
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
757
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
758
            new_shape, new_revid)