##// END OF EJS Templates
dirstate.write: don't ignore stat data if mtime is in the future (issue1790)...
Adrian Buehlmann -
r9378:1a7bcf58 default
parent child Browse files
Show More
@@ -0,0 +1,13 b''
1 #!/bin/sh
2
3 hg init
4 echo a > a
5 hg add
6 hg ci -m1
7
8 # set mtime of a into the future
9 touch -t 202101011200 a
10
11 # status must not set a's entry to unset (issue1790)
12 hg status
13 hg debugstate
@@ -0,0 +1,2 b''
1 adding a
2 n 644 2 2021-01-01 12:00:00 a
@@ -1,601 +1,602 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 try:
377 gran = int(self._ui.config('dirstate', 'granularity', 1))
377 gran = int(self._ui.config('dirstate', 'granularity', 1))
378 except ValueError:
378 except ValueError:
379 gran = 1
379 gran = 1
380 limit = sys.maxint
381 if gran > 0:
380 if gran > 0:
382 limit = util.fstat(st).st_mtime - gran
381 hlimit = util.fstat(st).st_mtime
382 llimit = hlimit - gran
383
383
384 cs = cStringIO.StringIO()
384 cs = cStringIO.StringIO()
385 copymap = self._copymap
385 copymap = self._copymap
386 pack = struct.pack
386 pack = struct.pack
387 write = cs.write
387 write = cs.write
388 write("".join(self._pl))
388 write("".join(self._pl))
389 for f, e in self._map.iteritems():
389 for f, e in self._map.iteritems():
390 if f in copymap:
390 if f in copymap:
391 f = "%s\0%s" % (f, copymap[f])
391 f = "%s\0%s" % (f, copymap[f])
392 if e[3] > limit and e[0] == 'n':
392 if gran > 0 and e[0] == 'n' and llimit < e[3] <= hlimit:
393 # file was updated too recently, ignore stat data
393 e = (e[0], 0, -1, -1)
394 e = (e[0], 0, -1, -1)
394 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
395 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
395 write(e)
396 write(e)
396 write(f)
397 write(f)
397 st.write(cs.getvalue())
398 st.write(cs.getvalue())
398 st.rename()
399 st.rename()
399 self._dirty = self._dirtypl = False
400 self._dirty = self._dirtypl = False
400
401
401 def _dirignore(self, f):
402 def _dirignore(self, f):
402 if f == '.':
403 if f == '.':
403 return False
404 return False
404 if self._ignore(f):
405 if self._ignore(f):
405 return True
406 return True
406 for p in _finddirs(f):
407 for p in _finddirs(f):
407 if self._ignore(p):
408 if self._ignore(p):
408 return True
409 return True
409 return False
410 return False
410
411
411 def walk(self, match, unknown, ignored):
412 def walk(self, match, unknown, ignored):
412 '''
413 '''
413 walk recursively through the directory tree, finding all files
414 walk recursively through the directory tree, finding all files
414 matched by the match function
415 matched by the match function
415
416
416 results are yielded in a tuple (filename, stat), where stat
417 results are yielded in a tuple (filename, stat), where stat
417 and st is the stat result if the file was found in the directory.
418 and st is the stat result if the file was found in the directory.
418 '''
419 '''
419
420
420 def fwarn(f, msg):
421 def fwarn(f, msg):
421 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
422 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
422 return False
423 return False
423
424
424 def badtype(mode):
425 def badtype(mode):
425 kind = _('unknown')
426 kind = _('unknown')
426 if stat.S_ISCHR(mode): kind = _('character device')
427 if stat.S_ISCHR(mode): kind = _('character device')
427 elif stat.S_ISBLK(mode): kind = _('block device')
428 elif stat.S_ISBLK(mode): kind = _('block device')
428 elif stat.S_ISFIFO(mode): kind = _('fifo')
429 elif stat.S_ISFIFO(mode): kind = _('fifo')
429 elif stat.S_ISSOCK(mode): kind = _('socket')
430 elif stat.S_ISSOCK(mode): kind = _('socket')
430 elif stat.S_ISDIR(mode): kind = _('directory')
431 elif stat.S_ISDIR(mode): kind = _('directory')
431 return _('unsupported file type (type is %s)') % kind
432 return _('unsupported file type (type is %s)') % kind
432
433
433 ignore = self._ignore
434 ignore = self._ignore
434 dirignore = self._dirignore
435 dirignore = self._dirignore
435 if ignored:
436 if ignored:
436 ignore = util.never
437 ignore = util.never
437 dirignore = util.never
438 dirignore = util.never
438 elif not unknown:
439 elif not unknown:
439 # if unknown and ignored are False, skip step 2
440 # if unknown and ignored are False, skip step 2
440 ignore = util.always
441 ignore = util.always
441 dirignore = util.always
442 dirignore = util.always
442
443
443 matchfn = match.matchfn
444 matchfn = match.matchfn
444 badfn = match.bad
445 badfn = match.bad
445 dmap = self._map
446 dmap = self._map
446 normpath = util.normpath
447 normpath = util.normpath
447 listdir = osutil.listdir
448 listdir = osutil.listdir
448 lstat = os.lstat
449 lstat = os.lstat
449 getkind = stat.S_IFMT
450 getkind = stat.S_IFMT
450 dirkind = stat.S_IFDIR
451 dirkind = stat.S_IFDIR
451 regkind = stat.S_IFREG
452 regkind = stat.S_IFREG
452 lnkkind = stat.S_IFLNK
453 lnkkind = stat.S_IFLNK
453 join = self._join
454 join = self._join
454 work = []
455 work = []
455 wadd = work.append
456 wadd = work.append
456
457
457 if self._checkcase:
458 if self._checkcase:
458 normalize = self._normalize
459 normalize = self._normalize
459 else:
460 else:
460 normalize = lambda x, y: x
461 normalize = lambda x, y: x
461
462
462 exact = skipstep3 = False
463 exact = skipstep3 = False
463 if matchfn == match.exact: # match.exact
464 if matchfn == match.exact: # match.exact
464 exact = True
465 exact = True
465 dirignore = util.always # skip step 2
466 dirignore = util.always # skip step 2
466 elif match.files() and not match.anypats(): # match.match, no patterns
467 elif match.files() and not match.anypats(): # match.match, no patterns
467 skipstep3 = True
468 skipstep3 = True
468
469
469 files = set(match.files())
470 files = set(match.files())
470 if not files or '.' in files:
471 if not files or '.' in files:
471 files = ['']
472 files = ['']
472 results = {'.hg': None}
473 results = {'.hg': None}
473
474
474 # step 1: find all explicit files
475 # step 1: find all explicit files
475 for ff in sorted(files):
476 for ff in sorted(files):
476 nf = normalize(normpath(ff), False)
477 nf = normalize(normpath(ff), False)
477 if nf in results:
478 if nf in results:
478 continue
479 continue
479
480
480 try:
481 try:
481 st = lstat(join(nf))
482 st = lstat(join(nf))
482 kind = getkind(st.st_mode)
483 kind = getkind(st.st_mode)
483 if kind == dirkind:
484 if kind == dirkind:
484 skipstep3 = False
485 skipstep3 = False
485 if nf in dmap:
486 if nf in dmap:
486 #file deleted on disk but still in dirstate
487 #file deleted on disk but still in dirstate
487 results[nf] = None
488 results[nf] = None
488 match.dir(nf)
489 match.dir(nf)
489 if not dirignore(nf):
490 if not dirignore(nf):
490 wadd(nf)
491 wadd(nf)
491 elif kind == regkind or kind == lnkkind:
492 elif kind == regkind or kind == lnkkind:
492 results[nf] = st
493 results[nf] = st
493 else:
494 else:
494 badfn(ff, badtype(kind))
495 badfn(ff, badtype(kind))
495 if nf in dmap:
496 if nf in dmap:
496 results[nf] = None
497 results[nf] = None
497 except OSError, inst:
498 except OSError, inst:
498 if nf in dmap: # does it exactly match a file?
499 if nf in dmap: # does it exactly match a file?
499 results[nf] = None
500 results[nf] = None
500 else: # does it match a directory?
501 else: # does it match a directory?
501 prefix = nf + "/"
502 prefix = nf + "/"
502 for fn in dmap:
503 for fn in dmap:
503 if fn.startswith(prefix):
504 if fn.startswith(prefix):
504 match.dir(nf)
505 match.dir(nf)
505 skipstep3 = False
506 skipstep3 = False
506 break
507 break
507 else:
508 else:
508 badfn(ff, inst.strerror)
509 badfn(ff, inst.strerror)
509
510
510 # step 2: visit subdirectories
511 # step 2: visit subdirectories
511 while work:
512 while work:
512 nd = work.pop()
513 nd = work.pop()
513 skip = None
514 skip = None
514 if nd == '.':
515 if nd == '.':
515 nd = ''
516 nd = ''
516 else:
517 else:
517 skip = '.hg'
518 skip = '.hg'
518 try:
519 try:
519 entries = listdir(join(nd), stat=True, skip=skip)
520 entries = listdir(join(nd), stat=True, skip=skip)
520 except OSError, inst:
521 except OSError, inst:
521 if inst.errno == errno.EACCES:
522 if inst.errno == errno.EACCES:
522 fwarn(nd, inst.strerror)
523 fwarn(nd, inst.strerror)
523 continue
524 continue
524 raise
525 raise
525 for f, kind, st in entries:
526 for f, kind, st in entries:
526 nf = normalize(nd and (nd + "/" + f) or f, True)
527 nf = normalize(nd and (nd + "/" + f) or f, True)
527 if nf not in results:
528 if nf not in results:
528 if kind == dirkind:
529 if kind == dirkind:
529 if not ignore(nf):
530 if not ignore(nf):
530 match.dir(nf)
531 match.dir(nf)
531 wadd(nf)
532 wadd(nf)
532 if nf in dmap and matchfn(nf):
533 if nf in dmap and matchfn(nf):
533 results[nf] = None
534 results[nf] = None
534 elif kind == regkind or kind == lnkkind:
535 elif kind == regkind or kind == lnkkind:
535 if nf in dmap:
536 if nf in dmap:
536 if matchfn(nf):
537 if matchfn(nf):
537 results[nf] = st
538 results[nf] = st
538 elif matchfn(nf) and not ignore(nf):
539 elif matchfn(nf) and not ignore(nf):
539 results[nf] = st
540 results[nf] = st
540 elif nf in dmap and matchfn(nf):
541 elif nf in dmap and matchfn(nf):
541 results[nf] = None
542 results[nf] = None
542
543
543 # step 3: report unseen items in the dmap hash
544 # step 3: report unseen items in the dmap hash
544 if not skipstep3 and not exact:
545 if not skipstep3 and not exact:
545 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
546 visit = sorted([f for f in dmap if f not in results and matchfn(f)])
546 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
547 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
547 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
548 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
548 st = None
549 st = None
549 results[nf] = st
550 results[nf] = st
550
551
551 del results['.hg']
552 del results['.hg']
552 return results
553 return results
553
554
554 def status(self, match, ignored, clean, unknown):
555 def status(self, match, ignored, clean, unknown):
555 listignored, listclean, listunknown = ignored, clean, unknown
556 listignored, listclean, listunknown = ignored, clean, unknown
556 lookup, modified, added, unknown, ignored = [], [], [], [], []
557 lookup, modified, added, unknown, ignored = [], [], [], [], []
557 removed, deleted, clean = [], [], []
558 removed, deleted, clean = [], [], []
558
559
559 dmap = self._map
560 dmap = self._map
560 ladd = lookup.append
561 ladd = lookup.append
561 madd = modified.append
562 madd = modified.append
562 aadd = added.append
563 aadd = added.append
563 uadd = unknown.append
564 uadd = unknown.append
564 iadd = ignored.append
565 iadd = ignored.append
565 radd = removed.append
566 radd = removed.append
566 dadd = deleted.append
567 dadd = deleted.append
567 cadd = clean.append
568 cadd = clean.append
568
569
569 for fn, st in self.walk(match, listunknown, listignored).iteritems():
570 for fn, st in self.walk(match, listunknown, listignored).iteritems():
570 if fn not in dmap:
571 if fn not in dmap:
571 if (listignored or match.exact(fn)) and self._dirignore(fn):
572 if (listignored or match.exact(fn)) and self._dirignore(fn):
572 if listignored:
573 if listignored:
573 iadd(fn)
574 iadd(fn)
574 elif listunknown:
575 elif listunknown:
575 uadd(fn)
576 uadd(fn)
576 continue
577 continue
577
578
578 state, mode, size, time = dmap[fn]
579 state, mode, size, time = dmap[fn]
579
580
580 if not st and state in "nma":
581 if not st and state in "nma":
581 dadd(fn)
582 dadd(fn)
582 elif state == 'n':
583 elif state == 'n':
583 if (size >= 0 and
584 if (size >= 0 and
584 (size != st.st_size
585 (size != st.st_size
585 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
586 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
586 or size == -2
587 or size == -2
587 or fn in self._copymap):
588 or fn in self._copymap):
588 madd(fn)
589 madd(fn)
589 elif time != int(st.st_mtime):
590 elif time != int(st.st_mtime):
590 ladd(fn)
591 ladd(fn)
591 elif listclean:
592 elif listclean:
592 cadd(fn)
593 cadd(fn)
593 elif state == 'm':
594 elif state == 'm':
594 madd(fn)
595 madd(fn)
595 elif state == 'a':
596 elif state == 'a':
596 aadd(fn)
597 aadd(fn)
597 elif state == 'r':
598 elif state == 'r':
598 radd(fn)
599 radd(fn)
599
600
600 return (lookup, modified, added, removed, deleted, unknown, ignored,
601 return (lookup, modified, added, removed, deleted, unknown, ignored,
601 clean)
602 clean)
General Comments 0
You need to be logged in to leave comments. Login now