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