~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

Revert the dirstate/transform changes, so we have a pure 'lstat/fstat' change.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
220
220
    inventory,
221
221
    lock,
222
222
    osutils,
 
223
    static_tuple,
223
224
    trace,
224
225
    )
225
226
 
264
265
        # return '%X.%X' % (int(st.st_mtime), st.st_mode)
265
266
 
266
267
 
 
268
def _unpack_stat(packed_stat):
 
269
    """Turn a packed_stat back into the stat fields.
 
270
 
 
271
    This is meant as a debugging tool, should not be used in real code.
 
272
    """
 
273
    (st_size, st_mtime, st_ctime, st_dev, st_ino,
 
274
     st_mode) = struct.unpack('>LLLLLL', binascii.a2b_base64(packed_stat))
 
275
    return dict(st_size=st_size, st_mtime=st_mtime, st_ctime=st_ctime,
 
276
                st_dev=st_dev, st_ino=st_ino, st_mode=st_mode)
 
277
 
 
278
 
267
279
class SHA1Provider(object):
268
280
    """An interface for getting sha1s of a file."""
269
281
 
548
560
           self._ensure_block(block_index, entry_index, utf8path)
549
561
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
550
562
        if self._id_index:
551
 
            self._id_index.setdefault(entry_key[2], set()).add(entry_key)
 
563
            self._add_to_id_index(self._id_index, entry_key)
552
564
 
553
565
    def _bisect(self, paths):
554
566
        """Bisect through the disk structure for specific rows.
1566
1578
            return
1567
1579
        id_index = self._get_id_index()
1568
1580
        for file_id in new_ids:
1569
 
            for key in id_index.get(file_id, []):
 
1581
            for key in id_index.get(file_id, ()):
1570
1582
                block_i, entry_i, d_present, f_present = \
1571
1583
                    self._get_block_entry_index(key[0], key[1], tree_index)
1572
1584
                if not f_present:
1980
1992
                                          ' tree_index, file_id and path')
1981
1993
            return entry
1982
1994
        else:
1983
 
            possible_keys = self._get_id_index().get(fileid_utf8, None)
 
1995
            possible_keys = self._get_id_index().get(fileid_utf8, ())
1984
1996
            if not possible_keys:
1985
1997
                return None, None
1986
1998
            for key in possible_keys:
2143
2155
                yield entry
2144
2156
 
2145
2157
    def _get_id_index(self):
2146
 
        """Get an id index of self._dirblocks."""
 
2158
        """Get an id index of self._dirblocks.
 
2159
        
 
2160
        This maps from file_id => [(directory, name, file_id)] entries where
 
2161
        that file_id appears in one of the trees.
 
2162
        """
2147
2163
        if self._id_index is None:
2148
2164
            id_index = {}
2149
2165
            for key, tree_details in self._iter_entries():
2150
 
                id_index.setdefault(key[2], set()).add(key)
 
2166
                self._add_to_id_index(id_index, key)
2151
2167
            self._id_index = id_index
2152
2168
        return self._id_index
2153
2169
 
 
2170
    def _add_to_id_index(self, id_index, entry_key):
 
2171
        """Add this entry to the _id_index mapping."""
 
2172
        # This code used to use a set for every entry in the id_index. However,
 
2173
        # it is *rare* to have more than one entry. So a set is a large
 
2174
        # overkill. And even when we do, we won't ever have more than the
 
2175
        # number of parent trees. Which is still a small number (rarely >2). As
 
2176
        # such, we use a simple tuple, and do our own uniqueness checks. While
 
2177
        # the 'in' check is O(N) since N is nicely bounded it shouldn't ever
 
2178
        # cause quadratic failure.
 
2179
        # TODO: This should use StaticTuple
 
2180
        file_id = entry_key[2]
 
2181
        entry_key = static_tuple.StaticTuple.from_sequence(entry_key)
 
2182
        if file_id not in id_index:
 
2183
            id_index[file_id] = static_tuple.StaticTuple(entry_key,)
 
2184
        else:
 
2185
            entry_keys = id_index[file_id]
 
2186
            if entry_key not in entry_keys:
 
2187
                id_index[file_id] = entry_keys + (entry_key,)
 
2188
 
 
2189
    def _remove_from_id_index(self, id_index, entry_key):
 
2190
        """Remove this entry from the _id_index mapping.
 
2191
 
 
2192
        It is an programming error to call this when the entry_key is not
 
2193
        already present.
 
