/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

mergeĀ fromĀ dirstate

Show diffs side-by-side

added added

removed removed

Lines of Context:
40
40
import bzrlib
41
41
from bzrlib import (
42
42
    bzrdir,
 
43
    cache_utf8,
43
44
    conflicts as _mod_conflicts,
44
45
    dirstate,
45
46
    errors,
49
50
    ignores,
50
51
    merge,
51
52
    osutils,
 
53
    revisiontree,
52
54
    textui,
53
55
    transform,
54
56
    urlutils,
81
83
    )
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
 
245
        inv_byid = inv._byid
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]
243
251
            try:
244
 
                parent_id = parent_ids[block[0]]
 
252
                parent_ie = parent_ies[block[0]]
245
253
            except KeyError:
246
254
                # all the paths in this block are not versioned in this tree
247
255
                continue
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
251
260
                    continue
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)
 
261
                name = key[1]
 
262
                name_unicode = utf8_decode(name)[0]
 
263
                file_id = key[2]
 
264
                kind = minikind_to_kind[minikind]
 
265
                inv_entry = factory[kind](file_id, name_unicode,
 
266
                                          parent_ie.file_id)
256
267
                if kind == 'file':
257
268
                    # not strictly needed: working tree
258
269
                    #entry.executable = executable
261
272
                    pass
262
273
                elif kind == 'directory':
263
274
                    # add this entry to the parent map.
264
 
                    parent_ids[(dirname + '/' + name).strip('/')] = file_id
265
 
                inv.add(inv_entry)
 
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
267
282
 
268
283
    def _get_entry(self, file_id=None, path=None):
279
294
        if file_id is None and path is None:
280
295
            raise errors.BzrError('must supply file_id or path')
281
296
        state = self.current_dirstate()
282
 
        if file_id is not None:
283
 
            file_id = file_id.encode('utf8')
284
297
        if path is not None:
285
298
            path = path.encode('utf8')
286
299
        return state._get_entry(0, fileid_utf8=file_id, path_utf8=path)
316
329
    @needs_read_lock
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]
320
333
 
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)
325
338
        if row is None:
326
339
            return False
329
342
 
330
343
    @needs_read_lock
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')
336
356
 
337
357
    @needs_read_lock
338
358
    def __iter__(self):
343
363
        """
344
364
        result = []
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
348
368
                continue
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)
353
373
 
354
374
    @needs_read_lock
360
380
        else:
361
381
            return None
362
382
 
 
383
    def lock_read(self):
 
384
        super(WorkingTree4, self).lock_read()
 
385
        if self._dirstate is None:
 
386
            self.current_dirstate()
 
387
            self._dirstate.lock_read()
 
388
 
 
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()
 
394
 
 
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()
 
400
 
363
401
    @needs_tree_write_lock
364
402
    def move(self, from_paths, to_dir=None, after=False, **kwargs):
365
403
        """See WorkingTree.move()."""
398
436
            raise errors.BzrMoveFailedError('',to_dir,
399
437
                errors.NotADirectory(to_abs))
400
438
 
401
 
        if to_entry[1][0][0] != 'directory':
 
439
        if to_entry[1][0][0] != 'd':
402
440
            raise errors.BzrMoveFailedError('',to_dir,
403
441
                errors.NotADirectory(to_abs))
404
442
 
406
444
            update_inventory = True
407
445
            inv = self.inventory
408
446
            to_dir_ie = inv[to_dir_id]
409
 
            to_dir_id = to_entry[0][2].decode('utf8')
 
447
            to_dir_id = to_entry[0][2]
410
448
        else:
411
449
            update_inventory = False
412
450
 
420
458
                raise errors.BzrMoveFailedError(from_rel,to_dir,
421
459
                    errors.NotVersionedError(path=str(from_rel)))
422
460
 
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],
 
540
                        kind,
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],
507
547
                        path_utf8=from_rel.encode('utf8')))
508
548
                # create new row in current block
509
549
                state.update_minimal(to_key,
510
 
                        old_entry_details[0][0],
 
550
                        kind,
511
551
                        num_present_parents=len(old_entry_details) - 1,
512
552
                        executable=old_entry_details[0][3],
513
553
                        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
548
588
        entry = self._get_entry(path=path)
549
589
        if entry == (None, None):
550
590
            return None
551
 
        return entry[0][2].decode('utf8')
 
591
        return entry[0][2]
552
592
 
553
593
    def paths2ids(self, paths, trees=[], require_versioned=True):
554
594
        """See Tree.paths2ids().
605
645
                for entry in path_entries:
606
646
                    # for each tree.
607
647
                    for index in search_indexes:
608
 
                        if entry[1][index][0] != 'absent':
 
648
                        if entry[1][index][0] != 'a': # absent
609
649
                            found_versioned = True
610
650
                            # all good: found a versioned cell
