/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
3462.1.2 by John Arbash Meinel
Change WT.set_parent_(ids/trees) to filter out ancestors.
163
    def test_set_duplicate_parent_ids(self):
164
        t = self.make_branch_and_tree('.')
165
        rev1 = t.commit('first post')
166
        uncommit(t.branch, tree=t)
167
        rev2 = t.commit('second post')
168
        uncommit(t.branch, tree=t)
169
        rev3 = t.commit('third post')
170
        uncommit(t.branch, tree=t)
171
        t.set_parent_ids([rev1, rev2, rev2, rev3])
172
        # We strip the duplicate, but preserve the ordering
173
        self.assertConsistentParents([rev1, rev2, rev3], t)
174
175
    def test_set_duplicate_parent_trees(self):
176
        t = self.make_branch_and_tree('.')
177
        rev1 = t.commit('first post')
178
        uncommit(t.branch, tree=t)
179
        rev2 = t.commit('second post')
180
        uncommit(t.branch, tree=t)
181
        rev3 = t.commit('third post')
182
        uncommit(t.branch, tree=t)
183
        rev_tree1 = t.branch.repository.revision_tree(rev1)
184
        rev_tree2 = t.branch.repository.revision_tree(rev2)
185
        rev_tree3 = t.branch.repository.revision_tree(rev3)
186
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
187
                            (rev2, rev_tree2), (rev3, rev_tree3)])
188
        # We strip the duplicate, but preserve the ordering
189
        self.assertConsistentParents([rev1, rev2, rev3], t)
190
191
    def test_set_parent_ids_in_ancestry(self):
192
        t = self.make_branch_and_tree('.')
193
        rev1 = t.commit('first post')
194
        rev2 = t.commit('second post')
195
        rev3 = t.commit('third post')
196
        # Reset the tree, back to rev1
197
        t.set_parent_ids([rev1])
198
        t.branch.set_last_revision_info(1, rev1)
199
        self.assertConsistentParents([rev1], t)
200
        t.set_parent_ids([rev1, rev2, rev3])
201
        # rev2 is in the ancestry of rev3, so it will be filtered out
202
        self.assertConsistentParents([rev1, rev3], t)
203
        # Order should be preserved, and the first revision should always be
204
        # kept
205
        t.set_parent_ids([rev2, rev3, rev1])
206
        self.assertConsistentParents([rev2, rev3], t)
207
208
    def test_set_parent_trees_in_ancestry(self):
209
        t = self.make_branch_and_tree('.')
210
        rev1 = t.commit('first post')
211
        rev2 = t.commit('second post')
212
        rev3 = t.commit('third post')
213
        # Reset the tree, back to rev1
214
        t.set_parent_ids([rev1])
215
        t.branch.set_last_revision_info(1, rev1)
216
        self.assertConsistentParents([rev1], t)
217
        rev_tree1 = t.branch.repository.revision_tree(rev1)
218
        rev_tree2 = t.branch.repository.revision_tree(rev2)
219
        rev_tree3 = t.branch.repository.revision_tree(rev3)
220
        t.set_parent_trees([(rev1, rev_tree1), (rev2, rev_tree2),
221
                            (rev3, rev_tree3)])
222
        # rev2 is in the ancestry of rev3, so it will be filtered out
223
        self.assertConsistentParents([rev1, rev3], t)
224
        # Order should be preserved, and the first revision should always be
225
        # kept
226
        t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
227
                            (rev3, rev_tree3)])
228
        self.assertConsistentParents([rev2, rev3], t)
229
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
230
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
231
class TestAddParent(TestParents):
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
232
233
    def test_add_first_parent_id(self):
234
        """Test adding the first parent id"""
235
        tree = self.make_branch_and_tree('.')
236
        first_revision = tree.commit('first post')
237
        uncommit(tree.branch, tree=tree)
238
        tree.add_parent_tree_id(first_revision)
239
        self.assertConsistentParents([first_revision], tree)
240
        
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.
241
    def test_add_first_parent_id_ghost_rejects(self):
242
        """Test adding the first parent id - as a ghost"""
243
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
244
        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.
245
            tree.add_parent_tree_id, 'first-revision')
246
        
247
    def test_add_first_parent_id_ghost_force(self):
248
        """Test adding the first parent id - as a ghost"""
249
        tree = self.make_branch_and_tree('.')
250
        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.
251
        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.
252
253
    def test_add_second_parent_id_with_ghost_first(self):
