1
# Copyright (C) 2006 Canonical Ltd
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.
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.
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
17
"""Tests of the parent related functions of WorkingTrees."""
19
from errno import EEXIST
25
revision as _mod_revision,
28
from bzrlib.inventory import (
34
from bzrlib.revision import Revision
35
from bzrlib.tests import SymlinkFeature, TestNotApplicable
36
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
37
from bzrlib.uncommit import uncommit
40
class TestParents(TestCaseWithWorkingTree):
42
def assertConsistentParents(self, expected, tree):
43
"""Check that the parents found are as expected.
45
This test helper also checks that they are consistent with
46
the pre-get_parent_ids() api - which is now deprecated.
48
self.assertEqual(expected, tree.get_parent_ids())
50
self.assertEqual(_mod_revision.NULL_REVISION,
51
_mod_revision.ensure_null(tree.last_revision()))
53
self.assertEqual(expected[0], tree.last_revision())
56
class TestGetParents(TestParents):
58
def test_get_parents(self):
59
t = self.make_branch_and_tree('.')
60
self.assertEqual([], t.get_parent_ids())
63
class TestSetParents(TestParents):
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([])
72
self.assertConsistentParents([], t)
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)
81
def test_set_one_ghost_parent_rejects(self):
82
t = self.make_branch_and_tree('.')
83
self.assertRaises(errors.GhostRevisionUnusableHere,
84
t.set_parent_trees, [('missing-revision-id', None)])
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)
90
self.assertConsistentParents(['missing-revision-id'], t)
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)])
100
self.assertConsistentParents([revision_in_repo, 'another-missing'], t)
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)])
116
self.assertConsistentParents(
117
[first_revision, second_revision, third_revision], t)
119
def test_set_no_parents_ids(self):
120
t = self.make_branch_and_tree('.')
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')
126
self.assertConsistentParents([], t)
128
def test_set_one_ghost_parent_ids_rejects(self):
129
t = self.make_branch_and_tree('.')
130
self.assertRaises(errors.GhostRevisionUnusableHere,
131
t.set_parent_ids, ['missing-revision-id'])
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)
137
self.assertConsistentParents(['missing-revision-id'], t)
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)
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)
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)
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)
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
205
t.set_parent_ids([rev2, rev3, rev1])
206
self.assertConsistentParents([rev2, rev3], t)
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),
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
226
t.set_parent_trees([(rev2, rev_tree2), (rev1, rev_tree1),
228
self.assertConsistentParents([rev2, rev3], t)
231
class TestAddParent(TestParents):
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)
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('.')
244
self.assertRaises(errors.GhostRevisionUnusableHere,
245
tree.add_parent_tree_id, 'first-revision')
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)
251
self.assertConsistentParents(['first-revision'], tree)
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)
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)
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)
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)
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('.')
288
self.assertRaises(errors.GhostRevisionUnusableHere,
289
tree.add_parent_tree, ('first-revision', None))
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)
296
self.assertConsistentParents(['first-revision'], tree)
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)
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)
316
class UpdateToOneParentViaDeltaTests(TestCaseWithWorkingTree):
317
"""Tests for the update_basis_by_delta call.
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.
325
def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
329
tree.update_basis_by_delta(revid, delta)
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
338
result_basis = tree.basis_tree()
339
result_basis.lock_read()
341
self.assertEqual(expected_inventory, result_basis.inventory)
343
result_basis.unlock()
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)
353
for file_id in deletes:
354
delta.append((old.id2path(file_id), None, file_id, None))
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]))
363
def fake_up_revision(self, tree, revid, shape):
366
tree.branch.repository.start_write_group()
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,
373
committer="Foo Bar <foo@example.com>",
377
tree.branch.repository.add_revision(revid, rev)
379
tree.branch.repository.abort_write_group()
382
tree.branch.repository.commit_write_group()
386
def add_entry(self, inv, rev_id, entry):
387
entry.revision = rev_id
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)
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)
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)
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, '')
409
self.add_dir(new_shape, new_revid, 'root-id', None, '')
411
def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
412
new_shape, new_revid, extra_parent=None):
413
# set the inventory revision ids.
414
basis_shape.revision_id = basis_revid
415
new_shape.revision_id = new_revid
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)
427
# give tree an inventory of new_shape
428
tree._write_inventory(new_shape)
429
self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
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.
435
osutils.rmtree('tree')
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,
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
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,
453
self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
454
self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
456
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
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',
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',
470
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
471
new_shape, new_revid)
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',
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',
484
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
485
new_shape, new_revid)
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,
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:
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)
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',
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',
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',
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',
531
self.assertTransitionFromBasisToShape(basis_shape, left_revid,
532
new_shape, new_revid, right_revid)
534
def test_parent_id_changed(self):
535
# test that when the only change to an entry is its parent id changing
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)
550
def test_name_changed(self):
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)
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)
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)
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)
599
def test_dir_to_root(self):
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)
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',
624
self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
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',
635
self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
637
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
638
new_shape, new_revid)
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',
653
self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
655
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
656
new_shape, new_revid)
658
def test_removes(self):
659
# test removing paths, including paths that are within other also
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',
668
self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
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)
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)
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)
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)
713
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
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)