2194
        """
 
2195
        file_id = entry_key[2]
 
2196
        entry_keys = list(id_index[file_id])
 
2197
        entry_keys.remove(entry_key)
 
2198
        id_index[file_id] = static_tuple.StaticTuple.from_sequence(entry_keys)
 
2199
 
2154
2200
    def _get_output_lines(self, lines):
2155
2201
        """Format lines for final output.
2156
2202
 
2414
2460
                continue
2415
2461
            by_path[entry[0]] = [entry[1][0]] + \
2416
2462
                [DirState.NULL_PARENT_DETAILS] * parent_count
2417
 
            id_index[entry[0][2]] = set([entry[0]])
 
2463
            # TODO: Possibly inline this, since we know it isn't present yet
 
2464
            #       id_index[entry[0][2]] = (entry[0],)
 
2465
            self._add_to_id_index(id_index, entry[0])
2418
2466
 
2419
2467
        # now the parent trees:
2420
2468
        for tree_index, tree in enumerate(parent_trees):
2442
2490
                new_entry_key = (dirname, basename, file_id)
2443
2491
                # tree index consistency: All other paths for this id in this tree
2444
2492
                # index must point to the correct path.
2445
 
                for entry_key in id_index.setdefault(file_id, set()):
 
2493
                for entry_key in id_index.get(file_id, ()):
2446
2494
                    # TODO:PROFILING: It might be faster to just update
2447
2495
                    # rather than checking if we need to, and then overwrite
2448
2496
                    # the one we are located at.
2454
2502
                        by_path[entry_key][tree_index] = ('r', path_utf8, 0, False, '')
2455
2503
                # by path consistency: Insert into an existing path record (trivial), or
2456
2504
                # add a new one with relocation pointers for the other tree indexes.
2457
 
                if new_entry_key in id_index[file_id]:
 
2505
                entry_keys = id_index.get(file_id, ())
 
2506
                if new_entry_key in entry_keys:
2458
2507
                    # there is already an entry where this data belongs, just insert it.
2459
2508
                    by_path[new_entry_key][tree_index] = \
2460
2509
                        self._inv_entry_to_details(entry)
2465
2514
                    new_details = []
2466
2515
                    for lookup_index in xrange(tree_index):
2467
2516
                        # boundary case: this is the first occurence of file_id
2468
 
                        # so there are no id_indexs, possibly take this out of
 
2517
                        # so there are no id_indexes, possibly take this out of
2469
2518
                        # the loop?
2470
 
                        if not len(id_index[file_id]):
 
2519
                        if not len(entry_keys):
2471
2520
                            new_details.append(DirState.NULL_PARENT_DETAILS)
2472
2521
                        else:
2473
2522
                            # grab any one entry, use it to find the right path.
2474
2523
                            # TODO: optimise this to reduce memory use in highly
2475
2524
                            # fragmented situations by reusing the relocation
2476
2525
                            # records.
2477
 
                            a_key = iter(id_index[file_id]).next()
 
2526
                            a_key = iter(entry_keys).next()
2478
2527
                            if by_path[a_key][lookup_index][0] in ('r', 'a'):
2479
2528
                                # its a pointer or missing statement, use it as is.
2480
2529
                                new_details.append(by_path[a_key][lookup_index])
2485
2534
                    new_details.append(self._inv_entry_to_details(entry))
2486
2535
                    new_details.extend(new_location_suffix)
2487
2536
                    by_path[new_entry_key] = new_details
2488
 
                    id_index[file_id].add(new_entry_key)
 
2537
                    self._add_to_id_index(id_index, new_entry_key)
2489
2538
        # --- end generation of full tree mappings
2490
2539
 
2491
2540
        # sort and output all the entries
2643
2692
        if tracing:
2644
2693
            trace.mutter("set_state_from_inventory complete.")
2645
2694
 
 
2695
    def set_state_from_scratch(self, working_inv, parent_trees, parent_ghosts):
 
2696
        """Wipe the currently stored state and set it to something new.
 
2697
 
 
2698
        This is a hard-reset for the data we are working with.
 
2699
        """
 
2700
        # Technically, we really want a write lock, but until we write, we
 
2701
        # don't really need it.
 
2702
        self._requires_lock()
 
2703
        # root dir and root dir contents with no children. We have to have a
 
2704
        # root for set_state_from_inventory to work correctly.
 
2705
        empty_root = (('', '', inventory.ROOT_ID),
 
2706
                      [('d', '', 0, False, DirState.NULLSTAT)])
 
2707
        empty_tree_dirblocks = [('', [empty_root]), ('', [])]
 
2708
        self._set_data([], empty_tree_dirblocks)
 
2709
        self.set_state_from_inventory(working_inv)
 
2710
        self.set_parent_trees(parent_trees, parent_ghosts)
 
2711
 
