/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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
19
from errno import EEXIST
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,
26
    symbol_versioning,
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
    )
34
from bzrlib.revision import Revision
35
from bzrlib.tests import SymlinkFeature, TestNotApplicable
1908.5.2 by Robert Collins
Create and test set_parent_trees.
36
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
37
from bzrlib.uncommit import uncommit
38
39
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.
40
class TestParents(TestCaseWithWorkingTree):
41
42
    def assertConsistentParents(self, expected, tree):
1908.7.6 by Robert Collins
Deprecate WorkingTree.last_revision.
43
        """Check that the parents found are as expected.
44
45
        This test helper also checks that they are consistent with
46
        the pre-get_parent_ids() api - which is now deprecated.
47
        """
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.
48
        self.assertEqual(expected, tree.get_parent_ids())
49
        if expected == []:
2598.5.7 by Aaron Bentley
Updates from review
50
            self.assertEqual(_mod_revision.NULL_REVISION,
51
                             _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.
52
        else:
1908.7.11 by Robert Collins
Merge bzr.dev and undeprecated WorkingTree.last_revision as per review feedback.
53
            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.
54
55
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
56
class TestGetParents(TestParents):
57
58
    def test_get_parents(self):
59
        t = self.make_branch_and_tree('.')
60
        self.assertEqual([], t.get_parent_ids())
61
62
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.
63
class TestSetParents(TestParents):
1908.5.2 by Robert Collins
Create and test set_parent_trees.
64
65
    def test_set_no_parents(self):
66
        t = self.make_branch_and_tree('.')
67
        t.set_parent_trees([])
68
        self.assertEqual([], t.get_parent_ids())
69
        # now give it a real parent, and then set it to no parents again.
70
        t.commit('first post')
71
        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.
72
        self.assertConsistentParents([], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
73
2598.5.2 by Aaron Bentley
Got all tests passing with Branch returning 'null:' for null revision
74
    def test_set_null_parent(self):
75
        t = self.make_branch_and_tree('.')
76
        self.assertRaises(errors.ReservedId, t.set_parent_ids, ['null:'],
77
                          allow_leftmost_as_ghost=True)
78
        self.assertRaises(errors.ReservedId, t.set_parent_trees,
79
                          [('null:', None)], allow_leftmost_as_ghost=True)
80
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.
81
    def test_set_one_ghost_parent_rejects(self):
82
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
83
        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.
84
            t.set_parent_trees, [('missing-revision-id', None)])
85
86
    def test_set_one_ghost_parent_force(self):
87
        t = self.make_branch_and_tree('.')
88
        t.set_parent_trees([('missing-revision-id', None)],
89
            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.
90
        self.assertConsistentParents(['missing-revision-id'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
91
92
    def test_set_two_parents_one_ghost(self):
93
        t = self.make_branch_and_tree('.')
94
        revision_in_repo = t.commit('first post')
95
        # remove the tree's history
96
        uncommit(t.branch, tree=t)
97
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
98
        t.set_parent_trees([(revision_in_repo, rev_tree),
99
            ('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.
100
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
1908.5.2 by Robert Collins
Create and test set_parent_trees.
101
102
    def test_set_three_parents(self):
103
        t = self.make_branch_and_tree('.')
104
        first_revision = t.commit('first post')
105
        uncommit(t.branch, tree=t)
106
        second_revision = t.commit('second post')
107
        uncommit(t.branch, tree=t)
108
        third_revision = t.commit('third post')
109
        uncommit(t.branch, tree=t)
110
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
111
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
112
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
113
        t.set_parent_trees([(first_revision, rev_tree1),
114
            (second_revision, rev_tree2),
115
            (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.
116
        self.assertConsistentParents(
117
            [first_revision, second_revision, third_revision], t)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
118
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
119
    def test_set_no_parents_ids(self):
120
        t = self.make_branch_and_tree('.')
121
        t.set_parent_ids([])
122
        self.assertEqual([], t.get_parent_ids())
123
        # now give it a real parent, and then set it to no parents again.
124
        t.commit('first post')
125
        t.set_parent_ids([])
126
        self.assertConsistentParents([], t)
127
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.
128
    def test_set_one_ghost_parent_ids_rejects(self):
129
        t = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
130
        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.
131
            t.set_parent_ids, ['missing-revision-id'])
132
133
    def test_set_one_ghost_parent_ids_force(self):
134
        t = self.make_branch_and_tree('.')
135
        t.set_parent_ids(['missing-revision-id'],
136
            allow_leftmost_as_ghost=True)
1908.5.5 by Robert Collins
Add WorkingTree.set_parent_ids.
137
        self.assertConsistentParents(['missing-revision-id'], t)
138
139
    def test_set_two_parents_one_ghost_ids(self):
140
        t = self.make_branch_and_tree('.')
141
        revision_in_repo = t.commit('first post')
142
        # remove the tree's history
143
        uncommit(t.branch, tree=t)
144
        rev_tree = t.branch.repository.revision_tree(revision_in_repo)
145
        t.set_parent_ids([revision_in_repo, 'another-missing'])
146
        self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
147
148
    def test_set_three_parents_ids(self):
149
        t = self.make_branch_and_tree('.')
150
        first_revision = t.commit('first post')
151
        uncommit(t.branch, tree=t)
152
        second_revision = t.commit('second post')
153
        uncommit(t.branch, tree=t)
154
        third_revision = t.commit('third post')
155
        uncommit(t.branch, tree=t)
156
        rev_tree1 = t.branch.repository.revision_tree(first_revision)
157
        rev_tree2 = t.branch.repository.revision_tree(second_revision)
158
        rev_tree3 = t.branch.repository.revision_tree(third_revision)
159
        t.set_parent_ids([first_revision, second_revision, third_revision])
160
        self.assertConsistentParents(
161
            [first_revision, second_revision, third_revision], t)
162
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
163
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
164
class TestAddParent(TestParents):
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
165
166
    def test_add_first_parent_id(self):
167
        """Test adding the first parent id"""
168
        tree = self.make_branch_and_tree('.')
169
        first_revision = tree.commit('first post')
170
        uncommit(tree.branch, tree=tree)
171
        tree.add_parent_tree_id(first_revision)
172
        self.assertConsistentParents([first_revision], tree)
173
        
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.
174
    def test_add_first_parent_id_ghost_rejects(self):
175
        """Test adding the first parent id - as a ghost"""
176
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
177
        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.
178
            tree.add_parent_tree_id, 'first-revision')
179
        
180
    def test_add_first_parent_id_ghost_force(self):
181
        """Test adding the first parent id - as a ghost"""
182
        tree = self.make_branch_and_tree('.')
183
        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.
184
        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.
185
186
    def test_add_second_parent_id_with_ghost_first(self):
187
        """Test adding the second parent when the first is a ghost."""
188
        tree = self.make_branch_and_tree('.')
189
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
190
        tree.add_parent_tree_id('second')
191
        self.assertConsistentParents(['first-revision', 'second'], tree)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
192
        
193
    def test_add_second_parent_id(self):
194
        """Test adding the second parent id"""
195
        tree = self.make_branch_and_tree('.')
196
        first_revision = tree.commit('first post')
197
        uncommit(tree.branch, tree=tree)
198
        second_revision = tree.commit('second post')
199
        tree.add_parent_tree_id(first_revision)
200
        self.assertConsistentParents([second_revision, first_revision], tree)
201
        
202
    def test_add_second_parent_id_ghost(self):
203
        """Test adding the second parent id - as a ghost"""
204
        tree = self.make_branch_and_tree('.')
205
        first_revision = tree.commit('first post')
206
        tree.add_parent_tree_id('second')
207
        self.assertConsistentParents([first_revision, 'second'], tree)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
208
        
209
    def test_add_first_parent_tree(self):
210
        """Test adding the first parent id"""
211
        tree = self.make_branch_and_tree('.')
212
        first_revision = tree.commit('first post')
213
        uncommit(tree.branch, tree=tree)
214
        tree.add_parent_tree((first_revision,
215
            tree.branch.repository.revision_tree(first_revision)))
216
        self.assertConsistentParents([first_revision], tree)
217
        
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.
218
    def test_add_first_parent_tree_ghost_rejects(self):
219
        """Test adding the first parent id - as a ghost"""
220
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
221
        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.
222
            tree.add_parent_tree, ('first-revision', None))
223
        
224
    def test_add_first_parent_tree_ghost_force(self):
225
        """Test adding the first parent id - as a ghost"""
226
        tree = self.make_branch_and_tree('.')
227
        tree.add_parent_tree(('first-revision', None),
228
            allow_leftmost_as_ghost=True)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
229
        self.assertConsistentParents(['first-revision'], tree)
230
        
231
    def test_add_second_parent_tree(self):
232
        """Test adding the second parent id"""
233
        tree = self.make_branch_and_tree('.')
234
        first_revision = tree.commit('first post')
235
        uncommit(tree.branch, tree=tree)
236
        second_revision = tree.commit('second post')
237
        tree.add_parent_tree((first_revision,
238
            tree.branch.repository.revision_tree(first_revision)))
239
        self.assertConsistentParents([second_revision, first_revision], tree)
240
        
241
    def test_add_second_parent_tree_ghost(self):
242
        """Test adding the second parent id - as a ghost"""
243
        tree = self.make_branch_and_tree('.')
244
        first_revision = tree.commit('first post')
245
        tree.add_parent_tree(('second', None))
246
        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.
247
248
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
249
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
2903.2.7 by Martin Pool
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta
250
    """Tests for the update_basis_by_delta call.
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
251
    
252
    This is intuitively defined as 'apply an inventory delta to the basis and
253
    discard other parents', but for trees that have an inventory that is not
254
    managed as a tree-by-id, the implementation requires roughly duplicated
255
    tests with those for apply_inventory_delta on the main tree.
256
    """
257
258
    def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
259
        expected_inventory):
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
260
        tree.lock_write()
261
        try:
262
            tree.update_basis_by_delta(revid, delta)
263
        finally:
264
            tree.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
265
        # check the last revision was adjusted to rev_id
266
        self.assertEqual(revid, tree.last_revision())
267
        # check the parents are what we expect
268
        self.assertEqual([revid], tree.get_parent_ids())
269
        # check that the basis tree has the inventory we expect from applying
270
        # the delta.
271
        result_basis = tree.basis_tree()
272
        result_basis.lock_read()
273
        self.addCleanup(result_basis.unlock)
274
        self.assertEqual(expected_inventory, result_basis.inventory)
275
276
    def make_inv_delta(self, old, new):
277
        """Make an inventory delta from two inventories."""
278
        old_ids = set(old._byid.iterkeys())
279
        new_ids = set(new._byid.iterkeys())
280
        adds = new_ids - old_ids
281
        deletes = old_ids - new_ids
282
        common = old_ids.intersection(new_ids)
283
        delta = []
284
        for file_id in deletes:
285
            delta.append((old.id2path(file_id), None, file_id, None))
286
        for file_id in adds:
287
            delta.append((None, new.id2path(file_id), file_id, new[file_id]))
288
        for file_id in common:
289
            if old[file_id] != new[file_id]:
290
                delta.append((old.id2path(file_id), new.id2path(file_id),
291
                    file_id, new[file_id]))
292
        return delta
293
294
    def fake_up_revision(self, tree, revid, shape):
295
        tree.lock_write()
296
        try:
297
            tree.branch.repository.start_write_group()
298
            try:
299
                if shape.root.revision is None:
300
                    shape.root.revision = revid
301
                sha1 = tree.branch.repository.add_inventory(revid, shape, [])
302
                rev = Revision(timestamp=0,
303
                               timezone=None,
304
                               committer="Foo Bar <foo@example.com>",
305
                               message="Message",
306
                               inventory_sha1=sha1,
307
                               revision_id=revid)
308
                tree.branch.repository.add_revision(revid, rev)
309
            except:
310
                tree.branch.repository.abort_write_group()
311
                raise
312
            else:
313
                tree.branch.repository.commit_write_group()
314
        finally:
315
            tree.unlock()
316
317
    def add_entry(self, inv, rev_id, entry):
318
        entry.revision = rev_id
319
        inv.add(entry)
320
321
    def add_dir(self, inv, rev_id, file_id, parent_id, name):
322
        new_dir = InventoryDirectory(file_id, name, parent_id)
323
        self.add_entry(inv, rev_id, new_dir)
324
325
    def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
326
        new_file = InventoryFile(file_id, name, parent_id)
327
        new_file.text_sha1 = sha
328
        new_file.text_size = size
329
        self.add_entry(inv, rev_id, new_file)
330
331
    def add_link(self, inv, rev_id, file_id, parent_id, name, target):
332
        new_link = InventoryLink(file_id, name, parent_id)
333
        new_link.symlink_target = target
334
        self.add_entry(inv, rev_id, new_link)
335
336
    def add_new_root(self, new_shape, old_revid, new_revid):
337
        if self.bzrdir_format.repository_format.rich_root_data:
338
            self.add_dir(new_shape, old_revid, 'root-id', None, '')
339
        else:
340
            self.add_dir(new_shape, new_revid, 'root-id', None, '')
341
342
    def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
343
        new_shape, new_revid, extra_parent=None):
2889.1.1 by Robert Collins
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
344
        # set the inventory revision ids.
345
        basis_shape.revision_id = basis_revid
346
        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.
347
        delta = self.make_inv_delta(basis_shape, new_shape)
348
        tree = self.make_branch_and_tree('tree')
349
        # the shapes need to be in the tree's repository to be able to set them
350
        # as a parent, but the file content is not needed.
351
        if basis_revid is not None:
352
            self.fake_up_revision(tree, basis_revid, basis_shape)
353
            parents = [basis_revid]
354
            if extra_parent is not None:
355
                parents.append(extra_parent)
356
            tree.set_parent_ids(parents)
357
        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
358
        # give tree an inventory of new_shape
359
        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.
360
        self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
361
            delta, new_shape)
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
362
        # The tree should be internally consistent; while this is a moderately
363
        # large hammer, this is a particularly sensitive area of code, so the
364
        # extra assurance is well worth it.
365
        tree._validate()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
366
        osutils.rmtree('tree')
367
368
    def test_no_parents_just_root(self):
369
        """Test doing an empty commit - no parent, set a root only."""
370
        basis_shape = Inventory(root_id=None) # empty tree
371
        new_shape = Inventory() # tree with a root
372
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
373
            'new_parent')
374
375
    def test_no_parents_full_tree(self):
376
        """Test doing a regular initial commit with files and dirs."""
377
        basis_shape = Inventory(root_id=None) # empty tree
378
        revid = 'new-parent'
379
        new_shape = Inventory(root_id=None)
380
        self.add_dir(new_shape, revid, 'root-id', None, '')
381
        self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
382
        self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
383
            12)
384
        self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
385
        self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
386
            '2' * 32, 24)
387
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
388
            revid)
