/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/dirstate.py

mergeĀ fromĀ dirstate

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
entry[1][1]: The second tree
58
58
 
59
59
For an entry for a tree, we have (using tree 0 - current tree) to demonstrate:
60
 
entry[1][0][0]: kind
 
60
entry[1][0][0]: minikind
61
61
entry[1][0][1]: fingerprint
62
62
entry[1][0][2]: size
63
63
entry[1][0][3]: executable
201
201
 
202
202
from bzrlib import (
203
203
    errors,
 
204
    lock,
204
205
    trace,
205
206
    )
206
207
import bzrlib.inventory
240
241
    # A pack_stat (the x's) that is just noise and will never match the output
241
242
    # of base64 encode.
242
243
    NULLSTAT = 'x' * 32
243
 
    NULL_PARENT_DETAILS = ('absent', '', 0, False, '')
 
244
    NULL_PARENT_DETAILS = ('a', '', 0, False, '')
244
245
 
245
 
    def __init__(self):
 
246
    def __init__(self, path):
246
247
        """Create a  DirState object.
247
248
 
248
249
        Attributes of note:
254
255
            - size of 0
255
256
            - a packed state
256
257
            - and no sha information.
 
258
        :param path: The path at which the dirstate file on disk should live.
257
259
        """
258
260
        # _header_state and _dirblock_state represent the current state
259
261
        # of the dirstate metadata and the per-row data respectiely.
271
273
        self._dirblocks = []
272
274
        self._ghosts = []
273
275
        self._parents = []
274
 
        self._state_file=None
 
276
        self._state_file = None
 
277
        self._filename = path
 
278
        self._lock_token = None
275
279
 
276
280
    def add(self, path, file_id, kind, stat, link_or_sha1):
277
281
        """Add a path to be tracked.
306
310
        # faster than three separate encodes.
307
311
        utf8path = (dirname + '/' + basename).strip('/').encode('utf8')
308
312
        dirname, basename = os.path.split(utf8path)
309
 
        entry_key = (dirname, basename, file_id.encode('utf8'))
 
313
        assert file_id.__class__ == str, \
 
314
            "must be a utf8 file_id not %s" % (type(file_id))
 
315
        entry_key = (dirname, basename, file_id)
310
316
        self._read_dirblocks_if_needed()
311
317
        block_index, present = self._find_block_index_from_key(entry_key)
312
318
        if not present:
323
329
            size = stat.st_size
324
330
            packed_stat = pack_stat(stat)
325
331
        parent_info = self._empty_parent_info()
 
332
        minikind = DirState._kind_to_minikind[kind]
326
333
        if kind == 'file':
327
334
            entry_data = entry_key, [
328
 
                (kind, link_or_sha1, size, False, packed_stat),
 
335
                (minikind, link_or_sha1, size, False, packed_stat),
329
336
                ] + parent_info
330
337
        elif kind == 'directory':
331
338
            entry_data = entry_key, [
332
 
                (kind, '', 0, False, packed_stat),
 
339
                (minikind, '', 0, False, packed_stat),
333
340
                ] + parent_info
334
341
        elif kind == 'symlink':
335
342
            entry_data = entry_key, [
336
 
                (kind, link_or_sha1, size, False, packed_stat),
 
343
                (minikind, link_or_sha1, size, False, packed_stat),
337
344
                ] + parent_info
338
345
        else:
339
346
            raise errors.BzrError('unknown kind %r' % kind)
401
408
        current_block = self._dirblocks[0][1]
402
409
        current_dirname = ''
403
410
        root_key = ('', '')
 
411
        append_entry = current_block.append
404
412
        for entry in new_entries:
405
413
            if entry[0][0] != current_dirname:
406
414
                # new block - different dirname
407
415
                current_block = []
408
416
                current_dirname = entry[0][0]
409
417
                self._dirblocks.append((current_dirname, current_block))
410
 
            elif entry[0][1]:
411
 
                # this is not a root entry for a tree (it has a basename)
412
 
                current_block = self._dirblocks[-1][1]
 
418
                append_entry = current_block.append
413
419
            # append the entry to the current block
414
 
            current_block.append(entry)
415
 
    
 
420
            append_entry(entry)
 
421
        self._split_root_dirblock_into_contents()
 
422
 
 
423
    def _split_root_dirblock_into_contents(self):
 
424
        """Split the root dirblocks into root and contents-of-root.
 
