65
65
OR (for non tree-0)
66
66
entry[1][1][4]: revision_id
68
There may be multiple rows at the root, one per id present in the root, so the in memory root row is now:
69
_root_entries[0][0]: ''
70
_root_entries[0][1]: ''
71
_root_entries[0][2]: file_id
72
_root_entries[1][0]: The tree data for the current tree for this fileid at /
68
There may be multiple rows at the root, one per id present in the root, so the
69
in memory root row is now:
70
self._dirblocks[0] -> ('', [entry ...]),
71
and the entries in there are
74
entries[0][2]: file_id
75
entries[1][0]: The tree data for the current tree for this fileid at /
298
300
# faster than three separate encodes.
299
301
utf8path = (dirname + '/' + basename).strip('/').encode('utf8')
300
302
dirname, basename = os.path.split(utf8path)
303
entry_key = (dirname, basename, file_id.encode('utf8'))
301
304
self._read_dirblocks_if_needed()
302
block_index = self._find_dirblock_index(dirname)
305
block_index, present = self._find_block_index_from_key(entry_key)
307
# TODO: This test is not complete - an empty directory, or a
308
# directory for a parent tree will fool it.
304
309
# some parent path has not been added - its an error to add this
306
311
raise errors.NotVersionedError(path, str(self))
329
333
raise errors.BzrError('unknown kind %r' % kind)
330
entry_index = bisect.bisect_left(block, entry_data)
331
if len(block) > entry_index:
332
assert block[entry_index][0][1] != basename, \
333
"basename %r already added" % basename
334
entry_index, present = self._find_entry_index(entry_key, block)
335
assert not present, "basename %r already added" % basename
334
336
block.insert(entry_index, entry_data)
336
338
if kind == 'directory':
338
340
self._ensure_block(block_index, entry_index, utf8path)
339
341
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
341
def add_deleted(self, fileid_utf8, parents):
342
"""Add fileid_utf8 with parents as deleted."""
343
raise Exception, "broken"
344
self._read_dirblocks_if_needed()
345
new_row = self._make_deleted_row(fileid_utf8, parents)
346
block_index = self._find_dirblock_index(new_row[0][0])
348
# no deleted block yet.
349
bisect.insort_left(self._dirblocks, (new_row[0][0], []))
350
block_index = self._find_dirblock_index(new_row[0][0])
351
block = self._dirblocks[block_index][1]
352
row_index = bisect.insort_left(block, new_row)
353
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
355
343
def _empty_parent_info(self):
356
344
return [DirState.NULL_PARENT_DETAILS] * (len(self._parents) -
357
345
len(self._ghosts))
376
364
:param dirname: The utf8 dirname to ensure there is a block for.
377
365
:return: The index for the block.
379
368
# the basename of the directory must be the end of its full name.
380
369
if not (parent_block_index == -1 and
381
370
parent_block_index == -1 and dirname == ''):
382
371
assert dirname.endswith(
383
372
self._dirblocks[parent_block_index][1][parent_row_index][0][1])
384
## In future, when doing partial parsing, this should load and
385
# populate the entire block.
386
index = bisect.bisect_left(self._dirblocks, (dirname, []))
387
if (index == len(self._dirblocks) or
388
self._dirblocks[index][0] != dirname):
389
self._dirblocks.insert(index, (dirname, []))
373
block_index, present = self._find_block_index_from_key((dirname, '', ''))
375
## In future, when doing partial parsing, this should load and
376
# populate the entire block.
377
self._dirblocks.insert(block_index, (dirname, []))
392
380
def _entries_to_current_state(self, new_entries):
393
"""Load new_entries into self._root_entries and self.dirblocks.
381
"""Load new_entries into self.dirblocks.
395
383
Process new_entries into the current state object, making them the active
402
390
assert new_entries[0][0][0:2] == ('', ''), \
403
391
"Missing root row %r" % new_entries[0][0]
404
self._root_entries = []
405
self._dirblocks = [('', [])]
406
current_block = self._root_entries
392
# The two blocks here are deliberate: the root block and the
393
# contents-of-root block.
394
self._dirblocks = [('', []), ('', [])]
395
current_block = self._dirblocks[0][1]
407
396
current_dirname = ''
408
397
root_key = ('', '')
409
398
for entry in new_entries:
410
399
if entry[0][0] != current_dirname:
400
# new block - different dirname
412
401
current_block = []
413
402
current_dirname = entry[0][0]
414
403
self._dirblocks.append((current_dirname, current_block))
415
elif entry[0][0:2] != root_key:
416
# this is not a root entry for a tree
405
# this is not a root entry for a tree (it has a basename)
417
406
current_block = self._dirblocks[-1][1]
418
407
# append the entry to the current block
419
408
current_block.append(entry)
443
432
:param key: A dirstate entry key.
444
433
:return: The block tuple.
446
if key[0:2] == ('', ''):
447
return ('', self._root_entries)
449
block_index, present = self._find_block_index_from_key(key)
452
self._dirblocks.insert(block_index, (key[0], []))
454
# some parent path has not been added - its an error to add this
456
raise errors.NotVersionedError(key[0:2], str(self))
457
return self._dirblocks[block_index]
435
block_index, present = self._find_block_index_from_key(key)
438
self._dirblocks.insert(block_index, (key[0], []))
440
# some parent path has not been added - its an error to add this
442
raise errors.NotVersionedError(key[0:2], str(self))
443
return self._dirblocks[block_index]
459
445
def _find_block_index_from_key(self, key):
460
446
"""Find the dirblock index for a key.
462
448
:return: The block index, True if the block for the key is present.
464
block_index = bisect.bisect_left(self._dirblocks, (key[0], []))
450
if key[0:2] == ('', ''):
452
block_index = bisect.bisect_left(self._dirblocks, (key[0], []), 1)
453
# _right returns one-past-where-key is so we have to subtract
454
# one to use it. we use _right here because there are two
455
# '' blocks - the root, and the contents of root
456
# we always have a minimum of 2 in self._dirblocks: root and
457
# root-contents, and for '', we get 2 back, so this is
458
# simple and correct:
465
459
present = (block_index < len(self._dirblocks) and
466
460
self._dirblocks[block_index][0] == key[0])
467
461
return block_index, present
469
def _find_dirblock_index(self, dirname):
470
"""Find the dirblock index for dirname.
472
:return: -1 if the dirname is not present, or the index in
473
self._dirblocks for it otherwise.
475
block_index = bisect.bisect_left(self._dirblocks, (dirname, []))
476
if (block_index == len(self._dirblocks) or
477
self._dirblocks[block_index][0] != dirname):
481
463
def _find_entry_index(self, key, block):
482
464
"""Find the entry index for a key in a block.
495
477
:param tree: The tree which should provide parent information and
480
result = DirState.initialize(dir_state_filename)
499
# XXX: aka the big ugly.: To fix this, turn it into:
500
# init; set_path_id(root); set_parents(tree.get_parnets); write_inventory(tree.inventory)
502
result._state_file = open(dir_state_filename, 'wb+')
504
_encode = base64.encodestring
506
482
parent_ids = tree.get_parent_ids()
507
483
num_parents = len(parent_ids)
509
raise ValueError('Cannot handle more than 3 parents')
511
484
parent_trees = []
512
485
for parent_id in parent_ids:
513
parent_trees.append(tree.branch.repository.revision_tree(parent_id))
514
parent_trees[-1].lock_read()
515
all_trees = [tree] + parent_trees
516
num_trees = len(all_trees)
518
# FIXME: is this utf8 safe?
520
to_minikind = DirState._kind_to_minikind
521
to_yesno = DirState._to_yesno
523
st = os.lstat(tree.basedir)
526
for tree_index, tree in enumerate(all_trees):
527
for path, tree_entry in tree.iter_entries_by_dir():
528
dirname, basename = os.path.split(path.encode('utf8'))
529
file_id = tree_entry.file_id.encode('utf8')
530
kind = tree_entry.kind
531
if kind == 'directory':
535
elif kind == 'symlink':
536
fingerprint = tree.symlink_target(path)
540
fingerprint = tree.get_file_sha1(tree_entry.file_id, path)
541
size = tree_entry.text_size
542
executable = tree.is_executable(tree_entry.file_id, path)
546
key = (dirname, basename, file_id)
547
if (dirname, basename) == ('', ''):
551
block_index = bisect.bisect_left(dirblocks, (dirname, []))
552
if block_index == len(dirblocks) or dirblocks[block_index][0] != dirname:
554
dirblocks.insert(block_index, (dirname, []))
555
block = dirblocks[block_index][1]
556
# find the data for this path within block:
557
entry_index = bisect.bisect_left(block, (key,))
558
if entry_index == len(block) or block[entry_index][0] != key:
559
# new key in this block, add blank data
560
block.insert(entry_index, (key, [None] * num_trees))
561
# get the right form of data for this trees type
564
st = os.lstat(tree.abspath(path))
565
tree_data = pack_stat(st)
569
tree_data = tree_entry.revision.encode('utf8')
570
block[entry_index][1][tree_index] = (
577
result._set_data(parent_ids, root_entries, dirblocks)
579
for tree in all_trees:
486
parent_trees.append((parent_id, tree.branch.repository.revision_tree(parent_id)))
487
parent_trees[-1][1].lock_read()
488
result.set_parent_trees(parent_trees, [])
489
result.set_state_from_inventory(tree.inventory)
491
for revid, parent in parent_trees:
583
496
def get_ghosts(self):
632
545
rather it indicates that there are at least some files in some
633
546
tree present there.
635
# looking up the root is not supported, because the root entries exist
636
# outside the used coordinate system
637
assert not (dirname == '' and basename == ''), 'blackhole lookup error'
638
548
self._read_dirblocks_if_needed()
639
block_index = bisect.bisect_left(self._dirblocks, (dirname, []))
640
if (block_index == len(self._dirblocks) or
641
self._dirblocks[block_index][0] != dirname):
549
key = dirname, basename, ''
550
block_index, present = self._find_block_index_from_key(key)
642
552
# no such directory - return the dir index and 0 for the row.
643
553
return block_index, 0, False, False
644
554
block = self._dirblocks[block_index][1] # access the entries only
645
search = ((dirname, basename),)
646
row_index = bisect.bisect_left(block, search)
555
entry_index, present = self._find_entry_index(key, block)
647
556
# linear search through present entries at this path to find the one
649
while row_index < len(block) and block[row_index][0][1] == basename:
650
if block[row_index][1][tree_index][0] not in \
558
while entry_index < len(block) and block[entry_index][0][1] == basename:
559
if block[entry_index][1][tree_index][0] not in \
651
560
('absent', 'relocated'):
652
return block_index, row_index, True, True
654
return block_index, row_index, True, False
561
return block_index, entry_index, True, True
563
return block_index, entry_index, True, False
656
565
def _get_entry(self, tree_index, fileid_utf8=None, path_utf8=None):
657
566
"""Get the dirstate entry for path in tree tree_index
671
580
if path_utf8 is not None:
672
581
assert path_utf8.__class__ == str, 'path_utf8 is not a str: %s %s' % (type(path_utf8), path_utf8)
673
582
# path lookups are faster
675
for entry in self._root_entries:
676
if entry[1][tree_index] not in ('absent', 'relocated'):
678
raise Exception, 'rootless trees not supported yet'
679
583
dirname, basename = os.path.split(path_utf8)
680
584
block_index, entry_index, dir_present, file_present = \
681
585
self._get_block_entry_index(dirname, basename, tree_index)
719
623
result = DirState()
720
624
result._state_file = open(path, 'wb+')
625
# root dir and root dir contents with no children.
626
empty_tree_dirblocks = [('', []), ('', [])]
721
627
# a new root directory, with a NULLSTAT.
722
root_entries = [(('', '', bzrlib.inventory.ROOT_ID), [
628
empty_tree_dirblocks[0][1].append(
629
(('', '', bzrlib.inventory.ROOT_ID), [
723
630
('directory', '', 0, False, DirState.NULLSTAT),
726
empty_tree_dirblocks = [('', [])] # root dir contents - no entries.
727
result._set_data([], root_entries, empty_tree_dirblocks)
632
result._set_data([], empty_tree_dirblocks)
760
665
return (kind, fingerprint, size, executable, tree_data)
762
def _iter_entries(self, root_entries=None, dirblocks=None):
667
def _iter_entries(self):
763
668
"""Iterate over all the entries in the dirstate.
765
670
Each yelt item is an entry in the standard format described in the
766
671
docstring of bzrlib.dirstate.
768
:param root_entries: Allows overriding of the root entries to be
770
:param dirblocks: Allows overriding of the source dirblock data.
772
673
self._read_dirblocks_if_needed()
773
if root_entries is None:
774
root_entries = self._root_entries
775
if dirblocks is None:
776
dirblocks = self._dirblocks
777
for entry in root_entries:
779
for directory in dirblocks:
674
for directory in self._dirblocks:
780
675
for entry in directory[1]:
678
def _get_id_index(self):
679
"""Get an id index of self._dirblocks."""
681
for key, tree_details in self._iter_entries():
682
id_index.setdefault(key[2], set()).add(key)
783
685
def _get_output_lines(self, lines):
784
686
"""format lines for final output.
811
713
def _read_dirblocks_if_needed(self):
812
714
"""Read in all the dirblocks from the file if they are not in memory.
814
This populates self._root_entries and self._dirblocks, and sets
815
self._dirblock_state to IN_MEMORY_UNMODIFIED. It is not currently ready
816
for incremental block loading.
716
This populates self._dirblocks, and sets self._dirblock_state to
717
IN_MEMORY_UNMODIFIED. It is not currently ready for incremental block
818
720
self._read_header_if_needed()
819
721
if self._dirblock_state == DirState.NOT_IN_MEMORY:
969
871
self._header_state = DirState.IN_MEMORY_UNMODIFIED
970
872
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
972
def _set_data(self, parent_ids, root_entries, dirblocks):
874
def _set_data(self, parent_ids, dirblocks):
973
875
"""Set the full dirstate data in memory.
975
877
This is an internal function used to completely replace the objects
976
878
in memory state. It puts the dirstate into state 'full-dirty'.
978
880
:param parent_ids: A list of parent tree revision ids.
979
:param root_entrie: The root entries: A list of entries, one per fileid found
981
881
:param dirblocks: A list containing one tuple for each directory in the
982
882
tree. Each tuple contains the directory path and a list of entries
983
883
found in that directory.
985
885
# our memory copy is now authoritative.
986
886
self._dirblocks = dirblocks
987
self._root_entries = root_entries
988
887
self._header_state = DirState.IN_MEMORY_MODIFIED
989
888
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
990
889
self._parents = list(parent_ids)
1007
906
# logic not written
1008
907
raise NotImplementedError(self.set_path_id)
1009
908
# TODO: check new id is unique
1010
entry = self._get_entry(0, path_utf8='')
1011
# TODO: version of _get_block_entry_index that works with the root so
1012
# we dont look up this twice.
1013
index = self._root_entries.index(entry)
1014
if new_id == entry[0][2]:
1017
if len(entry[1]) > 1:
1018
# TODO: split the record.
1019
raise NotImplementedError(self.set_path_id)
1020
root_info, root_parents = self._root_entrie
1021
if len(root_parents):
1022
self.add_deleted(root_info[3], root_parents)
1023
# replace the entry:
1024
self._root_entries[index] = (('', '', new_id), entry[1])
909
entry = self._get_entry(0, path_utf8=path)
910
# mark the old path absent, and insert a new root path
911
present_parents = len(entry[1]) - 1
912
self._make_absent(entry)
913
id_index = self._get_id_index()
914
self.update_minimal(('', '', new_id), 'directory', present_parents,
915
path_utf8='', id_index=id_index, packed_stat=entry[1][0][4])
1025
916
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1027
918
def set_parent_trees(self, trees, ghosts):
1040
931
# all by path in parallel for 'optimal' common-case performance.
1041
932
# generate new root row.
1042
933
self._read_dirblocks_if_needed()
1043
old_roots = self._root_entries
1044
root_info = self._root_entries[0]
1045
new_parent_count = len(trees)
1046
934
# TODO future sketch: Examine the existing parents to generate a change
1047
935
# map and then walk the new parent trees only, mapping them into the
1048
936
# dirstate. Walk the dirstate at the same time to remove unreferenced
1297
1183
# it must not be absent at the moment
1298
1184
assert update_tree_details[0][0] != 'absent'
1299
1185
update_tree_details[0] = DirState.NULL_PARENT_DETAILS
1186
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1300
1187
return last_reference
1302
1189
def update_minimal(self, key, kind, num_present_parents, executable=False,
1303
1190
fingerprint='', packed_stat=None, size=0, id_index=None,
1304
1191
path_utf8=None):
1305
1192
"""Update an entry to the state in tree 0."""
1306
if key[0:2] == ('', ''):
1307
block = self._root_entries
1309
block = self._find_block(key)[1]
1193
block = self._find_block(key)[1]
1310
1194
if packed_stat is None:
1311
1195
packed_stat = DirState.NULLSTAT
1312
1196
entry_index, present = self._find_entry_index(key, block)
1313
1197
new_details = (kind, fingerprint, size, executable, packed_stat)
1314
assert id_index, 'need an id index to do updates for now !'
1198
assert id_index is not None, 'need an id index to do updates for now !'
1315
1199
if not present:
1316
1200
# new entry, synthesis cross reference here,
1317
1201
existing_keys = id_index.setdefault(key[2], set())
1387
1272
self._dirblocks[block_index][1][entry_index][1][0] = \
1388
1273
('relocated', path_utf8, 0, False, '')
1274
# add a containing dirblock if needed.
1275
if new_details[0] == 'directory':
1276
subdir_key = (os.path.join(*key[0:2]), '', '')
1277
block_index, present = self._find_block_index_from_key(subdir_key)
1279
self._dirblocks.insert(block_index, (subdir_key[0], []))
1390
1281
self._dirblock_state = DirState.IN_MEMORY_MODIFIED