389
390
    def test_file_content_change(self):
391
        old_revid = 'old-parent'
392
        basis_shape = Inventory(root_id=None)
393
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
394
        self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
395
            '1' * 32, 12)
396
        new_revid = 'new-parent'
397
        new_shape = Inventory(root_id=None)
398
        self.add_new_root(new_shape, old_revid, new_revid)
399
        self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
400
            '2' * 32, 24)
401
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
402
            new_shape, new_revid)
403
404
    def test_link_content_change(self):
405
        old_revid = 'old-parent'
406
        basis_shape = Inventory(root_id=None)
407
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
408
        self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
409
            'old-target')
410
        new_revid = 'new-parent'
411
        new_shape = Inventory(root_id=None)
412
        self.add_new_root(new_shape, old_revid, new_revid)
413
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
414
            'new-target')
415
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
416
            new_shape, new_revid)
417
418
    def test_kind_changes(self):
419
        def do_file(inv, revid):
420
            self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
421
                12)
422
        def do_link(inv, revid):
423
            self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
424
        def do_dir(inv, revid):
425
            self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
426
        for old_factory in (do_file, do_link, do_dir):
427
            for new_factory in (do_file, do_link, do_dir):
428
                if old_factory == new_factory:
429
                    continue
430
                old_revid = 'old-parent'