254
        """Test adding the second parent when the first is a ghost."""
255
        tree = self.make_branch_and_tree('.')
256
        tree.add_parent_tree_id('first-revision', allow_leftmost_as_ghost=True)
257
        tree.add_parent_tree_id('second')
258
        self.assertConsistentParents(['first-revision', 'second'], tree)
1908.5.4 by Robert Collins
Add add_parent_tree_id WorkingTree helper api.
259
        
260
    def test_add_second_parent_id(self):
261
        """Test adding the second parent id"""
262
        tree = self.make_branch_and_tree('.')
263
        first_revision = tree.commit('first post')
264
        uncommit(tree.branch, tree=tree)
265
        second_revision = tree.commit('second post')
266
        tree.add_parent_tree_id(first_revision)
267
        self.assertConsistentParents([second_revision, first_revision], tree)
268
        
269
    def test_add_second_parent_id_ghost(self):
270
        """Test adding the second parent id - as a ghost"""
271
        tree = self.make_branch_and_tree('.')
272
        first_revision = tree.commit('first post')
273
        tree.add_parent_tree_id('second')
274
        self.assertConsistentParents([first_revision, 'second'], tree)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
275
        
276
    def test_add_first_parent_tree(self):
277
        """Test adding the first parent id"""
278
        tree = self.make_branch_and_tree('.')
279
        first_revision = tree.commit('first post')
280
        uncommit(tree.branch, tree=tree)
281
        tree.add_parent_tree((first_revision,
282
            tree.branch.repository.revision_tree(first_revision)))
283
        self.assertConsistentParents([first_revision], tree)
284
        
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.
285
    def test_add_first_parent_tree_ghost_rejects(self):
286
        """Test adding the first parent id - as a ghost"""
287
        tree = self.make_branch_and_tree('.')
1908.5.12 by Robert Collins
Apply review feedback - paired with Martin.
288
        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.
289
            tree.add_parent_tree, ('first-revision', None))
290
        
291
    def test_add_first_parent_tree_ghost_force(self):
292
        """Test adding the first parent id - as a ghost"""
293
        tree = self.make_branch_and_tree('.')
294
        tree.add_parent_tree(('first-revision', None),
295
            allow_leftmost_as_ghost=True)
1908.5.6 by Robert Collins
Add add_parent_tree to WorkingTree.
296
        self.assertConsistentParents(['first-revision'], tree)
297
        
298
    def test_add_second_parent_tree(self):
299
        """Test adding the second parent id"""
300
        tree = self.make_branch_and_tree('.')
301
        first_revision = tree.commit('first post')
302
        uncommit(tree.branch, tree=tree)
303
        second_revision = tree.commit('second post')
304
        tree.add_parent_tree((first_revision,
305
            tree.branch.repository.revision_tree(first_revision)))
306
        self.assertConsistentParents([second_revision, first_revision], tree)
307
        
308
    def test_add_second_parent_tree_ghost(self):
309
        """Test adding the second parent id - as a ghost"""
310
        tree = self.make_branch_and_tree('.')
311
        first_revision = tree.commit('first post')
312
        tree.add_parent_tree(('second', None))
313
        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.
314
315
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
316
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
2903.2.7 by Martin Pool
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta
317
    """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.
318
    
319
    This is intuitively defined as 'apply an inventory delta to the basis and
320
    discard other parents', but for trees that have an inventory that is not
321
    managed as a tree-by-id, the implementation requires roughly duplicated
322
    tests with those for apply_inventory_delta on the main tree.
323
    """
324
325
    def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
326
        expected_inventory):
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
327
        tree.lock_write()
328
        try:
329
            tree.update_basis_by_delta(revid, delta)
330
        finally:
331
            tree.unlock()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
332
        # check the last revision was adjusted to rev_id
333
        self.assertEqual(revid, tree.last_revision())
334
        # check the parents are what we expect
335
        self.assertEqual([revid], tree.get_parent_ids())
336
        # check that the basis tree has the inventory we expect from applying
337
        # the delta.
338
        result_basis = tree.basis_tree()
339
        result_basis.lock_read()
3619.6.6 by Mark Hammond
eagerly unlock the result_basis to prevent handles staying open.
340
        try:
341
            self.assertEqual(expected_inventory, result_basis.inventory)
342
        finally:
343
            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.
344
345
    def make_inv_delta(self, old, new):
