428
501
tree2 = self.make_to_branch_and_tree('2')
429
502
tree1 = self.get_tree_no_parents_no_content(tree1)
430
503
tree2 = self.get_tree_no_parents_no_content(tree2)
431
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
504
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
432
505
self.assertEqual([], self.do_iter_changes(tree1, tree2))
506
self.check_has_changes(False, tree1, tree2)
434
508
def added(self, tree, file_id):
435
entry = tree.inventory[file_id]
436
path = tree.id2path(file_id)
509
path, entry = self.get_path_entry(tree, file_id)
437
510
return (file_id, (None, path), True, (False, True), (None, entry.parent_id),
438
511
(None, entry.name), (None, entry.kind),
439
512
(None, entry.executable))
515
def get_path_entry(tree, file_id):
516
iterator = tree.iter_entries_by_dir(specific_file_ids=[file_id])
517
return iterator.next()
441
519
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),
520
path, entry = self.get_path_entry(tree, file_id)
521
return (file_id, (path, path), True, (True, True),
522
(entry.parent_id, entry.parent_id),
445
523
(entry.name, entry.name), (entry.kind, entry.kind),
446
524
(entry.executable, entry.executable))
448
526
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),
527
from_path, old_entry = self.get_path_entry(from_tree, file_id)
528
path, new_entry = self.get_path_entry(to_tree, file_id)
529
return (file_id, (from_path, path), True, (True, True),
530
(old_entry.parent_id, new_entry.parent_id),
531
(old_entry.name, new_entry.name),
532
(old_entry.kind, new_entry.kind),
455
533
(old_entry.executable, new_entry.executable))
457
535
def missing(self, file_id, from_path, to_path, parent_id, kind):
641
729
tree2 = self.make_to_branch_and_tree('2')
642
730
tree1 = self.get_tree_no_parents_abc_content(tree1)
643
731
tree2 = self.get_tree_no_parents_abc_content_5(tree2)
644
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
732
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
645
733
root_id = tree1.path2id('')
646
734
self.assertEqual([('a-id', ('a', 'd'), True, (True, True),
647
735
(root_id, root_id), ('a', 'd'), ('file', 'file'),
648
736
(False, False))],
649
737
self.do_iter_changes(tree1, tree2))
739
def test_specific_content_modification_grabs_parents(self):
740
# WHen the only direct change to a specified file is a content change,
741
# and its in a reparented subtree, the parents are grabbed.
742
tree1 = self.make_branch_and_tree('1')
743
tree1.mkdir('changing', 'parent-id')
744
tree1.mkdir('changing/unchanging', 'mid-id')
745
tree1.add(['changing/unchanging/file'], ['file-id'], ['file'])
746
tree1.put_file_bytes_non_atomic('file-id', 'a file')
747
tree2 = self.make_to_branch_and_tree('2')
748
tree2.set_root_id(tree1.get_root_id())
749
tree2.mkdir('changed', 'parent-id')
750
tree2.mkdir('changed/unchanging', 'mid-id')
751
tree2.add(['changed/unchanging/file'], ['file-id'], ['file'])
752
tree2.put_file_bytes_non_atomic('file-id', 'changed content')
753
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
754
# parent-id has changed, as has file-id
755
root_id = tree1.path2id('')
756
self.assertEqualIterChanges(
757
[self.renamed(tree1, tree2, 'parent-id', False),
758
self.renamed(tree1, tree2, 'file-id', True)],
759
self.do_iter_changes(tree1, tree2,
760
specific_files=['changed/unchanging/file']))
762
def test_specific_content_modification_grabs_parents_root_changes(self):
763
# WHen the only direct change to a specified file is a content change,
764
# and its in a reparented subtree, the parents are grabbed, even if
765
# that includes the root.
766
tree1 = self.make_branch_and_tree('1')
767
tree1.set_root_id('old')
768
tree1.mkdir('changed', 'parent-id')
769
tree1.mkdir('changed/unchanging', 'mid-id')
770
tree1.add(['changed/unchanging/file'], ['file-id'], ['file'])
771
tree1.put_file_bytes_non_atomic('file-id', 'a file')
772
tree2 = self.make_to_branch_and_tree('2')
773
tree2.set_root_id('new')
774
tree2.mkdir('changed', 'parent-id')
775
tree2.mkdir('changed/unchanging', 'mid-id')
776
tree2.add(['changed/unchanging/file'], ['file-id'], ['file'])
777
tree2.put_file_bytes_non_atomic('file-id', 'changed content')
778
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
779
# old is gone, new is added, parent-id has changed(reparented), as has
781
root_id = tree1.path2id('')
782
self.assertEqualIterChanges(
783
[self.renamed(tree1, tree2, 'parent-id', False),
784
self.added(tree2, 'new'),
785
self.deleted(tree1, 'old'),
786
self.renamed(tree1, tree2, 'file-id', True)],
787
self.do_iter_changes(tree1, tree2,
788
specific_files=['changed/unchanging/file']))
790
def test_specific_with_rename_under_new_dir_reports_new_dir(self):
791
tree1 = self.make_branch_and_tree('1')
792
tree2 = self.make_to_branch_and_tree('2')
793
tree1 = self.get_tree_no_parents_abc_content(tree1)
794
tree2 = self.get_tree_no_parents_abc_content_7(tree2)
795
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
796
# d(d-id) is new, e is b-id renamed.
797
root_id = tree1.path2id('')
798
self.assertEqualIterChanges(
799
[self.renamed(tree1, tree2, 'b-id', False),
800
self.added(tree2, 'd-id')],
801
self.do_iter_changes(tree1, tree2, specific_files=['d/e']))
803
def test_specific_with_rename_under_dir_under_new_dir_reports_new_dir(self):
804
tree1 = self.make_branch_and_tree('1')
805
tree2 = self.make_to_branch_and_tree('2')
806
tree1 = self.get_tree_no_parents_abc_content(tree1)
807
tree2 = self.get_tree_no_parents_abc_content_7(tree2)
808
tree2.rename_one('a', 'd/e/a')
809
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
810
# d is new, d/e is b-id renamed, d/e/a is a-id renamed
811
root_id = tree1.path2id('')
812
self.assertEqualIterChanges(
813
[self.renamed(tree1, tree2, 'b-id', False),
814
self.added(tree2, 'd-id'),
815
self.renamed(tree1, tree2, 'a-id', False)],
816
self.do_iter_changes(tree1, tree2, specific_files=['d/e/a']))
818
def test_specific_old_parent_same_path_new_parent(self):
819
# when a parent is new at its path, if the path was used in the source
820
# it must be emitted as a change.
821
tree1 = self.make_branch_and_tree('1')
822
tree1.add(['a'], ['a-id'], ['file'])
823
tree1.put_file_bytes_non_atomic('a-id', 'a file')
824
tree2 = self.make_to_branch_and_tree('2')
825
tree2.set_root_id(tree1.get_root_id())
826
tree2.mkdir('a', 'b-id')
827
tree2.add(['a/c'], ['c-id'], ['file'])
828
tree2.put_file_bytes_non_atomic('c-id', 'another file')
829
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
830
# a-id is gone, b-id and c-id are added.
831
self.assertEqualIterChanges(
832
[self.deleted(tree1, 'a-id'),
833
self.added(tree2, 'b-id'),
834
self.added(tree2, 'c-id')],
835
self.do_iter_changes(tree1, tree2, specific_files=['a/c']))
837
def test_specific_old_parent_becomes_file(self):
838
# When an old parent included because of a path conflict becomes a
839
# non-directory, its children have to be all included in the delta.
840
tree1 = self.make_branch_and_tree('1')
841
tree1.mkdir('a', 'a-old-id')
842
tree1.mkdir('a/reparented', 'reparented-id')
843
tree1.mkdir('a/deleted', 'deleted-id')
844
tree2 = self.make_to_branch_and_tree('2')
845
tree2.set_root_id(tree1.get_root_id())
846
tree2.mkdir('a', 'a-new-id')
847
tree2.mkdir('a/reparented', 'reparented-id')
848
tree2.add(['b'], ['a-old-id'], ['file'])
849
tree2.put_file_bytes_non_atomic('a-old-id', '')
850
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
851
# a-old-id is kind-changed, a-new-id is added, reparented-id is renamed,
853
self.assertEqualIterChanges(
854
[self.kind_changed(tree1, tree2, 'a-old-id'),
855
self.added(tree2, 'a-new-id'),
856
self.renamed(tree1, tree2, 'reparented-id', False),
857
self.deleted(tree1, 'deleted-id')],
858
self.do_iter_changes(tree1, tree2,
859
specific_files=['a/reparented']))
861
def test_specific_old_parent_is_deleted(self):
862
# When an old parent included because of a path conflict is removed,
863
# its children have to be all included in the delta.
864
tree1 = self.make_branch_and_tree('1')
865
tree1.mkdir('a', 'a-old-id')
866
tree1.mkdir('a/reparented', 'reparented-id')
867
tree1.mkdir('a/deleted', 'deleted-id')
868
tree2 = self.make_to_branch_and_tree('2')
869
tree2.set_root_id(tree1.get_root_id())
870
tree2.mkdir('a', 'a-new-id')
871
tree2.mkdir('a/reparented', 'reparented-id')
872
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
873
# a-old-id is gone, a-new-id is added, reparented-id is renamed,
875
self.assertEqualIterChanges(
876
[self.deleted(tree1, 'a-old-id'),
877
self.added(tree2, 'a-new-id'),
878
self.renamed(tree1, tree2, 'reparented-id', False),
879
self.deleted(tree1, 'deleted-id')],
880
self.do_iter_changes(tree1, tree2,
881
specific_files=['a/reparented']))
883
def test_specific_old_parent_child_collides_with_unselected_new(self):
884
# When the child of an old parent because of a path conflict becomes a
885
# path conflict with some unselected item in the source, that item also
886
# needs to be included (because otherwise the output of applying the
887
# delta to the source would have two items at that path).
888
tree1 = self.make_branch_and_tree('1')
889
tree1.mkdir('a', 'a-old-id')
890
tree1.mkdir('a/reparented', 'reparented-id')
891
tree1.mkdir('collides', 'collides-id')
892
tree2 = self.make_to_branch_and_tree('2')
893
tree2.set_root_id(tree1.get_root_id())
894
tree2.mkdir('a', 'a-new-id')
895
tree2.mkdir('a/selected', 'selected-id')
896
tree2.mkdir('collides', 'reparented-id')
897
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
898
# a-old-id is one, a-new-id is added, reparented-id is renamed,
899
# collides-id is gone, selected-id is new.
900
self.assertEqualIterChanges(
901
[self.deleted(tree1, 'a-old-id'),
902
self.added(tree2, 'a-new-id'),
903
self.renamed(tree1, tree2, 'reparented-id', False),
904
self.deleted(tree1, 'collides-id'),
905
self.added(tree2, 'selected-id')],
906
self.do_iter_changes(tree1, tree2,
907
specific_files=['a/selected']))
909
def test_specific_old_parent_child_dir_stops_being_dir(self):
910
# When the child of an old parent also stops being a directory, its
911
# children must also be included. This test checks that downward
912
# recursion is done appropriately by starting at a child of the root of
913
# a deleted subtree (a/reparented), and checking that a sibling
914
# directory (a/deleted) has its children included in the delta.
915
tree1 = self.make_branch_and_tree('1')
916
tree1.mkdir('a', 'a-old-id')
917
tree1.mkdir('a/reparented', 'reparented-id-1')
918
tree1.mkdir('a/deleted', 'deleted-id-1')
919
tree1.mkdir('a/deleted/reparented', 'reparented-id-2')
920
tree1.mkdir('a/deleted/deleted', 'deleted-id-2')
921
tree2 = self.make_to_branch_and_tree('2')
922
tree2.set_root_id(tree1.get_root_id())
923
tree2.mkdir('a', 'a-new-id')
924
tree2.mkdir('a/reparented', 'reparented-id-1')
925
tree2.mkdir('reparented', 'reparented-id-2')
926
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
927
# a-old-id is gone, a-new-id is added, reparented-id-1, -2 are renamed,
928
# deleted-id-1 and -2 are gone.
929
self.assertEqualIterChanges(
930
[self.deleted(tree1, 'a-old-id'),
931
self.added(tree2, 'a-new-id'),
932
self.renamed(tree1, tree2, 'reparented-id-1', False),
933
self.renamed(tree1, tree2, 'reparented-id-2', False),
934
self.deleted(tree1, 'deleted-id-1'),
935
self.deleted(tree1, 'deleted-id-2')],
936
self.do_iter_changes(tree1, tree2,
937
specific_files=['a/reparented']))
651
939
def test_file_rename_and_meta_modification(self):
652
940
tree1 = self.make_branch_and_tree('1')
653
941
tree2 = self.make_to_branch_and_tree('2')
654
942
tree1 = self.get_tree_no_parents_abc_content(tree1)
655
943
tree2 = self.get_tree_no_parents_abc_content_6(tree2)
656
tree1, tree2 = self.mutable_trees_to_test_trees(tree1, tree2)
944
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
657
945
root_id = tree1.path2id('')
658
946
self.assertEqual([('c-id', ('b/c', 'e'), False, (True, True),
659
947
('b-id', root_id), ('c', 'e'), ('file', 'file'),
661
949
self.do_iter_changes(tree1, tree2))
951
def test_file_becomes_unversionable_bug_438569(self):
952
# This isn't strictly a intertree problem, but its the intertree code
953
# path that triggers all stat cache updates on both xml and dirstate
955
# In bug 438569, a file becoming a fifo causes an assert. Fifo's are
956
# not versionable or diffable. For now, we simply stop cold when they
957
# are detected (because we don't know how far through the code the
958
# assumption 'fifo's do not exist' goes). In future we could report
959
# the kind change and have commit refuse to go futher, or something
960
# similar. One particular reason for choosing this approach is that
961
# there is no minikind for 'fifo' in dirstate today, so we can't
962
# actually update records that way.
963
# To add confusion, the totally generic code path works - but it
964
# doesn't update persistent metadata. So this test permits InterTrees
965
# to either work, or fail with BadFileKindError.
966
self.requireFeature(tests.OsFifoFeature)
967
tree1 = self.make_branch_and_tree('1')
968
self.build_tree(['1/a'])
969
tree1.set_root_id('root-id')
970
tree1.add(['a'], ['a-id'])
971
tree2 = self.make_branch_and_tree('2')
973
tree2.add(['a'], ['a-id'], ['file'])
975
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
977
raise tests.TestNotApplicable(
978
"Cannot represent a FIFO in this case %s" % self.id())
980
self.do_iter_changes(tree1, tree2)
981
except errors.BadFileKindError:
663
984
def test_missing_in_target(self):
664
985
"""Test with the target files versioned but absent from disk."""
665
986
tree1 = self.make_branch_and_tree('1')
696
1020
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1022
def test_only_in_source_and_missing(self):
1023
tree1 = self.make_branch_and_tree('tree1')
1024
tree2 = self.make_to_branch_and_tree('tree2')
1025
tree2.set_root_id(tree1.get_root_id())
1026
self.build_tree(['tree1/file'])
1027
tree1.add(['file'], ['file-id'])
1028
os.unlink('tree1/file')
1029
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1030
self.not_applicable_if_missing_in('file', tree1)
1031
root_id = tree1.path2id('')
1032
expected = [('file-id', ('file', None), False, (True, False),
1033
(root_id, None), ('file', None), (None, None), (False, None))]
1034
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1036
def test_only_in_target_and_missing(self):
1037
tree1 = self.make_branch_and_tree('tree1')
1038
tree2 = self.make_to_branch_and_tree('tree2')
1039
tree2.set_root_id(tree1.get_root_id())
1040
self.build_tree(['tree2/file'])
1041
tree2.add(['file'], ['file-id'])
1042
os.unlink('tree2/file')
1043
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1044
self.not_applicable_if_missing_in('file', tree2)
1045
root_id = tree1.path2id('')
1046
expected = [('file-id', (None, 'file'), False, (False, True),
1047
(None, root_id), (None, 'file'), (None, None), (None, False))]
1048
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1050
def test_only_in_target_missing_subtree_specific_bug_367632(self):
1051
tree1 = self.make_branch_and_tree('tree1')
1052
tree2 = self.make_to_branch_and_tree('tree2')
1053
tree2.set_root_id(tree1.get_root_id())
1054
self.build_tree(['tree2/a-dir/', 'tree2/a-dir/a-file'])
1055
tree2.add(['a-dir', 'a-dir/a-file'], ['dir-id', 'file-id'])
1056
os.unlink('tree2/a-dir/a-file')
1057
os.rmdir('tree2/a-dir')
1058
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1059
self.not_applicable_if_missing_in('a-dir', tree2)
1060
root_id = tree1.path2id('')
1062
('dir-id', (None, 'a-dir'), False, (False, True),
1063
(None, root_id), (None, 'a-dir'), (None, None), (None, False)),
1064
('file-id', (None, 'a-dir/a-file'), False, (False, True),
1065
(None, 'dir-id'), (None, 'a-file'), (None, None), (None, False))
1067
# bug 367632 showed that specifying the root broke some code paths,
1068
# so we check this contract with and without it.
1069
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1070
self.assertEqual(expected,
1071
self.do_iter_changes(tree1, tree2, specific_files=['']))
698
1073
def test_unchanged_with_renames_and_modifications(self):
699
1074
"""want_unchanged should generate a list of unchanged entries."""
700
1075
tree1 = self.make_branch_and_tree('1')