431
                basis_shape = Inventory(root_id=None)
432
                self.add_dir(basis_shape, old_revid, 'root-id', None, '')
433
                old_factory(basis_shape, old_revid)
434
                new_revid = 'new-parent'
435
                new_shape = Inventory(root_id=None)
436
                self.add_new_root(new_shape, old_revid, new_revid)
437
                new_factory(new_shape, new_revid)
438
                self.assertTransitionFromBasisToShape(basis_shape, old_revid,
439
                    new_shape, new_revid)
440
441
    def test_content_from_second_parent_is_dropped(self):
442
        left_revid = 'left-parent'
443
        basis_shape = Inventory(root_id=None)
444
        self.add_dir(basis_shape, left_revid, 'root-id', None, '')
445
        self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
446
            'left-target')
447
        # the right shape has content - file, link, subdir with a child,
448
        # that should all be discarded by the call.
449
        right_revid = 'right-parent'
450
        right_shape = Inventory(root_id=None)
451
        self.add_dir(right_shape, left_revid, 'root-id', None, '')
452
        self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
2865.1.3 by Robert Collins
Review feedback.
453
            'some-target')
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
454
        self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
455
        self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
456
            '2' * 32, 24)
457
        new_revid = 'new-parent'
458
        new_shape = Inventory(root_id=None)
