51
51
def _change_key(change):
52
52
"""Return a valid key for sorting Tree.iter_changes entries."""
53
53
(file_id, paths, content_changed, versioned, parent, name, kind,
55
55
return (file_id or b'', (paths[0] or '', paths[1] or ''), versioned,
56
56
parent, name, kind, executable)
96
96
self.assertEqual([('a', b'a-id', 'file'),
97
97
('b', b'b-id', 'directory'),
98
98
('b/c', b'c-id', 'file'),
100
100
self.assertEqual([], d.modified)
101
101
self.assertEqual([], d.removed)
102
102
self.assertEqual([], d.renamed)
142
142
self.assertEqual([('a', tree1.path2id('a'), 'file'),
143
143
('b', tree1.path2id('b'), 'directory'),
144
144
('b/c', tree1.path2id('b/c'), 'file'),
146
146
self.assertEqual([], d.renamed)
147
147
self.assertEqual([], d.unchanged)
155
155
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
156
156
d = self.intertree_class(tree1, tree2).compare()
157
157
self.assertEqual([], d.added)
158
self.assertEqual([('a', tree1.path2id('a'), 'file', True, False)], d.modified)
159
[('a', tree1.path2id('a'), 'file', True, False)], d.modified)
159
160
self.assertEqual([], d.removed)
160
161
self.assertEqual([], d.renamed)
161
162
self.assertEqual([], d.unchanged)
169
170
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
170
171
d = self.intertree_class(tree1, tree2).compare()
171
172
self.assertEqual([], d.added)
172
self.assertEqual([('b/c', tree1.path2id('b/c'), 'file', False, True)], d.modified)
174
[('b/c', tree1.path2id('b/c'), 'file', False, True)], d.modified)
173
175
self.assertEqual([], d.removed)
174
176
self.assertEqual([], d.renamed)
175
177
self.assertEqual([], d.unchanged)
185
187
self.assertEqual([], d.added)
186
188
self.assertEqual([], d.modified)
187
189
self.assertEqual([], d.removed)
188
self.assertEqual([('a', 'd', tree1.path2id('a'), 'file', False, False)], d.renamed)
191
[('a', 'd', tree1.path2id('a'), 'file', False, False)], d.renamed)
189
192
self.assertEqual([], d.unchanged)
191
194
def test_file_rename_and_modification(self):
199
202
self.assertEqual([], d.added)
200
203
self.assertEqual([], d.modified)
201
204
self.assertEqual([], d.removed)
202
self.assertEqual([('a', 'd', tree1.path2id('a'), 'file', True, False)], d.renamed)
206
[('a', 'd', tree1.path2id('a'), 'file', True, False)], d.renamed)
203
207
self.assertEqual([], d.unchanged)
205
209
def test_file_rename_and_meta_modification(self):
213
217
self.assertEqual([], d.added)
214
218
self.assertEqual([], d.modified)
215
219
self.assertEqual([], d.removed)
216
self.assertEqual([('b/c', 'e', tree1.path2id('b/c'), 'file', False, True)], d.renamed)
221
[('b/c', 'e', tree1.path2id('b/c'), 'file', False, True)], d.renamed)
217
222
self.assertEqual([], d.unchanged)
219
224
def test_empty_to_abc_content_a_only(self):
338
343
tree2 = self.get_tree_no_parents_abc_content(tree2)
339
344
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
340
345
self.assertRaises(errors.PathsNotVersionedError,
341
self.intertree_class(tree1, tree2).compare,
342
specific_files=['d'],
343
require_versioned=True)
346
self.intertree_class(tree1, tree2).compare,
347
specific_files=['d'],
348
require_versioned=True)
345
350
def test_default_ignores_unversioned_files(self):
346
351
tree1 = self.make_branch_and_tree('tree1')
355
360
d = self.intertree_class(tree1, tree2).compare()
356
361
self.assertEqual([], d.added)
357
362
self.assertEqual([(u'a', b'a-id', 'file', True, False),
358
(u'c', b'c-id', 'file', True, False)], d.modified)
363
(u'c', b'c-id', 'file', True, False)], d.modified)
359
364
self.assertEqual([], d.removed)
360
365
self.assertEqual([], d.renamed)
361
366
self.assertEqual([], d.unchanged)
431
436
# when all the ids are unique on both sides.
432
437
left_dict = dict((item[0], item) for item in left_changes)
433
438
right_dict = dict((item[0], item) for item in right_changes)
434
if (len(left_dict) != len(left_changes) or
435
len(right_dict) != len(right_changes)):
439
if (len(left_dict) != len(left_changes)
440
or len(right_dict) != len(right_changes)):
436
441
# Can't do a direct comparison. We could do a sequence diff, but
437
442
# for now just do a regular assertEqual for now.
438
443
self.assertEqual(left_changes, right_changes)
446
451
same.append(str(left_item))
448
453
different.append(" %s\n %s" % (left_item, right_item))
449
self.fail("iter_changes output different. Unchanged items:\n" +
450
"\n".join(same) + "\nChanged items:\n" + "\n".join(different))
454
self.fail("iter_changes output different. Unchanged items:\n"
455
+ "\n".join(same) + "\nChanged items:\n" + "\n".join(different))
452
457
def do_iter_changes(self, tree1, tree2, **extra_args):
453
458
"""Helper to run iter_changes from tree1 to tree2.
460
465
with tree1.lock_read(), tree2.lock_read():
461
466
# sort order of output is not strictly defined
462
467
return self.sorted(self.intertree_class(tree1, tree2)
463
.iter_changes(**extra_args))
468
.iter_changes(**extra_args))
465
470
def check_has_changes(self, expected, tree1, tree2):
466
471
# has_changes is defined for mutable trees only
473
478
# Neither tree can be used
479
return tree2.has_changes(tree1)
480
with tree1.lock_read(), tree2.lock_read():
481
return tree2.has_changes(tree1)
485
483
def mutable_trees_to_locked_test_trees(self, tree1, tree2):
486
484
"""Convert the working trees into test trees.
537
535
with_slashes.append(base_path + '/' + d + '/')
538
536
with_slashes.append(base_path + '/' + d + '/f')
538
paths.append(d + '/f')
541
539
path_ids.append((d.replace('/', '_') + '-id').encode('ascii'))
542
540
path_ids.append((d.replace('/', '_') + '_f-id').encode('ascii'))
543
541
self.build_tree(with_slashes)
590
588
_, to_basename = os.path.split(to_path)
591
589
# missing files have both paths, but no kind.
592
590
return (file_id, (from_path, to_path), True, (True, True),
593
(parent_id, parent_id),
594
(from_basename, to_basename), (kind, None), (False, False))
591
(parent_id, parent_id),
592
(from_basename, to_basename), (kind, None), (False, False))
596
594
def deleted(self, tree, file_id):
597
595
entry = tree.root_inventory.get_entry(file_id)
604
602
from_path, from_entry = self.get_path_entry(from_tree, file_id)
605
603
to_path, to_entry = self.get_path_entry(to_tree, file_id)
606
604
return (file_id, (from_path, to_path), content_changed, (True, True),
607
(from_entry.parent_id, to_entry.parent_id),
608
(from_entry.name, to_entry.name),
609
(from_entry.kind, to_entry.kind),
610
(from_entry.executable, to_entry.executable))
605
(from_entry.parent_id, to_entry.parent_id),
606
(from_entry.name, to_entry.name),
607
(from_entry.kind, to_entry.kind),
608
(from_entry.executable, to_entry.executable))
612
610
def unchanged(self, tree, file_id):
613
611
path, entry = self.get_path_entry(tree, file_id)
616
614
kind = entry.kind
617
615
executable = entry.executable
618
616
return (file_id, (path, path), False, (True, True),
619
(parent, parent), (name, name), (kind, kind),
620
(executable, executable))
617
(parent, parent), (name, name), (kind, kind),
618
(executable, executable))
622
620
def unversioned(self, tree, path):
623
621
"""Create an unversioned result."""
652
650
tree2 = self.get_tree_no_parents_abc_content(tree2)
653
651
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
654
652
self.assertEqual([],
655
self.do_iter_changes(tree1, tree2, specific_files=[]))
653
self.do_iter_changes(tree1, tree2, specific_files=[]))
657
655
def test_no_specific_files(self):
658
656
tree1 = self.make_branch_and_tree('1')
677
675
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
678
676
self.assertEqual(
679
677
self.sorted([self.added(tree2, b'root-id'),
680
self.added(tree2, b'a-id'),
681
self.deleted(tree1, b'empty-root-id')]),
678
self.added(tree2, b'a-id'),
679
self.deleted(tree1, b'empty-root-id')]),
682
680
self.do_iter_changes(tree1, tree2, specific_files=['a']))
684
682
def test_abc_content_to_empty_a_only(self):
710
708
tree2 = self.get_tree_no_parents_abc_content(tree2)
711
709
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
712
710
expected_result = self.sorted([self.added(tree2, b'root-id'),
713
self.added(tree2, b'a-id'), self.added(tree2, b'b-id'),
714
self.added(tree2, b'c-id'), self.deleted(tree1, b'empty-root-id')])
712
tree2, b'a-id'), self.added(tree2, b'b-id'),
713
self.added(tree2, b'c-id'), self.deleted(tree1, b'empty-root-id')])
715
714
self.assertEqual(expected_result,
716
self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
715
self.do_iter_changes(tree1, tree2, specific_files=['a', 'b/c']))
718
717
def test_abc_content_to_empty(self):
719
718
tree1 = self.make_branch_and_tree('1')
751
750
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
752
751
self.assertEqual([(b'c-id', ('b/c', 'b/c'), False, (True, True),
753
752
(b'b-id', b'b-id'), ('c', 'c'), ('file', 'file'),
755
754
self.do_iter_changes(tree1, tree2))
757
756
def test_empty_dir(self):
800
799
tree1.mkdir('changing/unchanging', b'mid-id')
801
800
tree1.add(['changing/unchanging/file'], [b'file-id'], ['file'])
802
801
tree1.put_file_bytes_non_atomic(
803
'changing/unchanging/file', b'a file', file_id=b'file-id')
802
'changing/unchanging/file', b'a file')
804
803
tree2 = self.make_to_branch_and_tree('2')
805
804
tree2.set_root_id(tree1.get_root_id())
806
805
tree2.mkdir('changed', b'parent-id')
807
806
tree2.mkdir('changed/unchanging', b'mid-id')
808
807
tree2.add(['changed/unchanging/file'], [b'file-id'], ['file'])
809
808
tree2.put_file_bytes_non_atomic(
810
'changed/unchanging/file', b'changed content', file_id=b'file-id')
809
'changed/unchanging/file', b'changed content')
811
810
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
812
811
# parent-id has changed, as has file-id
813
812
root_id = tree1.path2id('')
814
813
self.assertEqualIterChanges(
815
814
[self.renamed(tree1, tree2, b'parent-id', False),
816
815
self.renamed(tree1, tree2, b'file-id', True)],
817
self.do_iter_changes(tree1, tree2,
818
specific_files=['changed/unchanging/file']))
816
self.do_iter_changes(tree1, tree2,
817
specific_files=['changed/unchanging/file']))
820
819
def test_specific_content_modification_grabs_parents_root_changes(self):
821
820
# WHen the only direct change to a specified file is a content change,
826
825
tree1.mkdir('changed', b'parent-id')
827
826
tree1.mkdir('changed/unchanging', b'mid-id')
828
827
tree1.add(['changed/unchanging/file'], [b'file-id'], ['file'])
829
tree1.put_file_bytes_non_atomic(
830
'changed/unchanging/file', b'a file',
828
tree1.put_file_bytes_non_atomic('changed/unchanging/file', b'a file')
832
829
tree2 = self.make_to_branch_and_tree('2')
833
830
tree2.set_root_id(b'new')
834
831
tree2.mkdir('changed', b'parent-id')
835
832
tree2.mkdir('changed/unchanging', b'mid-id')
836
833
tree2.add(['changed/unchanging/file'], [b'file-id'], ['file'])
837
834
tree2.put_file_bytes_non_atomic(
838
'changed/unchanging/file', b'changed content', file_id=b'file-id')
835
'changed/unchanging/file', b'changed content')
839
836
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
840
837
# old is gone, new is added, parent-id has changed(reparented), as has
841
838
# file-id(content)
845
842
self.added(tree2, b'new'),
846
843
self.deleted(tree1, b'old'),
847
844
self.renamed(tree1, tree2, b'file-id', True)],
848
self.do_iter_changes(tree1, tree2,
849
specific_files=['changed/unchanging/file']))
845
self.do_iter_changes(tree1, tree2,
846
specific_files=['changed/unchanging/file']))
851
848
def test_specific_with_rename_under_new_dir_reports_new_dir(self):
852
849
tree1 = self.make_branch_and_tree('1')
854
851
tree1 = self.get_tree_no_parents_abc_content(tree1)
855
852
tree2 = self.get_tree_no_parents_abc_content_7(tree2)
856
853
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
857
# d(d-id) is new, e is b-id renamed.
854
# d(d-id) is new, e is b-id renamed.
858
855
root_id = tree1.path2id('')
859
856
self.assertEqualIterChanges(
860
857
[self.renamed(tree1, tree2, b'b-id', False),
861
858
self.added(tree2, b'd-id')],
862
self.do_iter_changes(tree1, tree2, specific_files=['d/e']))
859
self.do_iter_changes(tree1, tree2, specific_files=['d/e']))
864
861
def test_specific_with_rename_under_dir_under_new_dir_reports_new_dir(self):
865
862
tree1 = self.make_branch_and_tree('1')
868
865
tree2 = self.get_tree_no_parents_abc_content_7(tree2)
869
866
tree2.rename_one('a', 'd/e/a')
870
867
tree1, tree2 = self.mutable_trees_to_test_trees(self, tree1, tree2)
871
# d is new, d/e is b-id renamed, d/e/a is a-id renamed
868
# d is new, d/e is b-id renamed, d/e/a is a-id renamed
872
869
root_id = tree1.path2id('')
873
870
self.assertEqualIterChanges(
874
871
[self.renamed(tree1, tree2, tree1.path2id('b'), False),
875
872
self.added(tree2, b'd-id'),
876
873
self.renamed(tree1, tree2, b'a-id', False)],
877
self.do_iter_changes(tree1, tree2, specific_files=['d/e/a']))
874
self.do_iter_changes(tree1, tree2, specific_files=['d/e/a']))
879
876
def test_specific_old_parent_same_path_new_parent(self):
880
877
# when a parent is new at its path, if the path was used in the source
893
890
[self.deleted(tree1, b'a-id'),
894
891
self.added(tree2, b'b-id'),
895
892
self.added(tree2, b'c-id')],
896
self.do_iter_changes(tree1, tree2, specific_files=['a/c']))
893
self.do_iter_changes(tree1, tree2, specific_files=['a/c']))
898
895
def test_specific_old_parent_becomes_file(self):
899
896
# When an old parent included because of a path conflict becomes a
916
913
self.added(tree2, b'a-new-id'),
917
914
self.renamed(tree1, tree2, b'reparented-id', False),
918
915
self.deleted(tree1, b'deleted-id')],
919
self.do_iter_changes(tree1, tree2,
920
specific_files=['a/reparented']))
916
self.do_iter_changes(tree1, tree2,
917
specific_files=['a/reparented']))
922
919
def test_specific_old_parent_is_deleted(self):
923
920
# When an old parent included because of a path conflict is removed,
938
935
self.added(tree2, b'a-new-id'),
939
936
self.renamed(tree1, tree2, b'reparented-id', False),
940
937
self.deleted(tree1, b'deleted-id')],
941
self.do_iter_changes(tree1, tree2,
942
specific_files=['a/reparented']))
938
self.do_iter_changes(tree1, tree2,
939
specific_files=['a/reparented']))
944
941
def test_specific_old_parent_child_collides_with_unselected_new(self):
945
942
# When the child of an old parent because of a path conflict becomes a
964
961
self.renamed(tree1, tree2, b'reparented-id', False),
965
962
self.deleted(tree1, b'collides-id'),
966
963
self.added(tree2, b'selected-id')],
967
self.do_iter_changes(tree1, tree2,
968
specific_files=['a/selected']))
964
self.do_iter_changes(tree1, tree2,
965
specific_files=['a/selected']))
970
967
def test_specific_old_parent_child_dir_stops_being_dir(self):
971
968
# When the child of an old parent also stops being a directory, its
994
991
self.renamed(tree1, tree2, b'reparented-id-2', False),
995
992
self.deleted(tree1, b'deleted-id-1'),
996
993
self.deleted(tree1, b'deleted-id-2')],
997
self.do_iter_changes(tree1, tree2,
998
specific_files=['a/reparented']))
994
self.do_iter_changes(tree1, tree2,
995
specific_files=['a/reparented']))
1000
997
def test_file_rename_and_meta_modification(self):
1001
998
tree1 = self.make_branch_and_tree('1')
1016
1013
# In bug 438569, a file becoming a fifo causes an assert. Fifo's are
1017
1014
# not versionable or diffable. For now, we simply stop cold when they
1018
# are detected (because we don't know how far through the code the
1019
# assumption 'fifo's do not exist' goes). In future we could report
1015
# are detected (because we don't know how far through the code the
1016
# assumption 'fifo's do not exist' goes). In future we could report
1020
1017
# the kind change and have commit refuse to go futher, or something
1021
1018
# similar. One particular reason for choosing this approach is that
1022
# there is no minikind for 'fifo' in dirstate today, so we can't
1019
# there is no minikind for 'fifo' in dirstate today, so we can't
1023
1020
# actually update records that way.
1024
1021
# To add confusion, the totally generic code path works - but it
1025
1022
# doesn't update persistent metadata. So this test permits InterTrees
1090
1087
self.not_applicable_if_missing_in('file', tree1)
1091
1088
root_id = tree1.path2id('')
1092
1089
expected = [(b'file-id', ('file', None), False, (True, False),
1093
(root_id, None), ('file', None), (None, None), (False, None))]
1090
(root_id, None), ('file', None), (None, None), (False, None))]
1094
1091
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1096
1093
def test_only_in_target_and_missing(self):
1104
1101
self.not_applicable_if_missing_in('file', tree2)
1105
1102
root_id = tree1.path2id('')
1106
1103
expected = [(b'file-id', (None, 'file'), False, (False, True),
1107
(None, root_id), (None, 'file'), (None, None), (None, False))]
1104
(None, root_id), (None, 'file'), (None, None), (None, False))]
1108
1105
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1110
1107
def test_only_in_target_missing_subtree_specific_bug_367632(self):
1120
1117
root_id = tree1.path2id('')
1122
1119
(b'dir-id', (None, 'a-dir'), False, (False, True),
1123
(None, root_id), (None, 'a-dir'), (None, None), (None, False)),
1120
(None, root_id), (None, 'a-dir'), (None, None), (None, False)),
1124
1121
(b'file-id', (None, 'a-dir/a-file'), False, (False, True),
1125
(None, b'dir-id'), (None, 'a-file'), (None, None), (None, False))
1122
(None, b'dir-id'), (None, 'a-file'), (None, None), (None, False))
1127
1124
# bug 367632 showed that specifying the root broke some code paths,
1128
1125
# so we check this contract with and without it.
1129
1126
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1130
1127
self.assertEqual(expected,
1131
self.do_iter_changes(tree1, tree2, specific_files=['']))
1128
self.do_iter_changes(tree1, tree2, specific_files=['']))
1133
1130
def test_unchanged_with_renames_and_modifications(self):
1134
1131
"""want_unchanged should generate a list of unchanged entries."""
1138
1135
tree2 = self.get_tree_no_parents_abc_content_5(tree2)
1139
1136
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1140
1137
self.assertEqual(sorted([self.unchanged(tree1, b'root-id'),
1141
self.unchanged(tree1, b'b-id'),
1142
(b'a-id', ('a', 'd'), True, (True, True),
1143
(b'root-id', b'root-id'), ('a', 'd'), ('file', 'file'),
1144
(False, False)), self.unchanged(tree1, b'c-id')]),
1145
self.do_iter_changes(tree1, tree2, include_unchanged=True))
1138
self.unchanged(tree1, b'b-id'),
1139
(b'a-id', ('a', 'd'), True, (True, True),
1140
(b'root-id', b'root-id'), ('a',
1141
'd'), ('file', 'file'),
1142
(False, False)), self.unchanged(tree1, b'c-id')]),
1143
self.do_iter_changes(tree1, tree2, include_unchanged=True))
1147
1145
def test_compare_subtrees(self):
1148
1146
tree1 = self.make_branch_and_tree('1')
1181
1179
('sub', 'sub'),
1182
1180
('tree-reference', 'tree-reference'),
1183
1181
(False, False))],
1184
list(tree2.iter_changes(tree1,
1185
include_unchanged=True)))
1182
list(tree2.iter_changes(tree1,
1183
include_unchanged=True)))
1187
1185
def test_disk_in_subtrees_skipped(self):
1188
1186
"""subtrees are considered not-in-the-current-tree.
1205
1203
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1206
1204
# this should filter correctly from above
1207
1205
self.assertEqual([self.added(tree2, b'subtree-id')],
1208
self.do_iter_changes(tree1, tree2, want_unversioned=True))
1206
self.do_iter_changes(tree1, tree2, want_unversioned=True))
1209
1207
# and when the path is named
1210
1208
self.assertEqual([self.added(tree2, b'subtree-id')],
1211
self.do_iter_changes(tree1, tree2, specific_files=['sub'],
1212
want_unversioned=True))
1209
self.do_iter_changes(tree1, tree2, specific_files=['sub'],
1210
want_unversioned=True))
1214
1212
def test_default_ignores_unversioned_files(self):
1215
1213
tree1 = self.make_branch_and_tree('tree1')
1251
1249
expected.append(self.unversioned(tree2, 'link'))
1252
1250
expected = self.sorted(expected)
1253
1251
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1254
want_unversioned=True))
1252
want_unversioned=True))
1256
1254
def test_unversioned_paths_in_tree_specific_files(self):
1257
1255
tree1 = self.make_branch_and_tree('tree1')
1268
1266
self.unversioned(tree2, 'file'),
1269
1267
self.unversioned(tree2, 'dir'),
1271
specific_files=['file', 'dir']
1269
specific_files = ['file', 'dir']
1272
1270
if links_supported:
1273
1271
expected.append(self.unversioned(tree2, 'link'))
1274
1272
specific_files.append('link')
1275
1273
expected = self.sorted(expected)
1276
1274
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1277
specific_files=specific_files, require_versioned=False,
1278
want_unversioned=True))
1275
specific_files=specific_files, require_versioned=False,
1276
want_unversioned=True))
1280
1278
def test_unversioned_paths_in_target_matching_source_old_names(self):
1281
1279
# its likely that naive implementations of unversioned file support
1289
1287
tree2 = self.make_to_branch_and_tree('tree2')
1290
1288
tree2.set_root_id(tree1.get_root_id())
1291
1289
self.build_tree(['tree2/file', 'tree2/dir/',
1292
'tree1/file', 'tree2/movedfile',
1293
'tree1/dir/', 'tree2/moveddir/'])
1290
'tree1/file', 'tree2/movedfile',
1291
'tree1/dir/', 'tree2/moveddir/'])
1294
1292
if has_symlinks():
1295
1293
os.symlink('target', 'tree1/link')
1296
1294
os.symlink('target', 'tree2/link')
1312
1310
self.unversioned(tree2, 'file'),
1313
1311
self.unversioned(tree2, 'dir'),
1315
specific_files=['file', 'dir']
1313
specific_files = ['file', 'dir']
1316
1314
if links_supported:
1317
1315
expected.append(self.renamed(tree1, tree2, b'link-id', False))
1318
1316
expected.append(self.unversioned(tree2, 'link'))
1321
1319
# run once with, and once without specific files, to catch
1322
1320
# potentially different code paths.
1323
1321
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1324
require_versioned=False,
1325
want_unversioned=True))
1322
require_versioned=False,
1323
want_unversioned=True))
1326
1324
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1327
specific_files=specific_files, require_versioned=False,
1328
want_unversioned=True))
1325
specific_files=specific_files, require_versioned=False,
1326
want_unversioned=True))
1330
1328
def test_similar_filenames(self):
1331
1329
"""Test when we have a few files with similar names."""
1347
1345
'tree2/a/b/c/d/',
1349
1347
'tree2/a-c/e/',
1351
1349
tree1.add(['a', 'a/b', 'a/b/c', 'a/b/c/d', 'a-c', 'a-c/e'],
1352
1350
[b'a-id', b'b-id', b'c-id', b'd-id', b'a-c-id', b'e-id'])
1353
1351
tree2.add(['a', 'a/b', 'a/b/c', 'a/b/c/d', 'a-c', 'a-c/e'],
1384
1381
self.unversioned(tree2, 'dir'),
1386
1383
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1387
want_unversioned=True))
1384
want_unversioned=True))
1389
1386
def make_trees_with_symlinks(self):
1390
1387
tree1 = self.make_branch_and_tree('tree1')
1399
1396
# we make the unchanged link point at unknown to catch incorrect
1400
1397
# symlink-following code in the specified_files test.
1401
1398
os.symlink('unknown', 'tree1/unchanged')
1402
os.symlink('new', 'tree2/added')
1403
os.symlink('new', 'tree2/changed')
1404
os.symlink('new', 'tree2/fromfile')
1405
os.symlink('new', 'tree2/fromdir')
1399
os.symlink('new', 'tree2/added')
1400
os.symlink('new', 'tree2/changed')
1401
os.symlink('new', 'tree2/fromfile')
1402
os.symlink('new', 'tree2/fromdir')
1406
1403
os.symlink('unknown', 'tree2/unchanged')
1407
1404
from_paths_and_ids = [
1425
tree1.add(from_paths_and_ids, [p.encode('utf-8') for p in from_paths_and_ids])
1426
tree2.add(to_paths_and_ids, [p.encode('utf-8') for p in to_paths_and_ids])
1422
tree1.add(from_paths_and_ids, [p.encode('utf-8')
1423
for p in from_paths_and_ids])
1424
tree2.add(to_paths_and_ids, [p.encode('utf-8')
1425
for p in to_paths_and_ids])
1427
1426
return self.mutable_trees_to_locked_test_trees(tree1, tree2)
1429
1428
def test_versioned_symlinks(self):
1446
1445
expected = self.sorted(expected)
1447
1446
self.assertEqual(expected,
1448
self.do_iter_changes(tree1, tree2, include_unchanged=True,
1449
want_unversioned=True))
1447
self.do_iter_changes(tree1, tree2, include_unchanged=True,
1448
want_unversioned=True))
1450
1449
self.check_has_changes(True, tree1, tree2)
1452
1451
def test_versioned_symlinks_specific_files(self):
1467
1466
# make sure that it is correctly not returned - and neither is the
1468
1467
# unknown path 'unknown' which it points at.
1469
1468
self.assertEqual(expected, self.do_iter_changes(tree1, tree2,
1470
specific_files=['added', 'changed', 'fromdir', 'fromfile',
1471
'removed', 'unchanged', 'todir', 'tofile']))
1469
specific_files=['added', 'changed', 'fromdir', 'fromfile',
1470
'removed', 'unchanged', 'todir', 'tofile']))
1472
1471
self.check_has_changes(True, tree1, tree2)
1474
1473
def test_tree_with_special_names(self):
1480
1479
def test_trees_with_special_names(self):
1481
1480
tree1, tree2, paths, path_ids = self.make_trees_with_special_names()
1482
1481
expected = self.sorted(self.content_changed(tree2, f_id) for f_id in path_ids
1483
if f_id.endswith(b'_f-id'))
1482
if f_id.endswith(b'_f-id'))
1484
1483
self.assertEqual(expected, self.do_iter_changes(tree1, tree2))
1485
1484
self.check_has_changes(True, tree1, tree2)
1506
1505
self.deleted(tree1, b'e-id'),
1508
1507
self.assertEqualIterChanges(expected,
1509
self.do_iter_changes(tree1, tree2))
1508
self.do_iter_changes(tree1, tree2))
1510
1509
self.check_has_changes(True, tree1, tree2)
1512
1511
def test_added_unicode(self):
1523
1522
self.build_tree([u'tree1/\u03b1/',
1524
1523
u'tree2/\u03b1/',
1525
1524
u'tree2/\u03b1/\u03c9-added',
1527
1526
except UnicodeError:
1528
1527
raise tests.TestSkipped("Could not create Unicode files.")
1529
1528
tree1.add([u'\u03b1'], [a_id])
1552
1551
self.build_tree([u'tree1/\u03b1/',
1553
1552
u'tree1/\u03b1/\u03c9-deleted',
1554
1553
u'tree2/\u03b1/',
1556
1555
except UnicodeError:
1557
1556
raise tests.TestSkipped("Could not create Unicode files.")
1558
1557
tree1.add([u'\u03b1', u'\u03b1/\u03c9-deleted'], [a_id, deleted_id])
1582
1581
u'tree1/\u03b1/\u03c9-modified',
1583
1582
u'tree2/\u03b1/',
1584
1583
u'tree2/\u03b1/\u03c9-modified',
1586
1585
except UnicodeError:
1587
1586
raise tests.TestSkipped("Could not create Unicode files.")
1588
1587
tree1.add([u'\u03b1', u'\u03b1/\u03c9-modified'], [a_id, mod_id])
1611
1610
self.build_tree([u'tree1/\u03b1/',
1612
1611
u'tree2/\u03b1/',
1614
1613
except UnicodeError:
1615
1614
raise tests.TestSkipped("Could not create Unicode files.")
1616
1615
self.build_tree_contents([(u'tree1/\u03c9-source', b'contents\n'),
1617
1616
(u'tree2/\u03b1/\u03c9-target', b'contents\n'),
1619
1618
tree1.add([u'\u03b1', u'\u03c9-source'], [a_id, rename_id])
1620
1619
tree2.add([u'\u03b1', u'\u03b1/\u03c9-target'], [a_id, rename_id])
1673
1672
self.unchanged(tree1, subfile_id),
1675
1674
self.assertEqual(expected,
1676
self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1'],
1677
include_unchanged=True))
1675
self.do_iter_changes(tree1, tree2, specific_files=[u'\u03b1'],
1676
include_unchanged=True))
1679
1678
def test_unknown_unicode(self):
1680
1679
tree1 = self.make_branch_and_tree('tree1')
1691
1690
u'tree2/\u03b1/unknown_file',
1692
1691
u'tree2/\u03b1/unknown_dir/file',
1693
1692
u'tree2/\u03c9-unknown_root_file',
1695
1694
except UnicodeError:
1696
1695
raise tests.TestSkipped("Could not create Unicode files.")
1697
1696
tree1.add([u'\u03b1'], [a_id])
1712
1711
self.do_iter_changes(tree1, tree2,
1713
1712
require_versioned=False,
1714
1713
want_unversioned=True))
1715
self.assertEqual([], # Without want_unversioned we should get nothing
1714
self.assertEqual([], # Without want_unversioned we should get nothing
1716
1715
self.do_iter_changes(tree1, tree2))
1717
1716
self.check_has_changes(False, tree1, tree2)
1726
1725
specific_files=[u'\u03b1'],
1727
1726
require_versioned=False,
1728
1727
want_unversioned=True))
1729
self.assertEqual([], # Without want_unversioned we should get nothing
1728
self.assertEqual([], # Without want_unversioned we should get nothing
1730
1729
self.do_iter_changes(tree1, tree2,
1731
1730
specific_files=[u'\u03b1']))
1747
1746
# Now create some unknowns in tree2
1748
1747
# We should find both a/file and a/dir as unknown, but we shouldn't
1749
1748
# recurse into a/dir to find that a/dir/subfile is also unknown.
1750
self.build_tree(['tree2/a/file', 'tree2/a/dir/', 'tree2/a/dir/subfile'])
1750
['tree2/a/file', 'tree2/a/dir/', 'tree2/a/dir/subfile'])
1752
1752
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)
1753
1753
self.not_applicable_if_cannot_represent_unversioned(tree2)
1755
expected = self.sorted([
1756
self.unversioned(tree2, u'a/file'),
1757
self.unversioned(tree2, u'a/dir'),
1759
self.assertEqual(expected,
1760
self.do_iter_changes(tree1, tree2,
1761
require_versioned=False,
1762
want_unversioned=True))
1755
if tree2.has_versioned_directories():
1756
expected = self.sorted([
1757
self.unversioned(tree2, u'a/file'),
1758
self.unversioned(tree2, u'a/dir'),
1760
self.assertEqual(expected,
1761
self.do_iter_changes(tree1, tree2,
1762
require_versioned=False,
1763
want_unversioned=True))
1765
expected = self.sorted([
1766
self.unversioned(tree2, u'a/file'),
1767
self.unversioned(tree2, u'a/dir/subfile'),
1769
self.assertEqual(expected,
1770
self.do_iter_changes(tree1, tree2,
1771
require_versioned=False,
1772
want_unversioned=True))
1764
1774
def test_rename_over_deleted(self):
1765
1775
tree1 = self.make_branch_and_tree('tree1')
1857
1867
('tree2/d', b'c contents\n'),
1859
1869
tree1.add(['b', 'c'], [b'b1-id', b'c1-id'])
1860
tree2.add(['a', 'b', 'c', 'd'], [b'b1-id', b'b2-id', b'c2-id', b'c1-id'])
1870
tree2.add(['a', 'b', 'c', 'd'], [
1871
b'b1-id', b'b2-id', b'c2-id', b'c1-id'])
1862
1873
tree1, tree2 = self.mutable_trees_to_locked_test_trees(tree1, tree2)