425
 
 
426
        After parsing by path, we end up with root entries and contents-of-root
 
427
        entries in the same block. This loop splits them out again.
 
428
        """
 
429
        # The above loop leaves the "root block" entries mixed with the
 
430
        # "contents-of-root block". But we don't want an if check on
 
431
        # all entries, so instead we just fix it up here.
 
432
        assert self._dirblocks[1] == ('', [])
 
433
        root_block = []
 
434
        contents_of_root_block = []
 
435
        for entry in self._dirblocks[0][1]:
 
436
            if not entry[0][1]: # This is a root entry
 
437
                root_block.append(entry)
 
438
            else:
 
439
                contents_of_root_block.append(entry)
 
440
        self._dirblocks[0] = ('', root_block)
 
441
        self._dirblocks[1] = ('', contents_of_root_block)
 
442
 
416
443
    def _entry_to_line(self, entry):
417
444
        """Serialize entry to a NULL delimited line ready for _get_output_lines.
418
445
        
420
447
        """
421
448
        entire_entry = list(entry[0])
422
449
        for tree_number, tree_data in enumerate(entry[1]):
423
 
            # (kind, fingerprint, size, executable, tree_specific_string)
 
450
            # (minikind, fingerprint, size, executable, tree_specific_string)
424
451
            entire_entry.extend(tree_data)
425
452
            # 3 for the key, 5 for the fields per tree.
426
453
            tree_offset = 3 + tree_number * 5
427
 
            # kind
428
 
            entire_entry[tree_offset + 0] = DirState._kind_to_minikind[tree_data[0]]
 
454
            # minikind
 
455
            entire_entry[tree_offset + 0] = tree_data[0]
429
456
            # size
430
457
            entire_entry[tree_offset + 2] = str(tree_data[2])
431
458
            # executable
501
528
 
502
529
        :param tree: The tree which should provide parent information and
503
530
            inventory ids.
 
531
        :return: a DirState object which is currently locked for writing.
 
532
            (it was locked by DirState.initialize)
504
533
        """
505
534
        result = DirState.initialize(dir_state_filename)
506
 
        tree.lock_read()
507
 
        parent_ids = tree.get_parent_ids()
508
 
        num_parents = len(parent_ids)
509
 
        parent_trees = []
510
 
        for parent_id in parent_ids:
511
 
            parent_trees.append((parent_id, tree.branch.repository.revision_tree(parent_id)))
512
 
            parent_trees[-1][1].lock_read()
513
 
        result.set_parent_trees(parent_trees, [])
514
 
        result.set_state_from_inventory(tree.inventory)
515
 
 
516
 
        for revid, parent in parent_trees:
517
 
            parent.unlock()
518
 
        tree.unlock()
 
535
        try:
 
536
            tree.lock_read()
 
537
            try:
 
538
                parent_ids = tree.get_parent_ids()
 
539
                num_parents = len(parent_ids)
 
540
                parent_trees = []
 
541
                for parent_id in parent_ids:
 
542
                    parent_tree = tree.branch.repository.revision_tree(parent_id)
 
543
                    parent_trees.append((parent_id, parent_tree))
 
544
                    parent_tree.lock_read()
 
545
                result.set_parent_trees(parent_trees, [])
 
546
                result.set_state_from_inventory(tree.inventory)
 
547
            finally:
 
548
                for revid, parent_tree in parent_trees:
 
549
                    parent_tree.unlock()
 
550
                tree.unlock()
 
