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