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