~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/hashcache.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-27 05:35:00 UTC
  • mfrom: (4570 +trunk)
  • mto: (4634.6.29 2.0)
  • mto: This revision was merged to the branch mainline in revision 4680.
  • Revision ID: andrew.bennetts@canonical.com-20090727053500-q76zsn2dx33jhmj5
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
# TODO: Up-front, stat all files in order and remove those which are deleted or
18
18
# out-of-date.  Don't actually re-read them until they're needed.  That ought
29
29
 
30
30
CACHE_HEADER = "### bzr hashcache v5\n"
31
31
 
32
 
import os, stat, time
 
32
import os
 
33
import stat
 
34
import time
33
35
 
34
 
from bzrlib.osutils import sha_file, sha_string, pathjoin, safe_unicode
35
 
from bzrlib.trace import mutter, warning
36
 
from bzrlib.atomicfile import AtomicFile
37
 
from bzrlib.errors import BzrError
 
36
from bzrlib import (
 
37
    atomicfile,
 
38
    errors,
 
39
    filters as _mod_filters,
 
40
    osutils,
 
41
    trace,
 
42
    )
38
43
 
39
44
 
40
45
FP_MTIME_COLUMN = 1
79
84
    """
80
85
    needs_write = False
81
86
 
82
 
    def __init__(self, root, cache_file_name, mode=None):
83
 
        """Create a hash cache in base dir, and set the file mode to mode."""
84
 
        self.root = safe_unicode(root)
 
87
    def __init__(self, root, cache_file_name, mode=None,
 
88
            content_filter_stack_provider=None):
 
89
        """Create a hash cache in base dir, and set the file mode to mode.
 
90
 
 
91
        :param content_filter_stack_provider: a function that takes a
 
92
            path (relative to the top of the tree) and a file-id as
 
93
            parameters and returns a stack of ContentFilters.
 
94
            If None, no content filtering is performed.
 
95
        """
 
96
        self.root = osutils.safe_unicode(root)
85
97
        self.root_utf8 = self.root.encode('utf8') # where is the filesystem encoding ?
86
98
        self.hit_count = 0
87
99
        self.miss_count = 0
91
103
        self.update_count = 0
92
104
        self._cache = {}
93
105
        self._mode = mode
94
 
        self._cache_file_name = safe_unicode(cache_file_name)
 
106
        self._cache_file_name = osutils.safe_unicode(cache_file_name)
 
107
        self._filter_provider = content_filter_stack_provider
95
108
 
96
109
    def cache_file_name(self):
97
110
        return self._cache_file_name
116
129
        prep.sort()
117
130
 
118
131
        for inum, path, cache_entry in prep:
119
 
            abspath = pathjoin(self.root, path)
 
132
            abspath = osutils.pathjoin(self.root, path)
120
133
            fp = self._fingerprint(abspath)
121
134
            self.stat_count += 1
122
135
 
132
145
        """Return the sha1 of a file.
133
146
        """
134
147
        if path.__class__ is str:
135
 
            abspath = pathjoin(self.root_utf8, path)
 
148
            abspath = osutils.pathjoin(self.root_utf8, path)
136
149
        else:
137
 
            abspath = pathjoin(self.root, path)
 
150
            abspath = osutils.pathjoin(self.root, path)
138
151
        self.stat_count += 1
139
152
        file_fp = self._fingerprint(abspath, stat_value)
140
153
 
161
174
 
162
175
        mode = file_fp[FP_MODE_COLUMN]
163
176
        if stat.S_ISREG(mode):
164
 
            digest = self._really_sha1_file(abspath)
 
177
            if self._filter_provider is None:
 
178
                filters = []
 
179
            else:
 
180
                filters = self._filter_provider(path=path, file_id=None)
 
181
            digest = self._really_sha1_file(abspath, filters)
165
182
        elif stat.S_ISLNK(mode):
166
 
            digest = sha_string(os.readlink(abspath))
 
183
            target = osutils.readlink(osutils.safe_unicode(abspath))
 
184
            digest = osutils.sha_string(target.encode('UTF-8'))
167
185
        else:
168
 
            raise BzrError("file %r: unknown file stat mode: %o"%(abspath,mode))
 
186
            raise errors.BzrError("file %r: unknown file stat mode: %o"
 
187
                                  % (abspath, mode))
169
188
 
170
189
        # window of 3 seconds to allow for 2s resolution on windows,
171
190
        # unsynchronized file servers, etc.
198
217
            self._cache[path] = (digest, file_fp)
199
218
        return digest
200
219
 
201
 
    def _really_sha1_file(self, abspath):
 
220
    def _really_sha1_file(self, abspath, filters):
202
221
        """Calculate the SHA1 of a file by reading the full text"""
203
 
        return sha_file(file(abspath, 'rb', buffering=65000))
 
222
        return _mod_filters.internal_size_sha_file_byname(abspath, filters)[1]
204
223
 
205
224
    def write(self):
206
225
        """Write contents of cache to file."""
207
 
        outf = AtomicFile(self.cache_file_name(), 'wb', new_mode=self._mode)
 
226
        outf = atomicfile.AtomicFile(self.cache_file_name(), 'wb',
 
227
                                     new_mode=self._mode)
208
228
        try:
209
229
            outf.write(CACHE_HEADER)
210
230
 
235
255
        try:
236
256
            inf = file(fn, 'rb', buffering=65000)
237
257
        except IOError, e:
238
 
            mutter("failed to open %s: %s", fn, e)
 
258
            trace.mutter("failed to open %s: %s", fn, e)
239
259
            # better write it now so it is valid
240
260
            self.needs_write = True
241
261
            return
242
262
 
243
263
        hdr = inf.readline()
244
264
        if hdr != CACHE_HEADER:
245
 
            mutter('cache header marker not found at top of %s;'
246
 
                   ' discarding cache', fn)
 
265
            trace.mutter('cache header marker not found at top of %s;'
 
266
                         ' discarding cache', fn)
247
267
            self.needs_write = True
248
268
            return
249
269
 
251
271
            pos = l.index('// ')
252
272
            path = l[:pos].decode('utf-8')
253
273
            if path in self._cache:
254
 
                warning('duplicated path %r in cache' % path)
 
274
                trace.warning('duplicated path %r in cache' % path)
255
275
                continue
256
276
 
257
277
            pos += 3
258
278
            fields = l[pos:].split(' ')
259
279
            if len(fields) != 7:
260
 
                warning("bad line in hashcache: %r" % l)
 
280
                trace.warning("bad line in hashcache: %r" % l)
261
281
                continue
262
282
 
263
283
            sha1 = fields[0]
264
284
            if len(sha1) != 40:
265
 
                warning("bad sha1 in hashcache: %r" % sha1)
 
285
                trace.warning("bad sha1 in hashcache: %r" % sha1)
266
286
                continue
267
287
 
268
288
            fp = tuple(map(long, fields[1:]))