120
95
b/h\xc3\xa5 h-\xc3\xa5-file #This is u'\xe5' encoded into utf-8
122
Notice that a/e is an empty directory.
124
:return: The dirstate, still write-locked.
97
# Notice that a/e is an empty directory.
99
state = dirstate.DirState.initialize('dirstate')
126
100
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
127
101
null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
128
102
root_entry = ('', '', 'a-root-value'), [
129
('d', '', 0, False, packed_stat),
103
('directory', '', 0, False, packed_stat),
131
105
a_entry = ('', 'a', 'a-dir'), [
132
('d', '', 0, False, packed_stat),
106
('directory', '', 0, False, packed_stat),
134
108
b_entry = ('', 'b', 'b-dir'), [
135
('d', '', 0, False, packed_stat),
109
('directory', '', 0, False, packed_stat),
137
111
c_entry = ('', 'c', 'c-file'), [
138
('f', null_sha, 10, False, packed_stat),
112
('file', null_sha, 10, False, packed_stat),
140
114
d_entry = ('', 'd', 'd-file'), [
141
('f', null_sha, 20, False, packed_stat),
115
('file', null_sha, 20, False, packed_stat),
143
117
e_entry = ('a', 'e', 'e-dir'), [
144
('d', '', 0, False, packed_stat),
118
('directory', '', 0, False, packed_stat),
146
120
f_entry = ('a', 'f', 'f-file'), [
147
('f', null_sha, 30, False, packed_stat),
121
('file', null_sha, 30, False, packed_stat),
149
123
g_entry = ('b', 'g', 'g-file'), [
150
('f', null_sha, 30, False, packed_stat),
124
('file', null_sha, 30, False, packed_stat),
152
126
h_entry = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file'), [
153
('f', null_sha, 40, False, packed_stat),
127
('file', null_sha, 40, False, packed_stat),
156
130
dirblocks.append(('', [root_entry]))
157
131
dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
158
132
dirblocks.append(('a', [e_entry, f_entry]))
159
133
dirblocks.append(('b', [g_entry, h_entry]))
160
state = dirstate.DirState.initialize('dirstate')
163
state._set_data([], dirblocks)
134
state._set_data([], dirblocks)
169
137
def check_state_with_reopen(self, expected_result, state):
170
138
"""Check that state has current state expected_result.
172
140
This will check the current state, open the file anew and check it
174
This function expects the current state to be locked for writing, and
175
will unlock it before re-opening.
176
This is required because we can't open a lock_read() while something
177
else has a lock_write().
178
write => mutually exclusive lock
181
# The state should already be write locked, since we just had to do
182
# some operation to get here.
183
self.assertTrue(state._lock_token is not None)
185
self.assertEqual(expected_result[0], state.get_parent_ids())
186
# there should be no ghosts in this tree.
187
self.assertEqual([], state.get_ghosts())
188
# there should be one fileid in this tree - the root of the tree.
189
self.assertEqual(expected_result[1], list(state._iter_entries()))
194
state = dirstate.DirState.on_file('dirstate')
197
self.assertEqual(expected_result[1], list(state._iter_entries()))
201
def create_basic_dirstate(self):
202
"""Create a dirstate with a few files and directories.
212
tree = self.make_branch_and_tree('tree')
213
paths = ['a', 'b/', 'b/c', 'b/d/', 'b/d/e', 'b-c', 'f']
214
file_ids = ['a-id', 'b-id', 'c-id', 'd-id', 'e-id', 'b-c-id', 'f-id']
215
self.build_tree(['tree/' + p for p in paths])
216
tree.set_root_id('TREE_ROOT')
217
tree.add([p.rstrip('/') for p in paths], file_ids)
218
tree.commit('initial', rev_id='rev-1')
219
revision_id = 'rev-1'
220
# a_packed_stat = dirstate.pack_stat(os.stat('tree/a'))
221
t = self.get_transport('tree')
222
a_text = t.get_bytes('a')
223
a_sha = osutils.sha_string(a_text)
225
# b_packed_stat = dirstate.pack_stat(os.stat('tree/b'))
226
# c_packed_stat = dirstate.pack_stat(os.stat('tree/b/c'))
227
c_text = t.get_bytes('b/c')
228
c_sha = osutils.sha_string(c_text)
230
# d_packed_stat = dirstate.pack_stat(os.stat('tree/b/d'))
231
# e_packed_stat = dirstate.pack_stat(os.stat('tree/b/d/e'))
232
e_text = t.get_bytes('b/d/e')
233
e_sha = osutils.sha_string(e_text)
235
b_c_text = t.get_bytes('b-c')
236
b_c_sha = osutils.sha_string(b_c_text)
237
b_c_len = len(b_c_text)
238
# f_packed_stat = dirstate.pack_stat(os.stat('tree/f'))
239
f_text = t.get_bytes('f')
240
f_sha = osutils.sha_string(f_text)
242
null_stat = dirstate.DirState.NULLSTAT
244
'':(('', '', 'TREE_ROOT'), [
245
('d', '', 0, False, null_stat),
246
('d', '', 0, False, revision_id),
248
'a':(('', 'a', 'a-id'), [
249
('f', '', 0, False, null_stat),
250
('f', a_sha, a_len, False, revision_id),
252
'b':(('', 'b', 'b-id'), [
253
('d', '', 0, False, null_stat),
254
('d', '', 0, False, revision_id),
256
'b/c':(('b', 'c', 'c-id'), [
257
('f', '', 0, False, null_stat),
258
('f', c_sha, c_len, False, revision_id),
260
'b/d':(('b', 'd', 'd-id'), [
261
('d', '', 0, False, null_stat),
262
('d', '', 0, False, revision_id),
264
'b/d/e':(('b/d', 'e', 'e-id'), [
265
('f', '', 0, False, null_stat),
266
('f', e_sha, e_len, False, revision_id),
268
'b-c':(('', 'b-c', 'b-c-id'), [
269
('f', '', 0, False, null_stat),
270
('f', b_c_sha, b_c_len, False, revision_id),
272
'f':(('', 'f', 'f-id'), [
273
('f', '', 0, False, null_stat),
274
('f', f_sha, f_len, False, revision_id),
277
state = dirstate.DirState.from_tree(tree, 'dirstate')
282
# Use a different object, to make sure nothing is pre-cached in memory.
283
state = dirstate.DirState.on_file('dirstate')
285
self.addCleanup(state.unlock)
286
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
287
state._dirblock_state)
288
# This is code is only really tested if we actually have to make more
289
# than one read, so set the page size to something smaller.
290
# We want it to contain about 2.2 records, so that we have a couple
291
# records that we can read per attempt
292
state._bisect_page_size = 200
293
return tree, state, expected
295
def create_duplicated_dirstate(self):
296
"""Create a dirstate with a deleted and added entries.
298
This grabs a basic_dirstate, and then removes and re adds every entry
301
tree, state, expected = self.create_basic_dirstate()
302
# Now we will just remove and add every file so we get an extra entry
303
# per entry. Unversion in reverse order so we handle subdirs
304
tree.unversion(['f-id', 'b-c-id', 'e-id', 'd-id', 'c-id', 'b-id', 'a-id'])
305
tree.add(['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f'],
306
['a-id2', 'b-id2', 'c-id2', 'd-id2', 'e-id2', 'b-c-id2', 'f-id2'])
308
# Update the expected dictionary.
309
for path in ['a', 'b', 'b/c', 'b/d', 'b/d/e', 'b-c', 'f']:
310
orig = expected[path]
312
# This record was deleted in the current tree
313
expected[path] = (orig[0], [dirstate.DirState.NULL_PARENT_DETAILS,
315
new_key = (orig[0][0], orig[0][1], orig[0][2]+'2')
316
# And didn't exist in the basis tree
317
expected[path2] = (new_key, [orig[1][0],
318
dirstate.DirState.NULL_PARENT_DETAILS])
320
# We will replace the 'dirstate' file underneath 'state', but that is
321
# okay as lock as we unlock 'state' first.
324
new_state = dirstate.DirState.from_tree(tree, 'dirstate')
330
# But we need to leave state in a read-lock because we already have
331
# a cleanup scheduled
333
return tree, state, expected
335
def create_renamed_dirstate(self):
336
"""Create a dirstate with a few internal renames.
338
This takes the basic dirstate, and moves the paths around.
340
tree, state, expected = self.create_basic_dirstate()
342
tree.rename_one('a', 'b/g')
344
tree.rename_one('b/d', 'h')
346
old_a = expected['a']
347
expected['a'] = (old_a[0], [('r', 'b/g', 0, False, ''), old_a[1][1]])
348
expected['b/g'] = (('b', 'g', 'a-id'), [old_a[1][0],
349
('r', 'a', 0, False, '')])
350
old_d = expected['b/d']
351
expected['b/d'] = (old_d[0], [('r', 'h', 0, False, ''), old_d[1][1]])
352
expected['h'] = (('', 'h', 'd-id'), [old_d[1][0],
353
('r', 'b/d', 0, False, '')])
355
old_e = expected['b/d/e']
356
expected['b/d/e'] = (old_e[0], [('r', 'h/e', 0, False, ''),
358
expected['h/e'] = (('h', 'e', 'e-id'), [old_e[1][0],
359
('r', 'b/d/e', 0, False, '')])
363
new_state = dirstate.DirState.from_tree(tree, 'dirstate')
370
return tree, state, expected
143
self.assertEqual(expected_result[0], state.get_parent_ids())
144
# there should be no ghosts in this tree.
145
self.assertEqual([], state.get_ghosts())
146
# there should be one fileid in this tree - the root of the tree.
147
self.assertEqual(expected_result[1], list(state._iter_entries()))
149
state = dirstate.DirState.on_file('dirstate')
150
self.assertEqual(expected_result[1], list(state._iter_entries()))
373
153
class TestTreeToDirState(TestCaseWithDirState):
733
326
def test_set_state_from_inventory_no_content_no_parents(self):
734
327
# setting the current inventory is a slow but important api to support.
328
state = dirstate.DirState.initialize('dirstate')
735
329
tree1 = self.make_branch_and_memory_tree('tree1')
736
330
tree1.lock_write()
739
revid1 = tree1.commit('foo').encode('utf8')
740
root_id = tree1.get_root_id()
741
inv = tree1.inventory
332
revid1 = tree1.commit('foo').encode('utf8')
333
root_id = tree1.inventory.root.file_id
334
state.set_state_from_inventory(tree1.inventory)
336
self.assertEqual(DirState.IN_MEMORY_UNMODIFIED, state._header_state)
337
self.assertEqual(DirState.IN_MEMORY_MODIFIED, state._dirblock_state)
744
338
expected_result = [], [
745
339
(('', '', root_id), [
746
('d', '', 0, False, dirstate.DirState.NULLSTAT)])]
747
state = dirstate.DirState.initialize('dirstate')
749
state.set_state_from_inventory(inv)
750
self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
752
self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
753
state._dirblock_state)
758
# This will unlock it
759
self.check_state_with_reopen(expected_result, state)
761
def test_set_state_from_inventory_preserves_hashcache(self):
762
# https://bugs.launchpad.net/bzr/+bug/146176
763
# set_state_from_inventory should preserve the stat and hash value for
764
# workingtree files that are not changed by the inventory.
766
tree = self.make_branch_and_tree('.')
767
# depends on the default format using dirstate...
770
# make a dirstate with some valid hashcache data
771
# file on disk, but that's not needed for this test
772
foo_contents = 'contents of foo'
773
self.build_tree_contents([('foo', foo_contents)])
774
tree.add('foo', 'foo-id')
776
foo_stat = os.stat('foo')
777
foo_packed = dirstate.pack_stat(foo_stat)
778
foo_sha = osutils.sha_string(foo_contents)
779
foo_size = len(foo_contents)
781
# should not be cached yet, because the file's too fresh
783
(('', 'foo', 'foo-id',),
784
[('f', '', 0, False, dirstate.DirState.NULLSTAT)]),
785
tree._dirstate._get_entry(0, 'foo-id'))
786
# poke in some hashcache information - it wouldn't normally be
787
# stored because it's too fresh
788
tree._dirstate.update_minimal(
789
('', 'foo', 'foo-id'),
790
'f', False, foo_sha, foo_packed, foo_size, 'foo')
791
# now should be cached
793
(('', 'foo', 'foo-id',),
794
[('f', foo_sha, foo_size, False, foo_packed)]),
795
tree._dirstate._get_entry(0, 'foo-id'))
797
# extract the inventory, and add something to it
798
inv = tree._get_inventory()
799
# should see the file we poked in...
800
self.assertTrue(inv.has_id('foo-id'))
801
self.assertTrue(inv.has_filename('foo'))
802
inv.add_path('bar', 'file', 'bar-id')
803
tree._dirstate._validate()
804
# this used to cause it to lose its hashcache
805
tree._dirstate.set_state_from_inventory(inv)
806
tree._dirstate._validate()
812
# now check that the state still has the original hashcache value
813
state = tree._dirstate
815
foo_tuple = state._get_entry(0, path_utf8='foo')
817
(('', 'foo', 'foo-id',),
818
[('f', foo_sha, len(foo_contents), False,
819
dirstate.pack_stat(foo_stat))]),
824
def test_set_state_from_inventory_mixed_paths(self):
825
tree1 = self.make_branch_and_tree('tree1')
826
self.build_tree(['tree1/a/', 'tree1/a/b/', 'tree1/a-b/',
827
'tree1/a/b/foo', 'tree1/a-b/bar'])
830
tree1.add(['a', 'a/b', 'a-b', 'a/b/foo', 'a-b/bar'],
831
['a-id', 'b-id', 'a-b-id', 'foo-id', 'bar-id'])
832
tree1.commit('rev1', rev_id='rev1')
833
root_id = tree1.get_root_id()
834
inv = tree1.inventory
837
expected_result1 = [('', '', root_id, 'd'),
838
('', 'a', 'a-id', 'd'),
839
('', 'a-b', 'a-b-id', 'd'),
840
('a', 'b', 'b-id', 'd'),
841
('a/b', 'foo', 'foo-id', 'f'),
842
('a-b', 'bar', 'bar-id', 'f'),
844
expected_result2 = [('', '', root_id, 'd'),
845
('', 'a', 'a-id', 'd'),
846
('', 'a-b', 'a-b-id', 'd'),
847
('a-b', 'bar', 'bar-id', 'f'),
849
state = dirstate.DirState.initialize('dirstate')
851
state.set_state_from_inventory(inv)
853
for entry in state._iter_entries():
854
values.append(entry[0] + entry[1][0][:1])
855
self.assertEqual(expected_result1, values)
857
state.set_state_from_inventory(inv)
859
for entry in state._iter_entries():
860
values.append(entry[0] + entry[1][0][:1])
861
self.assertEqual(expected_result2, values)
340
('directory', '', 0, False, DirState.NULLSTAT)])]
341
self.check_state_with_reopen(expected_result, state)
865
343
def test_set_path_id_no_parents(self):
866
344
"""The id of a path can be changed trivally with no parents."""
867
345
state = dirstate.DirState.initialize('dirstate')
869
# check precondition to be sure the state does change appropriately.
870
root_entry = (('', '', 'TREE_ROOT'), [('d', '', 0, False, 'x'*32)])
871
self.assertEqual([root_entry], list(state._iter_entries()))
872
self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
873
self.assertEqual(root_entry,
874
state._get_entry(0, fileid_utf8='TREE_ROOT'))
875
self.assertEqual((None, None),
876
state._get_entry(0, fileid_utf8='second-root-id'))
877
state.set_path_id('', 'second-root-id')
878
new_root_entry = (('', '', 'second-root-id'),
879
[('d', '', 0, False, 'x'*32)])
880
expected_rows = [new_root_entry]
881
self.assertEqual(expected_rows, list(state._iter_entries()))
882
self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
883
self.assertEqual(new_root_entry,
884
state._get_entry(0, fileid_utf8='second-root-id'))
885
self.assertEqual((None, None),
886
state._get_entry(0, fileid_utf8='TREE_ROOT'))
887
# should work across save too
891
state = dirstate.DirState.on_file('dirstate')
895
self.assertEqual(expected_rows, list(state._iter_entries()))
899
def test_set_path_id_with_parents(self):
900
"""Set the root file id in a dirstate with parents"""
901
mt = self.make_branch_and_tree('mt')
902
# in case the default tree format uses a different root id
903
mt.set_root_id('TREE_ROOT')
904
mt.commit('foo', rev_id='parent-revid')
905
rt = mt.branch.repository.revision_tree('parent-revid')
906
state = dirstate.DirState.initialize('dirstate')
909
state.set_parent_trees([('parent-revid', rt)], ghosts=[])
910
root_entry = (('', '', 'TREE_ROOT'),
911
[('d', '', 0, False, 'x'*32),
912
('d', '', 0, False, 'parent-revid')])
913
self.assertEqual(root_entry, state._get_entry(0, path_utf8=''))
914
self.assertEqual(root_entry,
915
state._get_entry(0, fileid_utf8='TREE_ROOT'))
916
self.assertEqual((None, None),
917
state._get_entry(0, fileid_utf8='Asecond-root-id'))
918
state.set_path_id('', 'Asecond-root-id')
920
# now see that it is what we expected
921
old_root_entry = (('', '', 'TREE_ROOT'),
922
[('a', '', 0, False, ''),
923
('d', '', 0, False, 'parent-revid')])
924
new_root_entry = (('', '', 'Asecond-root-id'),
925
[('d', '', 0, False, ''),
926
('a', '', 0, False, '')])
927
expected_rows = [new_root_entry, old_root_entry]
929
self.assertEqual(expected_rows, list(state._iter_entries()))
930
self.assertEqual(new_root_entry, state._get_entry(0, path_utf8=''))
931
self.assertEqual(old_root_entry, state._get_entry(1, path_utf8=''))
932
self.assertEqual((None, None),
933
state._get_entry(0, fileid_utf8='TREE_ROOT'))
934
self.assertEqual(old_root_entry,
935
state._get_entry(1, fileid_utf8='TREE_ROOT'))
936
self.assertEqual(new_root_entry,
937
state._get_entry(0, fileid_utf8='Asecond-root-id'))
938
self.assertEqual((None, None),
939
state._get_entry(1, fileid_utf8='Asecond-root-id'))
940
# should work across save too
944
# now flush & check we get the same
945
state = dirstate.DirState.on_file('dirstate')
949
self.assertEqual(expected_rows, list(state._iter_entries()))
952
# now change within an existing file-backed state
956
state.set_path_id('', 'tree-root-2')
346
# check precondition to be sure the state does change appropriately.
348
[(('', '', 'TREE_ROOT'), [('directory', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])],
349
list(state._iter_entries()))
350
state.set_path_id('', 'foobarbaz')
352
(('', '', 'foobarbaz'), [('directory', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])]
353
self.assertEqual(expected_rows, list(state._iter_entries()))
354
# should work across save too
356
state = dirstate.DirState.on_file('dirstate')
357
self.assertEqual(expected_rows, list(state._iter_entries()))
961
359
def test_set_parent_trees_no_content(self):
962
360
# set_parent_trees is a slow but important api to support.
361
state = dirstate.DirState.initialize('dirstate')
963
362
tree1 = self.make_branch_and_memory_tree('tree1')
964
363
tree1.lock_write()
967
revid1 = tree1.commit('foo')
365
revid1 = tree1.commit('foo')
970
367
branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
971
tree2 = memorytree.MemoryTree.create_on_branch(branch2)
368
tree2 = MemoryTree.create_on_branch(branch2)
972
369
tree2.lock_write()
974
revid2 = tree2.commit('foo')
975
root_id = tree2.get_root_id()
978
state = dirstate.DirState.initialize('dirstate')
980
state.set_path_id('', root_id)
981
state.set_parent_trees(
982
((revid1, tree1.branch.repository.revision_tree(revid1)),
983
(revid2, tree2.branch.repository.revision_tree(revid2)),
984
('ghost-rev', None)),
986
# check we can reopen and use the dirstate after setting parent
370
revid2 = tree2.commit('foo')
371
root_id = tree2.inventory.root.file_id
372
state.set_path_id('', root_id)
374
state.set_parent_trees(
375
((revid1, tree1.branch.repository.revision_tree(revid1)),
376
(revid2, tree2.branch.repository.revision_tree(revid2)),
377
('ghost-rev', None)),
379
# check we can reopen and use the dirstate after setting parent trees.
993
381
state = dirstate.DirState.on_file('dirstate')
996
self.assertEqual([revid1, revid2, 'ghost-rev'],
997
state.get_parent_ids())
998
# iterating the entire state ensures that the state is parsable.
999
list(state._iter_entries())
1000
# be sure that it sets not appends - change it
1001
state.set_parent_trees(
1002
((revid1, tree1.branch.repository.revision_tree(revid1)),
1003
('ghost-rev', None)),
1005
# and now put it back.
1006
state.set_parent_trees(
1007
((revid1, tree1.branch.repository.revision_tree(revid1)),
1008
(revid2, tree2.branch.repository.revision_tree(revid2)),
1009
('ghost-rev', tree2.branch.repository.revision_tree(
1010
_mod_revision.NULL_REVISION))),
1012
self.assertEqual([revid1, revid2, 'ghost-rev'],
1013
state.get_parent_ids())
1014
# the ghost should be recorded as such by set_parent_trees.
1015
self.assertEqual(['ghost-rev'], state.get_ghosts())
1017
[(('', '', root_id), [
1018
('d', '', 0, False, dirstate.DirState.NULLSTAT),
1019
('d', '', 0, False, revid1),
1020
('d', '', 0, False, revid1)
1022
list(state._iter_entries()))
382
self.assertEqual([revid1, revid2, 'ghost-rev'], state.get_parent_ids())
383
# iterating the entire state ensures that the state is parsable.
384
list(state._iter_entries())
385
# be sure that it sets not appends - change it
386
state.set_parent_trees(
387
((revid1, tree1.branch.repository.revision_tree(revid1)),
388
('ghost-rev', None)),
390
# and now put it back.
391
state.set_parent_trees(
392
((revid1, tree1.branch.repository.revision_tree(revid1)),
393
(revid2, tree2.branch.repository.revision_tree(revid2)),
394
('ghost-rev', tree2.branch.repository.revision_tree(None))),
396
self.assertEqual([revid1, revid2, 'ghost-rev'], state.get_parent_ids())
397
# the ghost should be recorded as such by set_parent_trees.
398
self.assertEqual(['ghost-rev'], state.get_ghosts())
400
[(('', '', root_id), [
401
('directory', '', 0, False, DirState.NULLSTAT),
402
('directory', '', 0, False, revid1),
403
('directory', '', 0, False, revid2)
405
list(state._iter_entries()))
1026
407
def test_set_parent_trees_file_missing_from_tree(self):
1027
408
# Adding a parent tree may reference files not in the current state.
1028
# they should get listed just once by id, even if they are in two
409
# they should get listed just once by id, even if they are in two
1029
410
# separate trees.
1030
411
# set_parent_trees is a slow but important api to support.
412
state = dirstate.DirState.initialize('dirstate')
1031
413
tree1 = self.make_branch_and_memory_tree('tree1')
1032
414
tree1.lock_write()
1035
tree1.add(['a file'], ['file-id'], ['file'])
1036
tree1.put_file_bytes_non_atomic('file-id', 'file-content')
1037
revid1 = tree1.commit('foo')
416
tree1.add(['a file'], ['file-id'], ['file'])
417
tree1.put_file_bytes_non_atomic('file-id', 'file-content')
418
revid1 = tree1.commit('foo')
1040
420
branch2 = tree1.branch.bzrdir.clone('tree2').open_branch()
1041
tree2 = memorytree.MemoryTree.create_on_branch(branch2)
421
tree2 = MemoryTree.create_on_branch(branch2)
1042
422
tree2.lock_write()
1044
tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
1045
revid2 = tree2.commit('foo')
1046
root_id = tree2.get_root_id()
423
tree2.put_file_bytes_non_atomic('file-id', 'new file-content')
424
revid2 = tree2.commit('foo')
425
root_id = tree2.inventory.root.file_id
426
state.set_path_id('', root_id)
428
state.set_parent_trees(
429
((revid1, tree1.branch.repository.revision_tree(revid1)),
430
(revid2, tree2.branch.repository.revision_tree(revid2)),
1049
432
# check the layout in memory
1050
433
expected_result = [revid1.encode('utf8'), revid2.encode('utf8')], [
1051
434
(('', '', root_id), [
1052
('d', '', 0, False, dirstate.DirState.NULLSTAT),
1053
('d', '', 0, False, revid1.encode('utf8')),
1054
('d', '', 0, False, revid1.encode('utf8'))
435
('directory', '', 0, False, DirState.NULLSTAT),
436
('directory', '', 0, False, revid1.encode('utf8')),
437
('directory', '', 0, False, revid2.encode('utf8'))]),
1056
438
(('', 'a file', 'file-id'), [
1057
('a', '', 0, False, ''),
1058
('f', '2439573625385400f2a669657a7db6ae7515d371', 12, False,
1059
revid1.encode('utf8')),
1060
('f', '542e57dc1cda4af37cb8e55ec07ce60364bb3c7d', 16, False,
1061
revid2.encode('utf8'))
439
('absent', '', 0, False, ''),
440
('file', '2439573625385400f2a669657a7db6ae7515d371', 12, False, revid1.encode('utf8')),
441
('file', '542e57dc1cda4af37cb8e55ec07ce60364bb3c7d', 16, False, revid2.encode('utf8'))])
1064
state = dirstate.DirState.initialize('dirstate')
1066
state.set_path_id('', root_id)
1067
state.set_parent_trees(
1068
((revid1, tree1.branch.repository.revision_tree(revid1)),
1069
(revid2, tree2.branch.repository.revision_tree(revid2)),
1075
# check_state_with_reopen will unlock
1076
self.check_state_with_reopen(expected_result, state)
443
self.check_state_with_reopen(expected_result, state)
1078
445
### add a path via _set_data - so we dont need delta work, just
1079
446
# raw data in, and ensure that it comes out via get_lines happily.
1081
448
def test_add_path_to_root_no_parents_all_data(self):
1082
449
# The most trivial addition of a path is when there are no parents and
1083
450
# its in the root and all data about the file is supplied
451
state = dirstate.DirState.initialize('dirstate')
1084
452
self.build_tree(['a file'])
1085
453
stat = os.lstat('a file')
1086
454
# the 1*20 is the sha1 pretend value.
1087
state = dirstate.DirState.initialize('dirstate')
455
state.add('a file', 'a file id', 'file', stat, '1'*20)
456
# having added it, it should be in the output of iter_entries.
1088
457
expected_entries = [
1089
458
(('', '', 'TREE_ROOT'), [
1090
('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
459
('directory', '', 0, False, dirstate.DirState.NULLSTAT), # current tree details
1092
(('', 'a file', 'a-file-id'), [
1093
('f', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree
461
(('', 'a file', 'a file id'), [
462
('file', '1'*20, 19, False, dirstate.pack_stat(stat)), # current tree details
1097
state.add('a file', 'a-file-id', 'file', stat, '1'*20)
1098
# having added it, it should be in the output of iter_entries.
1099
self.assertEqual(expected_entries, list(state._iter_entries()))
1100
# saving and reloading should not affect this.
465
self.assertEqual(expected_entries, list(state._iter_entries()))
466
# saving and reloading should not affect this.
1104
468
state = dirstate.DirState.on_file('dirstate')
1106
self.addCleanup(state.unlock)
1107
469
self.assertEqual(expected_entries, list(state._iter_entries()))
1109
471
def test_add_path_to_unversioned_directory(self):
1110
472
"""Adding a path to an unversioned directory should error.
1112
This is a duplicate of TestWorkingTree.test_add_in_unversioned,
474
This is a duplicate of TestWorkingTree.test_add_in_unversioned,
1113
475
once dirstate is stable and if it is merged with WorkingTree3, consider
1114
476
removing this copy of the test.
478
state = dirstate.DirState.initialize('dirstate')
1116
479
self.build_tree(['unversioned/', 'unversioned/a file'])
1117
state = dirstate.DirState.initialize('dirstate')
1118
self.addCleanup(state.unlock)
1119
480
self.assertRaises(errors.NotVersionedError, state.add,
1120
'unversioned/a file', 'a-file-id', 'file', None, None)
481
'unversioned/a file', 'a file id', 'file', None, None)
1122
483
def test_add_directory_to_root_no_parents_all_data(self):
1123
484
# The most trivial addition of a dir is when there are no parents and
1124
485
# its in the root and all data about the file is supplied
486
state = dirstate.DirState.initialize('dirstate')
1125
487
self.build_tree(['a dir/'])
1126
488
stat = os.lstat('a dir')
489
state.add('a dir', 'a dir id', 'directory', stat, None)
490
# having added it, it should be in the output of iter_entries.
1127
491
expected_entries = [
1128
492
(('', '', 'TREE_ROOT'), [
1129
('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
493
('directory', '', 0, False, dirstate.DirState.NULLSTAT), # current tree details
1131
495
(('', 'a dir', 'a dir id'), [
1132
('d', '', 0, False, dirstate.pack_stat(stat)), # current tree
496
('directory', '', 0, False, dirstate.pack_stat(stat)), # current tree details
1135
state = dirstate.DirState.initialize('dirstate')
1137
state.add('a dir', 'a dir id', 'directory', stat, None)
1138
# having added it, it should be in the output of iter_entries.
1139
self.assertEqual(expected_entries, list(state._iter_entries()))
1140
# saving and reloading should not affect this.
499
self.assertEqual(expected_entries, list(state._iter_entries()))
500
# saving and reloading should not affect this.
1144
502
state = dirstate.DirState.on_file('dirstate')
1146
self.addCleanup(state.unlock)
1148
503
self.assertEqual(expected_entries, list(state._iter_entries()))
1150
def _test_add_symlink_to_root_no_parents_all_data(self, link_name, target):
505
def test_add_symlink_to_root_no_parents_all_data(self):
1151
506
# The most trivial addition of a symlink when there are no parents and
1152
507
# its in the root and all data about the file is supplied
1153
# bzr doesn't support fake symlinks on windows, yet.
1154
self.requireFeature(tests.SymlinkFeature)
1155
os.symlink(target, link_name)
1156
stat = os.lstat(link_name)
508
state = dirstate.DirState.initialize('dirstate')
509
## TODO: windows: dont fail this test. Also, how are symlinks meant to
510
# be represented on windows.
511
os.symlink('target', 'a link')
512
stat = os.lstat('a link')
513
state.add('a link', 'a link id', 'symlink', stat, 'target')
514
# having added it, it should be in the output of iter_entries.
1157
515
expected_entries = [
1158
516
(('', '', 'TREE_ROOT'), [
1159
('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
517
('directory', '', 0, False, dirstate.DirState.NULLSTAT), # current tree details
1161
(('', link_name.encode('UTF-8'), 'a link id'), [
1162
('l', target.encode('UTF-8'), stat[6],
1163
False, dirstate.pack_stat(stat)), # current tree
519
(('', 'a link', 'a link id'), [
520
('symlink', 'target', 6, False, dirstate.pack_stat(stat)), # current tree details
1166
state = dirstate.DirState.initialize('dirstate')
1168
state.add(link_name, 'a link id', 'symlink', stat,
1169
target.encode('UTF-8'))
1170
# having added it, it should be in the output of iter_entries.
1171
self.assertEqual(expected_entries, list(state._iter_entries()))
1172
# saving and reloading should not affect this.
523
self.assertEqual(expected_entries, list(state._iter_entries()))
524
# saving and reloading should not affect this.
1176
526
state = dirstate.DirState.on_file('dirstate')
1178
self.addCleanup(state.unlock)
1179
527
self.assertEqual(expected_entries, list(state._iter_entries()))
1181
def test_add_symlink_to_root_no_parents_all_data(self):
1182
self._test_add_symlink_to_root_no_parents_all_data('a link', 'target')
1184
def test_add_symlink_unicode_to_root_no_parents_all_data(self):
1185
self.requireFeature(tests.UnicodeFilenameFeature)
1186
self._test_add_symlink_to_root_no_parents_all_data(
1187
u'\N{Euro Sign}link', u'targ\N{Euro Sign}et')
1189
529
def test_add_directory_and_child_no_parents_all_data(self):
1190
530
# after adding a directory, we should be able to add children to it.
531
state = dirstate.DirState.initialize('dirstate')
1191
532
self.build_tree(['a dir/', 'a dir/a file'])
1192
dirstat = os.lstat('a dir')
533
stat = os.lstat('a dir')
534
state.add('a dir', 'a dir id', 'directory', stat, None)
1193
535
filestat = os.lstat('a dir/a file')
536
state.add('a dir/a file', 'a file id', 'file', filestat, '1'*20)
537
# having added it, it should be in the output of iter_entries.
1194
538
expected_entries = [
1195
539
(('', '', 'TREE_ROOT'), [
1196
('d', '', 0, False, dirstate.DirState.NULLSTAT), # current tree
540
('directory', '', 0, False, dirstate.DirState.NULLSTAT), # current tree details
1198
542
(('', 'a dir', 'a dir id'), [
1199
('d', '', 0, False, dirstate.pack_stat(dirstat)), # current tree
543
('directory', '', 0, False, dirstate.pack_stat(stat)), # current tree details
1201
(('a dir', 'a file', 'a-file-id'), [
1202
('f', '1'*20, 25, False,
1203
dirstate.pack_stat(filestat)), # current tree details
545
(('a dir', 'a file', 'a file id'), [
546
('file', '1'*20, 25, False, dirstate.pack_stat(filestat)), # current tree details
1206
state = dirstate.DirState.initialize('dirstate')
1208
state.add('a dir', 'a dir id', 'directory', dirstat, None)
1209
state.add('a dir/a file', 'a-file-id', 'file', filestat, '1'*20)
1210
# added it, it should be in the output of iter_entries.
1211
self.assertEqual(expected_entries, list(state._iter_entries()))
1212
# saving and reloading should not affect this.
549
self.assertEqual(expected_entries, list(state._iter_entries()))
550
# saving and reloading should not affect this.
1216
552
state = dirstate.DirState.on_file('dirstate')
1218
self.addCleanup(state.unlock)
1219
553
self.assertEqual(expected_entries, list(state._iter_entries()))
1221
def test_add_tree_reference(self):
1222
# make a dirstate and add a tree reference
1223
state = dirstate.DirState.initialize('dirstate')
1225
('', 'subdir', 'subdir-id'),
1226
[('t', 'subtree-123123', 0, False,
1227
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')],
1230
state.add('subdir', 'subdir-id', 'tree-reference', None, 'subtree-123123')
1231
entry = state._get_entry(0, 'subdir-id', 'subdir')
1232
self.assertEqual(entry, expected_entry)
1237
# now check we can read it back
1239
self.addCleanup(state.unlock)
1241
entry2 = state._get_entry(0, 'subdir-id', 'subdir')
1242
self.assertEqual(entry, entry2)
1243
self.assertEqual(entry, expected_entry)
1244
# and lookup by id should work too
1245
entry2 = state._get_entry(0, fileid_utf8='subdir-id')
1246
self.assertEqual(entry, expected_entry)
1248
def test_add_forbidden_names(self):
1249
state = dirstate.DirState.initialize('dirstate')
1250
self.addCleanup(state.unlock)
1251
self.assertRaises(errors.BzrError,
1252
state.add, '.', 'ass-id', 'directory', None, None)
1253
self.assertRaises(errors.BzrError,
1254
state.add, '..', 'ass-id', 'directory', None, None)
1256
def test_set_state_with_rename_b_a_bug_395556(self):
1257
# bug 395556 uncovered a bug where the dirstate ends up with a false
1258
# relocation record - in a tree with no parents there should be no
1259
# absent or relocated records. This then leads to further corruption
1260
# when a commit occurs, as the incorrect relocation gathers an
1261
# incorrect absent in tree 1, and future changes go to pot.
1262
tree1 = self.make_branch_and_tree('tree1')
1263
self.build_tree(['tree1/b'])
1266
tree1.add(['b'], ['b-id'])
1267
root_id = tree1.get_root_id()
1268
inv = tree1.inventory
1269
state = dirstate.DirState.initialize('dirstate')
1271
# Set the initial state with 'b'
1272
state.set_state_from_inventory(inv)
1273
inv.rename('b-id', root_id, 'a')
1274
# Set the new state with 'a', which currently corrupts.
1275
state.set_state_from_inventory(inv)
1276
expected_result1 = [('', '', root_id, 'd'),
1277
('', 'a', 'b-id', 'f'),
1280
for entry in state._iter_entries():
1281
values.append(entry[0] + entry[1][0][:1])
1282
self.assertEqual(expected_result1, values)
1289
556
class TestGetLines(TestCaseWithDirState):
1291
558
def test_get_line_with_2_rows(self):
1292
559
state = self.create_dirstate_with_root_and_subdir()
1294
self.assertEqual(['#bazaar dirstate flat format 3\n',
1295
'crc32: 41262208\n',
1299
'\x00\x00a-root-value\x00'
1300
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
1301
'\x00subdir\x00subdir-id\x00'
1302
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
1303
], state.get_lines())
560
self.assertEqual(['#bazaar dirstate flat format 2\n',
561
'adler32: -1327947603\n',
565
'\x00\x00a-root-value\x00'
566
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'
567
'\x00subdir\x00subdir-id\x00'
568
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00\n\x00'],
1307
571
def test_entry_to_line(self):
1308
572
state = self.create_dirstate_with_root()
1311
'\x00\x00a-root-value\x00d\x00\x000\x00n'
1312
'\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
1313
state._entry_to_line(state._dirblocks[0][1][0]))
574
'\x00\x00a-root-value\x00d\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk',
575
state._entry_to_line(state._dirblocks[0][1][0]))
1317
577
def test_entry_to_line_with_parent(self):
578
state = dirstate.DirState.initialize('dirstate')
1318
579
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1319
580
root_entry = ('', '', 'a-root-value'), [
1320
('d', '', 0, False, packed_stat), # current tree details
1321
# first: a pointer to the current location
1322
('a', 'dirname/basename', 0, False, ''),
581
('directory', '', 0, False, packed_stat), # current tree details
582
('absent', 'dirname/basename', 0, False, ''), # first: a pointer to the current location
1324
state = dirstate.DirState.initialize('dirstate')
1327
'\x00\x00a-root-value\x00'
1328
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
1329
'a\x00dirname/basename\x000\x00n\x00',
1330
state._entry_to_line(root_entry))
585
'\x00\x00a-root-value\x00'
586
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
587
'a\x00dirname/basename\x000\x00n\x00',
588
state._entry_to_line(root_entry))
1334
590
def test_entry_to_line_with_two_parents_at_different_paths(self):
1335
591
# / in the tree, at / in one parent and /dirname/basename in the other.
592
state = dirstate.DirState.initialize('dirstate')
1336
593
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1337
594
root_entry = ('', '', 'a-root-value'), [
1338
('d', '', 0, False, packed_stat), # current tree details
1339
('d', '', 0, False, 'rev_id'), # first parent details
1340
# second: a pointer to the current location
1341
('a', 'dirname/basename', 0, False, ''),
595
('directory', '', 0, False, packed_stat), # current tree details
596
('directory', '', 0, False, 'rev_id'), # first parent details
597
('absent', 'dirname/basename', 0, False, ''), # second: a pointer to the current location
1343
state = dirstate.DirState.initialize('dirstate')
1346
'\x00\x00a-root-value\x00'
1347
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
1348
'd\x00\x000\x00n\x00rev_id\x00'
1349
'a\x00dirname/basename\x000\x00n\x00',
1350
state._entry_to_line(root_entry))
600
'\x00\x00a-root-value\x00'
601
'd\x00\x000\x00n\x00AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk\x00'
602
'd\x00\x000\x00n\x00rev_id\x00'
603
'a\x00dirname/basename\x000\x00n\x00',
604
state._entry_to_line(root_entry))
1354
606
def test_iter_entries(self):
1355
607
# we should be able to iterate the dirstate entries from end to end
1356
608
# this is for get_lines to be easy to read.
609
state = dirstate.DirState.initialize('dirstate')
1357
610
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1359
612
root_entries = [(('', '', 'a-root-value'), [
1360
('d', '', 0, False, packed_stat), # current tree details
613
('directory', '', 0, False, packed_stat), # current tree details
1362
615
dirblocks.append(('', root_entries))
1363
616
# add two files in the root
1364
617
subdir_entry = ('', 'subdir', 'subdir-id'), [
1365
('d', '', 0, False, packed_stat), # current tree details
618
('directory', '', 0, False, packed_stat), # current tree details
1367
620
afile_entry = ('', 'afile', 'afile-id'), [
1368
('f', 'sha1value', 34, False, packed_stat), # current tree details
621
('file', 'sha1value', 34, False, packed_stat), # current tree details
1370
623
dirblocks.append(('', [subdir_entry, afile_entry]))
1371
624
# and one in subdir
1372
625
file_entry2 = ('subdir', '2file', '2file-id'), [
1373
('f', 'sha1value', 23, False, packed_stat), # current tree details
626
('file', 'sha1value', 23, False, packed_stat), # current tree details
1375
628
dirblocks.append(('subdir', [file_entry2]))
1376
state = dirstate.DirState.initialize('dirstate')
1378
state._set_data([], dirblocks)
1379
expected_entries = [root_entries[0], subdir_entry, afile_entry,
1381
self.assertEqual(expected_entries, list(state._iter_entries()))
629
state._set_data([], dirblocks)
630
expected_entries = [root_entries[0], subdir_entry, afile_entry, file_entry2]
631
self.assertEqual(expected_entries, list(state._iter_entries()))
1386
634
class TestGetBlockRowIndex(TestCaseWithDirState):
1486
724
def test_get_entry_uninitialized(self):
1487
725
"""Calling get_entry will load data if it needs to"""
1488
726
state = self.create_dirstate_with_root()
1494
729
state = dirstate.DirState.on_file('dirstate')
1497
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
1498
state._header_state)
1499
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY,
1500
state._dirblock_state)
1501
self.assertEntryEqual('', '', 'a-root-value', state, '', 0)
1506
class TestIterChildEntries(TestCaseWithDirState):
1508
def create_dirstate_with_two_trees(self):
1509
"""This dirstate contains multiple files and directories.
1519
b/h\xc3\xa5 h-\xc3\xa5-file #This is u'\xe5' encoded into utf-8
1521
Notice that a/e is an empty directory.
1523
There is one parent tree, which has the same shape with the following variations:
1524
b/g in the parent is gone.
1525
b/h in the parent has a different id
1526
b/i is new in the parent
1527
c is renamed to b/j in the parent
1529
:return: The dirstate, still write-locked.
1531
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
1532
null_sha = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
1533
NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1534
root_entry = ('', '', 'a-root-value'), [
1535
('d', '', 0, False, packed_stat),
1536
('d', '', 0, False, 'parent-revid'),
1538
a_entry = ('', 'a', 'a-dir'), [
1539
('d', '', 0, False, packed_stat),
1540
('d', '', 0, False, 'parent-revid'),
1542
b_entry = ('', 'b', 'b-dir'), [
1543
('d', '', 0, False, packed_stat),
1544
('d', '', 0, False, 'parent-revid'),
1546
c_entry = ('', 'c', 'c-file'), [
1547
('f', null_sha, 10, False, packed_stat),
1548
('r', 'b/j', 0, False, ''),
1550
d_entry = ('', 'd', 'd-file'), [
1551
('f', null_sha, 20, False, packed_stat),
1552
('f', 'd', 20, False, 'parent-revid'),
1554
e_entry = ('a', 'e', 'e-dir'), [
1555
('d', '', 0, False, packed_stat),
1556
('d', '', 0, False, 'parent-revid'),
1558
f_entry = ('a', 'f', 'f-file'), [
1559
('f', null_sha, 30, False, packed_stat),
1560
('f', 'f', 20, False, 'parent-revid'),
1562
g_entry = ('b', 'g', 'g-file'), [
1563
('f', null_sha, 30, False, packed_stat),
1564
NULL_PARENT_DETAILS,
1566
h_entry1 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file1'), [
1567
('f', null_sha, 40, False, packed_stat),
1568
NULL_PARENT_DETAILS,
1570
h_entry2 = ('b', 'h\xc3\xa5', 'h-\xc3\xa5-file2'), [
1571
NULL_PARENT_DETAILS,
1572
('f', 'h', 20, False, 'parent-revid'),
1574
i_entry = ('b', 'i', 'i-file'), [
1575
NULL_PARENT_DETAILS,
1576
('f', 'h', 20, False, 'parent-revid'),
1578
j_entry = ('b', 'j', 'c-file'), [
1579
('r', 'c', 0, False, ''),
1580
('f', 'j', 20, False, 'parent-revid'),
1583
dirblocks.append(('', [root_entry]))
1584
dirblocks.append(('', [a_entry, b_entry, c_entry, d_entry]))
1585
dirblocks.append(('a', [e_entry, f_entry]))
1586
dirblocks.append(('b', [g_entry, h_entry1, h_entry2, i_entry, j_entry]))
1587
state = dirstate.DirState.initialize('dirstate')
1590
state._set_data(['parent'], dirblocks)
1594
return state, dirblocks
1596
def test_iter_children_b(self):
1597
state, dirblocks = self.create_dirstate_with_two_trees()
1598
self.addCleanup(state.unlock)
1599
expected_result = []
1600
expected_result.append(dirblocks[3][1][2]) # h2
1601
expected_result.append(dirblocks[3][1][3]) # i
1602
expected_result.append(dirblocks[3][1][4]) # j
1603
self.assertEqual(expected_result,
1604
list(state._iter_child_entries(1, 'b')))
1606
def test_iter_child_root(self):
1607
state, dirblocks = self.create_dirstate_with_two_trees()
1608
self.addCleanup(state.unlock)
1609
expected_result = []
1610
expected_result.append(dirblocks[1][1][0]) # a
1611
expected_result.append(dirblocks[1][1][1]) # b
1612
expected_result.append(dirblocks[1][1][3]) # d
1613
expected_result.append(dirblocks[2][1][0]) # e
1614
expected_result.append(dirblocks[2][1][1]) # f
1615
expected_result.append(dirblocks[3][1][2]) # h2
1616
expected_result.append(dirblocks[3][1][3]) # i
1617
expected_result.append(dirblocks[3][1][4]) # j
1618
self.assertEqual(expected_result,
1619
list(state._iter_child_entries(1, '')))
1622
class TestDirstateSortOrder(tests.TestCaseWithTransport):
1623
"""Test that DirState adds entries in the right order."""
1625
def test_add_sorting(self):
1626
"""Add entries in lexicographical order, we get path sorted order.
1628
This tests it to a depth of 4, to make sure we don't just get it right
1629
at a single depth. 'a/a' should come before 'a-a', even though it
1630
doesn't lexicographically.
1632
dirs = ['a', 'a/a', 'a/a/a', 'a/a/a/a',
1633
'a-a', 'a/a-a', 'a/a/a-a', 'a/a/a/a-a',
1636
state = dirstate.DirState.initialize('dirstate')
1637
self.addCleanup(state.unlock)
1639
fake_stat = os.stat('dirstate')
1641
d_id = d.replace('/', '_')+'-id'
1642
file_path = d + '/f'
1643
file_id = file_path.replace('/', '_')+'-id'
1644
state.add(d, d_id, 'directory', fake_stat, null_sha)
1645
state.add(file_path, file_id, 'file', fake_stat, null_sha)
1647
expected = ['', '', 'a',
1648
'a/a', 'a/a/a', 'a/a/a/a',
1649
'a/a/a/a-a', 'a/a/a-a', 'a/a-a', 'a-a',
1651
split = lambda p:p.split('/')
1652
self.assertEqual(sorted(expected, key=split), expected)
1653
dirblock_names = [d[0] for d in state._dirblocks]
1654
self.assertEqual(expected, dirblock_names)
1656
def test_set_parent_trees_correct_order(self):
1657
"""After calling set_parent_trees() we should maintain the order."""
1658
dirs = ['a', 'a-a', 'a/a']
1660
state = dirstate.DirState.initialize('dirstate')
1661
self.addCleanup(state.unlock)
1663
fake_stat = os.stat('dirstate')
1665
d_id = d.replace('/', '_')+'-id'
1666
file_path = d + '/f'
1667
file_id = file_path.replace('/', '_')+'-id'
1668
state.add(d, d_id, 'directory', fake_stat, null_sha)
1669
state.add(file_path, file_id, 'file', fake_stat, null_sha)
1671
expected = ['', '', 'a', 'a/a', 'a-a']
1672
dirblock_names = [d[0] for d in state._dirblocks]
1673
self.assertEqual(expected, dirblock_names)
1675
# *really* cheesy way to just get an empty tree
1676
repo = self.make_repository('repo')
1677
empty_tree = repo.revision_tree(_mod_revision.NULL_REVISION)
1678
state.set_parent_trees([('null:', empty_tree)], [])
1680
dirblock_names = [d[0] for d in state._dirblocks]
1681
self.assertEqual(expected, dirblock_names)
1684
class InstrumentedDirState(dirstate.DirState):
1685
"""An DirState with instrumented sha1 functionality."""
1687
def __init__(self, path, sha1_provider):
1688
super(InstrumentedDirState, self).__init__(path, sha1_provider)
1689
self._time_offset = 0
1691
# member is dynamically set in DirState.__init__ to turn on trace
1692
self._sha1_provider = sha1_provider
1693
self._sha1_file = self._sha1_file_and_log
1695
def _sha_cutoff_time(self):
1696
timestamp = super(InstrumentedDirState, self)._sha_cutoff_time()
1697
self._cutoff_time = timestamp + self._time_offset
1699
def _sha1_file_and_log(self, abspath):
1700
self._log.append(('sha1', abspath))
1701
return self._sha1_provider.sha1(abspath)
1703
def _read_link(self, abspath, old_link):
1704
self._log.append(('read_link', abspath, old_link))
1705
return super(InstrumentedDirState, self)._read_link(abspath, old_link)
1707
def _lstat(self, abspath, entry):
1708
self._log.append(('lstat', abspath))
1709
return super(InstrumentedDirState, self)._lstat(abspath, entry)
1711
def _is_executable(self, mode, old_executable):
1712
self._log.append(('is_exec', mode, old_executable))
1713
return super(InstrumentedDirState, self)._is_executable(mode,
1716
def adjust_time(self, secs):
1717
"""Move the clock forward or back.
1719
:param secs: The amount to adjust the clock by. Positive values make it
1720
seem as if we are in the future, negative values make it seem like we
1723
self._time_offset += secs
1724
self._cutoff_time = None
1727
class _FakeStat(object):
1728
"""A class with the same attributes as a real stat result."""
1730
def __init__(self, size, mtime, ctime, dev, ino, mode):
1732
self.st_mtime = mtime
1733
self.st_ctime = ctime
1740
return _FakeStat(st.st_size, st.st_mtime, st.st_ctime, st.st_dev,
1741
st.st_ino, st.st_mode)
1744
class TestPackStat(tests.TestCaseWithTransport):
1746
def assertPackStat(self, expected, stat_value):
1747
"""Check the packed and serialized form of a stat value."""
1748
self.assertEqual(expected, dirstate.pack_stat(stat_value))
1750
def test_pack_stat_int(self):
1751
st = _FakeStat(6859L, 1172758614, 1172758617, 777L, 6499538L, 0100644)
1752
# Make sure that all parameters have an impact on the packed stat.
1753
self.assertPackStat('AAAay0Xm4FZF5uBZAAADCQBjLNIAAIGk', st)
1756
self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1757
st.st_mtime = 1172758620
1759
self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1760
st.st_ctime = 1172758630
1762
self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1765
self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNIAAIGk', st)
1766
st.st_ino = 6499540L
1768
self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIGk', st)
1769
st.st_mode = 0100744
1771
self.assertPackStat('AAAbWEXm4FxF5uBmAAADeABjLNQAAIHk', st)
1773
def test_pack_stat_float(self):
1774
"""On some platforms mtime and ctime are floats.
1776
Make sure we don't get warnings or errors, and that we ignore changes <
1779
st = _FakeStat(7000L, 1172758614.0, 1172758617.0,
1780
777L, 6499538L, 0100644)
1781
# These should all be the same as the integer counterparts
1782
self.assertPackStat('AAAbWEXm4FZF5uBZAAADCQBjLNIAAIGk', st)
1783
st.st_mtime = 1172758620.0
1785
self.assertPackStat('AAAbWEXm4FxF5uBZAAADCQBjLNIAAIGk', st)
1786
st.st_ctime = 1172758630.0
1788
self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1789
# fractional seconds are discarded, so no change from above
1790
st.st_mtime = 1172758620.453
1791
self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1792
st.st_ctime = 1172758630.228
1793
self.assertPackStat('AAAbWEXm4FxF5uBmAAADCQBjLNIAAIGk', st)
1796
class TestBisect(TestCaseWithDirState):
1797
"""Test the ability to bisect into the disk format."""
1799
def assertBisect(self, expected_map, map_keys, state, paths):
1800
"""Assert that bisecting for paths returns the right result.
1802
:param expected_map: A map from key => entry value
1803
:param map_keys: The keys to expect for each path
1804
:param state: The DirState object.
1805
:param paths: A list of paths, these will automatically be split into
1806
(dir, name) tuples, and sorted according to how _bisect
1809
result = state._bisect(paths)
1810
# For now, results are just returned in whatever order we read them.
1811
# We could sort by (dir, name, file_id) or something like that, but in
1812
# the end it would still be fairly arbitrary, and we don't want the
1813
# extra overhead if we can avoid it. So sort everything to make sure
1815
self.assertEqual(len(map_keys), len(paths))
1817
for path, keys in zip(paths, map_keys):
1819
# This should not be present in the output
1821
expected[path] = sorted(expected_map[k] for k in keys)
1823
# The returned values are just arranged randomly based on when they
1824
# were read, for testing, make sure it is properly sorted.
1828
self.assertEqual(expected, result)
1830
def assertBisectDirBlocks(self, expected_map, map_keys, state, paths):
1831
"""Assert that bisecting for dirbblocks returns the right result.
1833
:param expected_map: A map from key => expected values
1834
:param map_keys: A nested list of paths we expect to be returned.
1835
Something like [['a', 'b', 'f'], ['b/c', 'b/d']]
1836
:param state: The DirState object.
1837
:param paths: A list of directories
1839
result = state._bisect_dirblocks(paths)
1840
self.assertEqual(len(map_keys), len(paths))
1842
for path, keys in zip(paths, map_keys):
1844
# This should not be present in the output
1846
expected[path] = sorted(expected_map[k] for k in keys)
1850
self.assertEqual(expected, result)
1852
def assertBisectRecursive(self, expected_map, map_keys, state, paths):
1853
"""Assert the return value of a recursive bisection.
1855
:param expected_map: A map from key => entry value
1856
:param map_keys: A list of paths we expect to be returned.
1857
Something like ['a', 'b', 'f', 'b/d', 'b/d2']
1858
:param state: The DirState object.
1859
:param paths: A list of files and directories. It will be broken up
1860
into (dir, name) pairs and sorted before calling _bisect_recursive.
1863
for key in map_keys:
1864
entry = expected_map[key]
1865
dir_name_id, trees_info = entry
1866
expected[dir_name_id] = trees_info
1868
result = state._bisect_recursive(paths)
1870
self.assertEqual(expected, result)
1872
def test_bisect_each(self):
1873
"""Find a single record using bisect."""
1874
tree, state, expected = self.create_basic_dirstate()
1876
# Bisect should return the rows for the specified files.
1877
self.assertBisect(expected, [['']], state, [''])
1878
self.assertBisect(expected, [['a']], state, ['a'])
1879
self.assertBisect(expected, [['b']], state, ['b'])
1880
self.assertBisect(expected, [['b/c']], state, ['b/c'])
1881
self.assertBisect(expected, [['b/d']], state, ['b/d'])
1882
self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1883
self.assertBisect(expected, [['b-c']], state, ['b-c'])
1884
self.assertBisect(expected, [['f']], state, ['f'])
1886
def test_bisect_multi(self):
1887
"""Bisect can be used to find multiple records at the same time."""
1888
tree, state, expected = self.create_basic_dirstate()
1889
# Bisect should be capable of finding multiple entries at the same time
1890
self.assertBisect(expected, [['a'], ['b'], ['f']],
1891
state, ['a', 'b', 'f'])
1892
self.assertBisect(expected, [['f'], ['b/d'], ['b/d/e']],
1893
state, ['f', 'b/d', 'b/d/e'])
1894
self.assertBisect(expected, [['b'], ['b-c'], ['b/c']],
1895
state, ['b', 'b-c', 'b/c'])
1897
def test_bisect_one_page(self):
1898
"""Test bisect when there is only 1 page to read"""
1899
tree, state, expected = self.create_basic_dirstate()
1900
state._bisect_page_size = 5000
1901
self.assertBisect(expected,[['']], state, [''])
1902
self.assertBisect(expected,[['a']], state, ['a'])
1903
self.assertBisect(expected,[['b']], state, ['b'])
1904
self.assertBisect(expected,[['b/c']], state, ['b/c'])
1905
self.assertBisect(expected,[['b/d']], state, ['b/d'])
1906
self.assertBisect(expected,[['b/d/e']], state, ['b/d/e'])
1907
self.assertBisect(expected,[['b-c']], state, ['b-c'])
1908
self.assertBisect(expected,[['f']], state, ['f'])
1909
self.assertBisect(expected,[['a'], ['b'], ['f']],
1910
state, ['a', 'b', 'f'])
1911
self.assertBisect(expected, [['b/d'], ['b/d/e'], ['f']],
1912
state, ['b/d', 'b/d/e', 'f'])
1913
self.assertBisect(expected, [['b'], ['b/c'], ['b-c']],
1914
state, ['b', 'b/c', 'b-c'])
1916
def test_bisect_duplicate_paths(self):
1917
"""When bisecting for a path, handle multiple entries."""
1918
tree, state, expected = self.create_duplicated_dirstate()
1920
# Now make sure that both records are properly returned.
1921
self.assertBisect(expected, [['']], state, [''])
1922
self.assertBisect(expected, [['a', 'a2']], state, ['a'])
1923
self.assertBisect(expected, [['b', 'b2']], state, ['b'])
1924
self.assertBisect(expected, [['b/c', 'b/c2']], state, ['b/c'])
1925
self.assertBisect(expected, [['b/d', 'b/d2']], state, ['b/d'])
1926
self.assertBisect(expected, [['b/d/e', 'b/d/e2']],
1928
self.assertBisect(expected, [['b-c', 'b-c2']], state, ['b-c'])
1929
self.assertBisect(expected, [['f', 'f2']], state, ['f'])
1931
def test_bisect_page_size_too_small(self):
1932
"""If the page size is too small, we will auto increase it."""
1933
tree, state, expected = self.create_basic_dirstate()
1934
state._bisect_page_size = 50
1935
self.assertBisect(expected, [None], state, ['b/e'])
1936
self.assertBisect(expected, [['a']], state, ['a'])
1937
self.assertBisect(expected, [['b']], state, ['b'])
1938
self.assertBisect(expected, [['b/c']], state, ['b/c'])
1939
self.assertBisect(expected, [['b/d']], state, ['b/d'])
1940
self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1941
self.assertBisect(expected, [['b-c']], state, ['b-c'])
1942
self.assertBisect(expected, [['f']], state, ['f'])
1944
def test_bisect_missing(self):
1945
"""Test that bisect return None if it cannot find a path."""
1946
tree, state, expected = self.create_basic_dirstate()
1947
self.assertBisect(expected, [None], state, ['foo'])
1948
self.assertBisect(expected, [None], state, ['b/foo'])
1949
self.assertBisect(expected, [None], state, ['bar/foo'])
1950
self.assertBisect(expected, [None], state, ['b-c/foo'])
1952
self.assertBisect(expected, [['a'], None, ['b/d']],
1953
state, ['a', 'foo', 'b/d'])
1955
def test_bisect_rename(self):
1956
"""Check that we find a renamed row."""
1957
tree, state, expected = self.create_renamed_dirstate()
1959
# Search for the pre and post renamed entries
1960
self.assertBisect(expected, [['a']], state, ['a'])
1961
self.assertBisect(expected, [['b/g']], state, ['b/g'])
1962
self.assertBisect(expected, [['b/d']], state, ['b/d'])
1963
self.assertBisect(expected, [['h']], state, ['h'])
1965
# What about b/d/e? shouldn't that also get 2 directory entries?
1966
self.assertBisect(expected, [['b/d/e']], state, ['b/d/e'])
1967
self.assertBisect(expected, [['h/e']], state, ['h/e'])
1969
def test_bisect_dirblocks(self):
1970
tree, state, expected = self.create_duplicated_dirstate()
1971
self.assertBisectDirBlocks(expected,
1972
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2']],
1974
self.assertBisectDirBlocks(expected,
1975
[['b/c', 'b/c2', 'b/d', 'b/d2']], state, ['b'])
1976
self.assertBisectDirBlocks(expected,
1977
[['b/d/e', 'b/d/e2']], state, ['b/d'])
1978
self.assertBisectDirBlocks(expected,
1979
[['', 'a', 'a2', 'b', 'b2', 'b-c', 'b-c2', 'f', 'f2'],
1980
['b/c', 'b/c2', 'b/d', 'b/d2'],
1981
['b/d/e', 'b/d/e2'],
1982
], state, ['', 'b', 'b/d'])
1984
def test_bisect_dirblocks_missing(self):
1985
tree, state, expected = self.create_basic_dirstate()
1986
self.assertBisectDirBlocks(expected, [['b/d/e'], None],
1987
state, ['b/d', 'b/e'])
1988
# Files don't show up in this search
1989
self.assertBisectDirBlocks(expected, [None], state, ['a'])
1990
self.assertBisectDirBlocks(expected, [None], state, ['b/c'])
1991
self.assertBisectDirBlocks(expected, [None], state, ['c'])
1992
self.assertBisectDirBlocks(expected, [None], state, ['b/d/e'])
1993
self.assertBisectDirBlocks(expected, [None], state, ['f'])
1995
def test_bisect_recursive_each(self):
1996
tree, state, expected = self.create_basic_dirstate()
1997
self.assertBisectRecursive(expected, ['a'], state, ['a'])
1998
self.assertBisectRecursive(expected, ['b/c'], state, ['b/c'])
1999
self.assertBisectRecursive(expected, ['b/d/e'], state, ['b/d/e'])
2000
self.assertBisectRecursive(expected, ['b-c'], state, ['b-c'])
2001
self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2003
self.assertBisectRecursive(expected, ['b', 'b/c', 'b/d', 'b/d/e'],
2005
self.assertBisectRecursive(expected, ['', 'a', 'b', 'b-c', 'f', 'b/c',
2009
def test_bisect_recursive_multiple(self):
2010
tree, state, expected = self.create_basic_dirstate()
2011
self.assertBisectRecursive(expected, ['a', 'b/c'], state, ['a', 'b/c'])
2012
self.assertBisectRecursive(expected, ['b/d', 'b/d/e'],
2013
state, ['b/d', 'b/d/e'])
2015
def test_bisect_recursive_missing(self):
2016
tree, state, expected = self.create_basic_dirstate()
2017
self.assertBisectRecursive(expected, [], state, ['d'])
2018
self.assertBisectRecursive(expected, [], state, ['b/e'])
2019
self.assertBisectRecursive(expected, [], state, ['g'])
2020
self.assertBisectRecursive(expected, ['a'], state, ['a', 'g'])
2022
def test_bisect_recursive_renamed(self):
2023
tree, state, expected = self.create_renamed_dirstate()
2025
# Looking for either renamed item should find the other
2026
self.assertBisectRecursive(expected, ['a', 'b/g'], state, ['a'])
2027
self.assertBisectRecursive(expected, ['a', 'b/g'], state, ['b/g'])
2028
# Looking in the containing directory should find the rename target,
2029
# and anything in a subdir of the renamed target.
2030
self.assertBisectRecursive(expected, ['a', 'b', 'b/c', 'b/d',
2031
'b/d/e', 'b/g', 'h', 'h/e'],
2035
class TestDirstateValidation(TestCaseWithDirState):
2037
def test_validate_correct_dirstate(self):
2038
state = self.create_complex_dirstate()
2041
# and make sure we can also validate with a read lock
2048
def test_dirblock_not_sorted(self):
2049
tree, state, expected = self.create_renamed_dirstate()
2050
state._read_dirblocks_if_needed()
2051
last_dirblock = state._dirblocks[-1]
2052
# we're appending to the dirblock, but this name comes before some of
2053
# the existing names; that's wrong
2054
last_dirblock[1].append(
2055
(('h', 'aaaa', 'a-id'),
2056
[('a', '', 0, False, ''),
2057
('a', '', 0, False, '')]))
2058
e = self.assertRaises(AssertionError,
2060
self.assertContainsRe(str(e), 'not sorted')
2062
def test_dirblock_name_mismatch(self):
2063
tree, state, expected = self.create_renamed_dirstate()
2064
state._read_dirblocks_if_needed()
2065
last_dirblock = state._dirblocks[-1]
2066
# add an entry with the wrong directory name
2067
last_dirblock[1].append(
2069
[('a', '', 0, False, ''),
2070
('a', '', 0, False, '')]))
2071
e = self.assertRaises(AssertionError,
2073
self.assertContainsRe(str(e),
2074
"doesn't match directory name")
2076
def test_dirblock_missing_rename(self):
2077
tree, state, expected = self.create_renamed_dirstate()
2078
state._read_dirblocks_if_needed()
2079
last_dirblock = state._dirblocks[-1]
2080
# make another entry for a-id, without a correct 'r' pointer to
2081
# the real occurrence in the working tree
2082
last_dirblock[1].append(
2083
(('h', 'z', 'a-id'),
2084
[('a', '', 0, False, ''),
2085
('a', '', 0, False, '')]))
2086
e = self.assertRaises(AssertionError,
2088
self.assertContainsRe(str(e),
2089
'file a-id is absent in row')
2092
class TestDirstateTreeReference(TestCaseWithDirState):
2094
def test_reference_revision_is_none(self):
2095
tree = self.make_branch_and_tree('tree', format='dirstate-with-subtree')
2096
subtree = self.make_branch_and_tree('tree/subtree',
2097
format='dirstate-with-subtree')
2098
subtree.set_root_id('subtree')
2099
tree.add_reference(subtree)
2101
state = dirstate.DirState.from_tree(tree, 'dirstate')
2102
key = ('', 'subtree', 'subtree')
2103
expected = ('', [(key,
2104
[('t', '', 0, False, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')])])
2107
self.assertEqual(expected, state._find_block(key))
2112
class TestDiscardMergeParents(TestCaseWithDirState):
2114
def test_discard_no_parents(self):
2115
# This should be a no-op
2116
state = self.create_empty_dirstate()
2117
self.addCleanup(state.unlock)
2118
state._discard_merge_parents()
2121
def test_discard_one_parent(self):
2123
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2124
root_entry_direntry = ('', '', 'a-root-value'), [
2125
('d', '', 0, False, packed_stat),
2126
('d', '', 0, False, packed_stat),
2129
dirblocks.append(('', [root_entry_direntry]))
2130
dirblocks.append(('', []))
2132
state = self.create_empty_dirstate()
2133
self.addCleanup(state.unlock)
2134
state._set_data(['parent-id'], dirblocks[:])
2137
state._discard_merge_parents()
2139
self.assertEqual(dirblocks, state._dirblocks)
2141
def test_discard_simple(self):
2143
packed_stat = 'AAAAREUHaIpFB2iKAAADAQAtkqUAAIGk'
2144
root_entry_direntry = ('', '', 'a-root-value'), [
2145
('d', '', 0, False, packed_stat),
2146
('d', '', 0, False, packed_stat),
2147
('d', '', 0, False, packed_stat),
2149
expected_root_entry_direntry = ('', '', 'a-root-value'), [
2150
('d', '', 0, False, packed_stat),
2151
('d', '', 0, False, packed_stat),
2154
dirblocks.append(('', [root_entry_direntry]))
2155
dirblocks.append(('', []))
2157
state = self.create_empty_dirstate()
2158
self.addCleanup(state.unlock)
2159
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2162
# This should strip of the extra column
2163
state._discard_merge_parents()
2165
expected_dirblocks = [('', [expected_root_entry_direntry]), ('', [])]
2166
self.assertEqual(expected_dirblocks, state._dirblocks)
2168
def test_discard_absent(self):
2169
"""If entries are only in a merge, discard should remove the entries"""
2170
null_stat = dirstate.DirState.NULLSTAT
2171
present_dir = ('d', '', 0, False, null_stat)
2172
present_file = ('f', '', 0, False, null_stat)
2173
absent = dirstate.DirState.NULL_PARENT_DETAILS
2174
root_key = ('', '', 'a-root-value')
2175
file_in_root_key = ('', 'file-in-root', 'a-file-id')
2176
file_in_merged_key = ('', 'file-in-merged', 'b-file-id')
2177
dirblocks = [('', [(root_key, [present_dir, present_dir, present_dir])]),
2178
('', [(file_in_merged_key,
2179
[absent, absent, present_file]),
2181
[present_file, present_file, present_file]),
2185
state = self.create_empty_dirstate()
2186
self.addCleanup(state.unlock)
2187
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2190
exp_dirblocks = [('', [(root_key, [present_dir, present_dir])]),
2191
('', [(file_in_root_key,
2192
[present_file, present_file]),
2195
state._discard_merge_parents()
2197
self.assertEqual(exp_dirblocks, state._dirblocks)
2199
def test_discard_renamed(self):
2200
null_stat = dirstate.DirState.NULLSTAT
2201
present_dir = ('d', '', 0, False, null_stat)
2202
present_file = ('f', '', 0, False, null_stat)
2203
absent = dirstate.DirState.NULL_PARENT_DETAILS
2204
root_key = ('', '', 'a-root-value')
2205
file_in_root_key = ('', 'file-in-root', 'a-file-id')
2206
# Renamed relative to parent
2207
file_rename_s_key = ('', 'file-s', 'b-file-id')
2208
file_rename_t_key = ('', 'file-t', 'b-file-id')
2209
# And one that is renamed between the parents, but absent in this
2210
key_in_1 = ('', 'file-in-1', 'c-file-id')
2211
key_in_2 = ('', 'file-in-2', 'c-file-id')
2214
('', [(root_key, [present_dir, present_dir, present_dir])]),
2216
[absent, present_file, ('r', 'file-in-2', 'c-file-id')]),
2218
[absent, ('r', 'file-in-1', 'c-file-id'), present_file]),
2220
[present_file, present_file, present_file]),
2222
[('r', 'file-t', 'b-file-id'), absent, present_file]),
2224
[present_file, absent, ('r', 'file-s', 'b-file-id')]),
2228
('', [(root_key, [present_dir, present_dir])]),
2229
('', [(key_in_1, [absent, present_file]),
2230
(file_in_root_key, [present_file, present_file]),
2231
(file_rename_t_key, [present_file, absent]),
2234
state = self.create_empty_dirstate()
2235
self.addCleanup(state.unlock)
2236
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2239
state._discard_merge_parents()
2241
self.assertEqual(exp_dirblocks, state._dirblocks)
2243
def test_discard_all_subdir(self):
2244
null_stat = dirstate.DirState.NULLSTAT
2245
present_dir = ('d', '', 0, False, null_stat)
2246
present_file = ('f', '', 0, False, null_stat)
2247
absent = dirstate.DirState.NULL_PARENT_DETAILS
2248
root_key = ('', '', 'a-root-value')
2249
subdir_key = ('', 'sub', 'dir-id')
2250
child1_key = ('sub', 'child1', 'child1-id')
2251
child2_key = ('sub', 'child2', 'child2-id')
2252
child3_key = ('sub', 'child3', 'child3-id')
2255
('', [(root_key, [present_dir, present_dir, present_dir])]),
2256
('', [(subdir_key, [present_dir, present_dir, present_dir])]),
2257
('sub', [(child1_key, [absent, absent, present_file]),
2258
(child2_key, [absent, absent, present_file]),
2259
(child3_key, [absent, absent, present_file]),
2263
('', [(root_key, [present_dir, present_dir])]),
2264
('', [(subdir_key, [present_dir, present_dir])]),
2267
state = self.create_empty_dirstate()
2268
self.addCleanup(state.unlock)
2269
state._set_data(['parent-id', 'merged-id'], dirblocks[:])
2272
state._discard_merge_parents()
2274
self.assertEqual(exp_dirblocks, state._dirblocks)
2277
class Test_InvEntryToDetails(tests.TestCase):
2279
def assertDetails(self, expected, inv_entry):
2280
details = dirstate.DirState._inv_entry_to_details(inv_entry)
2281
self.assertEqual(expected, details)
2282
# details should always allow join() and always be a plain str when
2284
(minikind, fingerprint, size, executable, tree_data) = details
2285
self.assertIsInstance(minikind, str)
2286
self.assertIsInstance(fingerprint, str)
2287
self.assertIsInstance(tree_data, str)
2289
def test_unicode_symlink(self):
2290
inv_entry = inventory.InventoryLink('link-file-id',
2291
u'nam\N{Euro Sign}e',
2293
inv_entry.revision = 'link-revision-id'
2294
target = u'link-targ\N{Euro Sign}t'
2295
inv_entry.symlink_target = target
2296
self.assertDetails(('l', target.encode('UTF-8'), 0, False,
2297
'link-revision-id'), inv_entry)
2300
class TestSHA1Provider(tests.TestCaseInTempDir):
2302
def test_sha1provider_is_an_interface(self):
2303
p = dirstate.SHA1Provider()
2304
self.assertRaises(NotImplementedError, p.sha1, "foo")
2305
self.assertRaises(NotImplementedError, p.stat_and_sha1, "foo")
2307
def test_defaultsha1provider_sha1(self):
2308
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2309
self.build_tree_contents([('foo', text)])
2310
expected_sha = osutils.sha_string(text)
2311
p = dirstate.DefaultSHA1Provider()
2312
self.assertEqual(expected_sha, p.sha1('foo'))
2314
def test_defaultsha1provider_stat_and_sha1(self):
2315
text = 'test\r\nwith\nall\rpossible line endings\r\n'
2316
self.build_tree_contents([('foo', text)])
2317
expected_sha = osutils.sha_string(text)
2318
p = dirstate.DefaultSHA1Provider()
2319
statvalue, sha1 = p.stat_and_sha1('foo')
2320
self.assertTrue(len(statvalue) >= 10)
2321
self.assertEqual(len(text), statvalue.st_size)
2322
self.assertEqual(expected_sha, sha1)
730
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY, state._header_state)
731
self.assertEqual(dirstate.DirState.NOT_IN_MEMORY, state._dirblock_state)
732
self.assertEntryEqual('', '', 'a-root-value', state, '', 0)