237
247
first_revision = tree.commit('first post')
238
248
tree.add_parent_tree(('second', None))
239
249
self.assertConsistentParents([first_revision, 'second'], tree)
252
class UpdateToOneParentViaDeltaTests(TestParents):
253
"""Tests for the update_basis_by_delta call.
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.
261
def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
265
tree.update_basis_by_delta(revid, delta)
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
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)
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)
287
for file_id in deletes:
288
delta.append((old.id2path(file_id), None, file_id, None))
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]))
297
def fake_up_revision(self, tree, revid, shape):
300
tree.branch.repository.start_write_group()
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,
307
committer="Foo Bar <foo@example.com>",
311
tree.branch.repository.add_revision(revid, rev)
313
tree.branch.repository.abort_write_group()
316
tree.branch.repository.commit_write_group()
320
def add_entry(self, inv, rev_id, entry):
321
entry.revision = rev_id
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)
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)
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)
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, '')
343
self.add_dir(new_shape, new_revid, 'root-id', None, '')
345
def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
346
new_shape, new_revid, extra_parent=None):
347
# set the inventory revision ids.
348
basis_shape.revision_id = basis_revid
349
new_shape.revision_id = new_revid
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)
361
# give tree an inventory of new_shape
362
tree._write_inventory(new_shape)
363
self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
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.
369
osutils.rmtree('tree')
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,
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
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,
387
self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
388
self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
390
self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
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',
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',
404
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
405
new_shape, new_revid)
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',
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',
418
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
419
new_shape, new_revid)
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,
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:
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)
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',
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',
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',
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',
465
self.assertTransitionFromBasisToShape(basis_shape, left_revid,
466
new_shape, new_revid, right_revid)
468
def test_parent_id_changed(self):
469
# test that when the only change to an entry is its parent id changing
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)
484
def test_name_changed(self):
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)
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)
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)
517
def test_path_swap(self):
518
# test a A->B and B->A path swap.
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', 'root-id', 'B')
524
self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
525
self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
526
self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
528
self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
530
new_revid = 'new-parent'
531
new_shape = Inventory(root_id=None)
532
self.add_new_root(new_shape, old_revid, new_revid)
533
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
534
self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
535
self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
536
self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
537
self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
539
self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
541
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
542
new_shape, new_revid)
545
# test adding paths and dirs, including adding to a newly added dir.
546
old_revid = 'old-parent'
547
basis_shape = Inventory(root_id=None)
548
# with a root, so its a commit after the first.
549
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
550
new_revid = 'new-parent'
551
new_shape = Inventory(root_id=None)
552
self.add_new_root(new_shape, old_revid, new_revid)
553
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
554
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
555
self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
557
self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
559
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
560
new_shape, new_revid)
562
def test_removes(self):
563
# test removing paths, including paths that are within other also
565
old_revid = 'old-parent'
566
basis_shape = Inventory(root_id=None)
567
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
568
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
569
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
570
self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
572
self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
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.assertTransitionFromBasisToShape(basis_shape, old_revid,
578
new_shape, new_revid)
580
def test_move_to_added_dir(self):
581
old_revid = 'old-parent'
582
basis_shape = Inventory(root_id=None)
583
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
584
self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
585
new_revid = 'new-parent'
586
new_shape = Inventory(root_id=None)
587
self.add_new_root(new_shape, old_revid, new_revid)
588
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
589
self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
590
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
591
new_shape, new_revid)
593
def test_move_from_removed_dir(self):
594
old_revid = 'old-parent'
595
basis_shape = Inventory(root_id=None)
596
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
597
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
598
self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
599
new_revid = 'new-parent'
600
new_shape = Inventory(root_id=None)
601
self.add_new_root(new_shape, old_revid, new_revid)
602
self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
603
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
604
new_shape, new_revid)
606
def test_move_moves_children_recursively(self):
607
old_revid = 'old-parent'
608
basis_shape = Inventory(root_id=None)
609
self.add_dir(basis_shape, old_revid, 'root-id', None, '')
610
self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
611
self.add_dir(basis_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
612
self.add_link(basis_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
613
new_revid = 'new-parent'
614
new_shape = Inventory(root_id=None)
615
self.add_new_root(new_shape, old_revid, new_revid)
617
self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
619
self.add_dir(new_shape, old_revid, 'dir-id-B', 'dir-id-A', 'B')
620
self.add_link(new_shape, old_revid, 'link-id-C', 'dir-id-B', 'C', 'D')
621
self.assertTransitionFromBasisToShape(basis_shape, old_revid,
622
new_shape, new_revid)