~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

merge dirstate

Show diffs side-by-side

added added

removed removed

Lines of Context:
195
195
 
196
196
import base64
197
197
import bisect
198
 
import cStringIO
 
198
import errno
199
199
import os
200
 
import sha
 
200
from stat import S_IEXEC
201
201
import struct
 
202
import sys
 
203
import time
202
204
import zlib
203
205
 
204
206
from bzrlib import (
208
210
    osutils,
209
211
    trace,
210
212
    )
211
 
from bzrlib.osutils import (
212
 
    pathjoin,
213
 
    sha_file,
214
 
    sha_string,
215
 
    walkdirs,
216
 
    )
217
213
 
218
214
 
219
215
class _Bisector(object):
296
292
        self._state_file = None
297
293
        self._filename = path
298
294
        self._lock_token = None
 
295
        self._lock_state = None
299
296
        self._id_index = None
300
297
        self._end_of_header = None
 
298
        self._cutoff_time = None
301
299
        self._split_path_cache = {}
302
300
        self._bisect_page_size = DirState.BISECT_PAGE_SIZE
303
301
 
310
308
        :param file_id: The file id of the path being added.
311
309
        :param kind: The kind of the path, as a string like 'file', 
312
310
            'directory', etc.
313
 
        :param stat: The output of os.lstate for the path.
 
311
        :param stat: The output of os.lstat for the path.
314
312
        :param fingerprint: The sha value of the file,
315
313
            or the target of a symlink,
316
314
            or the referenced revision id for tree-references,
919
917
 
920
918
    def _entry_to_line(self, entry):
921
919
        """Serialize entry to a NULL delimited line ready for _get_output_lines.
922
 
        
 
920
 
923
921
        :param entry: An entry_tuple as defined in the module docstring.
924
922
        """
925
923
        entire_entry = list(entry[0])
1033
1031
            raise
1034
1032
        return result
1035
1033
 
 
1034
    def update_entry(self, entry, abspath, stat_value=None):
 
1035
        """Update the entry based on what is actually on disk.
 
1036
 
 
1037
        :param entry: This is the dirblock entry for the file in question.
 
1038
        :param abspath: The path on disk for this file.
 
1039
        :param stat_value: (optional) if we already have done a stat on the
 
1040
            file, re-use it.
 
1041
        :return: The sha1 hexdigest of the file (40 bytes) or link target of a
 
1042
                symlink.
 
1043
        """
 
1044
        # This code assumes that the entry passed in is directly held in one of
 
1045
        # the internal _dirblocks. So the dirblock state must have already been
 
1046
        # read.
 
1047
        assert self._dirblock_state != DirState.NOT_IN_MEMORY
 
1048
        if stat_value is None:
 
1049
            try:
 
1050
                # We could inline os.lstat but the common case is that
 
1051
                # stat_value will be passed in, not read here.
 
1052
                stat_value = self._lstat(abspath, entry)
 
1053
            except (OSError, IOError), e:
 
1054
                if e.errno in (errno.ENOENT, errno.EACCES,
 
1055
                               errno.EPERM):
 
1056
                    # The entry is missing, consider it gone
 
1057
                    return None
 
1058
                raise
 
1059
 
 
1060
        kind = osutils.file_kind_from_stat_mode(stat_value.st_mode)
 
1061
        try:
 
1062
            minikind = DirState._kind_to_minikind[kind]
 
1063
        except KeyError: # Unknown kind
 
1064
            return None
 
1065
        packed_stat = pack_stat(stat_value)
 
1066
        (saved_minikind, saved_link_or_sha1, saved_file_size,
 
1067
         saved_executable, saved_packed_stat) = entry[1][0]
 
1068
 
 
1069
        if (minikind == saved_minikind
 
1070
            and packed_stat == saved_packed_stat
 
1071
            # size should also be in packed_stat
 
1072
            and saved_file_size == stat_value.st_size):
 
1073
            # The stat hasn't changed since we saved, so we can potentially
 
1074
            # re-use the saved sha hash.
 
1075
            if minikind == 'd':
 
1076
                return None
 
1077
 
 
1078
            if self._cutoff_time is None:
 
1079
                self._sha_cutoff_time()
 
1080
 
 
1081
            if (stat_value.st_mtime < self._cutoff_time
 
1082
                and stat_value.st_ctime < self._cutoff_time):
 
1083
                # Return the existing fingerprint
 
1084
                return saved_link_or_sha1
 
1085
 
 
1086
        # If we have gotten this far, that means that we need to actually
 
1087
        # process this entry.
 
1088
        link_or_sha1 = None
 
1089
        if minikind == 'f':
 
1090
            link_or_sha1 = self._sha1_file(abspath, entry)
 
1091
            executable = self._is_executable(stat_value.st_mode,
 
1092
                                             saved_executable)
 
1093
            entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
 
1094
                           executable, packed_stat)
 
1095
        elif minikind == 'd':
 
