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