~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/hashcache.py

  • Committer: John Arbash Meinel
  • Date: 2006-02-15 15:18:44 UTC
  • mto: (1185.79.1 bzr-jam-pending)
  • mto: This revision was merged to the branch mainline in revision 1554.
  • Revision ID: john@arbash-meinel.com-20060215151844-ce3e3efccd19da3f
Reverting gpg changes, should not be mainline, see gpg_uses_tempfile plugin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
# TODO: Perhaps return more details on the file to avoid statting it
24
24
# again: nonexistent, file type, size, etc
25
25
 
 
26
# TODO: Perhaps use a Python pickle instead of a text file; might be faster.
 
27
 
26
28
 
27
29
 
28
30
CACHE_HEADER = "### bzr hashcache v5\n"
29
31
 
30
32
import os, stat, time
 
33
import sha
31
34
 
32
 
from bzrlib.osutils import sha_file
 
35
from bzrlib.osutils import sha_file, pathjoin
33
36
from bzrlib.trace import mutter, warning
34
 
 
35
 
 
 
37
from bzrlib.atomicfile import AtomicFile
 
38
from bzrlib.errors import BzrError
 
39
 
 
40
 
 
41
FP_MTIME_COLUMN = 1
 
42
FP_CTIME_COLUMN = 2
 
43
FP_MODE_COLUMN = 5
36
44
 
37
45
def _fingerprint(abspath):
38
46
    try:
47
55
    # we discard any high precision because it's not reliable; perhaps we
48
56
    # could do better on some systems?
49
57
    return (fs.st_size, long(fs.st_mtime),
50
 
            long(fs.st_ctime), fs.st_ino, fs.st_dev)
 
58
            long(fs.st_ctime), fs.st_ino, fs.st_dev, fs.st_mode)
51
59
 
52
60
 
53
61
class HashCache(object):
96
104
        self.update_count = 0
97
105
        self._cache = {}
98
106
 
99
 
 
100
107
    def cache_file_name(self):
101
 
        return os.sep.join([self.basedir, '.bzr', 'stat-cache'])
102
 
 
103
 
 
104
 
 
 
108
        # FIXME: duplicate path logic here, this should be 
 
109
        # something like 'branch.controlfile'.
 
110
        return pathjoin(self.basedir, '.bzr', 'stat-cache')
105
111
 
106
112
    def clear(self):
107
113
        """Discard all cached information.
122
128
        prep.sort()
123
129
        
124
130
        for inum, path, cache_entry in prep:
125
 
            abspath = os.sep.join([self.basedir, path])
 
131
            abspath = pathjoin(self.basedir, path)
126
132
            fp = _fingerprint(abspath)
127
133
            self.stat_count += 1
128
134
            
135
141
                del self._cache[path]
136
142
 
137
143
 
138
 
 
139
144
    def get_sha1(self, path):
140
145
        """Return the sha1 of a file.
141
146
        """
142
 
        abspath = os.sep.join([self.basedir, path])
 
147
        abspath = pathjoin(self.basedir, path)
143
148
        self.stat_count += 1
144
149
        file_fp = _fingerprint(abspath)
145
150
        
161
166
            return cache_sha1
162
167
        
163
168
        self.miss_count += 1
164
 
        digest = sha_file(file(abspath, 'rb', buffering=65000))
 
169
 
 
170
 
 
171
        mode = file_fp[FP_MODE_COLUMN]
 
172
        if stat.S_ISREG(mode):
 
173
            digest = sha_file(file(abspath, 'rb', buffering=65000))
 
174
        elif stat.S_ISLNK(mode):
 
175
            digest = sha.new(os.readlink(abspath)).hexdigest()
 
176
        else:
 
177
            raise BzrError("file %r: unknown file stat mode: %o"%(abspath,mode))
165
178
 
166
179
        now = int(time.time())
167
 
        if file_fp[1] >= now or file_fp[2] >= now:
 
180
        if file_fp[FP_MTIME_COLUMN] >= now or file_fp[FP_CTIME_COLUMN] >= now:
168
181
            # changed too recently; can't be cached.  we can
169
182
            # return the result and it could possibly be cached
170
183
            # next time.
 
184
            #
 
185
            # the point is that we only want to cache when we are sure that any
 
186
            # subsequent modifications of the file can be detected.  If a
 
187
            # modification neither changes the inode, the device, the size, nor
 
188
            # the mode, then we can only distinguish it by time; therefore we
 
189
            # need to let sufficient time elapse before we may cache this entry
 
190
            # again.  If we didn't do this, then, for example, a very quick 1
 
191
            # byte replacement in the file might go undetected.
171
192
            self.danger_count += 1 
172
193
            if cache_fp:
173
194
                self.removed_count += 1
177
198
            self.update_count += 1
178
199
            self.needs_write = True
179
200
            self._cache[path] = (digest, file_fp)
180
 
        
181
201
        return digest
182
202
        
183
 
 
184
 
 
185
 
 
186
203
    def write(self):
187
204
        """Write contents of cache to file."""
188
 
        from atomicfile import AtomicFile
189
 
 
190
205
        outf = AtomicFile(self.cache_file_name(), 'wb')
191
206
        try:
192
207
            print >>outf, CACHE_HEADER,
205
220
        finally:
206
221
            if not outf.closed:
207
222
                outf.abort()
208
 
        
209
 
 
210
223
 
211
224
    def read(self):
212
225
        """Reinstate cache from file.
221
234
        try:
222
235
            inf = file(fn, 'rb', buffering=65000)
223
236
        except IOError, e:
224
 
            mutter("failed to open %s: %s" % (fn, e))
 
237
            mutter("failed to open %s: %s", fn, e)
 
238
            # better write it now so it is valid
 
239
            self.needs_write = True
225
240
            return
226
241
 
227
242
 
228
243
        hdr = inf.readline()
229
244
        if hdr != CACHE_HEADER:
230
 
            mutter('cache header marker not found at top of %s; discarding cache'
231
 
                   % fn)
 
245
            mutter('cache header marker not found at top of %s;'
 
246
                   ' discarding cache', fn)
 
247
            self.needs_write = True
232
248
            return
233
249
 
234
250
        for l in inf:
240
256
 
241
257
            pos += 3
242
258
            fields = l[pos:].split(' ')
243
 
            if len(fields) != 6:
 
259
            if len(fields) != 7:
244
260
                warning("bad line in hashcache: %r" % l)
245
261
                continue
246
262