459
        self.add_new_root(new_shape, left_revid, new_revid)
460
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
461
            'new-target')
462
        self.assertTransitionFromBasisToShape(basis_shape, left_revid,
463
            new_shape, new_revid, right_revid)
464
465
    def test_parent_id_changed(self):
2865.1.3 by Robert Collins
Review feedback.
466
        # 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.
467
        # that it is handled correctly (that is it keeps the same path)
468
        old_revid = 'old-parent'
469
        basis_shape = Inventory(root_id=None)
470
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
471
        self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
472
        self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
473
        new_revid = 'new-parent'
474
        new_shape = Inventory(root_id=None)
475
        self.add_new_root(new_shape, old_revid, new_revid)
476
        self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
477
        self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
478
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
479
            new_shape, new_revid)
480
481
    def test_name_changed(self):
2865.1.3 by Robert Collins
Review feedback.
482
        # test that when the only change to an entry is its name changing that
483
        # 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.
484
        old_revid = 'old-parent'
485
        basis_shape = Inventory(root_id=None)
486
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
487
        self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
488
        self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
489
        new_revid = 'new-parent'
490
        new_shape = Inventory(root_id=None)
491
        self.add_new_root(new_shape, old_revid, new_revid)
492
        self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
493
        self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
494
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
495
            new_shape, new_revid)
