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