##// END OF EJS Templates
dirstate: speed up sorting in findfiles
Matt Mackall -
r5002:4d079df2 default
parent child Browse files
Show More
@@ -1,518 +1,520 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
144
145 pos = 40
145 pos = 40
146 e_size = struct.calcsize(_format)
146 e_size = struct.calcsize(_format)
147
147
148 while pos < len(st):
148 while pos < len(st):
149 newpos = pos + e_size
149 newpos = pos + e_size
150 e = unpack(_format, st[pos:newpos])
150 e = unpack(_format, st[pos:newpos])
151 l = e[4]
151 l = e[4]
152 pos = newpos
152 pos = newpos
153 newpos = pos + l
153 newpos = pos + l
154 f = st[pos:newpos]
154 f = st[pos:newpos]
155 if '\0' in f:
155 if '\0' in f:
156 f, c = f.split('\0')
156 f, c = f.split('\0')
157 copymap[f] = c
157 copymap[f] = c
158 dmap[f] = e[:4]
158 dmap[f] = e[:4]
159 pos = newpos
159 pos = newpos
160
160
161 def invalidate(self):
161 def invalidate(self):
162 for a in "_map _copymap _branch _pl _dirs _ignore".split():
162 for a in "_map _copymap _branch _pl _dirs _ignore".split():
163 if a in self.__dict__:
163 if a in self.__dict__:
164 delattr(self, a)
164 delattr(self, a)
165 self._dirty = False
165 self._dirty = False
166
166
167 def copy(self, source, dest):
167 def copy(self, source, dest):
168 self._dirty = True
168 self._dirty = True
169 self._copymap[dest] = source
169 self._copymap[dest] = source
170
170
171 def copied(self, file):
171 def copied(self, file):
172 return self._copymap.get(file, None)
172 return self._copymap.get(file, None)
173
173
174 def copies(self):
174 def copies(self):
175 return self._copymap
175 return self._copymap
176
176
177 def _incpath(self, path):
177 def _incpath(self, path):
178 for c in strutil.findall(path, '/'):
178 for c in strutil.findall(path, '/'):
179 pc = path[:c]
179 pc = path[:c]
180 self._dirs.setdefault(pc, 0)
180 self._dirs.setdefault(pc, 0)
181 self._dirs[pc] += 1
181 self._dirs[pc] += 1
182
182
183 def _decpath(self, path):
183 def _decpath(self, path):
184 for c in strutil.findall(path, '/'):
184 for c in strutil.findall(path, '/'):
185 pc = path[:c]
185 pc = path[:c]
186 self._dirs.setdefault(pc, 0)
186 self._dirs.setdefault(pc, 0)
187 self._dirs[pc] -= 1
187 self._dirs[pc] -= 1
188
188
189 def _incpathcheck(self, f):
189 def _incpathcheck(self, f):
190 if '\r' in f or '\n' in f:
190 if '\r' in f or '\n' in f:
191 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
191 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
192 # shadows
192 # shadows
193 if f in self._dirs:
193 if f in self._dirs:
194 raise util.Abort(_('directory named %r already in dirstate') % f)
194 raise util.Abort(_('directory named %r already in dirstate') % f)
195 for c in strutil.rfindall(f, '/'):
195 for c in strutil.rfindall(f, '/'):
196 d = f[:c]
196 d = f[:c]
197 if d in self._dirs:
197 if d in self._dirs:
198 break
198 break
199 if d in self._map:
199 if d in self._map:
200 raise util.Abort(_('file named %r already in dirstate') % d)
200 raise util.Abort(_('file named %r already in dirstate') % d)
201 self._incpath(f)
201 self._incpath(f)
202
202
203 def normal(self, f):
203 def normal(self, f):
204 'mark a file normal'
204 'mark a file normal'
205 self._dirty = True
205 self._dirty = True
206 s = os.lstat(self._join(f))
206 s = os.lstat(self._join(f))
207 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime)
207 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime)
208 if self._copymap.has_key(f):
208 if self._copymap.has_key(f):
209 del self._copymap[f]
209 del self._copymap[f]
210
210
211 def normaldirty(self, f):
211 def normaldirty(self, f):
212 'mark a file normal, but possibly dirty'
212 'mark a file normal, but possibly dirty'
213 self._dirty = True
213 self._dirty = True
214 s = os.lstat(self._join(f))
214 s = os.lstat(self._join(f))
215 self._map[f] = ('n', s.st_mode, -1, -1)
215 self._map[f] = ('n', s.st_mode, -1, -1)
216 if f in self._copymap:
216 if f in self._copymap:
217 del self._copymap[f]
217 del self._copymap[f]
218
218
219 def add(self, f):
219 def add(self, f):
220 'mark a file added'
220 'mark a file added'
221 self._dirty = True
221 self._dirty = True
222 self._incpathcheck(f)
222 self._incpathcheck(f)
223 self._map[f] = ('a', 0, -1, -1)
223 self._map[f] = ('a', 0, -1, -1)
224 if f in self._copymap:
224 if f in self._copymap:
225 del self._copymap[f]
225 del self._copymap[f]
226
226
227 def remove(self, f):
227 def remove(self, f):
228 'mark a file removed'
228 'mark a file removed'
229 self._dirty = True
229 self._dirty = True
230 self._map[f] = ('r', 0, 0, 0)
230 self._map[f] = ('r', 0, 0, 0)
231 self._decpath(f)
231 self._decpath(f)
232 if f in self._copymap:
232 if f in self._copymap:
233 del self._copymap[f]
233 del self._copymap[f]
234
234
235 def merge(self, f):
235 def merge(self, f):
236 'mark a file merged'
236 'mark a file merged'
237 self._dirty = True
237 self._dirty = True
238 s = os.lstat(self._join(f))
238 s = os.lstat(self._join(f))
239 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime)
239 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime)
240 if f in self._copymap:
240 if f in self._copymap:
241 del self._copymap[f]
241 del self._copymap[f]
242
242
243 def forget(self, f):
243 def forget(self, f):
244 'forget a file'
244 'forget a file'
245 self._dirty = True
245 self._dirty = True
246 try:
246 try:
247 del self._map[f]
247 del self._map[f]
248 self._decpath(f)
248 self._decpath(f)
249 except KeyError:
249 except KeyError:
250 self._ui.warn(_("not in dirstate: %s!\n") % f)
250 self._ui.warn(_("not in dirstate: %s!\n") % f)
251
251
252 def rebuild(self, parent, files):
252 def rebuild(self, parent, files):
253 self.invalidate()
253 self.invalidate()
254 for f in files:
254 for f in files:
255 if files.execf(f):
255 if files.execf(f):
256 self._map[f] = ('n', 0777, -1, 0)
256 self._map[f] = ('n', 0777, -1, 0)
257 else:
257 else:
258 self._map[f] = ('n', 0666, -1, 0)
258 self._map[f] = ('n', 0666, -1, 0)
259 self._pl = (parent, nullid)
259 self._pl = (parent, nullid)
260 self._dirty = True
260 self._dirty = True
261
261
262 def write(self):
262 def write(self):
263 if not self._dirty:
263 if not self._dirty:
264 return
264 return
265 cs = cStringIO.StringIO()
265 cs = cStringIO.StringIO()
266 cs.write("".join(self._pl))
266 cs.write("".join(self._pl))
267 for f, e in self._map.iteritems():
267 for f, e in self._map.iteritems():
268 c = self.copied(f)
268 c = self.copied(f)
269 if c:
269 if c:
270 f = f + "\0" + c
270 f = f + "\0" + c
271 e = struct.pack(_format, e[0], e[1], e[2], e[3], len(f))
271 e = struct.pack(_format, e[0], e[1], e[2], e[3], len(f))
272 cs.write(e)
272 cs.write(e)
273 cs.write(f)
273 cs.write(f)
274 st = self._opener("dirstate", "w", atomictemp=True)
274 st = self._opener("dirstate", "w", atomictemp=True)
275 st.write(cs.getvalue())
275 st.write(cs.getvalue())
276 st.rename()
276 st.rename()
277 self._dirty = self._dirtypl = False
277 self._dirty = self._dirtypl = False
278
278
279 def _filter(self, files):
279 def _filter(self, files):
280 ret = {}
280 ret = {}
281 unknown = []
281 unknown = []
282
282
283 for x in files:
283 for x in files:
284 if x == '.':
284 if x == '.':
285 return self._map.copy()
285 return self._map.copy()
286 if x not in self._map:
286 if x not in self._map:
287 unknown.append(x)
287 unknown.append(x)
288 else:
288 else:
289 ret[x] = self._map[x]
289 ret[x] = self._map[x]
290
290
291 if not unknown:
291 if not unknown:
292 return ret
292 return ret
293
293
294 b = self._map.keys()
294 b = self._map.keys()
295 b.sort()
295 b.sort()
296 blen = len(b)
296 blen = len(b)
297
297
298 for x in unknown:
298 for x in unknown:
299 bs = bisect.bisect(b, "%s%s" % (x, '/'))
299 bs = bisect.bisect(b, "%s%s" % (x, '/'))
300 while bs < blen:
300 while bs < blen:
301 s = b[bs]
301 s = b[bs]
302 if len(s) > len(x) and s.startswith(x):
302 if len(s) > len(x) and s.startswith(x):
303 ret[s] = self._map[s]
303 ret[s] = self._map[s]
304 else:
304 else:
305 break
305 break
306 bs += 1
306 bs += 1
307 return ret
307 return ret
308
308
309 def _supported(self, f, mode, verbose=False):
309 def _supported(self, f, mode, verbose=False):
310 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
310 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
311 return True
311 return True
312 if verbose:
312 if verbose:
313 kind = 'unknown'
313 kind = 'unknown'
314 if stat.S_ISCHR(mode): kind = _('character device')
314 if stat.S_ISCHR(mode): kind = _('character device')
315 elif stat.S_ISBLK(mode): kind = _('block device')
315 elif stat.S_ISBLK(mode): kind = _('block device')
316 elif stat.S_ISFIFO(mode): kind = _('fifo')
316 elif stat.S_ISFIFO(mode): kind = _('fifo')
317 elif stat.S_ISSOCK(mode): kind = _('socket')
317 elif stat.S_ISSOCK(mode): kind = _('socket')
318 elif stat.S_ISDIR(mode): kind = _('directory')
318 elif stat.S_ISDIR(mode): kind = _('directory')
319 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
319 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
320 % (self.pathto(f), kind))
320 % (self.pathto(f), kind))
321 return False
321 return False
322
322
323 def walk(self, files=None, match=util.always, badmatch=None):
323 def walk(self, files=None, match=util.always, badmatch=None):
324 # filter out the stat
324 # filter out the stat
325 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
325 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
326 yield src, f
326 yield src, f
327
327
328 def statwalk(self, files=None, match=util.always, ignored=False,
328 def statwalk(self, files=None, match=util.always, ignored=False,
329 badmatch=None, directories=False):
329 badmatch=None, directories=False):
330 '''
330 '''
331 walk recursively through the directory tree, finding all files
331 walk recursively through the directory tree, finding all files
332 matched by the match function
332 matched by the match function
333
333
334 results are yielded in a tuple (src, filename, st), where src
334 results are yielded in a tuple (src, filename, st), where src
335 is one of:
335 is one of:
336 'f' the file was found in the directory tree
336 'f' the file was found in the directory tree
337 'd' the file is a directory of the tree
337 'd' the file is a directory of the tree
338 'm' the file was only in the dirstate and not in the tree
338 'm' the file was only in the dirstate and not in the tree
339 'b' file was not found and matched badmatch
339 'b' file was not found and matched badmatch
340
340
341 and st is the stat result if the file was found in the directory.
341 and st is the stat result if the file was found in the directory.
342 '''
342 '''
343
343
344 # walk all files by default
344 # walk all files by default
345 if not files:
345 if not files:
346 files = ['.']
346 files = ['.']
347 dc = self._map.copy()
347 dc = self._map.copy()
348 else:
348 else:
349 files = util.unique(files)
349 files = util.unique(files)
350 dc = self._filter(files)
350 dc = self._filter(files)
351
351
352 def imatch(file_):
352 def imatch(file_):
353 if file_ not in dc and self._ignore(file_):
353 if file_ not in dc and self._ignore(file_):
354 return False
354 return False
355 return match(file_)
355 return match(file_)
356
356
357 ignore = self._ignore
357 ignore = self._ignore
358 if ignored:
358 if ignored:
359 imatch = match
359 imatch = match
360 ignore = util.never
360 ignore = util.never
361
361
362 # self._root may end with a path separator when self._root == '/'
362 # self._root may end with a path separator when self._root == '/'
363 common_prefix_len = len(self._root)
363 common_prefix_len = len(self._root)
364 if not self._root.endswith(os.sep):
364 if not self._root.endswith(os.sep):
365 common_prefix_len += 1
365 common_prefix_len += 1
366
366
367 normpath = util.normpath
367 normpath = util.normpath
368 listdir = os.listdir
368 listdir = os.listdir
369 lstat = os.lstat
369 lstat = os.lstat
370 bisect_left = bisect.bisect_left
370 bisect_left = bisect.bisect_left
371 isdir = os.path.isdir
371 isdir = os.path.isdir
372 pconvert = util.pconvert
372 pconvert = util.pconvert
373 join = os.path.join
373 join = os.path.join
374 s_isdir = stat.S_ISDIR
374 s_isdir = stat.S_ISDIR
375 supported = self._supported
375 supported = self._supported
376 _join = self._join
376 _join = self._join
377 known = {'.hg': 1}
377 known = {'.hg': 1}
378
378
379 # recursion free walker, faster than os.walk.
379 # recursion free walker, faster than os.walk.
380 def findfiles(s):
380 def findfiles(s):
381 work = [s]
381 work = [s]
382 wadd = work.append
383 found = []
384 add = found.append
382 if directories:
385 if directories:
383 yield 'd', normpath(s[common_prefix_len:]), lstat(s)
386 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
384 while work:
387 while work:
385 top = work.pop()
388 top = work.pop()
386 names = listdir(top)
389 names = listdir(top)
387 names.sort()
390 names.sort()
388 # nd is the top of the repository dir tree
391 # nd is the top of the repository dir tree
389 nd = normpath(top[common_prefix_len:])
392 nd = normpath(top[common_prefix_len:])
390 if nd == '.':
393 if nd == '.':
391 nd = ''
394 nd = ''
392 else:
395 else:
393 # do not recurse into a repo contained in this
396 # do not recurse into a repo contained in this
394 # one. use bisect to find .hg directory so speed
397 # one. use bisect to find .hg directory so speed
395 # is good on big directory.
398 # is good on big directory.
396 hg = bisect_left(names, '.hg')
399 hg = bisect_left(names, '.hg')
397 if hg < len(names) and names[hg] == '.hg':
400 if hg < len(names) and names[hg] == '.hg':
398 if isdir(join(top, '.hg')):
401 if isdir(join(top, '.hg')):
399 continue
402 continue
400 for f in names:
403 for f in names:
401 np = pconvert(join(nd, f))
404 np = pconvert(join(nd, f))
402 if np in known:
405 if np in known:
403 continue
406 continue
404 known[np] = 1
407 known[np] = 1
405 p = join(top, f)
408 p = join(top, f)
406 # don't trip over symlinks
409 # don't trip over symlinks
407 st = lstat(p)
410 st = lstat(p)
408 if s_isdir(st.st_mode):
411 if s_isdir(st.st_mode):
409 if not ignore(np):
412 if not ignore(np):
410 work.append(p)
413 wadd(p)
411 if directories:
414 if directories:
412 yield 'd', np, st
415 add((np, 'd', st))
413 if np in dc and match(np):
416 if np in dc and match(np):
414 yield 'm', np, st
417 add((np, 'm', st))
415 elif imatch(np):
418 elif imatch(np):
416 if supported(np, st.st_mode):
419 if supported(np, st.st_mode):
417 yield 'f', np, st
420 add((np, 'f', st))
418 elif np in dc:
421 elif np in dc:
419 yield 'm', np, st
422 add((np, 'm', st))
423 found.sort()
424 return found
420
425
421 # step one, find all files that match our criteria
426 # step one, find all files that match our criteria
422 files.sort()
427 files.sort()
423 for ff in files:
428 for ff in files:
424 nf = normpath(ff)
429 nf = normpath(ff)
425 f = _join(ff)
430 f = _join(ff)
426 try:
431 try:
427 st = lstat(f)
432 st = lstat(f)
428 except OSError, inst:
433 except OSError, inst:
429 found = False
434 found = False
430 for fn in dc:
435 for fn in dc:
431 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
436 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
432 found = True
437 found = True
433 break
438 break
434 if not found:
439 if not found:
435 if inst.errno != errno.ENOENT or not badmatch:
440 if inst.errno != errno.ENOENT or not badmatch:
436 self._ui.warn('%s: %s\n' %
441 self._ui.warn('%s: %s\n' %
437 (self.pathto(ff), inst.strerror))
442 (self.pathto(ff), inst.strerror))
438 elif badmatch and badmatch(ff) and imatch(nf):
443 elif badmatch and badmatch(ff) and imatch(nf):
439 yield 'b', ff, None
444 yield 'b', ff, None
440 continue
445 continue
441 if s_isdir(st.st_mode):
446 if s_isdir(st.st_mode):
442 cmp1 = (lambda x, y: cmp(x[1], y[1]))
447 for f, src, st in findfiles(f):
443 sorted_ = [ x for x in findfiles(f) ]
448 yield src, f, st
444 sorted_.sort(cmp1)
445 for e in sorted_:
446 yield e
447 else:
449 else:
448 if nf in known:
450 if nf in known:
449 continue
451 continue
450 known[nf] = 1
452 known[nf] = 1
451 if match(nf):
453 if match(nf):
452 if supported(ff, st.st_mode, verbose=True):
454 if supported(ff, st.st_mode, verbose=True):
453 yield 'f', nf, st
455 yield 'f', nf, st
454 elif ff in dc:
456 elif ff in dc:
455 yield 'm', nf, st
457 yield 'm', nf, st
456
458
457 # step two run through anything left in the dc hash and yield
459 # step two run through anything left in the dc hash and yield
458 # if we haven't already seen it
460 # if we haven't already seen it
459 ks = dc.keys()
461 ks = dc.keys()
460 ks.sort()
462 ks.sort()
461 for k in ks:
463 for k in ks:
462 if k in known:
464 if k in known:
463 continue
465 continue
464 known[k] = 1
466 known[k] = 1
465 if imatch(k):
467 if imatch(k):
466 yield 'm', k, None
468 yield 'm', k, None
467
469
468 def status(self, files, match, list_ignored, list_clean):
470 def status(self, files, match, list_ignored, list_clean):
469 lookup, modified, added, unknown, ignored = [], [], [], [], []
471 lookup, modified, added, unknown, ignored = [], [], [], [], []
470 removed, deleted, clean = [], [], []
472 removed, deleted, clean = [], [], []
471
473
472 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
474 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
473 try:
475 try:
474 type_, mode, size, time = self._map[fn]
476 type_, mode, size, time = self._map[fn]
475 except KeyError:
477 except KeyError:
476 if list_ignored and self._ignore(fn):
478 if list_ignored and self._ignore(fn):
477 ignored.append(fn)
479 ignored.append(fn)
478 else:
480 else:
479 unknown.append(fn)
481 unknown.append(fn)
480 continue
482 continue
481 if src == 'm':
483 if src == 'm':
482 nonexistent = True
484 nonexistent = True
483 if not st:
485 if not st:
484 try:
486 try:
485 st = os.lstat(self._join(fn))
487 st = os.lstat(self._join(fn))
486 except OSError, inst:
488 except OSError, inst:
487 if inst.errno != errno.ENOENT:
489 if inst.errno != errno.ENOENT:
488 raise
490 raise
489 st = None
491 st = None
490 # We need to re-check that it is a valid file
492 # We need to re-check that it is a valid file
491 if st and self._supported(fn, st.st_mode):
493 if st and self._supported(fn, st.st_mode):
492 nonexistent = False
494 nonexistent = False
493 # XXX: what to do with file no longer present in the fs
495 # XXX: what to do with file no longer present in the fs
494 # who are not removed in the dirstate ?
496 # who are not removed in the dirstate ?
495 if nonexistent and type_ in "nm":
497 if nonexistent and type_ in "nm":
496 deleted.append(fn)
498 deleted.append(fn)
497 continue
499 continue
498 # check the common case first
500 # check the common case first
499 if type_ == 'n':
501 if type_ == 'n':
500 if not st:
502 if not st:
501 st = os.lstat(self._join(fn))
503 st = os.lstat(self._join(fn))
502 if (size >= 0 and (size != st.st_size
504 if (size >= 0 and (size != st.st_size
503 or (mode ^ st.st_mode) & 0100)
505 or (mode ^ st.st_mode) & 0100)
504 or fn in self._copymap):
506 or fn in self._copymap):
505 modified.append(fn)
507 modified.append(fn)
506 elif time != int(st.st_mtime):
508 elif time != int(st.st_mtime):
507 lookup.append(fn)
509 lookup.append(fn)
508 elif list_clean:
510 elif list_clean:
509 clean.append(fn)
511 clean.append(fn)
510 elif type_ == 'm':
512 elif type_ == 'm':
511 modified.append(fn)
513 modified.append(fn)
512 elif type_ == 'a':
514 elif type_ == 'a':
513 added.append(fn)
515 added.append(fn)
514 elif type_ == 'r':
516 elif type_ == 'r':
515 removed.append(fn)
517 removed.append(fn)
516
518
517 return (lookup, modified, added, removed, deleted, unknown, ignored,
519 return (lookup, modified, added, removed, deleted, unknown, ignored,
518 clean)
520 clean)
General Comments 0
You need to be logged in to leave comments. Login now