551
        except:
 
552
            # The caller won't have a chance to unlock this, so make sure we
 
553
            # cleanup ourselves
 
554
            result.unlock()
 
555
            raise
519
556
        return result
520
557
 
521
558
    def get_ghosts(self):
548
585
    def _get_fields_to_entry(self):
549
586
        """Get a function which converts entry fields into a entry record.
550
587
 
551
 
        This handles kind, size, and executable, as well as parent records.
 
588
        This handles size and executable, as well as parent records.
552
589
 
553
590
        :return: A function which takes a list of fields, and returns an
554
591
            appropriate record for storing in memory.
556
593
        # This is intentionally unrolled for performance
557
594
        num_present_parents = self._num_present_parents()
558
595
        if num_present_parents == 0:
559
 
            def fields_to_entry_0_parents(fields, _int=int, _tuple=tuple,
560
 
                                          _mini_to_kind=self._minikind_to_kind):
561
 
                path_name_file_id_key = _tuple(fields[:3])
 
596
            def fields_to_entry_0_parents(fields, _int=int):
 
597
                path_name_file_id_key = (fields[0], fields[1], fields[2])
562
598
                return (path_name_file_id_key, [
563
599
                    ( # Current tree
564
 
                        _mini_to_kind[fields[3]], # kind
 
600
                        fields[3],                # minikind
565
601
                        fields[4],                # fingerprint
566
602
                        _int(fields[5]),          # size
567
603
                        fields[6] == 'y',         # executable
569
605
                    )])
570
606
            return fields_to_entry_0_parents
571
607
        elif num_present_parents == 1:
572
 
            def fields_to_entry_1_parent(fields, _int=int, _tuple=tuple,
573
 
                                         _mini_to_kind=self._minikind_to_kind):
574
 
                path_name_file_id_key = _tuple(fields[:3])
 
608
            def fields_to_entry_1_parent(fields, _int=int):
 
609
                path_name_file_id_key = (fields[0], fields[1], fields[2])
575
610
                return (path_name_file_id_key, [
576
611
                    ( # Current tree
577
 
                        _mini_to_kind[fields[3]], # kind
 
612
                        fields[3],                # minikind
578
613
                        fields[4],                # fingerprint
579
614
                        _int(fields[5]),          # size
580
615
                        fields[6] == 'y',         # executable
581
616
                        fields[7],                # packed_stat or revision_id
582
617
                    ),
583
618
                    ( # Parent 1
584
 
                        _mini_to_kind[fields[8]], # kind
 
619
                        fields[8],                # minikind
585
620
                        fields[9],                # fingerprint
586
621
                        _int(fields[10]),         # size
587
622
                        fields[11] == 'y',        # executable
590
625
                    ])
591
626
            return fields_to_entry_1_parent
592
627
        elif num_present_parents == 2:
593
 
            def fields_to_entry_2_parents(fields, _int=int, _tuple=tuple,
594
 
                                          _mini_to_kind=self._minikind_to_kind):
595
 
                path_name_file_id_key = _tuple(fields[:3])
 
628
            def fields_to_entry_2_parents(fields, _int=int):
 
629
                path_name_file_id_key = (fields[0], fields[1], fields[2])
596
630
                return (path_name_file_id_key, [
597
631
                    ( # Current tree
598
 
                        _mini_to_kind[fields[3]], # kind
 
632
                        fields[3],                # minikind
599
633
                        fields[4],                # fingerprint
600
634
                        _int(fields[5]),          # size
601
635
                        fields[6] == 'y',         # executable
602
636
                        fields[7],                # packed_stat or revision_id
603
637
                    ),
604
638
                    ( # Parent 1
605
 
                        _mini_to_kind[fields[8]], # kind
 
639
                        fields[8],                # minikind
606
640
                        fields[9],                # fingerprint
607
641
                        _int(fields[10]),         # size
608
642
                        fields[11] == 'y',        # executable
609
643
                        fields[12],               # packed_stat or revision_id
610
644
                    ),
611
645
                    ( # Parent 2
612
 
                        _mini_to_kind[fields[13]],# kind
 
646
                        fields[13],               # minikind
613
647
                        fields[14],               # fingerprint
614
648
                        _int(fields[15]),         # size
615
649
                        fields[16] == 'y',        # executable
618
652
                    ])
619
653
            return fields_to_entry_2_parents
620
654
        else:
621
 
            def fields_to_entry_n_parents(fields, _int=int, _tuple=tuple,
622
 
                                          _mini_to_kind=self._minikind_to_kind):
623
 
                path_name_file_id_key = _tuple(fields[:3])
624
 
                trees = [(_mini_to_kind[fields[cur]], # kind
 
655
            def fields_to_entry_n_parents(fields, _int=int):
 
656
                path_name_file_id_key = (fields[0], fields[1], fields[2])
 
657
                trees = [(fields[cur],                # minikind
625
658
                          fields[cur+1],              # fingerprint
626
659
                          _int(fields[cur+2]),        # size
627
660
                          fields[cur+3] == 'y',       # executable
667
700
        # requested.
668
701
        while entry_index < len(block) and block[entry_index][0][1] == basename:
669
702
            if block[entry_index][1][tree_index][0] not in \
670
 
                       ('absent', 'relocated'):
 
703
                       ('a', 'r'): # absent, relocated
671
704
                return block_index, entry_index, True, True
672
705
            entry_index += 1
673
706
        return block_index, entry_index, True, False
696
729
            if not file_present:
697
730
                return None, None
698
731
            entry = self._dirblocks[block_index][1][entry_index]
699
 
            assert entry[0][2] and entry[1][tree_index][0] not in ('absent', 'relocated'), 'unversioned entry?!?!'
 
732
            assert entry[0][2] and entry[1][tree_index][0] not in ('a', 'r'), 'unversioned entry?!?!'
700
733
            if fileid_utf8:
701
734
                if entry[0][2] != fileid_utf8:
702
735
                    raise BzrError('integrity error ? : mismatching tree_index, file_id and path')
704
737
        else:
705
738
            for entry in self._iter_entries():
706
739
                if entry[0][2] == fileid_utf8:
707
 
                    if entry[1][tree_index][0] == 'relocated':
 
740
                    if entry[1][tree_index][0] == 'r': # relocated
708
741
                        # look up the real location directly by path
709
742
                        return self._get_entry(tree_index,
710
743
                            fileid_utf8=fileid_utf8,
711
744
                            path_utf8=entry[1][tree_index][1])
712
 
                    if entry[1][tree_index][0] == 'absent':
 
745
                    if entry[1][tree_index][0] == 'a': # absent
713
746
                        # not in the tree at all.
714
747
                        return None, None
715
748
                    return entry
722
755
        The new dirstate will be an empty tree - that is it has no parents,
723
756
        and only a root node - which has id ROOT_ID.
724
757
 
 
758
        The object will be write locked when returned to the caller,
 
759
        unless there was an exception in the writing, in which case it
 
760
        will be unlocked.
 
761
 
725
762
        :param path: The name of the file for the dirstate.
726
763
        :return: A DirState object.
727
764
        """
