~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

  • Committer: Alexander Belchenko
  • Date: 2012-03-29 08:34:13 UTC
  • mto: (6015.44.14 2.4)
  • mto: This revision was merged to the branch mainline in revision 6513.
  • Revision ID: bialix@ukr.net-20120329083413-d4bqqdtfn2yrxp4f
change st_dev, st_ino, st_uid, st_gid from int members to properties.

Show diffs side-by-side

added added

removed removed

Lines of Context:
218
218
 
219
219
"""
220
220
 
221
 
from __future__ import absolute_import
222
 
 
223
221
import bisect
 
222
import binascii
224
223
import errno
225
224
import operator
226
225
import os
227
226
from stat import S_IEXEC
228
227
import stat
 
228
import struct
229
229
import sys
230
230
import time
231
231
import zlib
251
251
ERROR_DIRECTORY = 267
252
252
 
253
253
 
 
254
if not getattr(struct, '_compile', None):
 
255
    # Cannot pre-compile the dirstate pack_stat
 
256
    def pack_stat(st, _encode=binascii.b2a_base64, _pack=struct.pack):
 
257
        """Convert stat values into a packed representation."""
 
258
        return _encode(_pack('>LLLLLL', st.st_size & 0xFFFFFFFF,
 
259
            int(st.st_mtime) & 0xFFFFFFFF, int(st.st_ctime) & 0xFFFFFFFF,
 
260
            st.st_dev & 0xFFFFFFFF, st.st_ino & 0xFFFFFFFF,
 
261
            st.st_mode))[:-1]
 
262
else:
 
263
    # compile the struct compiler we need, so as to only do it once
 
264
    from _struct import Struct
 
265
    _compiled_pack = Struct('>LLLLLL').pack
 
266
    def pack_stat(st, _encode=binascii.b2a_base64, _pack=_compiled_pack):
 
267
        """Convert stat values into a packed representation."""
 
268
        # jam 20060614 it isn't really worth removing more entries if we
 
269
        # are going to leave it in packed form.
 
270
        # With only st_mtime and st_mode filesize is 5.5M and read time is 275ms
 
271
        # With all entries, filesize is 5.9M and read time is maybe 280ms
 
272
        # well within the noise margin
 
273
 
 
274
        # base64 encoding always adds a final newline, so strip it off
 
275
        # The current version
 
276
        return _encode(_pack(st.st_size, int(st.st_mtime), int(st.st_ctime),
 
277
            st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
 
278
        # This is 0.060s / 1.520s faster by not encoding as much information
 
279
        # return _encode(_pack('>LL', int(st.st_mtime), st.st_mode))[:-1]
 
280
        # This is not strictly faster than _encode(_pack())[:-1]
 
281
        # return '%X.%X.%X.%X.%X.%X' % (
 
282
        #      st.st_size, int(st.st_mtime), int(st.st_ctime),
 
283
        #      st.st_dev, st.st_ino, st.st_mode)
 
284
        # Similar to the _encode(_pack('>LL'))
 
285
        # return '%X.%X' % (int(st.st_mtime), st.st_mode)
 
286
 
 
287
 
 
288
def _unpack_stat(packed_stat):
 
289
    """Turn a packed_stat back into the stat fields.
 
290
 
 
291
    This is meant as a debugging tool, should not be used in real code.
 
