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