730
767
        # stock empty dirstate information - a root with ROOT_ID, no children,
731
768
        # and no parents. Finally it calls save() to ensure that this data will
732
769
        # persist.
733
 
        result = DirState()
734
 
        result._state_file = open(path, 'wb+')
 
770
        result = DirState(path)
735
771
        # root dir and root dir contents with no children.
736
772
        empty_tree_dirblocks = [('', []), ('', [])]
737
773
        # a new root directory, with a NULLSTAT.
738
774
        empty_tree_dirblocks[0][1].append(
739
775
            (('', '', bzrlib.inventory.ROOT_ID), [
740
 
                ('directory', '', 0, False, DirState.NULLSTAT),
 
776
                ('d', '', 0, False, DirState.NULLSTAT),
741
777
            ]))
742
 
        result._set_data([], empty_tree_dirblocks)
 
778
        result.lock_write()
743
779
        try:
 
780
            result._set_data([], empty_tree_dirblocks)
744
781
            result.save()
745
782
        except:
746
 
            result._state_file.close()
 
783
            result.unlock()
747
784
            raise
748
785
        return result
749
786
 
756
793
            id.
757
794
        """
758
795
        kind = inv_entry.kind
759
 
        tree_data = inv_entry.revision.encode('utf8')
 
796
        minikind = DirState._kind_to_minikind[kind]
 
797
        tree_data = inv_entry.revision
760
798
        assert len(tree_data) > 0, 'empty revision for the inv_entry.'
761
799
        if kind == 'directory':
762
800
            fingerprint = ''
772
810
            executable = inv_entry.executable
773
811
        else:
774
812
            raise Exception
775
 
        return (kind, fingerprint, size, executable, tree_data)
 
813
        return (minikind, fingerprint, size, executable, tree_data)
776
814
 
777
815
    def _iter_entries(self):
778
816
        """Iterate over all the entries in the dirstate.
