218
311
first_revision = tree.commit('first post')
219
312
tree.add_parent_tree(('second', None))
220
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()
340
self.addCleanup(result_basis.unlock)
341
self.assertEqual(expected_inventory, result_basis.inventory)
343
def make_inv_delta(self, old, new):
344
"""Make an inventory delta from two inventories."""
345
old_ids = set(old._byid.iterkeys())
346
new_ids = set(new._byid.iterkeys())
347
adds = new_ids - old_ids
348
deletes = old_ids - new_ids
349
common = old_ids.intersection(new_ids)
351
for file_id in deletes:
352
delta.append((old.id2path(file_id), None, file_id, None))
354
delta.append((None, new.id2path(file_id), file_id, new[file_id]))
355
for file_id in common:
356
if old[file_id] != new[file_id]:
357
delta.append((old.id2path(file_id), new.id2path(file_id),
358
file_id, new[file_id]))
361
def fake_up_revision(self, tree, revid, shape):
364
tree.branch.repository.start_write_group()
366
if shape.root.revision is None:
367
shape.root.revision = revid
368
sha1 = tree.branch.repository.add_inventory(revid, shape, [])
369
rev = Revision(timestamp=0,
371
committer="Foo Bar <foo@example.com>",
375
tree.branch.repository.add_revision(revid, rev)
377
tree.branch.repository.abort_write_group()
380
tree.branch.repository.commit_write_group()
384
def add_entry(self, inv, rev_id, entry):
385
entry.revision = rev_id
388
def add_dir(self, inv, rev_id, file_id, parent_id, name):
389
new_dir = InventoryDirectory(file_id, name, parent_id)
390
self.add_entry(inv, rev_id, new_dir)
392
def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
393
new_file = InventoryFile(file_id, name, parent_id)
394
new_file.text_sha1 = sha
395
new_file.text_size = size
396
self.add_entry(inv, rev_id, new_file)
398
def add_link(self, inv, rev_id, file_id, parent_id, name, target):
399
new_link = InventoryLink(file_id, name, parent_id)
400
new_link.symlink_target = target
401
self.add_entry(inv, rev_id, new_link)
403
def add_new_root(self, new_shape, old_revid, new_revid):
404
if self.bzrdir_format.repository_format.rich_root_data:
405
self.add_dir(new_shape, old_revid, 'root-id', None, '')
407
self.add_dir(new_shape, new_revid, 'root-id', None, '')
409
def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
410
new_shape, new_revid, extra_parent=None):
411
# set the inventory revision ids.
412
basis_shape.revision_id = basis_revid
413
new_shape.revision_id = new_revid
414
delta = self.make_inv_delta(basis_shape, new_shape)
415
tree = self.make_branch_and_tree('tree')
416
# the shapes need to be in the tree's repository to be able to set them
417
# as a parent, but the file content is not needed.
418
if basis_revid is not None:
419
self.fake_up_revision(tree, basis_revid, basis_shape)
420
parents = [basis_revid]
421
if extra_parent is not None:
422
parents.append(extra_parent)
423
tree.set_parent_ids(parents)
424
self.fake_up_revision(tree, new_revid, new_shape)
425
# give tree an inventory of new_shape
426
tree._write_inventory(new_shape)
427
self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
429
# The tree should be internally consistent; while this is a moderately
430
# large hammer, this is a particularly sensitive area of code, so the
431
# extra assurance is well worth it.
433
osutils.rmtree('tree')
435
def test_no_parents_just_root(self):
436
"""Test doing an empty commit - no parent, set a root only."""
437
basis_shape = Inventory(root_id=None) # empty tree
438
new_shape = Inventory() # tree with a root
439
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
442
def test_no_parents_full_tree(self):
443
"""Test doing a regular initial commit with files and dirs."""
444
basis_shape = Inventory(root_id=None) # empty tree
446
new_shape = Inventory(root_id=None)
447
self.add_dir(new_shape, revid, 'root-id', None, '')
448
self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
449
self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
451
self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
452
self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
454
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
457
def test_file_content_change(self):
458
old_revid = 'old-parent'
459
basis_shape = Inventory(root_id=None)
460
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
461
self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
463
new_revid = 'new-parent'
464
new_shape = Inventory(root_id=None)
465
self.add_new_root(new_shape, old_revid, new_revid)
466
self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
468
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
469
new_shape, new_revid)
471
def test_link_content_change(self):
472
old_revid = 'old-parent'
473
basis_shape = Inventory(root_id=None)
474
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
475
self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
477
new_revid = 'new-parent'
478
new_shape = Inventory(root_id=None)
479
self.add_new_root(new_shape, old_revid, new_revid)
480
self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
482
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
483
new_shape, new_revid)
485
def test_kind_changes(self):
486
def do_file(inv, revid):
487
self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
489
def do_link(inv, revid):
490
self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
491
def do_dir(inv, revid):
492
self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
493
for old_factory in (do_file, do_link, do_dir):
494
for new_factory in (do_file, do_link, do_dir):
495
if old_factory == new_factory:
497
old_revid = 'old-parent'
498
basis_shape = Inventory(root_id=None)
499
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
500
old_factory(basis_shape, old_revid)
501
new_revid = 'new-parent'
502
new_shape = Inventory(root_id=None)
503
self.add_new_root(new_shape, old_revid, new_revid)
504
new_factory(new_shape, new_revid)
505
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
506
new_shape, new_revid)
508
def test_content_from_second_parent_is_dropped(self):
509
left_revid = 'left-parent'
510
basis_shape = Inventory(root_id=None)
511
self.add_dir(basis_shape, left_revid, 'root-id', None, '')
512
self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
514
# the right shape has content - file, link, subdir with a child,
515
# that should all be discarded by the call.
516
right_revid = 'right-parent'
517
right_shape = Inventory(root_id=None)
518
self.add_dir(right_shape, left_revid, 'root-id', None, '')
519
self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
521
self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
522
self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
524
new_revid = 'new-parent'
525
new_shape = Inventory(root_id=None)
526
self.add_new_root(new_shape, left_revid, new_revid)
527
self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
529
self.assertTransitionFromBasisToShape(basis_shape, left_revid,
530
new_shape, new_revid, right_revid)
532
def test_parent_id_changed(self):
533
# test that when the only change to an entry is its parent id changing
534
# that it is handled correctly (that is it keeps the same path)
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, 'orig-parent-id', 'root-id', 'dir')
539
self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
540
new_revid = 'new-parent'
541
new_shape = Inventory(root_id=None)
542
self.add_new_root(new_shape, old_revid, new_revid)
543
self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
544
self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
545
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
546
new_shape, new_revid)
548
def test_name_changed(self):
549
# test that when the only change to an entry is its name changing that
550
# it is handled correctly (that is it keeps the same parent id)
551
old_revid = 'old-parent'
552
basis_shape = Inventory(root_id=None)
553
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
554
self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
555
self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
556
new_revid = 'new-parent'
557
new_shape = Inventory(root_id=None)
558
self.add_new_root(new_shape, old_revid, new_revid)
559
self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
560
self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
561
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
562
new_shape, new_revid)
564
def test_parent_child_swap(self):
565
# test a A->A/B and A/B->A path swap.
566
old_revid = 'old-parent'
567
basis_shape = Inventory(root_id=None)
568
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
569
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
570
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
571
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
572
new_revid = 'new-parent'
573
new_shape = Inventory(root_id=None)
574
self.add_new_root(new_shape, old_revid, new_revid)
575
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
576
self.add_dir(new_shape, new_revid, 'dir-id-A', 'dir-id-B', 'B')
577
self.add_link(new_shape, new_revid, 'link-id-C', 'dir-id-A', 'C', 'C')
578
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
579
new_shape, new_revid)
581
def test_parent_deleted_child_renamed(self):
582
# test a A->None and A/B->A.
583
old_revid = 'old-parent'
584
basis_shape = Inventory(root_id=None)
585
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
586
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
587
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
588
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
589
new_revid = 'new-parent'
590
new_shape = Inventory(root_id=None)
591
self.add_new_root(new_shape, old_revid, new_revid)
592
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
593
self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'C')
594
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
595
new_shape, new_revid)
597
def test_dir_to_root(self):
599
old_revid = 'old-parent'
600
basis_shape = Inventory(root_id=None)
601
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
602
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
603
self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
604
new_revid = 'new-parent'
605
new_shape = Inventory(root_id=None)
606
self.add_dir(new_shape, new_revid, 'dir-id-A', None, '')
607
self.add_link(new_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'B')
608
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
609
new_shape, new_revid)
611
def test_path_swap(self):
612
# test a A->B and B->A path swap.
613
old_revid = 'old-parent'
614
basis_shape = Inventory(root_id=None)
615
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
616
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
617
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
618
self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
619
self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
620
self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
622
self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
624
new_revid = 'new-parent'
625
new_shape = Inventory(root_id=None)
626
self.add_new_root(new_shape, old_revid, new_revid)
627
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
628
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
629
self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
630
self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
631
self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
633
self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
635
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
636
new_shape, new_revid)
639
# test adding paths and dirs, including adding to a newly added dir.
640
old_revid = 'old-parent'
641
basis_shape = Inventory(root_id=None)
642
# with a root, so its a commit after the first.
643
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
644
new_revid = 'new-parent'
645
new_shape = Inventory(root_id=None)
646
self.add_new_root(new_shape, old_revid, new_revid)
647
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
648
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
649
self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
651
self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
653
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
654
new_shape, new_revid)
656
def test_removes(self):
657
# test removing paths, including paths that are within other also
659
old_revid = 'old-parent'
660
basis_shape = Inventory(root_id=None)
661
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
662
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
663
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
664
self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
666
self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
668
new_revid = 'new-parent'
669
new_shape = Inventory(root_id=None)
670
self.add_new_root(new_shape, old_revid, new_revid)
671
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
672
new_shape, new_revid)
674
def test_move_to_added_dir(self):
675
old_revid = 'old-parent'
676
basis_shape = Inventory(root_id=None)
677
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
678
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
679
new_revid = 'new-parent'
680
new_shape = Inventory(root_id=None)
681
self.add_new_root(new_shape, old_revid, new_revid)
682
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
683
self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
684
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
685
new_shape, new_revid)
687
def test_move_from_removed_dir(self):
688
old_revid = 'old-parent'
689
basis_shape = Inventory(root_id=None)
690
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
691
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
692
self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
693
new_revid = 'new-parent'
694
new_shape = Inventory(root_id=None)
695
self.add_new_root(new_shape, old_revid, new_revid)
696
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
697
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
698
new_shape, new_revid)
700
def test_move_moves_children_recursively(self):
701
old_revid = 'old-parent'
702
basis_shape = Inventory(root_id=None)
703
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
704
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
705
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
706
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
707
new_revid = 'new-parent'
708
new_shape = Inventory(root_id=None)
709
self.add_new_root(new_shape, old_revid, new_revid)
711
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
713
self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
714
self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
715
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
716
new_shape, new_revid)