346
        """Make an inventory delta from two inventories."""
347
        old_ids = set(old._byid.iterkeys())
348
        new_ids = set(new._byid.iterkeys())
349
        adds = new_ids - old_ids
350
        deletes = old_ids - new_ids
351
        common = old_ids.intersection(new_ids)
352
        delta = []
353
        for file_id in deletes:
354
            delta.append((old.id2path(file_id), None, file_id, None))
355
        for file_id in adds:
356
            delta.append((None, new.id2path(file_id), file_id, new[file_id]))
357
        for file_id in common:
358
            if old[file_id] != new[file_id]:
359
                delta.append((old.id2path(file_id), new.id2path(file_id),
360
                    file_id, new[file_id]))
361
        return delta
362
363
    def fake_up_revision(self, tree, revid, shape):
364
        tree.lock_write()
365
        try:
366
            tree.branch.repository.start_write_group()
367
            try:
368
                if shape.root.revision is None:
369
                    shape.root.revision = revid
370
                sha1 = tree.branch.repository.add_inventory(revid, shape, [])
371
                rev = Revision(timestamp=0,
372
                               timezone=None,
373
                               committer="Foo Bar <foo@example.com>",
374
                               message="Message",
375
                               inventory_sha1=sha1,
376
                               revision_id=revid)
377
                tree.branch.repository.add_revision(revid, rev)
378
            except:
379
                tree.branch.repository.abort_write_group()
380
                raise
381
            else:
382
                tree.branch.repository.commit_write_group()
383
        finally:
384
            tree.unlock()
385
386
    def add_entry(self, inv, rev_id, entry):
387
        entry.revision = rev_id
388
        inv.add(entry)
389
390
    def add_dir(self, inv, rev_id, file_id, parent_id, name):
391
        new_dir = InventoryDirectory(file_id, name, parent_id)
392
        self.add_entry(inv, rev_id, new_dir)
393
394
    def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
395
        new_file = InventoryFile(file_id, name, parent_id)
396
        new_file.text_sha1 = sha
397
        new_file.text_size = size
398
        self.add_entry(inv, rev_id, new_file)
399
400
    def add_link(self, inv, rev_id, file_id, parent_id, name, target):
401
        new_link = InventoryLink(file_id, name, parent_id)
402
        new_link.symlink_target = target
403
        self.add_entry(inv, rev_id, new_link)
404
405
    def add_new_root(self, new_shape, old_revid, new_revid):
406
        if self.bzrdir_format.repository_format.rich_root_data:
407
            self.add_dir(new_shape, old_revid, 'root-id', None, '')
408
        else:
409
            self.add_dir(new_shape, new_revid, 'root-id', None, '')
410
411
    def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
412
        new_shape, new_revid, extra_parent=None):
2889.1.1 by Robert Collins
* The class ``bzrlib.repofmt.knitrepo.KnitRepository3`` has been folded into
413
        # set the inventory revision ids.
414
        basis_shape.revision_id = basis_revid
415
        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.
416
        delta = self.make_inv_delta(basis_shape, new_shape)
417
        tree = self.make_branch_and_tree('tree')
418
        # the shapes need to be in the tree's repository to be able to set them
419
        # as a parent, but the file content is not needed.
420
        if basis_revid is not None:
421
            self.fake_up_revision(tree, basis_revid, basis_shape)
422
            parents = [basis_revid]
423
            if extra_parent is not None:
424
                parents.append(extra_parent)
425
            tree.set_parent_ids(parents)
426
        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
427
        # give tree an inventory of new_shape
428
        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.
429
        self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
430
            delta, new_shape)
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
431
        # The tree should be internally consistent; while this is a moderately
432
        # large hammer, this is a particularly sensitive area of code, so the
433
        # extra assurance is well worth it.
434
        tree._validate()
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
435
        osutils.rmtree('tree')
436
437
    def test_no_parents_just_root(self):
438
        """Test doing an empty commit - no parent, set a root only."""
439
        basis_shape = Inventory(root_id=None) # empty tree
440
        new_shape = Inventory() # tree with a root
441
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
442
            'new_parent')
443
444
    def test_no_parents_full_tree(self):
445
        """Test doing a regular initial commit with files and dirs."""
446
        basis_shape = Inventory(root_id=None) # empty tree
447
        revid = 'new-parent'
448
        new_shape = Inventory(root_id=None)
449
        self.add_dir(new_shape, revid, 'root-id', None, '')