819
857
 
820
858
    @staticmethod
821
859
    def on_file(path):
822
 
        """Construct a DirState on the file at path path."""
823
 
        result = DirState()
824
 
        result._state_file = open(path, 'rb+')
 
860
        """Construct a DirState on the file at path path.
 
861
 
 
862
        :return: An unlocked DirState object, associated with the given path.
 
863
        """
 
864
        result = DirState(path)
825
865
        return result
826
866
 
827
867
    def _read_dirblocks_if_needed(self):
868
908
                    field_count - cur, expected_field_count, entry_size,
869
909
                    self._num_entries, fields)
870
910
 
871
 
            fields_to_entry = self._get_fields_to_entry()
872
 
            entries = [fields_to_entry(fields[pos:pos+entry_size])
873
 
                       for pos in xrange(cur, field_count, entry_size)]
874
 
            self._entries_to_current_state(entries)
 
911
            if num_present_parents == 1:
 
912
                # Bind external functions to local names
 
913
                _int = int
 
914
                # We access all fields in order, so we can just iterate over
 
915
                # them. Grab an straight iterator over the fields. (We use an
 
916
                # iterator because we don't want to do a lot of additions, nor
 
917
                # do we want to do a lot of slicing)
 
918
                next = iter(fields).next
 
919
                # Move the iterator to the current position
 
920
                for x in xrange(cur):
 
921
                    next()
 
922
                # The two blocks here are deliberate: the root block and the
 
923
                # contents-of-root block.
 
924
                self._dirblocks = [('', []), ('', [])]
 
925
                current_block = self._dirblocks[0][1]
 
926
                current_dirname = ''
 
927
                append_entry = current_block.append
 
928
                for count in xrange(self._num_entries):
 
929
                    dirname = next()
 
930
                    name = next()
 
931
                    file_id = next()
 
932
                    if dirname != current_dirname:
 
933
                        # new block - different dirname
 
934
                        current_block = []
 
935
                        current_dirname = dirname
 
936
                        self._dirblocks.append((current_dirname, current_block))
 
937
                        append_entry = current_block.append
 
938
                    # we know current_dirname == dirname, so re-use it to avoid
 
939
                    # creating new strings
 
940
                    entry = ((current_dirname, name, file_id),
 
941
                             [(# Current Tree
 
942
                                 next(),                # minikind
 
943
                                 next(),                # fingerprint
 
944
                                 _int(next()),          # size
 
945
                                 next() == 'y',         # executable
 
946
                                 next(),                # packed_stat or revision_id
 
947
                             ),
 
948
                             ( # Parent 1
 
949
                                 next(),                # minikind
 
950
                                 next(),                # fingerprint
 
951
                                 _int(next()),          # size
 
952
                                 next() == 'y',         # executable
 
953
                                 next(),                # packed_stat or revision_id
 
954
                             ),
 
955
                             ])
 
