##// END OF EJS Templates
dirstate: skip step 3 in walk if nothing new will match...
Simon Heimberg -
r8589:3edf133d default
parent child Browse files
Show More
@@ -1,600 +1,620 b''
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import nullid
8 from node import nullid
9 from i18n import _
9 from i18n import _
10 import util, ignore, osutil, parsers
10 import util, ignore, osutil, parsers
11 import struct, os, stat, errno
11 import struct, os, stat, errno
12 import cStringIO, sys
12 import cStringIO, sys
13
13
14 _unknown = ('?', 0, 0, 0)
14 _unknown = ('?', 0, 0, 0)
15 _format = ">cllll"
15 _format = ">cllll"
16 propertycache = util.propertycache
16 propertycache = util.propertycache
17
17
18 def _finddirs(path):
18 def _finddirs(path):
19 pos = path.rfind('/')
19 pos = path.rfind('/')
20 while pos != -1:
20 while pos != -1:
21 yield path[:pos]
21 yield path[:pos]
22 pos = path.rfind('/', 0, pos)
22 pos = path.rfind('/', 0, pos)
23
23
24 def _incdirs(dirs, path):
24 def _incdirs(dirs, path):
25 for base in _finddirs(path):
25 for base in _finddirs(path):
26 if base in dirs:
26 if base in dirs:
27 dirs[base] += 1
27 dirs[base] += 1
28 return
28 return
29 dirs[base] = 1
29 dirs[base] = 1
30
30
31 def _decdirs(dirs, path):
31 def _decdirs(dirs, path):
32 for base in _finddirs(path):
32 for base in _finddirs(path):
33 if dirs[base] > 1:
33 if dirs[base] > 1:
34 dirs[base] -= 1
34 dirs[base] -= 1
35 return
35 return
36 del dirs[base]
36 del dirs[base]
37
37
38 class dirstate(object):
38 class dirstate(object):
39
39
40 def __init__(self, opener, ui, root):
40 def __init__(self, opener, ui, root):
41 self._opener = opener
41 self._opener = opener
42 self._root = root
42 self._root = root
43 self._rootdir = os.path.join(root, '')
43 self._rootdir = os.path.join(root, '')
44 self._dirty = False
44 self._dirty = False
45 self._dirtypl = False
45 self._dirtypl = False
46 self._ui = ui
46 self._ui = ui
47
47
48 @propertycache
48 @propertycache
49 def _map(self):
49 def _map(self):
50 self._read()
50 self._read()
51 return self._map
51 return self._map
52
52
53 @propertycache
53 @propertycache
54 def _copymap(self):
54 def _copymap(self):
55 self._read()
55 self._read()
56 return self._copymap
56 return self._copymap
57
57
58 @propertycache
58 @propertycache
59 def _foldmap(self):
59 def _foldmap(self):
60 f = {}
60 f = {}
61 for name in self._map:
61 for name in self._map:
62 f[os.path.normcase(name)] = name
62 f[os.path.normcase(name)] = name
63 return f
63 return f
64
64
65 @propertycache
65 @propertycache
66 def _branch(self):
66 def _branch(self):
67 try:
67 try:
68 return self._opener("branch").read().strip() or "default"
68 return self._opener("branch").read().strip() or "default"
69 except IOError:
69 except IOError:
70 return "default"
70 return "default"
71
71
72 @propertycache
72 @propertycache
73 def _pl(self):
73 def _pl(self):
74 try:
74 try:
75 st = self._opener("dirstate").read(40)
75 st = self._opener("dirstate").read(40)
76 if len(st) == 40:
76 if len(st) == 40:
77 return st[:20], st[20:40]
77 return st[:20], st[20:40]
78 except IOError, err:
78 except IOError, err:
79 if err.errno != errno.ENOENT: raise
79 if err.errno != errno.ENOENT: raise
80 return [nullid, nullid]
80 return [nullid, nullid]
81
81
82 @propertycache
82 @propertycache
83 def _dirs(self):
83 def _dirs(self):
84 dirs = {}
84 dirs = {}
85 for f,s in self._map.iteritems():
85 for f,s in self._map.iteritems():
86 if s[0] != 'r':
86 if s[0] != 'r':
87 _incdirs(dirs, f)
87 _incdirs(dirs, f)
88 return dirs
88 return dirs
89
89
90 @propertycache
90 @propertycache
91 def _ignore(self):
91 def _ignore(self):
92 files = [self._join('.hgignore')]
92 files = [self._join('.hgignore')]
93 for name, path in self._ui.configitems("ui"):
93 for name, path in self._ui.configitems("ui"):
94 if name == 'ignore' or name.startswith('ignore.'):
94 if name == 'ignore' or name.startswith('ignore.'):
95 files.append(os.path.expanduser(path))
95 files.append(os.path.expanduser(path))
96 return ignore.ignore(self._root, files, self._ui.warn)
96 return ignore.ignore(self._root, files, self._ui.warn)
97
97
98 @propertycache
98 @propertycache
99 def _slash(self):
99 def _slash(self):
100 return self._ui.configbool('ui', 'slash') and os.sep != '/'
100 return self._ui.configbool('ui', 'slash') and os.sep != '/'
101
101
102 @propertycache
102 @propertycache
103 def _checklink(self):
103 def _checklink(self):
104 return util.checklink(self._root)
104 return util.checklink(self._root)
105
105
106 @propertycache
106 @propertycache
107 def _checkexec(self):
107 def _checkexec(self):
108 return util.checkexec(self._root)
108 return util.checkexec(self._root)
109
109
110 @propertycache
110 @propertycache
111 def _checkcase(self):
111 def _checkcase(self):
112 return not util.checkcase(self._join('.hg'))
112 return not util.checkcase(self._join('.hg'))
113
113
114 @propertycache
114 @propertycache
115 def normalize(self):
115 def normalize(self):
116 if self._checkcase:
116 if self._checkcase:
117 return self._normalize
117 return self._normalize
118 return lambda x, y=False: x
118 return lambda x, y=False: x
119
119
120 def _join(self, f):
120 def _join(self, f):
121 # much faster than os.path.join()
121 # much faster than os.path.join()
122 # it's safe because f is always a relative path
122 # it's safe because f is always a relative path
123 return self._rootdir + f
123 return self._rootdir + f
124
124
125 def flagfunc(self, fallback):
125 def flagfunc(self, fallback):
126 if self._checklink:
126 if self._checklink:
127 if self._checkexec:
127 if self._checkexec:
128 def f(x):
128 def f(x):
129 p = self._join(x)
129 p = self._join(x)
130 if os.path.islink(p):
130 if os.path.islink(p):
131 return 'l'
131 return 'l'
132 if util.is_exec(p):
132 if util.is_exec(p):
133 return 'x'
133 return 'x'
134 return ''
134 return ''
135 return f
135 return f
136 def f(x):
136 def f(x):
137 if os.path.islink(self._join(x)):
137 if os.path.islink(self._join(x)):
138 return 'l'
138 return 'l'
139 if 'x' in fallback(x):
139 if 'x' in fallback(x):
140 return 'x'
140 return 'x'
141 return ''
141 return ''
142 return f
142 return f
143 if self._checkexec:
143 if self._checkexec:
144 def f(x):
144 def f(x):
145 if 'l' in fallback(x):
145 if 'l' in fallback(x):
146 return 'l'
146 return 'l'
147 if util.is_exec(self._join(x)):
147 if util.is_exec(self._join(x)):
148 return 'x'
148 return 'x'
149 return ''
149 return ''
150 return f
150 return f
151 return fallback
151 return fallback
152
152
153 def getcwd(self):
153 def getcwd(self):
154 cwd = os.getcwd()
154 cwd = os.getcwd()
155 if cwd == self._root: return ''
155 if cwd == self._root: return ''
156 # self._root ends with a path separator if self._root is '/' or 'C:\'
156 # self._root ends with a path separator if self._root is '/' or 'C:\'
157 rootsep = self._root
157 rootsep = self._root
158 if not util.endswithsep(rootsep):
158 if not util.endswithsep(rootsep):
159 rootsep += os.sep
159 rootsep += os.sep
160 if cwd.startswith(rootsep):
160 if cwd.startswith(rootsep):
161 return cwd[len(rootsep):]
161 return cwd[len(rootsep):]
162 else:
162 else:
163 # we're outside the repo. return an absolute path.
163 # we're outside the repo. return an absolute path.
164 return cwd
164 return cwd
165
165
166 def pathto(self, f, cwd=None):
166 def pathto(self, f, cwd=None):
167 if cwd is None:
167 if cwd is None:
168 cwd = self.getcwd()
168 cwd = self.getcwd()
169 path = util.pathto(self._root, cwd, f)
169 path = util.pathto(self._root, cwd, f)
170 if self._slash:
170 if self._slash:
171 return util.normpath(path)
171 return util.normpath(path)
172 return path
172 return path
173
173
174 def __getitem__(self, key):
174 def __getitem__(self, key):
175 ''' current states:
175 ''' current states:
176 n normal
176 n normal
177 m needs merging
177 m needs merging
178 r marked for removal
178 r marked for removal
179 a marked for addition
179 a marked for addition
180 ? not tracked'''
180 ? not tracked'''
181 return self._map.get(key, ("?",))[0]
181 return self._map.get(key, ("?",))[0]
182
182
183 def __contains__(self, key):
183 def __contains__(self, key):
184 return key in self._map
184 return key in self._map
185
185
186 def __iter__(self):
186 def __iter__(self):
187 for x in sorted(self._map):
187 for x in sorted(self._map):
188 yield x
188 yield x
189
189
190 def parents(self):
190 def parents(self):
191 return self._pl
191 return self._pl
192
192
193 def branch(self):
193 def branch(self):
194 return self._branch
194 return self._branch
195
195
196 def setparents(self, p1, p2=nullid):
196 def setparents(self, p1, p2=nullid):
197 self._dirty = self._dirtypl = True
197 self._dirty = self._dirtypl = True
198 self._pl = p1, p2
198 self._pl = p1, p2
199
199
200 def setbranch(self, branch):
200 def setbranch(self, branch):
201 self._branch = branch
201 self._branch = branch
202 self._opener("branch", "w").write(branch + '\n')
202 self._opener("branch", "w").write(branch + '\n')
203
203
204 def _read(self):
204 def _read(self):
205 self._map = {}
205 self._map = {}
206 self._copymap = {}
206 self._copymap = {}
207 try:
207 try:
208 st = self._opener("dirstate").read()
208 st = self._opener("dirstate").read()
209 except IOError, err:
209 except IOError, err:
210 if err.errno != errno.ENOENT: raise
210 if err.errno != errno.ENOENT: raise
211 return
211 return
212 if not st:
212 if not st:
213 return
213 return
214
214
215 p = parsers.parse_dirstate(self._map, self._copymap, st)
215 p = parsers.parse_dirstate(self._map, self._copymap, st)
216 if not self._dirtypl:
216 if not self._dirtypl:
217 self._pl = p
217 self._pl = p
218
218
219 def invalidate(self):
219 def invalidate(self):
220 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
220 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
221 if a in self.__dict__:
221 if a in self.__dict__:
222 delattr(self, a)
222 delattr(self, a)
223 self._dirty = False
223 self._dirty = False
224
224
225 def copy(self, source, dest):
225 def copy(self, source, dest):
226 """Mark dest as a copy of source. Unmark dest if source is None.
226 """Mark dest as a copy of source. Unmark dest if source is None.
227 """
227 """
228 if source == dest:
228 if source == dest:
229 return
229 return
230 self._dirty = True
230 self._dirty = True
231 if source is not None:
231 if source is not None:
232 self._copymap[dest] = source
232 self._copymap[dest] = source
233 elif dest in self._copymap:
233 elif dest in self._copymap:
234 del self._copymap[dest]
234 del self._copymap[dest]
235
235
236 def copied(self, file):
236 def copied(self, file):
237 return self._copymap.get(file, None)
237 return self._copymap.get(file, None)
238
238
239 def copies(self):
239 def copies(self):
240 return self._copymap
240 return self._copymap
241
241
242 def _droppath(self, f):
242 def _droppath(self, f):
243 if self[f] not in "?r" and "_dirs" in self.__dict__:
243 if self[f] not in "?r" and "_dirs" in self.__dict__:
244 _decdirs(self._dirs, f)
244 _decdirs(self._dirs, f)
245
245
246 def _addpath(self, f, check=False):
246 def _addpath(self, f, check=False):
247 oldstate = self[f]
247 oldstate = self[f]
248 if check or oldstate == "r":
248 if check or oldstate == "r":
249 if '\r' in f or '\n' in f:
249 if '\r' in f or '\n' in f:
250 raise util.Abort(
250 raise util.Abort(
251 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
251 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
252 if f in self._dirs:
252 if f in self._dirs:
253 raise util.Abort(_('directory %r already in dirstate') % f)
253 raise util.Abort(_('directory %r already in dirstate') % f)
254 # shadows
254 # shadows
255 for d in _finddirs(f):
255 for d in _finddirs(f):
256 if d in self._dirs:
256 if d in self._dirs:
257 break
257 break
258 if d in self._map and self[d] != 'r':
258 if d in self._map and self[d] != 'r':
259 raise util.Abort(
259 raise util.Abort(
260 _('file %r in dirstate clashes with %r') % (d, f))
260 _('file %r in dirstate clashes with %r') % (d, f))
261 if oldstate in "?r" and "_dirs" in self.__dict__:
261 if oldstate in "?r" and "_dirs" in self.__dict__:
262 _incdirs(self._dirs, f)
262 _incdirs(self._dirs, f)
263
263
264 def normal(self, f):
264 def normal(self, f):
265 'mark a file normal and clean'
265 'mark a file normal and clean'
266 self._dirty = True
266 self._dirty = True
267 self._addpath(f)
267 self._addpath(f)
268 s = os.lstat(self._join(f))
268 s = os.lstat(self._join(f))
269 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
269 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
270 if f in self._copymap:
270 if f in self._copymap:
271 del self._copymap[f]
271 del self._copymap[f]
272
272
273 def normallookup(self, f):
273 def normallookup(self, f):
274 'mark a file normal, but possibly dirty'
274 'mark a file normal, but possibly dirty'
275 if self._pl[1] != nullid and f in self._map:
275 if self._pl[1] != nullid and f in self._map:
276 # if there is a merge going on and the file was either
276 # if there is a merge going on and the file was either
277 # in state 'm' or dirty before being removed, restore that state.
277 # in state 'm' or dirty before being removed, restore that state.
278 entry = self._map[f]
278 entry = self._map[f]
279 if entry[0] == 'r' and entry[2] in (-1, -2):
279 if entry[0] == 'r' and entry[2] in (-1, -2):
280 source = self._copymap.get(f)
280 source = self._copymap.get(f)
281 if entry[2] == -1:
281 if entry[2] == -1:
282 self.merge(f)
282 self.merge(f)
283 elif entry[2] == -2:
283 elif entry[2] == -2:
284 self.normaldirty(f)
284 self.normaldirty(f)
285 if source:
285 if source:
286 self.copy(source, f)
286 self.copy(source, f)
287 return
287 return
288 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
288 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
289 return
289 return
290 self._dirty = True
290 self._dirty = True
291 self._addpath(f)
291 self._addpath(f)
292 self._map[f] = ('n', 0, -1, -1)
292 self._map[f] = ('n', 0, -1, -1)
293 if f in self._copymap:
293 if f in self._copymap:
294 del self._copymap[f]
294 del self._copymap[f]
295
295
296 def normaldirty(self, f):
296 def normaldirty(self, f):
297 'mark a file normal, but dirty'
297 'mark a file normal, but dirty'
298 self._dirty = True
298 self._dirty = True
299 self._addpath(f)
299 self._addpath(f)
300 self._map[f] = ('n', 0, -2, -1)
300 self._map[f] = ('n', 0, -2, -1)
301 if f in self._copymap:
301 if f in self._copymap:
302 del self._copymap[f]
302 del self._copymap[f]
303
303
304 def add(self, f):
304 def add(self, f):
305 'mark a file added'
305 'mark a file added'
306 self._dirty = True
306 self._dirty = True
307 self._addpath(f, True)
307 self._addpath(f, True)
308 self._map[f] = ('a', 0, -1, -1)
308 self._map[f] = ('a', 0, -1, -1)
309 if f in self._copymap:
309 if f in self._copymap:
310 del self._copymap[f]
310 del self._copymap[f]
311
311
312 def remove(self, f):
312 def remove(self, f):
313 'mark a file removed'
313 'mark a file removed'
314 self._dirty = True
314 self._dirty = True
315 self._droppath(f)
315 self._droppath(f)
316 size = 0
316 size = 0
317 if self._pl[1] != nullid and f in self._map:
317 if self._pl[1] != nullid and f in self._map:
318 entry = self._map[f]
318 entry = self._map[f]
319 if entry[0] == 'm':
319 if entry[0] == 'm':
320 size = -1
320 size = -1
321 elif entry[0] == 'n' and entry[2] == -2:
321 elif entry[0] == 'n' and entry[2] == -2:
322 size = -2
322 size = -2
323 self._map[f] = ('r', 0, size, 0)
323 self._map[f] = ('r', 0, size, 0)
324 if size == 0 and f in self._copymap:
324 if size == 0 and f in self._copymap:
325 del self._copymap[f]
325 del self._copymap[f]
326
326
327 def merge(self, f):
327 def merge(self, f):
328 'mark a file merged'
328 'mark a file merged'
329 self._dirty = True
329 self._dirty = True
330 s = os.lstat(self._join(f))
330 s = os.lstat(self._join(f))
331 self._addpath(f)
331 self._addpath(f)
332 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
332 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
333 if f in self._copymap:
333 if f in self._copymap:
334 del self._copymap[f]
334 del self._copymap[f]
335
335
336 def forget(self, f):
336 def forget(self, f):
337 'forget a file'
337 'forget a file'
338 self._dirty = True
338 self._dirty = True
339 try:
339 try:
340 self._droppath(f)
340 self._droppath(f)
341 del self._map[f]
341 del self._map[f]
342 except KeyError:
342 except KeyError:
343 self._ui.warn(_("not in dirstate: %s\n") % f)
343 self._ui.warn(_("not in dirstate: %s\n") % f)
344
344
345 def _normalize(self, path, knownpath=False):
345 def _normalize(self, path, knownpath=False):
346 norm_path = os.path.normcase(path)
346 norm_path = os.path.normcase(path)
347 fold_path = self._foldmap.get(norm_path, None)
347 fold_path = self._foldmap.get(norm_path, None)
348 if fold_path is None:
348 if fold_path is None:
349 if knownpath or not os.path.exists(os.path.join(self._root, path)):
349 if knownpath or not os.path.exists(os.path.join(self._root, path)):
350 fold_path = path
350 fold_path = path
351 else:
351 else:
352 fold_path = self._foldmap.setdefault(norm_path,
352 fold_path = self._foldmap.setdefault(norm_path,
353 util.fspath(path, self._root))
353 util.fspath(path, self._root))
354 return fold_path
354 return fold_path
355
355
356 def clear(self):
356 def clear(self):
357 self._map = {}
357 self._map = {}
358 if "_dirs" in self.__dict__:
358 if "_dirs" in self.__dict__:
359 delattr(self, "_dirs");
359 delattr(self, "_dirs");
360 self._copymap = {}
360 self._copymap = {}
361 self._pl = [nullid, nullid]
361 self._pl = [nullid, nullid]
362 self._dirty = True
362 self._dirty = True
363
363
364 def rebuild(self, parent, files):
364 def rebuild(self, parent, files):
365 self.clear()
365 self.clear()
366 for f in files:
366 for f in files:
367 if 'x' in files.flags(f):
367 if 'x' in files.flags(f):
368 self._map[f] = ('n', 0777, -1, 0)
368 self._map[f] = ('n', 0777, -1, 0)
369 else:
369 else:
370 self._map[f] = ('n', 0666, -1, 0)
370 self._map[f] = ('n', 0666, -1, 0)
371 self._pl = (parent, nullid)
371 self._pl = (parent, nullid)
372 self._dirty = True
372 self._dirty = True
373
373
374 def write(self):
374 def write(self):
375 if not self._dirty:
375 if not self._dirty:
376 return
376 return
377 st = self._opener("dirstate", "w", atomictemp=True)
377 st = self._opener("dirstate", "w", atomictemp=True)
378
378
379 try:
379 try:
380 gran = int(self._ui.config('dirstate', 'granularity', 1))
380 gran = int(self._ui.config('dirstate', 'granularity', 1))
381 except ValueError:
381 except ValueError:
382 gran = 1
382 gran = 1
383 limit = sys.maxint
383 limit = sys.maxint
384 if gran > 0:
384 if gran > 0:
385 limit = util.fstat(st).st_mtime - gran
385 limit = util.fstat(st).st_mtime - gran
386
386
387 cs = cStringIO.StringIO()
387 cs = cStringIO.StringIO()
388 copymap = self._copymap
388 copymap = self._copymap
389 pack = struct.pack
389 pack = struct.pack
390 write = cs.write
390 write = cs.write
391 write("".join(self._pl))
391 write("".join(self._pl))
392 for f, e in self._map.iteritems():
392 for f, e in self._map.iteritems():
393 if f in copymap:
393 if f in copymap:
394 f = "%s\0%s" % (f, copymap[f])
394 f = "%s\0%s" % (f, copymap[f])
395 if e[3] > limit and e[0] == 'n':
395 if e[3] > limit and e[0] == 'n':
396 e = (e[0], 0, -1, -1)
396 e = (e[0], 0, -1, -1)
397 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
397 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
398 write(e)
398 write(e)
399 write(f)
399 write(f)
400 st.write(cs.getvalue())
400 st.write(cs.getvalue())
401 st.rename()
401 st.rename()
402 self._dirty = self._dirtypl = False
402 self._dirty = self._dirtypl = False
403
403
404 def _dirignore(self, f):
404 def _dirignore(self, f):
405 if f == '.':
405 if f == '.':
406 return False
406 return False
407 if self._ignore(f):
407 if self._ignore(f):
408 return True
408 return True
409 for p in _finddirs(f):
409 for p in _finddirs(f):
410 if self._ignore(p):
410 if self._ignore(p):
411 return True
411 return True
412 return False
412 return False
413
413
414 def walk(self, match, unknown, ignored):
414 def walk(self, match, unknown, ignored):
415 '''
415 '''
416 walk recursively through the directory tree, finding all files
416 walk recursively through the directory tree, finding all files
417 matched by the match function
417 matched by the match function
418
418
419 results are yielded in a tuple (filename, stat), where stat
419 results are yielded in a tuple (filename, stat), where stat
420 and st is the stat result if the file was found in the directory.
420 and st is the stat result if the file was found in the directory.
421 '''
421 '''
422
422
423 def fwarn(f, msg):
423 def fwarn(f, msg):
424 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
424 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
425 return False
425 return False
426 badfn = fwarn
426 badfn = fwarn
427 if hasattr(match, 'bad'):
427 if hasattr(match, 'bad'):
428 badfn = match.bad
428 badfn = match.bad
429
429
430 def badtype(f, mode):
430 def badtype(f, mode):
431 kind = _('unknown')
431 kind = _('unknown')
432 if stat.S_ISCHR(mode): kind = _('character device')
432 if stat.S_ISCHR(mode): kind = _('character device')
433 elif stat.S_ISBLK(mode): kind = _('block device')
433 elif stat.S_ISBLK(mode): kind = _('block device')
434 elif stat.S_ISFIFO(mode): kind = _('fifo')
434 elif stat.S_ISFIFO(mode): kind = _('fifo')
435 elif stat.S_ISSOCK(mode): kind = _('socket')
435 elif stat.S_ISSOCK(mode): kind = _('socket')
436 elif stat.S_ISDIR(mode): kind = _('directory')
436 elif stat.S_ISDIR(mode): kind = _('directory')
437 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
437 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
438 % (self.pathto(f), kind))
438 % (self.pathto(f), kind))
439
439
440 ignore = self._ignore
440 ignore = self._ignore
441 dirignore = self._dirignore
441 dirignore = self._dirignore
442 if ignored:
442 if ignored:
443 ignore = util.never
443 ignore = util.never
444 dirignore = util.never
444 dirignore = util.never
445 elif not unknown:
445 elif not unknown:
446 # if unknown and ignored are False, skip step 2
446 # if unknown and ignored are False, skip step 2
447 ignore = util.always
447 ignore = util.always
448 dirignore = util.always
448 dirignore = util.always
449
449
450 matchfn = match.matchfn
450 matchfn = match.matchfn
451 dmap = self._map
451 dmap = self._map
452 normpath = util.normpath
452 normpath = util.normpath
453 normalize = self.normalize
453 normalize = self.normalize
454 listdir = osutil.listdir
454 listdir = osutil.listdir
455 lstat = os.lstat
455 lstat = os.lstat
456 getkind = stat.S_IFMT
456 getkind = stat.S_IFMT
457 dirkind = stat.S_IFDIR
457 dirkind = stat.S_IFDIR
458 regkind = stat.S_IFREG
458 regkind = stat.S_IFREG
459 lnkkind = stat.S_IFLNK
459 lnkkind = stat.S_IFLNK
460 join = self._join
460 join = self._join
461 work = []
461 work = []
462 wadd = work.append
462 wadd = work.append
463
463
464 if match.anypats():
465 #match.match with patterns
466 dostep3 = True
467 nomatches = False
468 elif not match.files():
469 #match.always or match.never
470 dostep3 = matchfn('')
471 nomatches = not dostep3
472 else:
473 #match.exact or match.match without pattern
474 dostep3 = False
475 nomatches = matchfn == match.exact
476
477 if nomatches:
478 #skip step 2
479 dirignore = util.always
480
464 files = set(match.files())
481 files = set(match.files())
465 if not files or '.' in files:
482 if not files or '.' in files:
466 files = ['']
483 files = ['']
467 results = {'.hg': None}
484 results = {'.hg': None}
468
485
469 # step 1: find all explicit files
486 # step 1: find all explicit files
470 for ff in sorted(files):
487 for ff in sorted(files):
471 nf = normalize(normpath(ff))
488 nf = normalize(normpath(ff))
472 if nf in results:
489 if nf in results:
473 continue
490 continue
474
491
475 try:
492 try:
476 st = lstat(join(nf))
493 st = lstat(join(nf))
477 kind = getkind(st.st_mode)
494 kind = getkind(st.st_mode)
478 if kind == dirkind:
495 if kind == dirkind:
496 dostep3 = True
479 if nf in dmap:
497 if nf in dmap:
480 #file deleted on disc but still in dirstate
498 #file deleted on disc but still in dirstate
481 results[nf] = None
499 results[nf] = None
482 if not dirignore(nf):
500 if not dirignore(nf):
483 wadd(nf)
501 wadd(nf)
484 elif kind == regkind or kind == lnkkind:
502 elif kind == regkind or kind == lnkkind:
485 results[nf] = st
503 results[nf] = st
486 else:
504 else:
487 badtype(ff, kind)
505 badtype(ff, kind)
488 if nf in dmap:
506 if nf in dmap:
489 results[nf] = None
507 results[nf] = None
490 except OSError, inst:
508 except OSError, inst:
491 keep = False
509 keep = False
492 prefix = nf + "/"
510 prefix = nf + "/"
493 for fn in dmap:
511 for fn in dmap:
494 if nf == fn:
512 if nf == fn:
495 if matchfn(nf):
513 if matchfn(nf):
496 results[nf] = None
514 results[nf] = None
497 keep = True
515 keep = True
498 break
516 break
499 elif fn.startswith(prefix):
517 elif fn.startswith(prefix):
518 dostep3 = True
500 keep = True
519 keep = True
501 break
520 break
502 if not keep:
521 if not keep:
503 if inst.errno != errno.ENOENT:
522 if inst.errno != errno.ENOENT:
504 fwarn(ff, inst.strerror)
523 fwarn(ff, inst.strerror)
505 elif badfn(ff, inst.strerror):
524 elif badfn(ff, inst.strerror):
506 if nf not in results and not ignore(nf) and matchfn(nf):
525 if nf not in results and not ignore(nf) and matchfn(nf):
507 results[nf] = None
526 results[nf] = None
508
527
509 # step 2: visit subdirectories
528 # step 2: visit subdirectories
510 while work:
529 while work:
511 nd = work.pop()
530 nd = work.pop()
512 if hasattr(match, 'dir'):
531 if hasattr(match, 'dir'):
513 match.dir(nd)
532 match.dir(nd)
514 skip = None
533 skip = None
515 if nd == '.':
534 if nd == '.':
516 nd = ''
535 nd = ''
517 else:
536 else:
518 skip = '.hg'
537 skip = '.hg'
519 try:
538 try:
520 entries = listdir(join(nd), stat=True, skip=skip)
539 entries = listdir(join(nd), stat=True, skip=skip)
521 except OSError, inst:
540 except OSError, inst:
522 if inst.errno == errno.EACCES:
541 if inst.errno == errno.EACCES:
523 fwarn(nd, inst.strerror)
542 fwarn(nd, inst.strerror)
524 continue
543 continue
525 raise
544 raise
526 for f, kind, st in entries:
545 for f, kind, st in entries:
527 nf = normalize(nd and (nd + "/" + f) or f, True)
546 nf = normalize(nd and (nd + "/" + f) or f, True)
528 if nf not in results:
547 if nf not in results:
529 if kind == dirkind:
548 if kind == dirkind:
530 if not ignore(nf):
549 if not ignore(nf):
531 wadd(nf)
550 wadd(nf)
532 if nf in dmap and matchfn(nf):
551 if nf in dmap and matchfn(nf):
533 results[nf] = None
552 results[nf] = None
534 elif kind == regkind or kind == lnkkind:
553 elif kind == regkind or kind == lnkkind:
535 if nf in dmap:
554 if nf in dmap:
536 if matchfn(nf):
555 if matchfn(nf):
537 results[nf] = st
556 results[nf] = st
538 elif matchfn(nf) and not ignore(nf):
557 elif matchfn(nf) and not ignore(nf):
539 results[nf] = st
558 results[nf] = st
540 elif nf in dmap and matchfn(nf):
559 elif nf in dmap and matchfn(nf):
541 results[nf] = None
560 results[nf] = None
542
561
543 # step 3: report unseen items in the dmap hash
562 # step 3: report unseen items in the dmap hash
544 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
563 if dostep3 and not nomatches:
545 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
564 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
546 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
565 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
547 st = None
566 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
548 results[nf] = st
567 st = None
568 results[nf] = st
549
569
550 del results['.hg']
570 del results['.hg']
551 return results
571 return results
552
572
553 def status(self, match, ignored, clean, unknown):
573 def status(self, match, ignored, clean, unknown):
554 listignored, listclean, listunknown = ignored, clean, unknown
574 listignored, listclean, listunknown = ignored, clean, unknown
555 lookup, modified, added, unknown, ignored = [], [], [], [], []
575 lookup, modified, added, unknown, ignored = [], [], [], [], []
556 removed, deleted, clean = [], [], []
576 removed, deleted, clean = [], [], []
557
577
558 dmap = self._map
578 dmap = self._map
559 ladd = lookup.append
579 ladd = lookup.append
560 madd = modified.append
580 madd = modified.append
561 aadd = added.append
581 aadd = added.append
562 uadd = unknown.append
582 uadd = unknown.append
563 iadd = ignored.append
583 iadd = ignored.append
564 radd = removed.append
584 radd = removed.append
565 dadd = deleted.append
585 dadd = deleted.append
566 cadd = clean.append
586 cadd = clean.append
567
587
568 for fn, st in self.walk(match, listunknown, listignored).iteritems():
588 for fn, st in self.walk(match, listunknown, listignored).iteritems():
569 if fn not in dmap:
589 if fn not in dmap:
570 if (listignored or match.exact(fn)) and self._dirignore(fn):
590 if (listignored or match.exact(fn)) and self._dirignore(fn):
571 if listignored:
591 if listignored:
572 iadd(fn)
592 iadd(fn)
573 elif listunknown:
593 elif listunknown:
574 uadd(fn)
594 uadd(fn)
575 continue
595 continue
576
596
577 state, mode, size, time = dmap[fn]
597 state, mode, size, time = dmap[fn]
578
598
579 if not st and state in "nma":
599 if not st and state in "nma":
580 dadd(fn)
600 dadd(fn)
581 elif state == 'n':
601 elif state == 'n':
582 if (size >= 0 and
602 if (size >= 0 and
583 (size != st.st_size
603 (size != st.st_size
584 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
604 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
585 or size == -2
605 or size == -2
586 or fn in self._copymap):
606 or fn in self._copymap):
587 madd(fn)
607 madd(fn)
588 elif time != int(st.st_mtime):
608 elif time != int(st.st_mtime):
589 ladd(fn)
609 ladd(fn)
590 elif listclean:
610 elif listclean:
591 cadd(fn)
611 cadd(fn)
592 elif state == 'm':
612 elif state == 'm':
593 madd(fn)
613 madd(fn)
594 elif state == 'a':
614 elif state == 'a':
595 aadd(fn)
615 aadd(fn)
596 elif state == 'r':
616 elif state == 'r':
597 radd(fn)
617 radd(fn)
598
618
599 return (lookup, modified, added, removed, deleted, unknown, ignored,
619 return (lookup, modified, added, removed, deleted, unknown, ignored,
600 clean)
620 clean)
General Comments 0
You need to be logged in to leave comments. Login now