496
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
497
    def test_parent_child_swap(self):
498
        # test a A->A/B and A/B->A path swap.
499
        old_revid = 'old-parent'
500
        basis_shape = Inventory(root_id=None)
501
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
502
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
503
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
504
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
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_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
509
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
510
        self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
511
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
512
            new_shape, new_revid)
513
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
514
    def test_parent_deleted_child_renamed(self):
515
        # test a A->None and A/B->A.
516
        old_revid = 'old-parent'
517
        basis_shape = Inventory(root_id=None)
518
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
519
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
520
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
521
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
522
        new_revid = 'new-parent'
523
        new_shape = Inventory(root_id=None)
524
        self.add_new_root(new_shape, old_revid, new_revid)
525
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
526
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
527
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
528
            new_shape, new_revid)
529
530
    def test_dir_to_root(self):
531
        # test a A->''.
532
        old_revid = 'old-parent'
533
        basis_shape = Inventory(root_id=None)
534
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
535
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
536
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
537
        new_revid = 'new-parent'
538
        new_shape = Inventory(root_id=None)
539
        self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
540
        self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
541
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
542
            new_shape, new_revid)
543
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
544
    def test_path_swap(self):
545
        # test a A->B and B->A path swap.
546
        old_revid = 'old-parent'
547
        basis_shape = Inventory(root_id=None)