956
                    trailing = next()
 
957
                    assert trailing == '\n'
 
958
                    # append the entry to the current block
 
959
                    append_entry(entry)
 
960
                self._split_root_dirblock_into_contents()
 
961
            else:
 
962
                fields_to_entry = self._get_fields_to_entry()
 
963
                entries = [fields_to_entry(fields[pos:pos+entry_size])
 
964
                           for pos in xrange(cur, field_count, entry_size)]
 
965
                self._entries_to_current_state(entries)
875
966
            self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
876
967
 
877
968
    def _read_header(self):
898
989
 
899
990
    def _read_header_if_needed(self):
900
991
        """Read the header of the dirstate file if needed."""
 
992
        # inline this as it will be called a lot
 
993
        if not self._lock_token:
 
994
            raise errors.ObjectNotLocked(self)
901
995
        if self._header_state == DirState.NOT_IN_MEMORY:
902
996
            self._read_header()
903
997
 
966
1060
 
967
1061
        :param path: The path inside the tree to set - '' is the root, 'foo'
968
1062
            is the path foo in the root.
969
 
        :param new_id: The new id to assign to the path. If unicode, it will
970
 
            be encoded to utf8. In future this will be deprecated: avoid using
971
 
            unicode ids if possible.
 
1063
        :param new_id: The new id to assign to the path. This must be a utf8
 
1064
            file id (not unicode, and not None).
