~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/hashcache.py

  • Committer: John Arbash Meinel
  • Date: 2006-07-13 18:38:58 UTC
  • mfrom: (1863 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1869.
  • Revision ID: john@arbash-meinel.com-20060713183858-ebf4aa1f9ef8bb6e
[merge] bzr.dev 1863

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
FP_CTIME_COLUMN = 2
43
43
FP_MODE_COLUMN = 5
44
44
 
45
 
def _fingerprint(abspath):
46
 
    try:
47
 
        fs = os.lstat(abspath)
48
 
    except OSError:
49
 
        # might be missing, etc
50
 
        return None
51
 
 
52
 
    if stat.S_ISDIR(fs.st_mode):
53
 
        return None
54
 
 
55
 
    # we discard any high precision because it's not reliable; perhaps we
56
 
    # could do better on some systems?
57
 
    return (fs.st_size, long(fs.st_mtime),
58
 
            long(fs.st_ctime), fs.st_ino, fs.st_dev, fs.st_mode)
59
45
 
60
46
 
61
47
class HashCache(object):
131
117
        
132
118
        for inum, path, cache_entry in prep:
133
119
            abspath = pathjoin(self.root, path)
134
 
            fp = _fingerprint(abspath)
 
120
            fp = self._fingerprint(abspath)
135
121
            self.stat_count += 1
136
122
            
137
123
            cache_fp = cache_entry[1]
142
128
                self.needs_write = True
143
129
                del self._cache[path]
144
130
 
145
 
 
146
131
    def get_sha1(self, path):
147
132
        """Return the sha1 of a file.
148
133
        """
149
134
        abspath = pathjoin(self.root, path)
150
135
        self.stat_count += 1
151
 
        file_fp = _fingerprint(abspath)
 
136
        file_fp = self._fingerprint(abspath)
152
137
        
153
138
        if not file_fp:
154
139
            # not a regular file or not existing
164
149
            cache_sha1, cache_fp = None, None
165
150
 
166
151
        if cache_fp == file_fp:
 
152
            ## mutter("hashcache hit for %s %r -> %s", path, file_fp, cache_sha1)
 
153
            ## mutter("now = %s", time.time())
167
154
            self.hit_count += 1
168
155
            return cache_sha1
169
156
        
170
157
        self.miss_count += 1
171
158
 
172
 
 
173
159
        mode = file_fp[FP_MODE_COLUMN]
174
160
        if stat.S_ISREG(mode):
175
 
            digest = sha_file(file(abspath, 'rb', buffering=65000))
 
161
            digest = self._really_sha1_file(abspath)
176
162
        elif stat.S_ISLNK(mode):
177
163
            digest = sha.new(os.readlink(abspath)).hexdigest()
178
164
        else:
179
165
            raise BzrError("file %r: unknown file stat mode: %o"%(abspath,mode))
180
166
 
181
 
        now = int(time.time())
182
 
        if file_fp[FP_MTIME_COLUMN] >= now or file_fp[FP_CTIME_COLUMN] >= now:
 
167
        # window of 3 seconds to allow for 2s resolution on windows,
 
168
        # unsynchronized file servers, etc.
 
169
        cutoff = self._cutoff_time()
 
170
        if file_fp[FP_MTIME_COLUMN] >= cutoff \
 
171
                or file_fp[FP_CTIME_COLUMN] >= cutoff:
183
172
            # changed too recently; can't be cached.  we can
184
173
            # return the result and it could possibly be cached
185
174
            # next time.
191
180
            # need to let sufficient time elapse before we may cache this entry
192
181
            # again.  If we didn't do this, then, for example, a very quick 1
193
182
            # byte replacement in the file might go undetected.
194
 
            self.danger_count += 1 
 
183
            ## mutter('%r modified too recently; not caching', path)
 
184
            self.danger_count += 1
195
185
            if cache_fp:
196
186
                self.removed_count += 1
197
187
                self.needs_write = True
198
188
                del self._cache[path]
199
189
        else:
 
190
            ## mutter('%r added to cache: now=%f, mtime=%d, ctime=%d',
 
191
            ##        path, time.time(), file_fp[FP_MTIME_COLUMN],
 
192
            ##        file_fp[FP_CTIME_COLUMN])
200
193
            self.update_count += 1
201
194
            self.needs_write = True
202
195
            self._cache[path] = (digest, file_fp)
203
196
        return digest
 
197
 
 
198
    def _really_sha1_file(self, abspath):
 
199
        """Calculate the SHA1 of a file by reading the full text"""
 
200
        return sha_file(file(abspath, 'rb', buffering=65000))
204
201
        
205
202
    def write(self):
206
203
        """Write contents of cache to file."""
218
215
                print >>outf
219
216
            outf.commit()
220
217
            self.needs_write = False
221
 
            mutter("write hash cache: %s hits=%d misses=%d stat=%d recent=%d updates=%d",
222
 
                   self.cache_file_name(), self.hit_count, self.miss_count,
223
 
                   self.stat_count,
224
 
                   self.danger_count, self.update_count)
 
218
            ## mutter("write hash cache: %s hits=%d misses=%d stat=%d recent=%d updates=%d",
 
219
            ##        self.cache_file_name(), self.hit_count, self.miss_count,
 
220
            ##        self.stat_count,
 
221
            ##        self.danger_count, self.update_count)
225
222
        finally:
226
223
            if not outf.closed:
227
224
                outf.abort()
244
241
            self.needs_write = True
245
242
            return
246
243
 
247
 
 
248
244
        hdr = inf.readline()
249
245
        if hdr != CACHE_HEADER:
250
246
            mutter('cache header marker not found at top of %s;'
275
271
            self._cache[path] = (sha1, fp)
276
272
 
277
273
        self.needs_write = False
 
274
 
 
275
    def _cutoff_time(self):
 
276
        """Return cutoff time.
 
277
 
 
278
        Files modified more recently than this time are at risk of being
 
279
        undetectably modified and so can't be cached.
 
280
        """
 
281
        return int(time.time()) - 3
278
282
           
279
 
 
280
 
 
281
 
        
 
283
    def _fingerprint(self, abspath):
 
284
        try:
 
285
            fs = os.lstat(abspath)
 
286
        except OSError:
 
287
            # might be missing, etc
 
288
            return None
 
289
        if stat.S_ISDIR(fs.st_mode):
 
290
            return None
 
291
        # we discard any high precision because it's not reliable; perhaps we
 
292
        # could do better on some systems?
 
293
        return (fs.st_size, long(fs.st_mtime),
 
294
                long(fs.st_ctime), fs.st_ino, fs.st_dev, fs.st_mode)