1096
            link_or_sha1 = None
 
1097
            entry[1][0] = ('d', '', 0, False, packed_stat)
 
1098
            if saved_minikind != 'd':
 
1099
                # This changed from something into a directory. Make sure we
 
1100
                # have a directory block for it. This doesn't happen very
 
1101
                # often, so this doesn't have to be super fast.
 
1102
                block_index, entry_index, dir_present, file_present = \
 
1103
                    self._get_block_entry_index(entry[0][0], entry[0][1], 0)
 
1104
                self._ensure_block(block_index, entry_index,
 
1105
                                   osutils.pathjoin(entry[0][0], entry[0][1]))
 
1106
        elif minikind == 'l':
 
1107
            link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
 
1108
            entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
 
1109
                           False, packed_stat)
 
1110
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
 
1111
        return link_or_sha1
 
1112
 
 
1113
    def _sha_cutoff_time(self):
 
1114
        """Return cutoff time.
 
1115
 
 
1116
        Files modified more recently than this time are at risk of being
 
1117
        undetectably modified and so can't be cached.
 
1118
        """
 
1119
        # Cache the cutoff time as long as we hold a lock.
 
1120
        # time.time() isn't super expensive (approx 3.38us), but
 
1121
        # when you call it 50,000 times it adds up.
 
1122
        # For comparison, os.lstat() costs 7.2us if it is hot.
 
1123
        self._cutoff_time = int(time.time()) - 3
 
1124
        return self._cutoff_time
 
1125
 
 
1126
    def _lstat(self, abspath, entry):
 
1127
        """Return the os.lstat value for this path."""
 
1128
        return os.lstat(abspath)
 
1129
 
 
1130
    def _sha1_file(self, abspath, entry):
 
1131
        """Calculate the SHA1 of a file by reading the full text"""
 
1132
        f = file(abspath, 'rb', buffering=65000)
 
1133
        try:
 
1134
            return osutils.sha_file(f)
 
1135
        finally:
 
1136
            f.close()
 
1137
 
 
1138
    def _is_executable(self, mode, old_executable):
 
1139
        """Is this file executable?"""
 
1140
        return bool(S_IEXEC & mode)
 
1141
 
 
1142
    def _is_executable_win32(self, mode, old_executable):
 
1143
        """On win32 the executable bit is stored in the dirstate."""
 
1144
        return old_executable
 
1145
 
 
1146
    if sys.platform == 'win32':
 
1147
        _is_executable = _is_executable_win32
 
1148
 
 
1149
    def _read_link(self, abspath, old_link):
 
1150
        """Read the target of a symlink"""
 
1151
        # TODO: jam 200700301 On Win32, this could just return the value
 
1152
        #       already in memory. However, this really needs to be done at a
 
1153
        #       higher level, because there either won't be anything on disk,
 
1154
        #       or the thing on disk will be a file.
 
1155
        return os.readlink(abspath)
 
1156
 
1036
1157
    def get_ghosts(self):
1037
1158
        """Return a list of the parent tree revision ids that are ghosts."""
1038
1159
        self._read_header_if_needed()
1245
1366
                        path_utf8=real_path)
1246
1367
            return None, None
1247
1368
 
1248
 
    @staticmethod
1249
 
    def initialize(path):
 
1369
    @classmethod
 
1370
    def initialize(cls, path):
1250
1371
        """Create a new dirstate on path.
1251
1372
 
1252
1373
        The new dirstate will be an empty tree - that is it has no parents,
1264
1385
        # stock empty dirstate information - a root with ROOT_ID, no children,
1265
1386
        # and no parents. Finally it calls save() to ensure that this data will
1266
1387
        # persist.
1267
 
        result = DirState(path)
 
1388
        result = cls(path)
1268
1389
        # root dir and root dir contents with no children.
1269
1390
        empty_tree_dirblocks = [('', []), ('', [])]
1270
1391
        # a new root directory, with a NULLSTAT.
1523
1644
        num_entries_line = self._state_file.readline()
1524
1645
        assert num_entries_line.startswith('num_entries: '), 'missing num_entries line'
1525
1646
        self._num_entries = int(num_entries_line[len('num_entries: '):-1])
1526
 
    
 
1647
 
1527
1648
    def save(self):
1528
1649
        """Save any pending changes created during this session.
1529
 
        
 
1650
 
1530
1651
        We reuse the existing file, because that prevents race conditions with
1531
 
        file creation, and we expect to be using oslocks on it in the near 
1532
 
        future to prevent concurrent modification and reads - because dirstates
1533
 
        incremental data aggretation is not compatible with reading a modified
1534
 
        file, and replacing a file in use by another process is impossible on 
1535
 
        windows.
 
1652
        file creation, and use oslocks on it to prevent concurrent modification
 
1653
        and reads - because dirstates incremental data aggretation is not
 
1654
        compatible with reading a modified file, and replacing a file in use by
 
1655
        another process is impossible on windows.
