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