292
    """
 
293
    (st_size, st_mtime, st_ctime, st_dev, st_ino,
 
294
     st_mode) = struct.unpack('>LLLLLL', binascii.a2b_base64(packed_stat))
 
295
    return dict(st_size=st_size, st_mtime=st_mtime, st_ctime=st_ctime,
 
296
                st_dev=st_dev, st_ino=st_ino, st_mode=st_mode)
 
297
 
 
298
 
254
299
class SHA1Provider(object):
255
300
    """An interface for getting sha1s of a file."""
256
301
 
1292
1337
                    parent_trees.append((parent_id, parent_tree))
1293
1338
                    parent_tree.lock_read()
1294
1339
                result.set_parent_trees(parent_trees, [])
1295
 
                result.set_state_from_inventory(tree.root_inventory)
 
1340
                result.set_state_from_inventory(tree.inventory)
1296
1341
            finally:
1297
1342
                for revid, parent_tree in parent_trees:
1298
1343
                    parent_tree.unlock()
1561
1606
                    else:
1562
1607
                        source_path = child_basename
1563
1608
                    if new_path_utf8:
1564
 
                        target_path = \
1565
 
                            new_path_utf8 + source_path[len(old_path_utf8):]
 
1609
                        target_path = new_path_utf8 + source_path[len(old_path):]
1566
1610
                    else:
1567
 
                        if old_path_utf8 == '':
 
1611
                        if old_path == '':
1568
1612
                            raise AssertionError("cannot rename directory to"
1569
1613
                                                 " itself")
1570
 
                        target_path = source_path[len(old_path_utf8) + 1:]
 
1614
                        target_path = source_path[len(old_path) + 1:]
1571
1615
                    adds.append((None, target_path, entry[0][2], entry[1][1], False))
1572
1616
                    deletes.append(
1573
1617
                        (source_path, target_path, entry[0][2], None, False))
1574
 
                deletes.append(
1575
 
                    (old_path_utf8, new_path_utf8, file_id, None, False))
1576
 
 
 
1618
                deletes.append((old_path_utf8, new_path, file_id, None, False))
1577
1619
        self._check_delta_ids_absent(new_ids, delta, 1)
1578
1620
        try:
1579
1621
            # Finish expunging deletes/first half of renames.
1648
1690
            entry_key = st(dirname, basename, file_id)
1649
1691
            block_index, present = self._find_block_index_from_key(entry_key)
1650
1692
            if not present:
1651
 
                # The block where we want to put the file is not present.
1652
 
                # However, it might have just been an empty directory. Look for
1653
 
                # the parent in the basis-so-far before throwing an error.
1654
 
                parent_dir, parent_base = osutils.split(dirname)
1655
 
                parent_block_idx, parent_entry_idx, _, parent_present = \
1656
 
                    self._get_block_entry_index(parent_dir, parent_base, 1)
1657
 
                if not parent_present:
1658
 
                    self._raise_invalid(new_path, file_id,
1659
 
                        "Unable to find block for this record."
1660
 
                        " Was the parent added?")
1661
 
                self._ensure_block(parent_block_idx, parent_entry_idx, dirname)
1662
 
 
 
1693
                self._raise_invalid(new_path, file_id,
 
1694
                    "Unable to find block for this record."
 
1695
                    " Was the parent added?")
1663
1696
            block = self._dirblocks[block_index][1]
1664
1697
            entry_index, present = self._find_entry_index(entry_key, block)
1665
1698
            if real_add:
1864
1897
                    file_id, "This parent is not a directory.")
1865
1898
 
1866
1899
    def _observed_sha1(self, entry, sha1, stat_value,
1867
 
        _stat_to_minikind=_stat_to_minikind):
 
1900
        _stat_to_minikind=_stat_to_minikind, _pack_stat=pack_stat):
1868
1901
        """Note the sha1 of a file.
1869
1902
 
1870
1903
        :param entry: The entry the sha1 is for.
1876
1909
        except KeyError:
1877
1910
            # Unhandled kind
1878
1911
            return None
 
1912
        packed_stat = _pack_stat(stat_value)
1879
1913
        if minikind == 'f':
1880
1914
            if self._cutoff_time is None:
1881
1915
                self._sha_cutoff_time()
1882
1916
            if (stat_value.st_mtime < self._cutoff_time
1883
1917
                and stat_value.st_ctime < self._cutoff_time):
1884
1918
                entry[1][0] = ('f', sha1, stat_value.st_size, entry[1][0][3],
1885
 
                               pack_stat(stat_value))
 
1919
                               packed_stat)
1886
1920
                self._mark_modified([entry])
1887
1921
 
1888
1922
    def _sha_cutoff_time(self):
1933
1967
            # paths are produced by UnicodeDirReader on purpose.
1934
1968
            abspath = abspath.encode(fs_encoding)
1935
1969
        target = os.readlink(abspath)
1936
 
        if fs_encoding not in ('utf-8', 'ascii'):
 
1970
        if fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968'):
1937
1971
            # Change encoding if needed
1938
1972
            target = target.decode(fs_encoding).encode('UTF-8')
