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