##// END OF EJS Templates
dirstate: speed up read and write...
Matt Mackall -
r5327:f46ab9ca default
parent child Browse files
Show More
@@ -1,558 +1,558 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
13 import cStringIO
13 import cStringIO
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 class dirstate(object):
18 class dirstate(object):
19
19
20 def __init__(self, opener, ui, root):
20 def __init__(self, opener, ui, root):
21 self._opener = opener
21 self._opener = opener
22 self._root = root
22 self._root = root
23 self._dirty = False
23 self._dirty = False
24 self._dirtypl = False
24 self._dirtypl = False
25 self._ui = ui
25 self._ui = ui
26
26
27 def __getattr__(self, name):
27 def __getattr__(self, name):
28 if name == '_map':
28 if name == '_map':
29 self._read()
29 self._read()
30 return self._map
30 return self._map
31 elif name == '_copymap':
31 elif name == '_copymap':
32 self._read()
32 self._read()
33 return self._copymap
33 return self._copymap
34 elif name == '_branch':
34 elif name == '_branch':
35 try:
35 try:
36 self._branch = (self._opener("branch").read().strip()
36 self._branch = (self._opener("branch").read().strip()
37 or "default")
37 or "default")
38 except IOError:
38 except IOError:
39 self._branch = "default"
39 self._branch = "default"
40 return self._branch
40 return self._branch
41 elif name == '_pl':
41 elif name == '_pl':
42 self._pl = [nullid, nullid]
42 self._pl = [nullid, nullid]
43 try:
43 try:
44 st = self._opener("dirstate").read(40)
44 st = self._opener("dirstate").read(40)
45 if len(st) == 40:
45 if len(st) == 40:
46 self._pl = st[:20], st[20:40]
46 self._pl = st[:20], st[20:40]
47 except IOError, err:
47 except IOError, err:
48 if err.errno != errno.ENOENT: raise
48 if err.errno != errno.ENOENT: raise
49 return self._pl
49 return self._pl
50 elif name == '_dirs':
50 elif name == '_dirs':
51 self._dirs = {}
51 self._dirs = {}
52 for f in self._map:
52 for f in self._map:
53 self._incpath(f)
53 self._incpath(f)
54 return self._dirs
54 return self._dirs
55 elif name == '_ignore':
55 elif name == '_ignore':
56 files = [self._join('.hgignore')]
56 files = [self._join('.hgignore')]
57 for name, path in self._ui.configitems("ui"):
57 for name, path in self._ui.configitems("ui"):
58 if name == 'ignore' or name.startswith('ignore.'):
58 if name == 'ignore' or name.startswith('ignore.'):
59 files.append(os.path.expanduser(path))
59 files.append(os.path.expanduser(path))
60 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
60 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
61 return self._ignore
61 return self._ignore
62 elif name == '_slash':
62 elif name == '_slash':
63 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
63 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
64 return self._slash
64 return self._slash
65 else:
65 else:
66 raise AttributeError, name
66 raise AttributeError, name
67
67
68 def _join(self, f):
68 def _join(self, f):
69 return os.path.join(self._root, f)
69 return os.path.join(self._root, f)
70
70
71 def getcwd(self):
71 def getcwd(self):
72 cwd = os.getcwd()
72 cwd = os.getcwd()
73 if cwd == self._root: return ''
73 if cwd == self._root: return ''
74 # self._root ends with a path separator if self._root is '/' or 'C:\'
74 # self._root ends with a path separator if self._root is '/' or 'C:\'
75 rootsep = self._root
75 rootsep = self._root
76 if not rootsep.endswith(os.sep):
76 if not rootsep.endswith(os.sep):
77 rootsep += os.sep
77 rootsep += os.sep
78 if cwd.startswith(rootsep):
78 if cwd.startswith(rootsep):
79 return cwd[len(rootsep):]
79 return cwd[len(rootsep):]
80 else:
80 else:
81 # we're outside the repo. return an absolute path.
81 # we're outside the repo. return an absolute path.
82 return cwd
82 return cwd
83
83
84 def pathto(self, f, cwd=None):
84 def pathto(self, f, cwd=None):
85 if cwd is None:
85 if cwd is None:
86 cwd = self.getcwd()
86 cwd = self.getcwd()
87 path = util.pathto(self._root, cwd, f)
87 path = util.pathto(self._root, cwd, f)
88 if self._slash:
88 if self._slash:
89 return path.replace(os.sep, '/')
89 return path.replace(os.sep, '/')
90 return path
90 return path
91
91
92 def __getitem__(self, key):
92 def __getitem__(self, key):
93 ''' current states:
93 ''' current states:
94 n normal
94 n normal
95 m needs merging
95 m needs merging
96 r marked for removal
96 r marked for removal
97 a marked for addition
97 a marked for addition
98 ? not tracked'''
98 ? not tracked'''
99 return self._map.get(key, ("?",))[0]
99 return self._map.get(key, ("?",))[0]
100
100
101 def __contains__(self, key):
101 def __contains__(self, key):
102 return key in self._map
102 return key in self._map
103
103
104 def __iter__(self):
104 def __iter__(self):
105 a = self._map.keys()
105 a = self._map.keys()
106 a.sort()
106 a.sort()
107 for x in a:
107 for x in a:
108 yield x
108 yield x
109
109
110 def parents(self):
110 def parents(self):
111 return self._pl
111 return self._pl
112
112
113 def branch(self):
113 def branch(self):
114 return self._branch
114 return self._branch
115
115
116 def setparents(self, p1, p2=nullid):
116 def setparents(self, p1, p2=nullid):
117 self._dirty = self._dirtypl = True
117 self._dirty = self._dirtypl = True
118 self._pl = p1, p2
118 self._pl = p1, p2
119
119
120 def setbranch(self, branch):
120 def setbranch(self, branch):
121 self._branch = branch
121 self._branch = branch
122 self._opener("branch", "w").write(branch + '\n')
122 self._opener("branch", "w").write(branch + '\n')
123
123
124 def _read(self):
124 def _read(self):
125 self._map = {}
125 self._map = {}
126 self._copymap = {}
126 self._copymap = {}
127 if not self._dirtypl:
127 if not self._dirtypl:
128 self._pl = [nullid, nullid]
128 self._pl = [nullid, nullid]
129 try:
129 try:
130 st = self._opener("dirstate").read()
130 st = self._opener("dirstate").read()
131 except IOError, err:
131 except IOError, err:
132 if err.errno != errno.ENOENT: raise
132 if err.errno != errno.ENOENT: raise
133 return
133 return
134 if not st:
134 if not st:
135 return
135 return
136
136
137 if not self._dirtypl:
137 if not self._dirtypl:
138 self._pl = [st[:20], st[20: 40]]
138 self._pl = [st[:20], st[20: 40]]
139
139
140 # deref fields so they will be local in loop
140 # deref fields so they will be local in loop
141 dmap = self._map
141 dmap = self._map
142 copymap = self._copymap
142 copymap = self._copymap
143 unpack = struct.unpack
143 unpack = struct.unpack
144
145 pos = 40
146 e_size = struct.calcsize(_format)
144 e_size = struct.calcsize(_format)
145 pos1 = 40
146 l = len(st)
147
147
148 while pos < len(st):
148 # the inner loop
149 newpos = pos + e_size
149 while pos1 < l:
150 e = unpack(_format, st[pos:newpos])
150 pos2 = pos1 + e_size
151 l = e[4]
151 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
152 pos = newpos
152 pos1 = pos2 + e[4]
153 newpos = pos + l
153 f = st[pos2:pos1]
154 f = st[pos:newpos]
155 if '\0' in f:
154 if '\0' in f:
156 f, c = f.split('\0')
155 f, c = f.split('\0')
157 copymap[f] = c
156 copymap[f] = c
158 dmap[f] = e[:4]
157 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
159 pos = newpos
160
158
161 def invalidate(self):
159 def invalidate(self):
162 for a in "_map _copymap _branch _pl _dirs _ignore".split():
160 for a in "_map _copymap _branch _pl _dirs _ignore".split():
163 if a in self.__dict__:
161 if a in self.__dict__:
164 delattr(self, a)
162 delattr(self, a)
165 self._dirty = False
163 self._dirty = False
166
164
167 def copy(self, source, dest):
165 def copy(self, source, dest):
168 self._dirty = True
166 self._dirty = True
169 self._copymap[dest] = source
167 self._copymap[dest] = source
170
168
171 def copied(self, file):
169 def copied(self, file):
172 return self._copymap.get(file, None)
170 return self._copymap.get(file, None)
173
171
174 def copies(self):
172 def copies(self):
175 return self._copymap
173 return self._copymap
176
174
177 def _incpath(self, path):
175 def _incpath(self, path):
178 c = path.rfind('/')
176 c = path.rfind('/')
179 if c >= 0:
177 if c >= 0:
180 dirs = self._dirs
178 dirs = self._dirs
181 base = path[:c]
179 base = path[:c]
182 if base not in dirs:
180 if base not in dirs:
183 self._incpath(base)
181 self._incpath(base)
184 dirs[base] = 1
182 dirs[base] = 1
185 else:
183 else:
186 dirs[base] += 1
184 dirs[base] += 1
187
185
188 def _decpath(self, path):
186 def _decpath(self, path):
189 if "_dirs" in self.__dict__:
187 if "_dirs" in self.__dict__:
190 c = path.rfind('/')
188 c = path.rfind('/')
191 if c >= 0:
189 if c >= 0:
192 base = path[:c]
190 base = path[:c]
193 dirs = self._dirs
191 dirs = self._dirs
194 if dirs[base] == 1:
192 if dirs[base] == 1:
195 del dirs[base]
193 del dirs[base]
196 self._decpath(base)
194 self._decpath(base)
197 else:
195 else:
198 dirs[base] -= 1
196 dirs[base] -= 1
199
197
200 def _incpathcheck(self, f):
198 def _incpathcheck(self, f):
201 if '\r' in f or '\n' in f:
199 if '\r' in f or '\n' in f:
202 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
200 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
203 # shadows
201 # shadows
204 if f in self._dirs:
202 if f in self._dirs:
205 raise util.Abort(_('directory %r already in dirstate') % f)
203 raise util.Abort(_('directory %r already in dirstate') % f)
206 for c in strutil.rfindall(f, '/'):
204 for c in strutil.rfindall(f, '/'):
207 d = f[:c]
205 d = f[:c]
208 if d in self._dirs:
206 if d in self._dirs:
209 break
207 break
210 if d in self._map:
208 if d in self._map:
211 raise util.Abort(_('file %r in dirstate clashes with %r') %
209 raise util.Abort(_('file %r in dirstate clashes with %r') %
212 (d, f))
210 (d, f))
213 self._incpath(f)
211 self._incpath(f)
214
212
215 def normal(self, f):
213 def normal(self, f):
216 'mark a file normal and clean'
214 'mark a file normal and clean'
217 self._dirty = True
215 self._dirty = True
218 s = os.lstat(self._join(f))
216 s = os.lstat(self._join(f))
219 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime)
217 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
220 if self._copymap.has_key(f):
218 if self._copymap.has_key(f):
221 del self._copymap[f]
219 del self._copymap[f]
222
220
223 def normallookup(self, f):
221 def normallookup(self, f):
224 'mark a file normal, but possibly dirty'
222 'mark a file normal, but possibly dirty'
225 self._dirty = True
223 self._dirty = True
226 self._map[f] = ('n', 0, -1, -1)
224 self._map[f] = ('n', 0, -1, -1, 0)
227 if f in self._copymap:
225 if f in self._copymap:
228 del self._copymap[f]
226 del self._copymap[f]
229
227
230 def normaldirty(self, f):
228 def normaldirty(self, f):
231 'mark a file normal, but dirty'
229 'mark a file normal, but dirty'
232 self._dirty = True
230 self._dirty = True
233 self._map[f] = ('n', 0, -2, -1)
231 self._map[f] = ('n', 0, -2, -1, 0)
234 if f in self._copymap:
232 if f in self._copymap:
235 del self._copymap[f]
233 del self._copymap[f]
236
234
237 def add(self, f):
235 def add(self, f):
238 'mark a file added'
236 'mark a file added'
239 self._dirty = True
237 self._dirty = True
240 self._incpathcheck(f)
238 self._incpathcheck(f)
241 self._map[f] = ('a', 0, -1, -1)
239 self._map[f] = ('a', 0, -1, -1, 0)
242 if f in self._copymap:
240 if f in self._copymap:
243 del self._copymap[f]
241 del self._copymap[f]
244
242
245 def remove(self, f):
243 def remove(self, f):
246 'mark a file removed'
244 'mark a file removed'
247 self._dirty = True
245 self._dirty = True
248 self._map[f] = ('r', 0, 0, 0)
246 self._map[f] = ('r', 0, 0, 0, 0)
249 self._decpath(f)
247 self._decpath(f)
250 if f in self._copymap:
248 if f in self._copymap:
251 del self._copymap[f]
249 del self._copymap[f]
252
250
253 def merge(self, f):
251 def merge(self, f):
254 'mark a file merged'
252 'mark a file merged'
255 self._dirty = True
253 self._dirty = True
256 s = os.lstat(self._join(f))
254 s = os.lstat(self._join(f))
257 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime)
255 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
258 if f in self._copymap:
256 if f in self._copymap:
259 del self._copymap[f]
257 del self._copymap[f]
260
258
261 def forget(self, f):
259 def forget(self, f):
262 'forget a file'
260 'forget a file'
263 self._dirty = True
261 self._dirty = True
264 try:
262 try:
265 del self._map[f]
263 del self._map[f]
266 self._decpath(f)
264 self._decpath(f)
267 except KeyError:
265 except KeyError:
268 self._ui.warn(_("not in dirstate: %s!\n") % f)
266 self._ui.warn(_("not in dirstate: %s!\n") % f)
269
267
270 def clear(self):
268 def clear(self):
271 self._map = {}
269 self._map = {}
272 self._copymap = {}
270 self._copymap = {}
273 self._pl = [nullid, nullid]
271 self._pl = [nullid, nullid]
274 self._dirty = True
272 self._dirty = True
275
273
276 def rebuild(self, parent, files):
274 def rebuild(self, parent, files):
277 self.clear()
275 self.clear()
278 for f in files:
276 for f in files:
279 if files.execf(f):
277 if files.execf(f):
280 self._map[f] = ('n', 0777, -1, 0)
278 self._map[f] = ('n', 0777, -1, 0, 0)
281 else:
279 else:
282 self._map[f] = ('n', 0666, -1, 0)
280 self._map[f] = ('n', 0666, -1, 0, 0)
283 self._pl = (parent, nullid)
281 self._pl = (parent, nullid)
284 self._dirty = True
282 self._dirty = True
285
283
286 def write(self):
284 def write(self):
287 if not self._dirty:
285 if not self._dirty:
288 return
286 return
289 cs = cStringIO.StringIO()
287 cs = cStringIO.StringIO()
290 cs.write("".join(self._pl))
288 copymap = self._copymap
289 pack = struct.pack
290 write = cs.write
291 write("".join(self._pl))
291 for f, e in self._map.iteritems():
292 for f, e in self._map.iteritems():
292 c = self.copied(f)
293 if f in copymap:
293 if c:
294 f = "%s\0%s" % (f, copymap[f])
294 f = f + "\0" + c
295 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
295 e = struct.pack(_format, e[0], e[1], e[2], e[3], len(f))
296 write(e)
296 cs.write(e)
297 write(f)
297 cs.write(f)
298 st = self._opener("dirstate", "w", atomictemp=True)
298 st = self._opener("dirstate", "w", atomictemp=True)
299 st.write(cs.getvalue())
299 st.write(cs.getvalue())
300 st.rename()
300 st.rename()
301 self._dirty = self._dirtypl = False
301 self._dirty = self._dirtypl = False
302
302
303 def _filter(self, files):
303 def _filter(self, files):
304 ret = {}
304 ret = {}
305 unknown = []
305 unknown = []
306
306
307 for x in files:
307 for x in files:
308 if x == '.':
308 if x == '.':
309 return self._map.copy()
309 return self._map.copy()
310 if x not in self._map:
310 if x not in self._map:
311 unknown.append(x)
311 unknown.append(x)
312 else:
312 else:
313 ret[x] = self._map[x]
313 ret[x] = self._map[x]
314
314
315 if not unknown:
315 if not unknown:
316 return ret
316 return ret
317
317
318 b = self._map.keys()
318 b = self._map.keys()
319 b.sort()
319 b.sort()
320 blen = len(b)
320 blen = len(b)
321
321
322 for x in unknown:
322 for x in unknown:
323 bs = bisect.bisect(b, "%s%s" % (x, '/'))
323 bs = bisect.bisect(b, "%s%s" % (x, '/'))
324 while bs < blen:
324 while bs < blen:
325 s = b[bs]
325 s = b[bs]
326 if len(s) > len(x) and s.startswith(x):
326 if len(s) > len(x) and s.startswith(x):
327 ret[s] = self._map[s]
327 ret[s] = self._map[s]
328 else:
328 else:
329 break
329 break
330 bs += 1
330 bs += 1
331 return ret
331 return ret
332
332
333 def _supported(self, f, mode, verbose=False):
333 def _supported(self, f, mode, verbose=False):
334 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
334 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
335 return True
335 return True
336 if verbose:
336 if verbose:
337 kind = 'unknown'
337 kind = 'unknown'
338 if stat.S_ISCHR(mode): kind = _('character device')
338 if stat.S_ISCHR(mode): kind = _('character device')
339 elif stat.S_ISBLK(mode): kind = _('block device')
339 elif stat.S_ISBLK(mode): kind = _('block device')
340 elif stat.S_ISFIFO(mode): kind = _('fifo')
340 elif stat.S_ISFIFO(mode): kind = _('fifo')
341 elif stat.S_ISSOCK(mode): kind = _('socket')
341 elif stat.S_ISSOCK(mode): kind = _('socket')
342 elif stat.S_ISDIR(mode): kind = _('directory')
342 elif stat.S_ISDIR(mode): kind = _('directory')
343 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
343 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
344 % (self.pathto(f), kind))
344 % (self.pathto(f), kind))
345 return False
345 return False
346
346
347 def walk(self, files=None, match=util.always, badmatch=None):
347 def walk(self, files=None, match=util.always, badmatch=None):
348 # filter out the stat
348 # filter out the stat
349 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
349 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
350 yield src, f
350 yield src, f
351
351
352 def statwalk(self, files=None, match=util.always, ignored=False,
352 def statwalk(self, files=None, match=util.always, ignored=False,
353 badmatch=None, directories=False):
353 badmatch=None, directories=False):
354 '''
354 '''
355 walk recursively through the directory tree, finding all files
355 walk recursively through the directory tree, finding all files
356 matched by the match function
356 matched by the match function
357
357
358 results are yielded in a tuple (src, filename, st), where src
358 results are yielded in a tuple (src, filename, st), where src
359 is one of:
359 is one of:
360 'f' the file was found in the directory tree
360 'f' the file was found in the directory tree
361 'd' the file is a directory of the tree
361 'd' the file is a directory of the tree
362 'm' the file was only in the dirstate and not in the tree
362 'm' the file was only in the dirstate and not in the tree
363 'b' file was not found and matched badmatch
363 'b' file was not found and matched badmatch
364
364
365 and st is the stat result if the file was found in the directory.
365 and st is the stat result if the file was found in the directory.
366 '''
366 '''
367
367
368 # walk all files by default
368 # walk all files by default
369 if not files:
369 if not files:
370 files = ['.']
370 files = ['.']
371 dc = self._map.copy()
371 dc = self._map.copy()
372 else:
372 else:
373 files = util.unique(files)
373 files = util.unique(files)
374 dc = self._filter(files)
374 dc = self._filter(files)
375
375
376 def imatch(file_):
376 def imatch(file_):
377 if file_ not in dc and self._ignore(file_):
377 if file_ not in dc and self._ignore(file_):
378 return False
378 return False
379 return match(file_)
379 return match(file_)
380
380
381 ignore = self._ignore
381 ignore = self._ignore
382 if ignored:
382 if ignored:
383 imatch = match
383 imatch = match
384 ignore = util.never
384 ignore = util.never
385
385
386 # self._root may end with a path separator when self._root == '/'
386 # self._root may end with a path separator when self._root == '/'
387 common_prefix_len = len(self._root)
387 common_prefix_len = len(self._root)
388 if not self._root.endswith(os.sep):
388 if not self._root.endswith(os.sep):
389 common_prefix_len += 1
389 common_prefix_len += 1
390
390
391 normpath = util.normpath
391 normpath = util.normpath
392 listdir = os.listdir
392 listdir = os.listdir
393 lstat = os.lstat
393 lstat = os.lstat
394 bisect_left = bisect.bisect_left
394 bisect_left = bisect.bisect_left
395 isdir = os.path.isdir
395 isdir = os.path.isdir
396 pconvert = util.pconvert
396 pconvert = util.pconvert
397 join = os.path.join
397 join = os.path.join
398 s_isdir = stat.S_ISDIR
398 s_isdir = stat.S_ISDIR
399 supported = self._supported
399 supported = self._supported
400 _join = self._join
400 _join = self._join
401 known = {'.hg': 1}
401 known = {'.hg': 1}
402
402
403 # recursion free walker, faster than os.walk.
403 # recursion free walker, faster than os.walk.
404 def findfiles(s):
404 def findfiles(s):
405 work = [s]
405 work = [s]
406 wadd = work.append
406 wadd = work.append
407 found = []
407 found = []
408 add = found.append
408 add = found.append
409 if directories:
409 if directories:
410 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
410 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
411 while work:
411 while work:
412 top = work.pop()
412 top = work.pop()
413 names = listdir(top)
413 names = listdir(top)
414 names.sort()
414 names.sort()
415 # nd is the top of the repository dir tree
415 # nd is the top of the repository dir tree
416 nd = normpath(top[common_prefix_len:])
416 nd = normpath(top[common_prefix_len:])
417 if nd == '.':
417 if nd == '.':
418 nd = ''
418 nd = ''
419 else:
419 else:
420 # do not recurse into a repo contained in this
420 # do not recurse into a repo contained in this
421 # one. use bisect to find .hg directory so speed
421 # one. use bisect to find .hg directory so speed
422 # is good on big directory.
422 # is good on big directory.
423 hg = bisect_left(names, '.hg')
423 hg = bisect_left(names, '.hg')
424 if hg < len(names) and names[hg] == '.hg':
424 if hg < len(names) and names[hg] == '.hg':
425 if isdir(join(top, '.hg')):
425 if isdir(join(top, '.hg')):
426 continue
426 continue
427 for f in names:
427 for f in names:
428 np = pconvert(join(nd, f))
428 np = pconvert(join(nd, f))
429 if np in known:
429 if np in known:
430 continue
430 continue
431 known[np] = 1
431 known[np] = 1
432 p = join(top, f)
432 p = join(top, f)
433 # don't trip over symlinks
433 # don't trip over symlinks
434 st = lstat(p)
434 st = lstat(p)
435 if s_isdir(st.st_mode):
435 if s_isdir(st.st_mode):
436 if not ignore(np):
436 if not ignore(np):
437 wadd(p)
437 wadd(p)
438 if directories:
438 if directories:
439 add((np, 'd', st))
439 add((np, 'd', st))
440 if np in dc and match(np):
440 if np in dc and match(np):
441 add((np, 'm', st))
441 add((np, 'm', st))
442 elif imatch(np):
442 elif imatch(np):
443 if supported(np, st.st_mode):
443 if supported(np, st.st_mode):
444 add((np, 'f', st))
444 add((np, 'f', st))
445 elif np in dc:
445 elif np in dc:
446 add((np, 'm', st))
446 add((np, 'm', st))
447 found.sort()
447 found.sort()
448 return found
448 return found
449
449
450 # step one, find all files that match our criteria
450 # step one, find all files that match our criteria
451 files.sort()
451 files.sort()
452 for ff in files:
452 for ff in files:
453 nf = normpath(ff)
453 nf = normpath(ff)
454 f = _join(ff)
454 f = _join(ff)
455 try:
455 try:
456 st = lstat(f)
456 st = lstat(f)
457 except OSError, inst:
457 except OSError, inst:
458 found = False
458 found = False
459 for fn in dc:
459 for fn in dc:
460 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
460 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
461 found = True
461 found = True
462 break
462 break
463 if not found:
463 if not found:
464 if inst.errno != errno.ENOENT or not badmatch:
464 if inst.errno != errno.ENOENT or not badmatch:
465 self._ui.warn('%s: %s\n' %
465 self._ui.warn('%s: %s\n' %
466 (self.pathto(ff), inst.strerror))
466 (self.pathto(ff), inst.strerror))
467 elif badmatch and badmatch(ff) and imatch(nf):
467 elif badmatch and badmatch(ff) and imatch(nf):
468 yield 'b', ff, None
468 yield 'b', ff, None
469 continue
469 continue
470 if s_isdir(st.st_mode):
470 if s_isdir(st.st_mode):
471 for f, src, st in findfiles(f):
471 for f, src, st in findfiles(f):
472 yield src, f, st
472 yield src, f, st
473 else:
473 else:
474 if nf in known:
474 if nf in known:
475 continue
475 continue
476 known[nf] = 1
476 known[nf] = 1
477 if match(nf):
477 if match(nf):
478 if supported(ff, st.st_mode, verbose=True):
478 if supported(ff, st.st_mode, verbose=True):
479 yield 'f', nf, st
479 yield 'f', nf, st
480 elif ff in dc:
480 elif ff in dc:
481 yield 'm', nf, st
481 yield 'm', nf, st
482
482
483 # step two run through anything left in the dc hash and yield
483 # step two run through anything left in the dc hash and yield
484 # if we haven't already seen it
484 # if we haven't already seen it
485 ks = dc.keys()
485 ks = dc.keys()
486 ks.sort()
486 ks.sort()
487 for k in ks:
487 for k in ks:
488 if k in known:
488 if k in known:
489 continue
489 continue
490 known[k] = 1
490 known[k] = 1
491 if imatch(k):
491 if imatch(k):
492 yield 'm', k, None
492 yield 'm', k, None
493
493
494 def status(self, files, match, list_ignored, list_clean):
494 def status(self, files, match, list_ignored, list_clean):
495 lookup, modified, added, unknown, ignored = [], [], [], [], []
495 lookup, modified, added, unknown, ignored = [], [], [], [], []
496 removed, deleted, clean = [], [], []
496 removed, deleted, clean = [], [], []
497
497
498 _join = self._join
498 _join = self._join
499 lstat = os.lstat
499 lstat = os.lstat
500 cmap = self._copymap
500 cmap = self._copymap
501 dmap = self._map
501 dmap = self._map
502 ladd = lookup.append
502 ladd = lookup.append
503 madd = modified.append
503 madd = modified.append
504 aadd = added.append
504 aadd = added.append
505 uadd = unknown.append
505 uadd = unknown.append
506 iadd = ignored.append
506 iadd = ignored.append
507 radd = removed.append
507 radd = removed.append
508 dadd = deleted.append
508 dadd = deleted.append
509 cadd = clean.append
509 cadd = clean.append
510
510
511 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
511 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
512 if fn in dmap:
512 if fn in dmap:
513 type_, mode, size, time = dmap[fn]
513 type_, mode, size, time, foo = dmap[fn]
514 else:
514 else:
515 if list_ignored and self._ignore(fn):
515 if list_ignored and self._ignore(fn):
516 iadd(fn)
516 iadd(fn)
517 else:
517 else:
518 uadd(fn)
518 uadd(fn)
519 continue
519 continue
520 if src == 'm':
520 if src == 'm':
521 nonexistent = True
521 nonexistent = True
522 if not st:
522 if not st:
523 try:
523 try:
524 st = lstat(_join(fn))
524 st = lstat(_join(fn))
525 except OSError, inst:
525 except OSError, inst:
526 if inst.errno != errno.ENOENT:
526 if inst.errno != errno.ENOENT:
527 raise
527 raise
528 st = None
528 st = None
529 # We need to re-check that it is a valid file
529 # We need to re-check that it is a valid file
530 if st and self._supported(fn, st.st_mode):
530 if st and self._supported(fn, st.st_mode):
531 nonexistent = False
531 nonexistent = False
532 # XXX: what to do with file no longer present in the fs
532 # XXX: what to do with file no longer present in the fs
533 # who are not removed in the dirstate ?
533 # who are not removed in the dirstate ?
534 if nonexistent and type_ in "nm":
534 if nonexistent and type_ in "nm":
535 dadd(fn)
535 dadd(fn)
536 continue
536 continue
537 # check the common case first
537 # check the common case first
538 if type_ == 'n':
538 if type_ == 'n':
539 if not st:
539 if not st:
540 st = lstat(_join(fn))
540 st = lstat(_join(fn))
541 if (size >= 0 and (size != st.st_size
541 if (size >= 0 and (size != st.st_size
542 or (mode ^ st.st_mode) & 0100)
542 or (mode ^ st.st_mode) & 0100)
543 or size == -2
543 or size == -2
544 or fn in self._copymap):
544 or fn in self._copymap):
545 madd(fn)
545 madd(fn)
546 elif time != int(st.st_mtime):
546 elif time != int(st.st_mtime):
547 ladd(fn)
547 ladd(fn)
548 elif list_clean:
548 elif list_clean:
549 cadd(fn)
549 cadd(fn)
550 elif type_ == 'm':
550 elif type_ == 'm':
551 madd(fn)
551 madd(fn)
552 elif type_ == 'a':
552 elif type_ == 'a':
553 aadd(fn)
553 aadd(fn)
554 elif type_ == 'r':
554 elif type_ == 'r':
555 radd(fn)
555 radd(fn)
556
556
557 return (lookup, modified, added, removed, deleted, unknown, ignored,
557 return (lookup, modified, added, removed, deleted, unknown, ignored,
558 clean)
558 clean)
General Comments 0
You need to be logged in to leave comments. Login now