~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/cache.py

  • Committer: John Arbash Meinel
  • Date: 2007-03-15 22:35:35 UTC
  • mto: This revision was merged to the branch mainline in revision 2363.
  • Revision ID: john@arbash-meinel.com-20070315223535-d3d4964oe1hc8zhg
Add an overzealous test, for Unicode support of _iter_changes.
For both knowns and unknowns.
And include a basic, if suboptimal, fix.
I would rather defer the decoding until we've determined that we are going to return the tuple.
There is still something broken with added files, but I'll get to that.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# (C) 2005 Canonical Ltd
2
 
 
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
 
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
 
13
 
# You should have received a copy of the GNU General Public License
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
16
 
 
17
 
import stat, os, sha, time
18
 
from binascii import b2a_qp, a2b_qp
19
 
 
20
 
from trace import mutter
21
 
 
22
 
 
23
 
# file fingerprints are: (path, size, mtime, ctime, ino, dev).
24
 
#
25
 
# if this is the same for this file as in the previous revision, we
26
 
# assume the content is the same and the SHA-1 is the same.
27
 
 
28
 
# This is stored in a fingerprint file that also contains the file-id
29
 
# and the content SHA-1.
30
 
 
31
 
# Thus for any given file we can quickly get the SHA-1, either from
32
 
# the cache or if the cache is out of date.
33
 
 
34
 
# At the moment this is stored in a simple textfile; it might be nice
35
 
# to use a tdb instead.
36
 
 
37
 
 
38
 
# What we need:
39
 
 
40
 
# build a new cache from scratch
41
 
# load cache, incrementally update it
42
 
 
43
 
# TODO: Have a paranoid mode where we always compare the texts and
44
 
# always recalculate the digest, to trap modification without stat
45
 
# change and SHA collisions.
46
 
 
47
 
 
48
 
 
49
 
def fingerprint(path, abspath):
50
 
    try:
51
 
        fs = os.lstat(abspath)
52
 
    except OSError:
53
 
        # might be missing, etc
54
 
        return None
55
 
 
56
 
    if stat.S_ISDIR(fs.st_mode):
57
 
        return None
58
 
 
59
 
    return (fs.st_size, fs.st_mtime,
60
 
            fs.st_ctime, fs.st_ino, fs.st_dev)
61
 
 
62
 
 
63
 
def write_cache(branch, entry_iter):
64
 
    outf = branch.controlfile('work-cache.tmp', 'wt')
65
 
    for entry in entry_iter:
66
 
        outf.write(entry[0] + ' ' + entry[1] + ' ')
67
 
        outf.write(b2a_qp(entry[2], True))
68
 
        outf.write(' %d %d %d %d %d\n' % entry[3:])
69
 
        
70
 
    outf.close()
71
 
    os.rename(branch.controlfilename('work-cache.tmp'),
72
 
              branch.controlfilename('work-cache'))
73
 
 
74
 
        
75
 
        
76
 
def load_cache(branch):
77
 
    cache = {}
78
 
 
79
 
    try:
80
 
        cachefile = branch.controlfile('work-cache', 'rt')
81
 
    except IOError:
82
 
        return cache
83
 
    
84
 
    for l in cachefile:
85
 
        f = l.split(' ')
86
 
        file_id = f[0]
87
 
        if file_id in cache:
88
 
            raise BzrError("duplicated file_id in cache: {%s}" % file_id)
89
 
        cache[file_id] = (f[0], f[1], a2b_qp(f[2])) + tuple([long(x) for x in f[3:]])
90
 
    return cache
91
 
 
92
 
 
93
 
 
94
 
 
95
 
def _files_from_inventory(inv):
96
 
    for path, ie in inv.iter_entries():
97
 
        if ie.kind != 'file':
98
 
            continue
99
 
        yield ie.file_id, path
100
 
    
101
 
 
102
 
def build_cache(branch):
103
 
    inv = branch.read_working_inventory()
104
 
 
105
 
    cache = {}
106
 
    _update_cache_from_list(branch, cache, _files_from_inventory(inv))
107
 
    
108
 
 
109
 
 
110
 
def update_cache(branch, inv):
111
 
    # TODO: It's supposed to be faster to stat the files in order by inum.
112
 
    # We don't directly know the inum of the files of course but we do
113
 
    # know where they were last sighted, so we can sort by that.
114
 
 
115
 
    cache = load_cache(branch)
116
 
    return _update_cache_from_list(branch, cache, _files_from_inventory(inv))
117
 
 
118
 
 
119
 
 
120
 
def _update_cache_from_list(branch, cache, to_update):
121
 
    """Update the cache to have info on the named files.
122
 
 
123
 
    to_update is a sequence of (file_id, path) pairs.
124
 
    """
125
 
    hardcheck = dirty = 0
126
 
    for file_id, path in to_update:
127
 
        fap = branch.abspath(path)
128
 
        fp = fingerprint(fap, path)
129
 
        cacheentry = cache.get(file_id)
130
 
 
131
 
        if fp == None: # not here
132
 
            if cacheentry:
133
 
                del cache[file_id]
134
 
                dirty += 1
135
 
            continue
136
 
 
137
 
        if cacheentry and (cacheentry[3:] == fp):
138
 
            continue                    # all stat fields unchanged
139
 
 
140
 
        hardcheck += 1
141
 
 
142
 
        dig = sha.new(file(fap, 'rb').read()).hexdigest()
143
 
 
144
 
        if cacheentry == None or dig != cacheentry[1]: 
145
 
            # if there was no previous entry for this file, or if the
146
 
            # SHA has changed, then update the cache
147
 
            cacheentry = (file_id, dig, path) + fp
148
 
            cache[file_id] = cacheentry
149
 
            dirty += 1
150
 
 
151
 
    mutter('work cache: read %d files, %d changed' % (hardcheck, dirty))
152
 
        
153
 
    if dirty:
154
 
        write_cache(branch, cache.itervalues())
155
 
 
156
 
    return cache