82
84
from bzrlib.trace import mutter, note
83
85
from bzrlib.transport.local import LocalTransport
86
from bzrlib.tree import InterTree
84
87
from bzrlib.progress import DummyProgress, ProgressPhase
85
88
from bzrlib.revision import NULL_REVISION, CURRENT_REVISION
86
89
from bzrlib.rio import RioReader, rio_file, Stanza
232
235
state = self.current_dirstate()
233
236
state._read_dirblocks_if_needed()
234
237
root_key, current_entry = self._get_entry(path='')
235
current_id = root_key[2].decode('utf8')
236
assert current_entry[0][0] == 'directory'
238
current_id = root_key[2]
239
assert current_entry[0][0] == 'd' # directory
237
240
inv = Inventory(root_id=current_id)
241
# Turn some things into local variables
242
minikind_to_kind = dirstate.DirState._minikind_to_kind
243
factory = entry_factory
244
utf8_decode = cache_utf8._utf8_decode
238
246
# we could do this straight out of the dirstate; it might be fast
239
247
# and should be profiled - RBC 20070216
240
parent_ids = {'' : inv.root.file_id}
248
parent_ies = {'' : inv.root}
241
249
for block in state._dirblocks[1:]: # skip the root
242
250
dirname = block[0]
244
parent_id = parent_ids[block[0]]
252
parent_ie = parent_ies[block[0]]
246
254
# all the paths in this block are not versioned in this tree
248
256
for key, entry in block[1]:
249
if entry[0][0] in ('absent', 'relocated'):
257
minikind, link_or_sha1, size, executable, stat = entry[0]
258
if minikind in ('a', 'r'): # absent, relocated
250
259
# a parent tree only entry
252
name = key[1].decode('utf8')
253
file_id = key[2].decode('utf8')
254
kind, link_or_sha1, size, executable, stat = entry[0]
255
inv_entry = entry_factory[kind](file_id, name, parent_id)
262
name_unicode = utf8_decode(name)[0]
264
kind = minikind_to_kind[minikind]
265
inv_entry = factory[kind](file_id, name_unicode,
256
267
if kind == 'file':
257
268
# not strictly needed: working tree
258
269
#entry.executable = executable
262
273
elif kind == 'directory':
263
274
# add this entry to the parent map.
264
parent_ids[(dirname + '/' + name).strip('/')] = file_id
275
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
276
# These checks cost us around 40ms on a 55k entry tree
277
assert file_id not in inv_byid
278
assert name_unicode not in parent_ie.children
279
inv_byid[file_id] = inv_entry
280
parent_ie.children[name_unicode] = inv_entry
266
281
self._inventory = inv
268
283
def _get_entry(self, file_id=None, path=None):
317
330
def get_root_id(self):
318
331
"""Return the id of this trees root"""
319
return self._get_entry(path='')[0][2].decode('utf8')
332
return self._get_entry(path='')[0][2]
321
334
def has_id(self, file_id):
322
335
state = self.current_dirstate()
323
fileid_utf8 = file_id.encode('utf8')
336
file_id = osutils.safe_file_id(file_id)
324
337
row, parents = self._get_entry(file_id=file_id)
331
344
def id2path(self, fileid):
332
state = self.current_dirstate()
333
fileid_utf8 = fileid.encode('utf8')
334
key, tree_details = state._get_entry(0, fileid_utf8=fileid_utf8)
335
return os.path.join(*key[0:2]).decode('utf8')
345
fileid = osutils.safe_file_id(fileid)
346
inv = self._get_inventory()
347
return inv.id2path(fileid)
348
# TODO: jam 20070222 At present dirstate is very slow at id => path,
349
# while inventory is very fast at it. So for now, just generate
350
# the inventory and do the id => path check.
351
# In the future, we want to make dirstate better at id=>path
352
# checks so that we don't have to create the inventory.
353
# state = self.current_dirstate()
354
# key, tree_details = state._get_entry(0, fileid_utf8=fileid)
355
# return os.path.join(*key[0:2]).decode('utf8')
338
358
def __iter__(self):
345
365
for key, tree_details in self.current_dirstate()._iter_entries():
346
if tree_details[0][0] in ('absent', 'relocated'):
366
if tree_details[0][0] in ('a', 'r'): # absent, relocated
347
367
# not relevant to the working tree
349
369
path = pathjoin(self.basedir, key[0].decode('utf8'), key[1].decode('utf8'))
350
370
if osutils.lexists(path):
351
result.append(key[2].decode('utf8'))
371
result.append(key[2])
352
372
return iter(result)
384
super(WorkingTree4, self).lock_read()
385
if self._dirstate is None:
386
self.current_dirstate()
387
self._dirstate.lock_read()
389
def lock_tree_write(self):
390
super(WorkingTree4, self).lock_tree_write()
391
if self._dirstate is None:
392
self.current_dirstate()
393
self._dirstate.lock_write()
395
def lock_write(self):
396
super(WorkingTree4, self).lock_write()
397
if self._dirstate is None:
398
self.current_dirstate()
399
self._dirstate.lock_write()
363
401
@needs_tree_write_lock
364
402
def move(self, from_paths, to_dir=None, after=False, **kwargs):
365
403
"""See WorkingTree.move()."""
420
458
raise errors.BzrMoveFailedError(from_rel,to_dir,
421
459
errors.NotVersionedError(path=str(from_rel)))
423
from_id = from_entry[0][2].decode('utf8')
461
from_id = from_entry[0][2]
424
462
to_rel = pathjoin(to_dir, from_tail)
425
463
item_to_entry = self._get_entry(path=to_rel)
426
464
if item_to_entry != (None, None):
495
533
from_key = old_block[old_entry_index][0]
496
534
to_key = ((to_block[0],) + from_key[1:3])
497
535
state._make_absent(old_block[old_entry_index])
536
minikind = old_entry_details[0][0]
537
kind = dirstate.DirState._minikind_to_kind[minikind]
498
538
rollbacks.append(
499
539
lambda:state.update_minimal(from_key,
500
old_entry_details[0][0],
501
541
num_present_parents=len(old_entry_details) - 1,
502
542
executable=old_entry_details[0][3],
503
543
fingerprint=old_entry_details[0][1],
518
558
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
519
559
new_entry = to_block[added_entry_index]
520
560
rollbacks.append(lambda:state._make_absent(new_entry))
521
if new_entry[1][0][0] == 'directory':
561
if new_entry[1][0][0] == 'd':
522
562
import pdb;pdb.set_trace()
523
563
# if a directory, rename all the contents of child blocks
524
564
# adding rollbacks as each is inserted to remove them and
638
678
nothing. Otherwise add the id to found_ids.
640
680
for index in search_indexes:
641
if entry[1][index][0] == 'relocated':
681
if entry[1][index][0] == 'r': # relocated
642
682
if not osutils.is_inside_any(searched_paths, entry[1][index][1]):
643
683
search_paths.add(entry[1][index][1])
644
elif entry[1][index][0] != 'absent':
684
elif entry[1][index][0] != 'a': # absent
645
685
found_ids.add(entry[0][2])
646
686
while search_paths:
647
687
current_root = search_paths.pop()
793
835
state = self.current_dirstate()
794
836
state._read_dirblocks_if_needed()
795
837
ids_to_unversion = set()
796
for fileid in file_ids:
797
ids_to_unversion.add(fileid.encode('utf8'))
838
for file_id in file_ids:
839
ids_to_unversion.add(osutils.safe_file_id(file_id))
798
840
paths_to_unversion = set()
800
842
# check if the root is to be unversioned, if so, assert for now.
801
843
# walk the state marking unversioned things as absent.
802
844
# if there are any un-unversioned ids at the end, raise
803
845
for key, details in state._dirblocks[0][1]:
804
if (details[0][0] not in ('absent', 'relocated') and
846
if (details[0][0] not in ('a', 'r') and # absent or relocated
805
847
key[2] in ids_to_unversion):
806
848
# I haven't written the code to unversion / yet - it should be
838
880
while entry_index < len(block[1]):
839
881
entry = block[1][entry_index]
840
if (entry[1][0][0] in ('absent', 'relocated') or
882
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
841
883
# ^ some parent row.
842
884
entry[0][2] not in ids_to_unversion):
843
885
# ^ not an id to unversion
846
if entry[1][0][0] == 'directory':
888
if entry[1][0][0] == 'd':
847
889
paths_to_unversion.add(os.path.join(*entry[0][0:2]))
848
890
if not state._make_absent(entry):
1011
1054
# This is identical now to the WorkingTree _generate_inventory except
1012
1055
# for the tree index use.
1013
1056
root_key, current_entry = self._dirstate._get_entry(parent_index, path_utf8='')
1014
current_id = root_key[2].decode('utf8')
1015
assert current_entry[parent_index][0] == 'directory'
1057
current_id = root_key[2]
1058
assert current_entry[parent_index][0] == 'd'
1016
1059
inv = Inventory(root_id=current_id, revision_id=self._revision_id)
1017
1060
inv.root.revision = current_entry[parent_index][4]
1061
# Turn some things into local variables
1062
minikind_to_kind = dirstate.DirState._minikind_to_kind
1063
factory = entry_factory
1064
utf8_decode = cache_utf8._utf8_decode
1065
inv_byid = inv._byid
1018
1066
# we could do this straight out of the dirstate; it might be fast
1019
1067
# and should be profiled - RBC 20070216
1020
parent_ids = {'' : inv.root.file_id}
1068
parent_ies = {'' : inv.root}
1021
1069
for block in self._dirstate._dirblocks[1:]: #skip root
1022
1070
dirname = block[0]
1024
parent_id = parent_ids[block[0]]
1072
parent_ie = parent_ies[dirname]
1025
1073
except KeyError:
1026
1074
# all the paths in this block are not versioned in this tree
1028
1076
for key, entry in block[1]:
1029
if entry[parent_index][0] in ('absent', 'relocated'):
1077
minikind, link_or_sha1, size, executable, revid = entry[parent_index]
1078
if minikind in ('a', 'r'): # absent, relocated
1030
1079
# not this tree
1032
name = key[1].decode('utf8')
1033
file_id = key[2].decode('utf8')
1034
kind, link_or_sha1, size, executable, revid = entry[parent_index]
1035
inv_entry = entry_factory[kind](file_id, name, parent_id)
1082
name_unicode = utf8_decode(name)[0]
1084
kind = minikind_to_kind[minikind]
1085
inv_entry = factory[kind](file_id, name_unicode,
1036
1087
inv_entry.revision = revid
1037
1088
if kind == 'file':
1038
1089
inv_entry.executable = executable
1039
1090
inv_entry.text_size = size
1040
1091
inv_entry.text_sha1 = link_or_sha1
1041
1092
elif kind == 'directory':
1042
parent_ids[(dirname + '/' + name).strip('/')] = file_id
1093
parent_ies[(dirname + '/' + name).strip('/')] = inv_entry
1043
1094
elif kind == 'symlink':
1044
1095
inv_entry.executable = False
1045
1096
inv_entry.text_size = size
1046
inv_entry.symlink_target = link_or_sha1.decode('utf8')
1097
inv_entry.symlink_target = utf8_decode(link_or_sha1)[0]
1048
1099
raise Exception, kind
1100
# These checks cost us around 40ms on a 55k entry tree
1101
assert file_id not in inv_byid
1102
assert name_unicode not in parent_ie.children
1103
inv_byid[file_id] = inv_entry
1104
parent_ie.children[name_unicode] = inv_entry
1050
1105
self._inventory = inv
1052
1107
def get_file_sha1(self, file_id, path=None, stat_value=None):
1169
1230
for dir in reversed(dirblock):
1170
1231
if dir[2] == _directory:
1171
1232
pending.append((dir[0], dir[4]))
1235
class InterDirStateTree(InterTree):
1236
"""Fast path optimiser for changes_from with dirstate trees."""
1239
def revision_tree_from_workingtree(tree):
1240
"""Create a revision tree from a working tree."""
1241
revid = tree.commit('save tree', allow_pointless=True)
1242
return tree.branch.repository.revision_tree(revid)
1243
_from_tree_converter = revision_tree_from_workingtree
1244
_matching_from_tree_format = WorkingTreeFormat4()
1245
_matching_to_tree_format = WorkingTreeFormat4()
1246
_to_tree_converter = staticmethod(lambda x: x)
1249
def is_compatible(source, target):
1250
# the target must be a dirstate working tree
1251
if not isinstance(target, WorkingTree4):
1253
# the source must be a revtreee or dirstate rev tree.
1254
if not isinstance(source,
1255
(revisiontree.RevisionTree, DirStateRevisionTree)):
1257
# the source revid must be in the target dirstate
1258
if not (source._revision_id == NULL_REVISION or
1259
source._revision_id in target.get_parent_ids()):
1260
# TODO: what about ghosts? it may well need to
1261
# check for them explicitly.
1265
InterTree.register_optimiser(InterDirStateTree)