1939
1973
        return target
2441
2475
            raise errors.BzrError('missing num_entries line')
2442
2476
        self._num_entries = int(num_entries_line[len('num_entries: '):-1])
2443
2477
 
2444
 
    def sha1_from_stat(self, path, stat_result):
 
2478
    def sha1_from_stat(self, path, stat_result, _pack_stat=pack_stat):
2445
2479
        """Find a sha1 given a stat lookup."""
2446
 
        return self._get_packed_stat_index().get(pack_stat(stat_result), None)
 
2480
        return self._get_packed_stat_index().get(_pack_stat(stat_result), None)
2447
2481
 
2448
2482
    def _get_packed_stat_index(self):
2449
2483
        """Get a packed_stat index of self._dirblocks."""
2575
2609
        self.update_minimal(('', '', new_id), 'd',
2576
2610
            path_utf8='', packed_stat=entry[1][0][4])
2577
2611
        self._mark_modified()
 
2612
        # XXX: This was added by Ian, we need to make sure there
 
2613
        #      are tests for it, because it isn't in bzr.dev TRUNK
 
2614
        #      It looks like the only place it is called is in setting the root
 
2615
        #      id of the tree. So probably we never had an _id_index when we
 
2616
        #      don't even have a root yet.
 
2617
        if self._id_index is not None:
 
2618
            self._add_to_id_index(self._id_index, entry[0])
2578
2619
 
2579
2620
    def set_parent_trees(self, trees, ghosts):
2580
2621
        """Set the parent trees for the dirstate.
3287
3328
        if self._id_index is not None:
3288
3329
            for file_id, entry_keys in self._id_index.iteritems():
3289
3330
                for entry_key in entry_keys:
3290
 
                    # Check that the entry in the map is pointing to the same
3291
 
                    # file_id
3292
3331
                    if entry_key[2] != file_id:
3293
3332
                        raise AssertionError(
3294
3333
                            'file_id %r did not match entry key %s'
3295
3334
                            % (file_id, entry_key))
3296
 
                    # And that from this entry key, we can look up the original
3297
 
                    # record
3298
 
                    block_index, present = self._find_block_index_from_key(entry_key)
3299
 
                    if not present:
3300
 
                        raise AssertionError('missing block for entry key: %r', entry_key)
3301
 
                    entry_index, present = self._find_entry_index(entry_key, self._dirblocks[block_index][1])
3302
 
                    if not present:
3303
 
                        raise AssertionError('missing entry for key: %r', entry_key)
3304
3335
                if len(entry_keys) != len(set(entry_keys)):
3305
3336
                    raise AssertionError(
3306
3337
                        'id_index contained non-unique data for %s'
3367
3398
 
3368
3399
 
3369
3400
def py_update_entry(state, entry, abspath, stat_value,
3370
 
                 _stat_to_minikind=DirState._stat_to_minikind):
 
3401
                 _stat_to_minikind=DirState._stat_to_minikind,
 
3402
                 _pack_stat=pack_stat):
3371
3403
    """Update the entry based on what is actually on disk.
3372
3404
 
3373
3405
    This function only calculates the sha if it needs to - if the entry is
3386
3418
    except KeyError:
3387
3419
        # Unhandled kind
3388
3420
        return None
3389
 
    packed_stat = pack_stat(stat_value)
 
3421
    packed_stat = _pack_stat(stat_value)
3390
3422
    (saved_minikind, saved_link_or_sha1, saved_file_size,
3391
3423
     saved_executable, saved_packed_stat) = entry[1][0]
3392
3424
 
4265
4297
        _bisect_path_left,
4266
4298
        _bisect_path_right,
4267
4299
        cmp_by_dirs,
4268
 
        pack_stat,
4269
4300
        ProcessEntryC as _process_entry,
4270
4301
        update_entry as update_entry,
4271
4302
        )
4277
4308
        _bisect_path_left,
4278
4309
        _bisect_path_right,
4279
4310
        cmp_by_dirs,
4280
 
        pack_stat,
4281
4311
        )
4282
4312
    # FIXME: It would be nice to be able to track moved lines so that the
4283
4313
    # corresponding python code can be moved to the _dirstate_helpers_py