1536
1656
 
1537
1657
        A dirstate in read only mode should be smart enough though to validate
1538
1658
        that the file has not changed, and otherwise discard its cache and
1541
1661
        """
1542
1662
        if (self._header_state == DirState.IN_MEMORY_MODIFIED or
1543
1663
            self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
1544
 
            self._state_file.seek(0)
1545
 
            self._state_file.writelines(self.get_lines())
1546
 
            self._state_file.truncate()
1547
 
            self._state_file.flush()
1548
 
            self._header_state = DirState.IN_MEMORY_UNMODIFIED
1549
 
            self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
 
1664
 
 
1665
            if self._lock_state == 'w':
 
1666
                out_file = self._state_file
 
1667
                wlock = None
 
1668
            else:
 
1669
                # Try to grab a write lock so that we can update the file.
 
1670
                try:
 
1671
                    wlock = lock.WriteLock(self._filename)
 
1672
                except (errors.LockError, errors.LockContention), e:
 
1673
                    # We couldn't grab the lock, so just leave things dirty in
 
1674
                    # memory.
 
1675
                    return
 
1676
                except IOError, e:
 
1677
                    # This may be a read-only tree, or someone else may have a
 
1678
                    # ReadLock. so handle the case when we cannot grab a write
 
1679
                    # lock
 
1680
                    if e.errno in (errno.ENOENT, errno.EPERM, errno.EACCES,
 
1681
                                   errno.EAGAIN):
 
1682
                        # Ignore these errors and just don't save anything
 
1683
                        return
 
1684
                    raise
 
1685
                out_file = wlock.f
 
1686
            try:
 
1687
                out_file.seek(0)
 
1688
                out_file.writelines(self.get_lines())
 
1689
                out_file.truncate()
 
1690
                out_file.flush()
 
1691
                self._header_state = DirState.IN_MEMORY_UNMODIFIED
 
1692
                self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
 
1693
            finally:
 
1694
                if wlock is not None:
 
1695
                    wlock.unlock()
1550
1696
 
1551
1697
    def _set_data(self, parent_ids, dirblocks):
1552
1698
        """Set the full dirstate data in memory.
2008
2154
        self._parents = []
2009
2155
        self._ghosts = []
2010
2156
        self._dirblocks = []
 
2157
        self._id_index = None
 
2158
        self._end_of_header = None
 
2159
        self._cutoff_time = None
 
2160
        self._split_path_cache = {}
2011
2161
 
2012
2162
    def lock_read(self):
2013
2163
        """Acquire a read lock on the dirstate"""
2014
2164
        if self._lock_token is not None:
2015
2165
            raise errors.LockContention(self._lock_token)
 
2166
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2167
        #       already in memory, we could read just the header and check for
 
2168
        #       any modification. If not modified, we can just leave things
 
2169
        #       alone
2016
2170
        self._lock_token = lock.ReadLock(self._filename)
 
2171
        self._lock_state = 'r'
2017
2172
        self._state_file = self._lock_token.f
2018
2173
        self._wipe_state()
2019
2174
 
2021
2176
        """Acquire a write lock on the dirstate"""
2022
2177
        if self._lock_token is not None:
2023
2178
            raise errors.LockContention(self._lock_token)
 
2179
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2180
        #       already in memory, we could read just the header and check for
 
2181
        #       any modification. If not modified, we can just leave things
 
2182
        #       alone
2024
2183
        self._lock_token = lock.WriteLock(self._filename)
 
2184
        self._lock_state = 'w'
2025
2185
        self._state_file = self._lock_token.f
2026
2186
        self._wipe_state()
2027
2187
 
2029
2189
        """Drop any locks held on the dirstate"""
2030
2190
        if self._lock_token is None:
2031
2191
            raise errors.LockNotHeld(self)
 
2192
        # TODO: jam 20070301 Rather than wiping completely, if the blocks are
 
2193
        #       already in memory, we could read just the header and check for
 
2194
        #       any modification. If not modified, we can just leave things
 
2195
        #       alone
2032
2196
        self._state_file = None
 
2197
        self._lock_state = None
2033
2198
        self._lock_token.unlock()
2034
2199
        self._lock_token = None
2035
2200
        self._split_path_cache = {}
2071
2236
    return lo
2072
2237
 
2073
2238
 
 
2239
 
2074
2240
def pack_stat(st, _encode=base64.encodestring, _pack=struct.pack):
2075
2241
    """Convert stat values into a packed representation."""
2076
2242
    # jam 20060614 it isn't really worth removing more entries if we
2081
2247
 
2082
2248
    # base64.encode always adds a final newline, so strip it off
2083
2249
    return _encode(_pack('>llllll'
2084
 
        , st.st_size, st.st_mtime, st.st_ctime
 
2250
        , st.st_size, int(st.st_mtime), int(st.st_ctime)
2085
2251
        , st.st_dev, st.st_ino, st.st_mode))[:-1]