972
1065
        """
973
1066
        # TODO: start warning here.
974
 
        if new_id.__class__ == unicode:
975
 
            new_id = new_id.encode('utf8')
 
1067
        assert new_id.__class__ == str
976
1068
        self._read_dirblocks_if_needed()
977
1069
        if len(path):
978
1070
            import pdb;pdb.set_trace()
1041
1133
        # one: the current tree
1042
1134
        for entry in self._iter_entries():
1043
1135
            # skip entries not in the current tree
1044
 
            if entry[1][0][0] in ('absent', 'relocated'):
 
1136
            if entry[1][0][0] in ('a', 'r'): # absent, relocated
1045
1137
                continue
1046
1138
            by_path[entry[0]] = [entry[1][0]] + \
1047
1139
                [DirState.NULL_PARENT_DETAILS] * parent_count
1067
1159
                # new entry at this path: by adding the id->path mapping last,
1068
1160
                # all the mappings are valid and have correct relocation
1069
1161
                # records where needed. 
1070
 
                file_id = entry.file_id.encode('utf8')
 
1162
                file_id = entry.file_id
1071
1163
                path_utf8 = path.encode('utf8')
1072
1164
                dirname, basename = os.path.split(path_utf8)
1073
1165
                new_entry_key = (dirname, basename, file_id)
1082
1174
                        # other trees, so put absent pointers there
1083
1175
                        # This is the vertical axis in the matrix, all pointing
1084
1176
                        # tot he real path.
1085
 
                        by_path[entry_key][tree_index] = ('relocated', path_utf8, 0, False, '')
 
1177
                        by_path[entry_key][tree_index] = ('r', path_utf8, 0, False, '')
1086
1178
                # by path consistency: Insert into an existing path record (trivial), or 
1087
1179
                # add a new one with relocation pointers for the other tree indexes.
1088
1180
                if new_entry_key in id_index[file_id]:
1106
1198
                            # fragmented situations by reusing the relocation
1107
1199
                            # records.
1108
1200
                            a_key = iter(id_index[file_id]).next()
1109
 
                            if by_path[a_key][lookup_index][0] in ('relocated', 'absent'):
 
1201
                            if by_path[a_key][lookup_index][0] in ('r', 'a'):
1110
1202
                                # its a pointer or missing statement, use it as is.
1111
1203
                                new_details.append(by_path[a_key][lookup_index])
1112
1204
                            else:
1113
1205
                                # we have the right key, make a pointer to it.
1114
1206
                                real_path = ('/'.join(a_key[0:2])).strip('/')
1115
 
                                new_details.append(('relocated', real_path, 0, False, ''))
 
1207
                                new_details.append(('r', real_path, 0, False, ''))
1116
1208
                    new_details.append(self._inv_entry_to_details(entry))
1117
1209
                    new_details.extend(new_location_suffix)
1118
1210
                    by_path[new_entry_key] = new_details
1158
1250
                return None
1159
1251
        while current_new or current_old:
1160
1252
            # skip entries in old that are not really there
1161
 
            if current_old and current_old[1][0][0] in ('relocated', 'absent'):
 
1253
            if current_old and current_old[1][0][0] in ('r', 'a'):
 
1254
                # relocated or absent
1162
1255
                current_old = advance(old_iterator)
1163
1256
                continue
1164
1257
            if current_new:
1165
1258
                # convert new into dirblock style
1166
1259
                new_path_utf8 = current_new[0].encode('utf8')
1167
1260
                new_dirname, new_basename = os.path.split(new_path_utf8)
1168
 
                new_id = current_new[1].file_id.encode('utf8')
 
1261
                new_id = current_new[1].file_id
1169
1262
                new_entry_key = (new_dirname, new_basename, new_id)
1170
1263
            else:
1171
1264
                # for safety disable variables
1187
1280
                # TODO: update the record if anything significant has changed.
1188
1281
                # the minimal required trigger is if the execute bit or cached
1189
1282
                # kind has changed.
 
1283
                kind = DirState._minikind_to_kind[current_old[1][0][0]]
1190
1284
                if (current_old[1][0][3] != current_new[1].executable or
1191
 
                    current_old[1][0][0] != current_new[1].kind):
 
1285
                    kind != current_new[1].kind):
1192
1286
                    self.update_minimal(current_old[0], current_new[1].kind,
1193
1287
                        num_present_parents,
1194
1288
                        executable=current_new[1].executable,
1223
1317
        all_remaining_keys = set()
1224
1318
        # Dont check the working tree, because its going.
1225
1319
        for details in current_old[1][1:]:
1226
 
            if details[0] not in ('absent', 'relocated'):
 
1320
            if details[0] not in ('a', 'r'): # absent, relocated
1227
1321
                all_remaining_keys.add(current_old[0])
1228
 
            elif details[0] == 'relocated':
 
1322
            elif details[0] == 'r': # relocated
1229
1323
                # record the key for the real path.
1230
1324
                all_remaining_keys.add(tuple(os.path.split(details[1])) + (current_old[0][2],))
1231
1325
            # absent rows are not present at any path.
1254
1348
            assert present
1255
1349
            update_tree_details = self._dirblocks[update_block_index][1][update_entry_index][1]
1256
1350
            # it must not be absent at the moment
1257
 
            assert update_tree_details[0][0] != 'absent'
 
1351
            assert update_tree_details[0][0] != 'a' # absent
1258
1352
            update_tree_details[0] = DirState.NULL_PARENT_DETAILS
1259
1353
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1260
1354
        return last_reference
1267
1361
        if packed_stat is None:
1268
1362
            packed_stat = DirState.NULLSTAT
1269
1363
        entry_index, present = self._find_entry_index(key, block)
1270
 
        new_details = (kind, fingerprint, size, executable, packed_stat)
 
1364
        minikind = DirState._kind_to_minikind[kind]
 
1365
        new_details = (minikind, fingerprint, size, executable, packed_stat)
1271
1366
        assert id_index is not None, 'need an id index to do updates for now !'
1272
1367
        if not present:
1273
1368
            # new entry, synthesis cross reference here,
1292
1387
                    assert present
1293
1388
                    assert path_utf8 is not None
1294
1389
                    self._dirblocks[other_block_index][1][other_entry_index][1][0] = \
1295
 
                        ('relocated', path_utf8, 0, False, '')
 
1390
                        ('r', path_utf8, 0, False, '')
1296
1391
 
1297
1392
                for lookup_index in xrange(1, num_present_parents + 1):
1298
1393
                    # grab any one entry, use it to find the right path.
1306
1401
                        self._find_entry_index(other_key, self._dirblocks[update_block_index][1])
1307
1402
                    assert present
1308
1403
                    update_details = self._dirblocks[update_block_index][1][update_entry_index][1][lookup_index]
1309
 
                    if update_details[0] in ('relocated', 'absent'):
 
1404
                    if update_details[0] in ('r', 'a'): # relocated, absent
1310
1405
                        # its a pointer or absent in lookup_index's tree, use
1311
1406
                        # it as is.
1312
1407
                        new_entry[1].append(update_details)
1313
1408
                    else:
1314
1409
                        # we have the right key, make a pointer to it.
1315
1410
                        pointer_path = os.path.join(*other_key[0:2])
1316
 
                        new_entry[1].append(('relocated', pointer_path, 0, False, ''))
 
1411
                        new_entry[1].append(('r', pointer_path, 0, False, ''))
1317
1412
            block.insert(entry_index, new_entry)
1318
1413
            existing_keys.add(key)
1319
1414
        else:
1343
1438
                    entry_index, present = self._find_entry_index(entry_key, self._dirblocks[block_index][1])
1344
1439
                    assert present
1345
1440
                    self._dirblocks[block_index][1][entry_index][1][0] = \
1346
 
                        ('relocated', path_utf8, 0, False, '')
 
1441
                        ('r', path_utf8, 0, False, '')
1347
1442
        # add a containing dirblock if needed.
1348
 
        if new_details[0] == 'directory':
 
1443
        if new_details[0] == 'd':
1349
1444
            subdir_key = (os.path.join(*key[0:2]), '', '')
1350
1445
            block_index, present = self._find_block_index_from_key(subdir_key)
1351
1446
            if not present:
1354
1449
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1355
1450
 
1356
1451
 
 
1452
    def _wipe_state(self):
 
1453
        """Forget all state information about the dirstate."""
 
1454
        self._header_state = DirState.NOT_IN_MEMORY
 
1455
        self._dirblock_state = DirState.NOT_IN_MEMORY
 
1456
        self._parents = []
 
1457
        self._ghosts = []
 
1458
        self._dirblocks = []
 
1459
 
 
1460
    def lock_read(self):
 
1461
        """Acquire a read lock on the dirstate"""
 
1462
        if self._lock_token is not None:
 
1463
            raise errors.LockContention(self._lock_token)
 
1464
        self._lock_token = lock.ReadLock(self._filename)
 
1465
        self._state_file = self._lock_token.f
 
1466
        self._wipe_state()
 
1467
 
 
1468
    def lock_write(self):
 
1469
        """Acquire a write lock on the dirstate"""
 
1470
        if self._lock_token is not None:
 
1471
            raise errors.LockContention(self._lock_token)
 
1472
        self._lock_token = lock.WriteLock(self._filename)
 
1473
        self._state_file = self._lock_token.f
 
1474
        self._wipe_state()
 
1475
 
 
1476
    def unlock(self):
 
1477
        """Drop any locks held on the dirstate"""
 
1478
        if self._lock_token is None:
 
1479
            raise errors.LockNotHeld(self)
 
1480
        self._state_file = None
 
1481
        self._lock_token.unlock()
 
1482
        self._lock_token = None
 
1483
 
 
1484
    def _requires_lock(self):
 
1485
        """Checks that a lock is currently held by someone on the dirstate"""
 
1486
        if not self._lock_token:
 
1487
            raise errors.ObjectNotLocked(self)
1357
1488
 
1358
1489
def pack_stat(st, _encode=base64.encodestring, _pack=struct.pack):
1359
1490
    """Convert stat values into a packed representation."""