2646
2712
    def _make_absent(self, current_old):
2647
2713
        """Mark current_old - an entry - as absent for tree 0.
2648
2714
 
2673
2739
            block[1].pop(entry_index)
2674
2740
            # if we have an id_index in use, remove this key from it for this id.
2675
2741
            if self._id_index is not None:
2676
 
                self._id_index[current_old[0][2]].remove(current_old[0])
 
2742
                self._remove_from_id_index(self._id_index, current_old[0])
2677
2743
        # update all remaining keys for this id to record it as absent. The
2678
2744
        # existing details may either be the record we are marking as deleted
2679
2745
        # (if there were other trees with the id present at this path), or may
2748
2814
                    else:
2749
2815
                        break
2750
2816
            # new entry, synthesis cross reference here,
2751
 
            existing_keys = id_index.setdefault(key[2], set())
 
2817
            existing_keys = id_index.get(key[2], ())
2752
2818
            if not existing_keys:
2753
2819
                # not currently in the state, simplest case
2754
2820
                new_entry = key, [new_details] + self._empty_parent_info()
2785
2851
                    # loop.
2786
2852
                    other_entry = other_block[other_entry_index]
2787
2853
                    other_entry[1][0] = ('r', path_utf8, 0, False, '')
2788
 
                    self._maybe_remove_row(other_block, other_entry_index,
2789
 
                        id_index)
 
2854
                    if self._maybe_remove_row(other_block, other_entry_index,
 
2855
                                              id_index):
 
2856
                        # If the row holding this was removed, we need to
 
2857
                        # recompute where this entry goes
 
2858
                        entry_index, _ = self._find_entry_index(key, block)
2790
2859
 
2791
2860
                # This loop:
2792
2861
                # adds a tuple to the new details for each column
2794
2863
                #  - or by creating a new pointer to the right row inside that column
2795
2864
                num_present_parents = self._num_present_parents()
2796
2865
                if num_present_parents:
 
2866
                    # TODO: This re-evaluates the existing_keys set, do we need
 
2867
                    #       to do that ourselves?
2797
2868
                    other_key = list(existing_keys)[0]
2798
2869
                for lookup_index in xrange(1, num_present_parents + 1):
2799
2870
                    # grab any one entry, use it to find the right path.
2818
2889
                        pointer_path = osutils.pathjoin(*other_key[0:2])
2819
2890
                        new_entry[1].append(('r', pointer_path, 0, False, ''))
2820
2891
            block.insert(entry_index, new_entry)
2821
 
            existing_keys.add(key)
 
2892
            self._add_to_id_index(id_index, key)
2822
2893
        else:
2823
2894
            # Does the new state matter?
2824
2895
            block[entry_index][1][0] = new_details
2833
2904
            # converted to relocated.
2834
2905
            if path_utf8 is None:
2835
2906
                raise AssertionError('no path')
2836
 
            for entry_key in id_index.setdefault(key[2], set()):
 
2907
            existing_keys = id_index.get(key[2], ())
 
2908
            if key not in existing_keys:
 
2909
                raise AssertionError('We found the entry in the blocks, but'
 
2910
                    ' the key is not in the id_index.'
 
2911
                    ' key: %s, existing_keys: %s' % (key, existing_keys))
 
2912
            for entry_key in existing_keys:
2837
2913
                # TODO:PROFILING: It might be faster to just update
2838
2914
                # rather than checking if we need to, and then overwrite
2839
2915
                # the one we are located at.
2863
2939
        """Remove index if it is absent or relocated across the row.
2864
2940
        
2865
2941
        id_index is updated accordingly.
 
2942
        :return: True if we removed the row, False otherwise
2866
2943
        """
2867
2944
        present_in_row = False
2868
2945
        entry = block[index]
2872
2949
                break
2873
2950
        if not present_in_row:
2874
2951
            block.pop(index)
2875
 
            id_index[entry[0][2]].remove(entry[0])
 
2952
            self._remove_from_id_index(id_index, entry[0])
 
2953
            return True
 
2954
        return False
2876
2955
 
2877
2956
    def _validate(self):
2878
2957
        """Check that invariants on the dirblock are correct.
3020
3099
                        raise AssertionError(
3021
3100
                            'file_id %r did not match entry key %s'
3022
3101
                            % (file_id, entry_key))
 
3102
                if len(entry_keys) != len(set(entry_keys)):
 
3103
                    raise AssertionError(
 
3104
                        'id_index contained non-unique data for %s'
 
3105
                        % (entry_keys,))
3023
3106
 
3024
3107
    def _wipe_state(self):
3025
3108
        """Forget all state information about the dirstate."""