##// END OF EJS Templates
dirstate: localize a bunch of methods in status fastpath
Matt Mackall -
r5003:4b1acb3e default
parent child Browse files
Show More
@@ -1,520 +1,533
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
382 wadd = work.append
383 found = []
383 found = []
384 add = found.append
384 add = found.append
385 if directories:
385 if directories:
386 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
386 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
387 while work:
387 while work:
388 top = work.pop()
388 top = work.pop()
389 names = listdir(top)
389 names = listdir(top)
390 names.sort()
390 names.sort()
391 # nd is the top of the repository dir tree
391 # nd is the top of the repository dir tree
392 nd = normpath(top[common_prefix_len:])
392 nd = normpath(top[common_prefix_len:])
393 if nd == '.':
393 if nd == '.':
394 nd = ''
394 nd = ''
395 else:
395 else:
396 # do not recurse into a repo contained in this
396 # do not recurse into a repo contained in this
397 # one. use bisect to find .hg directory so speed
397 # one. use bisect to find .hg directory so speed
398 # is good on big directory.
398 # is good on big directory.
399 hg = bisect_left(names, '.hg')
399 hg = bisect_left(names, '.hg')
400 if hg < len(names) and names[hg] == '.hg':
400 if hg < len(names) and names[hg] == '.hg':
401 if isdir(join(top, '.hg')):
401 if isdir(join(top, '.hg')):
402 continue
402 continue
403 for f in names:
403 for f in names:
404 np = pconvert(join(nd, f))
404 np = pconvert(join(nd, f))
405 if np in known:
405 if np in known:
406 continue
406 continue
407 known[np] = 1
407 known[np] = 1
408 p = join(top, f)
408 p = join(top, f)
409 # don't trip over symlinks
409 # don't trip over symlinks
410 st = lstat(p)
410 st = lstat(p)
411 if s_isdir(st.st_mode):
411 if s_isdir(st.st_mode):
412 if not ignore(np):
412 if not ignore(np):
413 wadd(p)
413 wadd(p)
414 if directories:
414 if directories:
415 add((np, 'd', st))
415 add((np, 'd', st))
416 if np in dc and match(np):
416 if np in dc and match(np):
417 add((np, 'm', st))
417 add((np, 'm', st))
418 elif imatch(np):
418 elif imatch(np):
419 if supported(np, st.st_mode):
419 if supported(np, st.st_mode):
420 add((np, 'f', st))
420 add((np, 'f', st))
421 elif np in dc:
421 elif np in dc:
422 add((np, 'm', st))
422 add((np, 'm', st))
423 found.sort()
423 found.sort()
424 return found
424 return found
425
425
426 # step one, find all files that match our criteria
426 # step one, find all files that match our criteria
427 files.sort()
427 files.sort()
428 for ff in files:
428 for ff in files:
429 nf = normpath(ff)
429 nf = normpath(ff)
430 f = _join(ff)
430 f = _join(ff)
431 try:
431 try:
432 st = lstat(f)
432 st = lstat(f)
433 except OSError, inst:
433 except OSError, inst:
434 found = False
434 found = False
435 for fn in dc:
435 for fn in dc:
436 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
436 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
437 found = True
437 found = True
438 break
438 break
439 if not found:
439 if not found:
440 if inst.errno != errno.ENOENT or not badmatch:
440 if inst.errno != errno.ENOENT or not badmatch:
441 self._ui.warn('%s: %s\n' %
441 self._ui.warn('%s: %s\n' %
442 (self.pathto(ff), inst.strerror))
442 (self.pathto(ff), inst.strerror))
443 elif badmatch and badmatch(ff) and imatch(nf):
443 elif badmatch and badmatch(ff) and imatch(nf):
444 yield 'b', ff, None
444 yield 'b', ff, None
445 continue
445 continue
446 if s_isdir(st.st_mode):
446 if s_isdir(st.st_mode):
447 for f, src, st in findfiles(f):
447 for f, src, st in findfiles(f):
448 yield src, f, st
448 yield src, f, st
449 else:
449 else:
450 if nf in known:
450 if nf in known:
451 continue
451 continue
452 known[nf] = 1
452 known[nf] = 1
453 if match(nf):
453 if match(nf):
454 if supported(ff, st.st_mode, verbose=True):
454 if supported(ff, st.st_mode, verbose=True):
455 yield 'f', nf, st
455 yield 'f', nf, st
456 elif ff in dc:
456 elif ff in dc:
457 yield 'm', nf, st
457 yield 'm', nf, st
458
458
459 # 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
460 # if we haven't already seen it
460 # if we haven't already seen it
461 ks = dc.keys()
461 ks = dc.keys()
462 ks.sort()
462 ks.sort()
463 for k in ks:
463 for k in ks:
464 if k in known:
464 if k in known:
465 continue
465 continue
466 known[k] = 1
466 known[k] = 1
467 if imatch(k):
467 if imatch(k):
468 yield 'm', k, None
468 yield 'm', k, None
469
469
470 def status(self, files, match, list_ignored, list_clean):
470 def status(self, files, match, list_ignored, list_clean):
471 lookup, modified, added, unknown, ignored = [], [], [], [], []
471 lookup, modified, added, unknown, ignored = [], [], [], [], []
472 removed, deleted, clean = [], [], []
472 removed, deleted, clean = [], [], []
473
473
474 _join = self._join
475 lstat = os.lstat
476 cmap = self._copymap
477 dmap = self._map
478 ladd = lookup.append
479 madd = modified.append
480 aadd = added.append
481 uadd = unknown.append
482 iadd = ignored.append
483 radd = removed.append
484 dadd = deleted.append
485 cadd = clean.append
486
474 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
487 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
475 try:
488 if fn in dmap:
476 type_, mode, size, time = self._map[fn]
489 type_, mode, size, time = dmap[fn]
477 except KeyError:
490 else:
478 if list_ignored and self._ignore(fn):
491 if list_ignored and self._ignore(fn):
479 ignored.append(fn)
492 iadd(fn)
480 else:
493 else:
481 unknown.append(fn)
494 uadd(fn)
482 continue
495 continue
483 if src == 'm':
496 if src == 'm':
484 nonexistent = True
497 nonexistent = True
485 if not st:
498 if not st:
486 try:
499 try:
487 st = os.lstat(self._join(fn))
500 st = lstat(_join(fn))
488 except OSError, inst:
501 except OSError, inst:
489 if inst.errno != errno.ENOENT:
502 if inst.errno != errno.ENOENT:
490 raise
503 raise
491 st = None
504 st = None
492 # We need to re-check that it is a valid file
505 # We need to re-check that it is a valid file
493 if st and self._supported(fn, st.st_mode):
506 if st and self._supported(fn, st.st_mode):
494 nonexistent = False
507 nonexistent = False
495 # XXX: what to do with file no longer present in the fs
508 # XXX: what to do with file no longer present in the fs
496 # who are not removed in the dirstate ?
509 # who are not removed in the dirstate ?
497 if nonexistent and type_ in "nm":
510 if nonexistent and type_ in "nm":
498 deleted.append(fn)
511 dadd(fn)
499 continue
512 continue
500 # check the common case first
513 # check the common case first
501 if type_ == 'n':
514 if type_ == 'n':
502 if not st:
515 if not st:
503 st = os.lstat(self._join(fn))
516 st = lstat(_join(fn))
504 if (size >= 0 and (size != st.st_size
517 if (size >= 0 and (size != st.st_size
505 or (mode ^ st.st_mode) & 0100)
518 or (mode ^ st.st_mode) & 0100)
506 or fn in self._copymap):
519 or fn in self._copymap):
507 modified.append(fn)
520 madd(fn)
508 elif time != int(st.st_mtime):
521 elif time != int(st.st_mtime):
509 lookup.append(fn)
522 ladd(fn)
510 elif list_clean:
523 elif list_clean:
511 clean.append(fn)
524 cadd(fn)
512 elif type_ == 'm':
525 elif type_ == 'm':
513 modified.append(fn)
526 madd(fn)
514 elif type_ == 'a':
527 elif type_ == 'a':
515 added.append(fn)
528 aadd(fn)
516 elif type_ == 'r':
529 elif type_ == 'r':
517 removed.append(fn)
530 radd(fn)
518
531
519 return (lookup, modified, added, removed, deleted, unknown, ignored,
532 return (lookup, modified, added, removed, deleted, unknown, ignored,
520 clean)
533 clean)
General Comments 0
You need to be logged in to leave comments. Login now