450
        self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
451
        self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
452
            12)
453
        self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
454
        self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
455
            '2' * 32, 24)
456
        self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
457
            revid)
458
459
    def test_file_content_change(self):
460
        old_revid = 'old-parent'
461
        basis_shape = Inventory(root_id=None)
462
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
463
        self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
464
            '1' * 32, 12)
465
        new_revid = 'new-parent'
466
        new_shape = Inventory(root_id=None)
467
        self.add_new_root(new_shape, old_revid, new_revid)
468
        self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
469
            '2' * 32, 24)
470
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
471
            new_shape, new_revid)
472
473
    def test_link_content_change(self):
474
        old_revid = 'old-parent'
475
        basis_shape = Inventory(root_id=None)
476
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
477
        self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
478
            'old-target')
479
        new_revid = 'new-parent'
480
        new_shape = Inventory(root_id=None)
481
        self.add_new_root(new_shape, old_revid, new_revid)
482
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
483
            'new-target')
484
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
485
            new_shape, new_revid)
486
487
    def test_kind_changes(self):
488
        def do_file(inv, revid):
489
            self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
490
                12)
491
        def do_link(inv, revid):
492
            self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
493
        def do_dir(inv, revid):
494
            self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
495
        for old_factory in (do_file, do_link, do_dir):
496
            for new_factory in (do_file, do_link, do_dir):
497
                if old_factory == new_factory:
498
                    continue
499
                old_revid = 'old-parent'
500
                basis_shape = Inventory(root_id=None)
501
                self.add_dir(basis_shape, old_revid, 'root-id', None, '')
502
                old_factory(basis_shape, old_revid)
503
                new_revid = 'new-parent'
504
                new_shape = Inventory(root_id=None)
505
                self.add_new_root(new_shape, old_revid, new_revid)
506
                new_factory(new_shape, new_revid)
507
                self.assertTransitionFromBasisToShape(basis_shape, old_revid,
508
                    new_shape, new_revid)
509
510
    def test_content_from_second_parent_is_dropped(self):
511
        left_revid = 'left-parent'
512
        basis_shape = Inventory(root_id=None)
513
        self.add_dir(basis_shape, left_revid, 'root-id', None, '')
514
        self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
515
            'left-target')
516
        # the right shape has content - file, link, subdir with a child,
517
        # that should all be discarded by the call.
518
        right_revid = 'right-parent'
519
        right_shape = Inventory(root_id=None)
520
        self.add_dir(right_shape, left_revid, 'root-id', None, '')
521
        self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
2865.1.3 by Robert Collins
Review feedback.
522
            'some-target')
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
523
        self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
524
        self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
525
            '2' * 32, 24)
526
        new_revid = 'new-parent'
527
        new_shape = Inventory(root_id=None)
528
        self.add_new_root(new_shape, left_revid, new_revid)
529
        self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
530
            'new-target')
531
        self.assertTransitionFromBasisToShape(basis_shape, left_revid,
532
            new_shape, new_revid, right_revid)
533
534
    def test_parent_id_changed(self):
2865.1.3 by Robert Collins
Review feedback.
535
        # 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.
536
        # that it is handled correctly (that is it keeps the same path)
537
        old_revid = 'old-parent'
538
        basis_shape = Inventory(root_id=None)
539
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
540
        self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
541
        self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
542
        new_revid = 'new-parent'
543
        new_shape = Inventory(root_id=None)
544
        self.add_new_root(new_shape, old_revid, new_revid)
545
        self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
546
        self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
547
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
548
            new_shape, new_revid)
549
550
    def test_name_changed(self):
2865.1.3 by Robert Collins
Review feedback.
551
        # test that when the only change to an entry is its name changing that
552
        # 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.
553
        old_revid = 'old-parent'
554
        basis_shape = Inventory(root_id=None)
555
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
556
        self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
557
        self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
558
        new_revid = 'new-parent'
559
        new_shape = Inventory(root_id=None)
560
        self.add_new_root(new_shape, old_revid, new_revid)
561
        self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
562
        self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
563
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
564
            new_shape, new_revid)
565
2929.2.2 by Robert Collins
Review feedback on dirstate update_basis_via_delta logic.
566
    def test_parent_child_swap(self):
567
        # test a A->A/B and A/B->A path swap.
568
        old_revid = 'old-parent'
569
        basis_shape = Inventory(root_id=None)