611
651
                            break
612
652
                if not found_versioned:
613
 
                    # non of the indexes was not 'absent' at all ids for this
 
653
                    # none of the indexes was not 'absent' at all ids for this
614
654
                    # path.
615
655
                    all_versioned = False
616
656
                    break
638
678
            nothing. Otherwise add the id to found_ids.
639
679
            """
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()
770
810
            if self._control_files._lock_mode == 'w':
771
811
                if self._dirty:
772
812
                    self.flush()
 
813
            if self._dirstate is not None:
 
814
                self._dirstate.unlock()
773
815
            self._dirstate = None
774
816
            self._inventory = None
775
817
        # reverse order of locking.
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()
799
841
        # sketch:
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
807
849
                # supported.
837
879
            entry_index = 0
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
844
886
                    entry_index += 1
845
887
                    continue
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):
849
891
                    entry_index += 1
905
947
        if revision_id is None:
906
948
            revision_id = branch.last_revision()
907
949
        local_path = transport.local_abspath('dirstate')
908
 
        dirstate.DirState.initialize(local_path)
 
950
        state = dirstate.DirState.initialize(local_path)
 
951
        state.unlock()
909
952
        wt = WorkingTree4(a_bzrdir.root_transport.local_abspath('.'),
910
953
                         branch,
911
954
                         _format=self,
949
992
        self._repository = repository
950
993
        self._inventory = None
951
994
        self._locked = 0
 
995
        self._dirstate_locked = False
952
996
 
953
997
    def annotate_iter(self, file_id):
954
998
        """See Tree.annotate_iter"""
988
1032
        """
989
1033
        if file_id is None and path is None:
990
1034
            raise errors.BzrError('must supply file_id or path')
991
 
        if file_id is not None:
992
 
            file_id = file_id.encode('utf8')
 
1035
        file_id = osutils.safe_file_id(file_id)
993
1036
        if path is not None:
994
1037
            path = path.encode('utf8')
995
1038
        parent_index = self._dirstate.get_parent_ids().index(self._revision_id) + 1
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]
1023
1071
            try:
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
1027
1075
                continue
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
1031
1080
                    continue
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)
 
1081
                name = key[1]
 
1082
                name_unicode = utf8_decode(name)[0]
 
1083
                file_id = key[2]
 
1084
                kind = minikind_to_kind[minikind]
 
1085
                inv_entry = factory[kind](file_id, name_unicode,
 
1086
                                          parent_ie.file_id)
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]
1047
1098
                else:
1048
1099
                    raise Exception, kind
1049
 
                inv.add(inv_entry)
 
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
1051
1106
 
1052
1107
    def get_file_sha1(self, file_id, path=None, stat_value=None):
1114
1169
        """Lock the tree for a set of operations."""
1115
1170
        if not self._locked:
1116
1171
            self._repository.lock_read()
 
1172
            if self._dirstate._lock_token is None:
 
1173
                self._dirstate.lock_read()
 
1174
                self._dirstate_locked = True
1117
1175
        self._locked += 1
1118
1176
 
1119
1177
    @needs_read_lock
1123
1181
        entry = self._get_entry(path=path)
1124
1182
        if entry == (None, None):
1125
1183
            return None
1126
 
        return entry[0][2].decode('utf8')
 
1184
        return entry[0][2]
1127
1185
 
1128
1186
    def unlock(self):
1129
1187
        """Unlock, freeing any cache memory used during the lock."""
1131
1189
        self._locked -=1
1132
1190
        if not self._locked:
1133
1191
            self._inventory = None
1134
 
            self._locked = False
 
1192
            self._locked = 0
 
1193
            if self._dirstate_locked:
 
1194
                self._dirstate.unlock()
 
1195
                self._dirstate_locked = False
1135
1196
            self._repository.unlock()
1136
1197
 
1137
1198
    def walkdirs(self, prefix=""):
1169
1230
            for dir in reversed(dirblock):
1170
1231
                if dir[2] == _directory:
1171
1232
                    pending.append((dir[0], dir[4]))
 
1233
 
 
1234
 
 
1235
class InterDirStateTree(InterTree):
 
1236
    """Fast path optimiser for changes_from with dirstate trees."""
 
1237
 
 
1238
    @staticmethod
 
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)
 
1247
 
 
1248
    @staticmethod
 
1249
    def is_compatible(source, target):
 
1250
        # the target must be a dirstate working tree
 
1251
        if not isinstance(target, WorkingTree4):
 
1252
            return False
 
1253
        # the source must be a revtreee or dirstate rev tree.
 
1254
        if not isinstance(source,
 
1255
            (revisiontree.RevisionTree, DirStateRevisionTree)):
 
1256
            return False
 
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.
 
1262
            return False
 
1263
        return True
 
1264
 
 
1265
InterTree.register_optimiser(InterDirStateTree)