215
290
tree1 = self.make_branch_and_tree('1')
216
291
tree2 = self.make_to_branch_and_tree('2')
217
292
tree1 = self.get_tree_no_parents_no_content(tree1)
218
tree2 = self.get_to_tree_no_parents_abc_content(tree2)
293
tree2 = self.get_tree_no_parents_abc_content(tree2)
294
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
219
295
self.assertRaises(errors.PathsNotVersionedError,
220
296
self.intertree_class(tree1, tree2).compare,
221
297
specific_files=['d'],
222
298
require_versioned=True)
300
def test_default_ignores_unversioned_files(self):
301
tree1 = self.make_branch_and_tree('tree1')
302
tree2 = self.make_to_branch_and_tree('tree2')
303
tree2.set_root_id(tree1.get_root_id())
304
self.build_tree(['tree1/a', 'tree1/c',
305
'tree2/a', 'tree2/b', 'tree2/c'])
306
tree1.add(['a', 'c'], ['a-id', 'c-id'])
307
tree2.add(['a', 'c'], ['a-id', 'c-id'])
309
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
310
d = self.intertree_class(tree1, tree2).compare()
311
self.assertEqual([], d.added)
312
self.assertEqual([(u'a', 'a-id', 'file', True, False),
313
(u'c', 'c-id', 'file', True, False)], d.modified)
314
self.assertEqual([], d.removed)
315
self.assertEqual([], d.renamed)
316
self.assertEqual([], d.unchanged)
317
self.assertEqual([], d.unversioned)
319
def test_unversioned_paths_in_tree(self):
320
tree1 = self.make_branch_and_tree('tree1')
321
tree2 = self.make_to_branch_and_tree('tree2')
322
tree2.set_root_id(tree1.get_root_id())
323
self.build_tree(['tree2/file', 'tree2/dir/'])
325
os.symlink('target', 'tree2/link')
326
links_supported = True
328
links_supported = False
329
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
330
d = self.intertree_class(tree1, tree2).compare(want_unversioned=True)
331
self.assertEqual([], d.added)
332
self.assertEqual([], d.modified)
333
self.assertEqual([], d.removed)
334
self.assertEqual([], d.renamed)
335
self.assertEqual([], d.unchanged)
336
expected_unversioned = [(u'dir', None, 'directory'),
337
(u'file', None, 'file')]
339
expected_unversioned.append((u'link', None, 'symlink'))
340
self.assertEqual(expected_unversioned, d.unversioned)
343
class TestIterChanges(TestCaseWithTwoTrees):
344
"""Test the comparison iterator"""
346
def do_iter_changes(self, tree1, tree2, **extra_args):
347
"""Helper to run _iter_changes from tree1 to tree2.
349
:param tree1, tree2: The source and target trees. These will be locked
351
:param **extra_args: Extra args to pass to _iter_changes. This is not
352
inspected by this test helper.
357
# sort order of output is not strictly defined
358
return sorted(self.intertree_class(tree1, tree2)
359
._iter_changes(**extra_args))
364
def mutable_trees_to_locked_test_trees(self, tree1, tree2):
365
"""Convert the working trees into test trees.
367
Read lock them, and add the unlock to the cleanup.
369
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
371
self.addCleanup(tree1.unlock)
373
self.addCleanup(tree2.unlock)
376
def make_tree_with_special_names(self):
377
"""Create a tree with filenames chosen to exercise the walk order."""
378
tree1 = self.make_branch_and_tree('tree1')
379
tree2 = self.make_to_branch_and_tree('tree2')
380
tree2.set_root_id(tree1.get_root_id())
381
paths, path_ids = self._create_special_names(tree2, 'tree2')
382
tree2.commit('initial', rev_id='rev-1')
383
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
384
return (tree1, tree2, paths, path_ids)
386
def make_trees_with_special_names(self):
387
"""Both trees will use the special names.
389
But the contents will differ for each file.
391
tree1 = self.make_branch_and_tree('tree1')
392
tree2 = self.make_to_branch_and_tree('tree2')
393
tree2.set_root_id(tree1.get_root_id())
394
paths, path_ids = self._create_special_names(tree1, 'tree1')
395
paths, path_ids = self._create_special_names(tree2, 'tree2')
396
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
397
return (tree1, tree2, paths, path_ids)
399
def _create_special_names(self, tree, base_path):
400
"""Create a tree with paths that expose differences in sort orders."""
401
# Each directory will have a single file named 'f' inside
416
with_slashes.append(base_path + '/' + d + '/')
417
with_slashes.append(base_path + '/' + d + '/f')
420
path_ids.append(d.replace('/', '_') + '-id')
421
path_ids.append(d.replace('/', '_') + '_f-id')
422
self.build_tree(with_slashes)
423
tree.add(paths, path_ids)
424
return paths, path_ids
426
def test_compare_empty_trees(self):
427
tree1 = self.make_branch_and_tree('1')
428
tree2 = self.make_to_branch_and_tree('2')
429
tree1 = self.get_tree_no_parents_no_content(tree1)
430
tree2 = self.get_tree_no_parents_no_content(tree2)
431
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
432
self.assertEqual([], self.do_iter_changes(tree1, tree2))
434
def added(self, tree, file_id):
435
entry = tree.inventory[file_id]
436
path = tree.id2path(file_id)
437
return (file_id, (None, path), True, (False, True), (None, entry.parent_id),
438
(None, entry.name), (None, entry.kind),
439
(None, entry.executable))
441
def content_changed(self, tree, file_id):
442
entry = tree.inventory[file_id]
443
path = tree.id2path(file_id)
444
return (file_id, (path, path), True, (True, True), (entry.parent_id, entry.parent_id),
445
(entry.name, entry.name), (entry.kind, entry.kind),
446
(entry.executable, entry.executable))
448
def kind_changed(self, from_tree, to_tree, file_id):
449
old_entry = from_tree.inventory[file_id]
450
new_entry = to_tree.inventory[file_id]
451
path = to_tree.id2path(file_id)
452
from_path = from_tree.id2path(file_id)
453
return (file_id, (from_path, path), True, (True, True), (old_entry.parent_id, new_entry.parent_id),
454
(old_entry.name, new_entry.name), (old_entry.kind, new_entry.kind),
455
(old_entry.executable, new_entry.executable))
457
def missing(self, file_id, from_path, to_path, parent_id, kind):
458
_, from_basename = os.path.split(from_path)
459
_, to_basename = os.path.split(to_path)
460
# missing files have both paths, but no kind.
461
return (file_id, (from_path, to_path), True, (True, True),
462
(parent_id, parent_id),
463
(from_basename, to_basename), (kind, None), (False, False))
465
def deleted(self, tree, file_id):
466
entry = tree.inventory[file_id]
467
path = tree.id2path(file_id)
468
return (file_id, (path, None), True, (True, False), (entry.parent_id, None),
469
(entry.name, None), (entry.kind, None),
470
(entry.executable, None))
472
def renamed(self, from_tree, to_tree, file_id, content_changed):
473
from_entry = from_tree.inventory[file_id]
474
to_entry = to_tree.inventory[file_id]
475
from_path = from_tree.id2path(file_id)
476
to_path = to_tree.id2path(file_id)
477
return (file_id, (from_path, to_path), content_changed, (True, True),
478
(from_entry.parent_id, to_entry.parent_id),
479
(from_entry.name, to_entry.name),
480
(from_entry.kind, to_entry.kind),
481
(from_entry.executable, to_entry.executable))
483
def unchanged(self, tree, file_id):
484
entry = tree.inventory[file_id]
485
parent = entry.parent_id
488
executable = entry.executable
489
path = tree.id2path(file_id)
490
return (file_id, (path, path), False, (True, True),
491
(parent, parent), (name, name), (kind, kind),
492
(executable, executable))
494
def unversioned(self, tree, path):
495
"""Create an unversioned result."""
496
_, basename = os.path.split(path)
497
kind = file_kind(tree.abspath(path))
498
return (None, (None, path), True, (False, False), (None, None),
499
(None, basename), (None, kind),
502
def test_empty_to_abc_content(self):
503
tree1 = self.make_branch_and_tree('1')
504
tree2 = self.make_to_branch_and_tree('2')
505
tree1 = self.get_tree_no_parents_no_content(tree1)
506
tree2 = self.get_tree_no_parents_abc_content(tree2)
507
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
508
expected_results = sorted([
509
self.added(tree2, 'root-id'),
510
self.added(tree2, 'a-id'),
511
self.added(tree2, 'b-id'),
512
self.added(tree2, 'c-id'),
513
self.deleted(tree1, 'empty-root-id')])
514
self.assertEqual(expected_results, self.do_iter_changes(tree1, tree2))
516
def test_empty_to_abc_content_a_only(self):
517
tree1 = self.make_branch_and_tree('1')
518
tree2 = self.make_to_branch_and_tree('2')
519
tree1 = self.get_tree_no_parents_no_content(tree1)
520
tree2 = self.get_tree_no_parents_abc_content(tree2)
521
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
523
[self.added(tree2, 'a-id')],
524
self.do_iter_changes(tree1, tree2, specific_files=['a']))
526
def test_abc_content_to_empty_to_abc_content_a_only(self):
527
tree1 = self.make_branch_and_tree('1')
528
tree2 = self.make_to_branch_and_tree('2')
529
tree1 = self.get_tree_no_parents_abc_content(tree1)
530
tree2 = self.get_tree_no_parents_no_content(tree2)
531
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
533
[self.deleted(tree1, 'a-id')],
534
self.do_iter_changes(tree1, tree2, specific_files=['a']))
536
def test_empty_to_abc_content_a_and_c_only(self):
537
tree1 = self.make_branch_and_tree('1')
538
tree2 = self.make_to_branch_and_tree('2')
539
tree1 = self.get_tree_no_parents_no_content(tree1)
540
tree2 = self.get_tree_no_parents_abc_content(tree2)
541
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
542
expected_result = [self.added(tree2, 'a-id'), self.added(tree2, 'c-id')]
543
self.assertEqual(expected_result,
544
self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
546
def test_abc_content_to_empty(self):
547
tree1 = self.make_branch_and_tree('1')
548
tree2 = self.make_to_branch_and_tree('2')
549
tree1 = self.get_tree_no_parents_abc_content(tree1)
550
tree2 = self.get_tree_no_parents_no_content(tree2)
551
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
552
def deleted(file_id):
553
entry = tree1.inventory[file_id]
554
path = tree1.id2path(file_id)
555
return (file_id, (path, None), True, (True, False),
556
(entry.parent_id, None),
557
(entry.name, None), (entry.kind, None),
558
(entry.executable, None))
559
expected_results = sorted([
560
self.added(tree2, 'empty-root-id'),
561
deleted('root-id'), deleted('a-id'),
562
deleted('b-id'), deleted('c-id')])
565
self.do_iter_changes(tree1, tree2))
567
def test_content_modification(self):
568
tree1 = self.make_branch_and_tree('1')
569
tree2 = self.make_to_branch_and_tree('2')
570
tree1 = self.get_tree_no_parents_abc_content(tree1)
571
tree2 = self.get_tree_no_parents_abc_content_2(tree2)
572
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
573
root_id = tree1.path2id('')
574
self.assertEqual([('a-id', ('a', 'a'), True, (True, True),
575
(root_id, root_id), ('a', 'a'),
576
('file', 'file'), (False, False))],
577
self.do_iter_changes(tree1, tree2))
579
def test_meta_modification(self):
580
tree1 = self.make_branch_and_tree('1')
581
tree2 = self.make_to_branch_and_tree('2')
582
tree1 = self.get_tree_no_parents_abc_content(tree1)
583
tree2 = self.get_tree_no_parents_abc_content_3(tree2)
584
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
585
self.assertEqual([('c-id', ('b/c', 'b/c'), False, (True, True),
586
('b-id', 'b-id'), ('c', 'c'), ('file', 'file'),
588
self.do_iter_changes(tree1, tree2))
590
def test_empty_dir(self):
591
"""an empty dir should not cause glitches to surrounding files."""
592
tree1 = self.make_branch_and_tree('1')
593
tree2 = self.make_to_branch_and_tree('2')
594
tree1 = self.get_tree_no_parents_abc_content(tree1)
595
tree2 = self.get_tree_no_parents_abc_content(tree2)
596
# the pathname is chosen to fall between 'a' and 'b'.
597
self.build_tree(['1/a-empty/', '2/a-empty/'])
598
tree1.add(['a-empty'], ['a-empty'])
599
tree2.add(['a-empty'], ['a-empty'])
600
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
602
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
604
def test_file_rename(self):
605
tree1 = self.make_branch_and_tree('1')
606
tree2 = self.make_to_branch_and_tree('2')
607
tree1 = self.get_tree_no_parents_abc_content(tree1)
608
tree2 = self.get_tree_no_parents_abc_content_4(tree2)
609
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
610
root_id = tree1.path2id('')
611
self.assertEqual([('a-id', ('a', 'd'), False, (True, True),
612
(root_id, root_id), ('a', 'd'), ('file', 'file'),
614
self.do_iter_changes(tree1, tree2))
616
def test_file_rename_and_modification(self):
617
tree1 = self.make_branch_and_tree('1')
618
tree2 = self.make_to_branch_and_tree('2')
619
tree1 = self.get_tree_no_parents_abc_content(tree1)
620
tree2 = self.get_tree_no_parents_abc_content_5(tree2)
621
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
622
root_id = tree1.path2id('')
623
self.assertEqual([('a-id', ('a', 'd'), True, (True, True),
624
(root_id, root_id), ('a', 'd'), ('file', 'file'),
626
self.do_iter_changes(tree1, tree2))
628
def test_file_rename_and_meta_modification(self):
629
tree1 = self.make_branch_and_tree('1')
630
tree2 = self.make_to_branch_and_tree('2')
631
tree1 = self.get_tree_no_parents_abc_content(tree1)
632
tree2 = self.get_tree_no_parents_abc_content_6(tree2)
633
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
634
root_id = tree1.path2id('')
635
self.assertEqual([('c-id', ('b/c', 'e'), False, (True, True),
636
('b-id', root_id), ('c', 'e'), ('file', 'file'),
638
self.do_iter_changes(tree1, tree2))
640
def test_missing_in_target(self):
641
"""Test with the target files versioned but absent from disk."""
642
tree1 = self.make_branch_and_tree('1')
643
tree2 = self.make_to_branch_and_tree('2')
644
tree1 = self.get_tree_no_parents_abc_content(tree1)
645
tree2 = self.get_tree_no_parents_abc_content(tree2)
648
# TODO ? have a symlink here?
649
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
650
root_id = tree1.path2id('')
652
self.missing('a-id', 'a', 'a', root_id, 'file'),
653
self.missing('b-id', 'b', 'b', root_id, 'directory'),
654
self.missing('c-id', 'b/c', 'b/c', 'b-id', 'file'),
656
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
658
def test_missing_and_renamed(self):
659
tree1 = self.make_branch_and_tree('tree1')
660
tree2 = self.make_to_branch_and_tree('tree2')
661
tree2.set_root_id(tree1.get_root_id())
662
self.build_tree(['tree1/file'])
663
tree1.add(['file'], ['file-id'])
664
self.build_tree(['tree2/directory/'])
665
tree2.add(['directory'], ['file-id'])
666
os.rmdir('tree2/directory')
667
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
669
root_id = tree1.path2id('')
671
self.missing('file-id', 'file', 'directory', root_id, 'file'),
673
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
675
def test_unchanged_with_renames_and_modifications(self):
676
"""want_unchanged should generate a list of unchanged entries."""
677
tree1 = self.make_branch_and_tree('1')
678
tree2 = self.make_to_branch_and_tree('2')
679
tree1 = self.get_tree_no_parents_abc_content(tree1)
680
tree2 = self.get_tree_no_parents_abc_content_5(tree2)
681
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
682
root_id = tree1.path2id('')
684
self.assertEqual(sorted([self.unchanged(tree1, root_id),
685
self.unchanged(tree1, 'b-id'),
686
('a-id', ('a', 'd'), True, (True, True),
687
(root_id, root_id), ('a', 'd'), ('file', 'file'),
688
(False, False)), self.unchanged(tree1, 'c-id')]),
689
self.do_iter_changes(tree1, tree2, include_unchanged=True))
691
def test_compare_subtrees(self):
692
tree1 = self.make_branch_and_tree('1')
693
if not tree1.supports_tree_reference():
695
tree1.set_root_id('root-id')
696
subtree1 = self.make_branch_and_tree('1/sub')
697
subtree1.set_root_id('subtree-id')
698
tree1.add_reference(subtree1)
700
tree2 = self.make_to_branch_and_tree('2')
701
if not tree2.supports_tree_reference():
703
tree2.set_root_id('root-id')
704
subtree2 = self.make_to_branch_and_tree('2/sub')
705
subtree2.set_root_id('subtree-id')
706
tree2.add_reference(subtree2)
707
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
709
self.assertEqual([], list(tree2._iter_changes(tree1)))
710
subtree1.commit('commit', rev_id='commit-a')
718
('directory', 'directory'),
724
('root-id', 'root-id'),
726
('tree-reference', 'tree-reference'),
728
list(tree2._iter_changes(tree1,
729
include_unchanged=True)))
731
def test_disk_in_subtrees_skipped(self):
732
"""subtrees are considered not-in-the-current-tree.
734
This test tests the trivial case, where the basis has no paths in the
735
current trees subtree.
737
tree1 = self.make_branch_and_tree('1')
738
tree1.set_root_id('root-id')
739
tree2 = self.make_to_branch_and_tree('2')
740
if not tree2.supports_tree_reference():
742
tree2.set_root_id('root-id')
743
subtree2 = self.make_to_branch_and_tree('2/sub')
744
subtree2.set_root_id('subtree-id')
745
tree2.add(['sub'], ['subtree-id'])
746
self.build_tree(['2/sub/file'])
747
subtree2.add(['file'])
749
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
750
# this should filter correctly from above
751
self.assertEqual([self.added(tree2, 'subtree-id')],
752
self.do_iter_changes(tree1, tree2, want_unversioned=True))
753
# and when the path is named
754
self.assertEqual([self.added(tree2, 'subtree-id')],
755
self.do_iter_changes(tree1, tree2, specific_files=['sub'],
756
want_unversioned=True))
758
def test_default_ignores_unversioned_files(self):
759
tree1 = self.make_branch_and_tree('tree1')
760
tree2 = self.make_to_branch_and_tree('tree2')
761
tree2.set_root_id(tree1.get_root_id())
762
self.build_tree(['tree1/a', 'tree1/c',
763
'tree2/a', 'tree2/b', 'tree2/c'])
764
tree1.add(['a', 'c'], ['a-id', 'c-id'])
765
tree2.add(['a', 'c'], ['a-id', 'c-id'])
767
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
769
# We should ignore the fact that 'b' exists in tree-2
770
# because the want_unversioned parameter was not given.
772
self.content_changed(tree2, 'a-id'),
773
self.content_changed(tree2, 'c-id'),
775
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
777
def test_unversioned_paths_in_tree(self):
778
tree1 = self.make_branch_and_tree('tree1')
779
tree2 = self.make_to_branch_and_tree('tree2')
780
tree2.set_root_id(tree1.get_root_id())
781
self.build_tree(['tree2/file', 'tree2/dir/'])
783
os.symlink('target', 'tree2/link')
784
links_supported = True
786
links_supported = False
787
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
789
self.unversioned(tree2, 'file'),
790
self.unversioned(tree2, 'dir'),
793
expected.append(self.unversioned(tree2, 'link'))
794
expected = sorted(expected)
795
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
796
want_unversioned=True))
798
def test_unversioned_paths_in_tree_specific_files(self):
799
tree1 = self.make_branch_and_tree('tree1')
800
tree2 = self.make_to_branch_and_tree('tree2')
801
self.build_tree(['tree2/file', 'tree2/dir/'])
803
os.symlink('target', 'tree2/link')
804
links_supported = True
806
links_supported = False
807
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
809
self.unversioned(tree2, 'file'),
810
self.unversioned(tree2, 'dir'),
812
specific_files=['file', 'dir']
814
expected.append(self.unversioned(tree2, 'link'))
815
specific_files.append('link')
816
expected = sorted(expected)
817
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
818
specific_files=specific_files, require_versioned=False,
819
want_unversioned=True))
821
def test_unversioned_paths_in_target_matching_source_old_names(self):
822
# its likely that naive implementations of unversioned file support
823
# will fail if the path was versioned, but is not any more,
824
# due to a rename, not due to unversioning it.
825
# That is, if the old tree has a versioned file 'foo', and
826
# the new tree has the same file but versioned as 'bar', and also
827
# has an unknown file 'foo', we should get back output for
829
tree1 = self.make_branch_and_tree('tree1')
830
tree2 = self.make_to_branch_and_tree('tree2')
831
tree2.set_root_id(tree1.get_root_id())
832
self.build_tree(['tree2/file', 'tree2/dir/',
833
'tree1/file', 'tree2/movedfile',
834
'tree1/dir/', 'tree2/moveddir/'])
836
os.symlink('target', 'tree1/link')
837
os.symlink('target', 'tree2/link')
838
os.symlink('target', 'tree2/movedlink')
839
links_supported = True
841
links_supported = False
842
tree1.add(['file', 'dir'], ['file-id', 'dir-id'])
843
tree2.add(['movedfile', 'moveddir'], ['file-id', 'dir-id'])
845
tree1.add(['link'], ['link-id'])
846
tree2.add(['movedlink'], ['link-id'])
847
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
848
root_id = tree1.path2id('')
850
self.renamed(tree1, tree2, 'dir-id', False),
851
self.renamed(tree1, tree2, 'file-id', True),
852
self.unversioned(tree2, 'file'),
853
self.unversioned(tree2, 'dir'),
855
specific_files=['file', 'dir']
857
expected.append(self.renamed(tree1, tree2, 'link-id', False))
858
expected.append(self.unversioned(tree2, 'link'))
859
specific_files.append('link')
860
expected = sorted(expected)
861
# run once with, and once without specific files, to catch
862
# potentially different code paths.
863
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
864
require_versioned=False,
865
want_unversioned=True))
866
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
867
specific_files=specific_files, require_versioned=False,
868
want_unversioned=True))
870
def test_similar_filenames(self):
871
"""Test when we have a few files with similar names."""
872
tree1 = self.make_branch_and_tree('tree1')
873
tree2 = self.make_branch_and_tree('tree2')
874
tree2.set_root_id(tree1.get_root_id())
876
# The trees are actually identical, but they happen to contain
877
# similarly named files.
878
self.build_tree(['tree1/a/',
891
tree1.add(['a', 'a/b', 'a/b/c', 'a/b/c/d', 'a-c', 'a-c/e'],
892
['a-id', 'b-id', 'c-id', 'd-id', 'a-c-id', 'e-id'])
893
tree2.add(['a', 'a/b', 'a/b/c', 'a/b/c/d', 'a-c', 'a-c/e'],
894
['a-id', 'b-id', 'c-id', 'd-id', 'a-c-id', 'e-id'])
896
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
898
self.assertEqual([], self.do_iter_changes(tree1, tree2,
899
want_unversioned=True))
901
self.unchanged(tree2, tree2.get_root_id()),
902
self.unchanged(tree2, 'a-id'),
903
self.unchanged(tree2, 'b-id'),
904
self.unchanged(tree2, 'c-id'),
905
self.unchanged(tree2, 'd-id'),
906
self.unchanged(tree2, 'a-c-id'),
907
self.unchanged(tree2, 'e-id'),
909
self.assertEqual(expected,
910
self.do_iter_changes(tree1, tree2,
911
want_unversioned=True,
912
include_unchanged=True))
915
def test_unversioned_subtree_only_emits_root(self):
916
tree1 = self.make_branch_and_tree('tree1')
917
tree2 = self.make_to_branch_and_tree('tree2')
918
tree2.set_root_id(tree1.get_root_id())
919
self.build_tree(['tree2/dir/', 'tree2/dir/file'])
920
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
922
self.unversioned(tree2, 'dir'),
924
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
925
want_unversioned=True))
927
def make_trees_with_symlinks(self):
928
tree1 = self.make_branch_and_tree('tree1')
929
tree2 = self.make_to_branch_and_tree('tree2')
930
tree2.set_root_id(tree1.get_root_id())
931
self.build_tree(['tree1/fromfile', 'tree1/fromdir/'])
932
self.build_tree(['tree2/tofile', 'tree2/todir/', 'tree2/unknown'])
934
os.symlink('original', 'tree1/changed')
935
os.symlink('original', 'tree1/removed')
936
os.symlink('original', 'tree1/tofile')
937
os.symlink('original', 'tree1/todir')
938
# we make the unchanged link point at unknown to catch incorrect
939
# symlink-following code in the specified_files test.
940
os.symlink('unknown', 'tree1/unchanged')
941
os.symlink('new', 'tree2/added')
942
os.symlink('new', 'tree2/changed')
943
os.symlink('new', 'tree2/fromfile')
944
os.symlink('new', 'tree2/fromdir')
945
os.symlink('unknown', 'tree2/unchanged')
946
from_paths_and_ids = [
964
tree1.add(from_paths_and_ids, from_paths_and_ids)
965
tree2.add(to_paths_and_ids, to_paths_and_ids)
967
# raise TestSkipped('OS does not support symlinks')
968
# links_supported = False
969
return self.mutable_trees_to_locked_test_trees(tree1, tree2)
971
def test_versioned_symlinks(self):
972
if not has_symlinks():
973
raise tests.TestSkipped("No symlink support")
974
tree1, tree2 = self.make_trees_with_symlinks()
975
root_id = tree1.path2id('')
977
self.unchanged(tree1, tree1.path2id('')),
978
self.added(tree2, 'added'),
979
self.content_changed(tree2, 'changed'),
980
self.kind_changed(tree1, tree2, 'fromdir'),
981
self.kind_changed(tree1, tree2, 'fromfile'),
982
self.deleted(tree1, 'removed'),
983
self.unchanged(tree2, 'unchanged'),
984
self.unversioned(tree2, 'unknown'),
985
self.kind_changed(tree1, tree2, 'todir'),
986
self.kind_changed(tree1, tree2, 'tofile'),
988
expected = sorted(expected)
989
self.assertEqual(expected,
990
self.do_iter_changes(tree1, tree2, include_unchanged=True,
991
want_unversioned=True))
993
def test_versioned_symlinks_specific_files(self):
994
if not has_symlinks():
995
raise tests.TestSkipped("No symlink support")
996
tree1, tree2 = self.make_trees_with_symlinks()
997
root_id = tree1.path2id('')
999
self.added(tree2, 'added'),
1000
self.content_changed(tree2, 'changed'),
1001
self.kind_changed(tree1, tree2, 'fromdir'),
1002
self.kind_changed(tree1, tree2, 'fromfile'),
1003
self.deleted(tree1, 'removed'),
1004
self.kind_changed(tree1, tree2, 'todir'),
1005
self.kind_changed(tree1, tree2, 'tofile'),
1007
expected = sorted(expected)
1008
# we should get back just the changed links. We pass in 'unchanged' to
1009
# make sure that it is correctly not returned - and neither is the
1010
# unknown path 'unknown' which it points at.
1011
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1012
specific_files=['added', 'changed', 'fromdir', 'fromfile',
1013
'removed', 'unchanged', 'todir', 'tofile']))
1015
def test_tree_with_special_names(self):
1016
tree1, tree2, paths, path_ids = self.make_tree_with_special_names()
1017
expected = sorted(self.added(tree2, f_id) for f_id in path_ids)
1018
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1020
def test_trees_with_special_names(self):
1021
tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
1022
expected = sorted(self.content_changed(tree2, f_id) for f_id in path_ids
1023
if f_id.endswith('_f-id'))
1024
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1026
def test_trees_with_deleted_dir(self):
1027
tree1 = self.make_branch_and_tree('tree1')
1028
tree2 = self.make_to_branch_and_tree('tree2')
1029
tree2.set_root_id(tree1.get_root_id())
1030
self.build_tree(['tree1/a', 'tree1/b/', 'tree1/b/c',
1031
'tree1/b/d/', 'tree1/b/d/e', 'tree1/f/', 'tree1/f/g',
1032
'tree2/a', 'tree2/f/', 'tree2/f/g'])
1033
tree1.add(['a', 'b', 'b/c', 'b/d/', 'b/d/e', 'f', 'f/g'],
1034
['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'f-id', 'g-id'])
1035
tree2.add(['a', 'f', 'f/g'], ['a-id', 'f-id', 'g-id'])
1037
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1038
# We should notice that 'b' and all its children are deleted
1040
self.content_changed(tree2, 'a-id'),
1041
self.content_changed(tree2, 'g-id'),
1042
self.deleted(tree1, 'b-id'),
1043
self.deleted(tree1, 'c-id'),
1044
self.deleted(tree1, 'd-id'),
1045
self.deleted(tree1, 'e-id'),
1047
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1049
def test_added_unicode(self):
1050
tree1 = self.make_branch_and_tree('tree1')
1051
tree2 = self.make_to_branch_and_tree('tree2')
1052
root_id = tree1.get_root_id()
1053
tree2.set_root_id(root_id)
1055
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1056
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1057
a_id = u'\u03b1-id'.encode('utf8')
1058
added_id = u'\u03c9_added_id'.encode('utf8')
1060
self.build_tree([u'tree1/\u03b1/',
1062
u'tree2/\u03b1/\u03c9-added',
1064
except UnicodeError:
1065
raise tests.TestSkipped("Could not create Unicode files.")
1066
tree1.add([u'\u03b1'], [a_id])
1067
tree2.add([u'\u03b1', u'\u03b1/\u03c9-added'], [a_id, added_id])
1069
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1071
self.assertEqual([self.added(tree2, added_id)],
1072
self.do_iter_changes(tree1, tree2))
1073
self.assertEqual([self.added(tree2, added_id)],
1074
self.do_iter_changes(tree1, tree2,
1075
specific_files=[u'\u03b1']))
1077
def test_deleted_unicode(self):
1078
tree1 = self.make_branch_and_tree('tree1')
1079
tree2 = self.make_to_branch_and_tree('tree2')
1080
root_id = tree1.get_root_id()
1081
tree2.set_root_id(root_id)
1083
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1084
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1085
a_id = u'\u03b1-id'.encode('utf8')
1086
deleted_id = u'\u03c9_deleted_id'.encode('utf8')
1088
self.build_tree([u'tree1/\u03b1/',
1089
u'tree1/\u03b1/\u03c9-deleted',
1092
except UnicodeError:
1093
raise tests.TestSkipped("Could not create Unicode files.")
1094
tree1.add([u'\u03b1', u'\u03b1/\u03c9-deleted'], [a_id, deleted_id])
1095
tree2.add([u'\u03b1'], [a_id])
1097
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1099
self.assertEqual([self.deleted(tree1, deleted_id)],
1100
self.do_iter_changes(tree1, tree2))
1101
self.assertEqual([self.deleted(tree1, deleted_id)],
1102
self.do_iter_changes(tree1, tree2,
1103
specific_files=[u'\u03b1']))
1105
def test_modified_unicode(self):
1106
tree1 = self.make_branch_and_tree('tree1')
1107
tree2 = self.make_to_branch_and_tree('tree2')
1108
root_id = tree1.get_root_id()
1109
tree2.set_root_id(root_id)
1111
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1112
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1113
a_id = u'\u03b1-id'.encode('utf8')
1114
mod_id = u'\u03c9_mod_id'.encode('utf8')
1116
self.build_tree([u'tree1/\u03b1/',
1117
u'tree1/\u03b1/\u03c9-modified',
1119
u'tree2/\u03b1/\u03c9-modified',
1121
except UnicodeError:
1122
raise tests.TestSkipped("Could not create Unicode files.")
1123
tree1.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
1124
tree2.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
1126
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1128
self.assertEqual([self.content_changed(tree1, mod_id)],
1129
self.do_iter_changes(tree1, tree2))
1130
self.assertEqual([self.content_changed(tree1, mod_id)],
1131
self.do_iter_changes(tree1, tree2,
1132
specific_files=[u'\u03b1']))
1134
def test_renamed_unicode(self):
1135
tree1 = self.make_branch_and_tree('tree1')
1136
tree2 = self.make_to_branch_and_tree('tree2')
1137
root_id = tree1.get_root_id()
1138
tree2.set_root_id(root_id)
1140
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1141
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1142
a_id = u'\u03b1-id'.encode('utf8')
1143
rename_id = u'\u03c9_rename_id'.encode('utf8')
1145
self.build_tree([u'tree1/\u03b1/',
1148
except UnicodeError:
1149
raise tests.TestSkipped("Could not create Unicode files.")
1150
self.build_tree_contents([(u'tree1/\u03c9-source', 'contents\n'),
1151
(u'tree2/\u03b1/\u03c9-target', 'contents\n'),
1153
tree1.add([u'\u03b1', u'\u03c9-source'], [a_id, rename_id])
1154
tree2.add([u'\u03b1', u'\u03b1/\u03c9-target'], [a_id, rename_id])
1156
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1158
self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1159
self.do_iter_changes(tree1, tree2))
1160
self.assertEqual([self.renamed(tree1, tree2, rename_id, False)],
1161
self.do_iter_changes(tree1, tree2,
1162
specific_files=[u'\u03b1']))
1164
def test_unchanged_unicode(self):
1165
tree1 = self.make_branch_and_tree('tree1')
1166
tree2 = self.make_to_branch_and_tree('tree2')
1167
root_id = tree1.get_root_id()
1168
tree2.set_root_id(root_id)
1169
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1170
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1171
a_id = u'\u03b1-id'.encode('utf8')
1172
subfile_id = u'\u03c9-subfile-id'.encode('utf8')
1173
rootfile_id = u'\u03c9-root-id'.encode('utf8')
1175
self.build_tree([u'tree1/\u03b1/',
1178
except UnicodeError:
1179
raise tests.TestSkipped("Could not create Unicode files.")
1180
self.build_tree_contents([
1181
(u'tree1/\u03b1/\u03c9-subfile', 'sub contents\n'),
1182
(u'tree2/\u03b1/\u03c9-subfile', 'sub contents\n'),
1183
(u'tree1/\u03c9-rootfile', 'root contents\n'),
1184
(u'tree2/\u03c9-rootfile', 'root contents\n'),
1186
tree1.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
1187
[a_id, subfile_id, rootfile_id])
1188
tree2.add([u'\u03b1', u'\u03b1/\u03c9-subfile', u'\u03c9-rootfile'],
1189
[a_id, subfile_id, rootfile_id])
1191
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1194
self.unchanged(tree1, root_id),
1195
self.unchanged(tree1, a_id),
1196
self.unchanged(tree1, subfile_id),
1197
self.unchanged(tree1, rootfile_id),
1199
self.assertEqual(expected,
1200
self.do_iter_changes(tree1, tree2,
1201
include_unchanged=True))
1203
# We should also be able to select just a subset
1205
self.unchanged(tree1, a_id),
1206
self.unchanged(tree1, subfile_id),
1208
self.assertEqual(expected,
1209
self.do_iter_changes(tree1, tree2,
1210
specific_files=[u'\u03b1'],
1211
include_unchanged=True))
1213
def test_unknown_unicode(self):
1214
tree1 = self.make_branch_and_tree('tree1')
1215
tree2 = self.make_to_branch_and_tree('tree2')
1216
root_id = tree1.get_root_id()
1217
tree2.set_root_id(root_id)
1218
# u'\u03b1' == GREEK SMALL LETTER ALPHA
1219
# u'\u03c9' == GREEK SMALL LETTER OMEGA
1220
a_id = u'\u03b1-id'.encode('utf8')
1222
self.build_tree([u'tree1/\u03b1/',
1224
u'tree2/\u03b1/unknown_dir/',
1225
u'tree2/\u03b1/unknown_file',
1226
u'tree2/\u03b1/unknown_dir/file',
1227
u'tree2/\u03c9-unknown_root_file',
1229
except UnicodeError:
1230
raise tests.TestSkipped("Could not create Unicode files.")
1231
tree1.add([u'\u03b1'], [a_id])
1232
tree2.add([u'\u03b1'], [a_id])
1234
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1237
self.unversioned(tree2, u'\u03b1/unknown_dir'),
1238
self.unversioned(tree2, u'\u03b1/unknown_file'),
1239
self.unversioned(tree2, u'\u03c9-unknown_root_file'),
1240
# a/unknown_dir/file should not be included because we should not
1241
# recurse into unknown_dir
1242
# self.unversioned(tree2, 'a/unknown_dir/file'),
1244
self.assertEqual(expected,
1245
self.do_iter_changes(tree1, tree2,
1246
require_versioned=False,
1247
want_unversioned=True))
1248
self.assertEqual([], # Without want_unversioned we should get nothing
1249
self.do_iter_changes(tree1, tree2))
1251
# We should also be able to select just a subset
1253
self.unversioned(tree2, u'\u03b1/unknown_dir'),
1254
self.unversioned(tree2, u'\u03b1/unknown_file'),
1256
self.assertEqual(expected,
1257
self.do_iter_changes(tree1, tree2,
1258
specific_files=[u'\u03b1'],
1259
require_versioned=False,
1260
want_unversioned=True))
1261
self.assertEqual([], # Without want_unversioned we should get nothing
1262
self.do_iter_changes(tree1, tree2,
1263
specific_files=[u'\u03b1']))
1265
def test_unknown_empty_dir(self):
1266
tree1 = self.make_branch_and_tree('tree1')
1267
tree2 = self.make_to_branch_and_tree('tree2')
1268
root_id = tree1.get_root_id()
1269
tree2.set_root_id(root_id)
1271
# Start with 2 identical trees
1272
self.build_tree(['tree1/a/', 'tree1/b/',
1273
'tree2/a/', 'tree2/b/'])
1274
self.build_tree_contents([('tree1/b/file', 'contents\n'),
1275
('tree2/b/file', 'contents\n')])
1276
tree1.add(['a', 'b', 'b/file'], ['a-id', 'b-id', 'b-file-id'])
1277
tree2.add(['a', 'b', 'b/file'], ['a-id', 'b-id', 'b-file-id'])
1279
# Now create some unknowns in tree2
1280
# We should find both a/file and a/dir as unknown, but we shouldn't
1281
# recurse into a/dir to find that a/dir/subfile is also unknown.
1282
self.build_tree(['tree2/a/file', 'tree2/a/dir/', 'tree2/a/dir/subfile'])
1284
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1287
self.unversioned(tree2, u'a/file'),
1288
self.unversioned(tree2, u'a/dir'),
1290
self.assertEqual(expected,
1291
self.do_iter_changes(tree1, tree2,
1292
require_versioned=False,
1293
want_unversioned=True))
1295
def test_rename_over_deleted(self):
1296
tree1 = self.make_branch_and_tree('tree1')
1297
tree2 = self.make_to_branch_and_tree('tree2')
1298
root_id = tree1.get_root_id()
1299
tree2.set_root_id(root_id)
1301
# The final changes should be:
1308
self.build_tree_contents([
1309
('tree1/a', 'a contents\n'),
1310
('tree1/b', 'b contents\n'),
1311
('tree1/c', 'c contents\n'),
1312
('tree1/d', 'd contents\n'),
1313
('tree2/a', 'b contents\n'),
1314
('tree2/d', 'c contents\n'),
1316
tree1.add(['a', 'b', 'c', 'd'], ['a-id', 'b-id', 'c-id', 'd-id'])
1317
tree2.add(['a', 'd'], ['b-id', 'c-id'])
1319
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1322
self.deleted(tree1, 'a-id'),
1323
self.deleted(tree1, 'd-id'),
1324
self.renamed(tree1, tree2, 'b-id', False),
1325
self.renamed(tree1, tree2, 'c-id', False),
1327
self.assertEqual(expected,
1328
self.do_iter_changes(tree1, tree2))
1330
def test_deleted_and_unknown(self):
1331
"""Test a file marked removed, but still present on disk."""
1332
tree1 = self.make_branch_and_tree('tree1')
1333
tree2 = self.make_to_branch_and_tree('tree2')
1334
root_id = tree1.get_root_id()
1335
tree2.set_root_id(root_id)
1337
# The final changes should be:
1340
self.build_tree_contents([
1341
('tree1/a', 'a contents\n'),
1342
('tree1/b', 'b contents\n'),
1343
('tree1/c', 'c contents\n'),
1344
('tree2/a', 'a contents\n'),
1345
('tree2/b', 'b contents\n'),
1346
('tree2/c', 'c contents\n'),
1348
tree1.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
1349
tree2.add(['a', 'c'], ['a-id', 'c-id'])
1351
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1354
self.deleted(tree1, 'b-id'),
1355
self.unversioned(tree2, 'b'),
1357
self.assertEqual(expected,
1358
self.do_iter_changes(tree1, tree2,
1359
want_unversioned=True))
1361
self.deleted(tree1, 'b-id'),
1363
self.assertEqual(expected,
1364
self.do_iter_changes(tree1, tree2,
1365
want_unversioned=False))
1367
def test_renamed_and_added(self):
1368
"""Test when we have renamed a file, and put another in its place."""
1369
tree1 = self.make_branch_and_tree('tree1')
1370
tree2 = self.make_to_branch_and_tree('tree2')
1371
root_id = tree1.get_root_id()
1372
tree2.set_root_id(root_id)
1374
# The final changes are:
1380
self.build_tree_contents([
1381
('tree1/b', 'b contents\n'),
1382
('tree1/c', 'c contents\n'),
1383
('tree2/a', 'b contents\n'),
1384
('tree2/b', 'new b contents\n'),
1385
('tree2/c', 'new c contents\n'),
1386
('tree2/d', 'c contents\n'),
1388
tree1.add(['b', 'c'], ['b1-id', 'c1-id'])
1389
tree2.add(['a', 'b', 'c', 'd'], ['b1-id', 'b2-id', 'c2-id', 'c1-id'])
1391
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1394
self.renamed(tree1, tree2, 'b1-id', False),
1395
self.renamed(tree1, tree2, 'c1-id', False),
1396
self.added(tree2, 'b2-id'),
1397
self.added(tree2, 'c2-id'),
1399
self.assertEqual(expected,
1400
self.do_iter_changes(tree1, tree2,
1401
want_unversioned=True))
1403
def test_renamed_and_unknown(self):
1404
"""A file was moved on the filesystem, but not in bzr."""
1405
tree1 = self.make_branch_and_tree('tree1')
1406
tree2 = self.make_to_branch_and_tree('tree2')
1407
root_id = tree1.get_root_id()
1408
tree2.set_root_id(root_id)
1410
# The final changes are:
1414
self.build_tree_contents([
1415
('tree1/a', 'a contents\n'),
1416
('tree1/b', 'b contents\n'),
1417
('tree2/a', 'a contents\n'),
1418
('tree2/b', 'b contents\n'),
1420
tree1.add(['a', 'b'], ['a-id', 'b-id'])
1421
tree2.add(['a', 'b'], ['a-id', 'b-id'])
1422
os.rename('tree2/a', 'tree2/a2')
1424
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1427
self.missing('a-id', 'a', 'a', tree2.get_root_id(), 'file'),
1428
self.unversioned(tree2, 'a2'),
1430
self.assertEqual(expected,
1431
self.do_iter_changes(tree1, tree2,
1432
want_unversioned=True))