570
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
571
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
572
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
573
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
574
        new_revid = 'new-parent'
575
        new_shape = Inventory(root_id=None)
576
        self.add_new_root(new_shape, old_revid, new_revid)
577
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
578
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
579
        self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
580
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
581
            new_shape, new_revid)
582
3253.2.1 by John Arbash Meinel
Fix moving directories to root nodes.
583
    def test_parent_deleted_child_renamed(self):
584
        # test a A->None and A/B->A.
585
        old_revid = 'old-parent'
586
        basis_shape = Inventory(root_id=None)
587
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
588
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
589
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
590
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
591
        new_revid = 'new-parent'
592
        new_shape = Inventory(root_id=None)
593
        self.add_new_root(new_shape, old_revid, new_revid)
594
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
595
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
596
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
597
            new_shape, new_revid)
598
599
    def test_dir_to_root(self):
600
        # test a A->''.
601
        old_revid = 'old-parent'
602
        basis_shape = Inventory(root_id=None)
603
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
604
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
605
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
606
        new_revid = 'new-parent'
607
        new_shape = Inventory(root_id=None)
608
        self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
609
        self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
610
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
611
            new_shape, new_revid)
612
2865.1.1 by Robert Collins
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
613
    def test_path_swap(self):
614
        # test a A->B and B->A path swap.
615
        old_revid = 'old-parent'
616
        basis_shape = Inventory(root_id=None)
617
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
618
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
619
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
620
        self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
621
        self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
622
        self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
623
            '1' * 32, 12)
624
        self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
625
            '2' * 32, 24)
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_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
630
        self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
631
        self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
632
        self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
633
        self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
634
            '1' * 32, 12)
635
        self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
636
            '2' * 32, 24)
637
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
638
            new_shape, new_revid)
639
640
    def test_adds(self):
641
        # test adding paths and dirs, including adding to a newly added dir.
642
        old_revid = 'old-parent'
643
        basis_shape = Inventory(root_id=None)
644
        # with a root, so its a commit after the first.
645
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
646
        new_revid = 'new-parent'
647
        new_shape = Inventory(root_id=None)
648
        self.add_new_root(new_shape, old_revid, new_revid)
649
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
650
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
651
        self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
652
            '1' * 32, 12)
653
        self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
654
            '2' * 32, 24)
655
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
656
            new_shape, new_revid)
657
658
    def test_removes(self):
659
        # test removing paths, including paths that are within other also
660
        # removed paths.
661
        old_revid = 'old-parent'
662
        basis_shape = Inventory(root_id=None)
663
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
664
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
665
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
666
        self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
667
            '1' * 32, 12)
668
        self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
669
            '2' * 32, 24)
670
        new_revid = 'new-parent'
671
        new_shape = Inventory(root_id=None)
672
        self.add_new_root(new_shape, old_revid, new_revid)
673
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
674
            new_shape, new_revid)
675
676
    def test_move_to_added_dir(self):
677
        old_revid = 'old-parent'
678
        basis_shape = Inventory(root_id=None)
679
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
680
        self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
681
        new_revid = 'new-parent'
682
        new_shape = Inventory(root_id=None)
683
        self.add_new_root(new_shape, old_revid, new_revid)
684
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
685
        self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
686
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
687
            new_shape, new_revid)
688
689
    def test_move_from_removed_dir(self):
690
        old_revid = 'old-parent'
691
        basis_shape = Inventory(root_id=None)
692
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
693
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
694
        self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
695
        new_revid = 'new-parent'
696
        new_shape = Inventory(root_id=None)
697
        self.add_new_root(new_shape, old_revid, new_revid)
698
        self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
699
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
700
            new_shape, new_revid)
2929.2.1 by Robert Collins
* Commit updates the state of the working tree via a delta rather than
701
702
    def test_move_moves_children_recursively(self):
703
        old_revid = 'old-parent'
704
        basis_shape = Inventory(root_id=None)
705
        self.add_dir(basis_shape, old_revid, 'root-id', None, '')
706
        self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
707
        self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
708
        self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
709
        new_revid = 'new-parent'
710
        new_shape = Inventory(root_id=None)
711
        self.add_new_root(new_shape, old_revid, new_revid)
712
        # the moved path:
713
        self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
714
        # unmoved children.
715
        self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
716
        self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
717
        self.assertTransitionFromBasisToShape(basis_shape, old_revid,
718
            new_shape, new_revid)