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