548
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
549
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
550
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
551
        self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
552
        self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
553
        self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
554
            '1' * 32, 12)
555
        self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
556
            '2' * 32, 24)
557
        new_revid = 'new-parent'
558
        new_shape = Inventory(root_id=None)
559
        self.add_new_root(new_shape, old_revid, new_revid)
560
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
561
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
562
        self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
563
        self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
564
        self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
565
            '1' * 32, 12)
566
        self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
567
            '2' * 32, 24)
568
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
569
            new_shape, new_revid)
570
571
    def test_adds(self):
572
        # test adding paths and dirs, including adding to a newly added dir.
573
        old_revid = 'old-parent'
574
        basis_shape = Inventory(root_id=None)
575
        # with a root, so its a commit after the first.
576
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
577
        new_revid = 'new-parent'
578
        new_shape = Inventory(root_id=None)
579
        self.add_new_root(new_shape, old_revid, new_revid)
580
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
581
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
582
        self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
583
            '1' * 32, 12)
584
        self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
585
            '2' * 32, 24)
586
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
587
            new_shape, new_revid)
588
589
    def test_removes(self):
590
        # test removing paths, including paths that are within other also
591
        # removed paths.
592
        old_revid = 'old-parent'
593
        basis_shape = Inventory(root_id=None)
594
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
595
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
596
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
597
        self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
598
            '1' * 32, 12)
599
        self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
600
            '2' * 32, 24)
601
        new_revid = 'new-parent'
602
        new_shape = Inventory(root_id=None)
603
        self.add_new_root(new_shape, old_revid, new_revid)
604
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
605
            new_shape, new_revid)
606
607
    def test_move_to_added_dir(self):
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_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
612
        new_revid = 'new-parent'
613
        new_shape = Inventory(root_id=None)
614
        self.add_new_root(new_shape, old_revid, new_revid)
615
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
616
        self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
617
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
618
            new_shape, new_revid)
619
620
    def test_move_from_removed_dir(self):
621
        old_revid = 'old-parent'
622
        basis_shape = Inventory(root_id=None)
623
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
624
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
625
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
626
        new_revid = 'new-parent'
627
        new_shape = Inventory(root_id=None)
628
        self.add_new_root(new_shape, old_revid, new_revid)
629
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
630
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
631
            new_shape, new_revid)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
632
633
    def test_move_moves_children_recursively(self):
634
        old_revid = 'old-parent'
635
        basis_shape = Inventory(root_id=None)
636
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
637
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
638
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
639
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
640
        new_revid = 'new-parent'
641
        new_shape = Inventory(root_id=None)
642
        self.add_new_root(new_shape, old_revid, new_revid)
643
        # the moved path:
644
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
645
        # unmoved children.
646
        self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
647
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
648
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
649
            new_shape, new_revid)