##// END OF EJS Templates
dirstate: explain why appending instead of os.path.join() is safe
Benoit Boissinot -
r6973:8c136043 default
parent child Browse files
Show More
@@ -1,601 +1,602 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import nullid
10 from node import nullid
11 from i18n import _
11 from i18n import _
12 import struct, os, bisect, stat, util, errno, ignore
12 import struct, os, bisect, stat, util, errno, ignore
13 import cStringIO, osutil, sys
13 import cStringIO, osutil, sys
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 def _finddirs(path):
18 def _finddirs(path):
19 pos = len(path)
19 pos = len(path)
20 while 1:
20 while 1:
21 pos = path.rfind('/', 0, pos)
21 pos = path.rfind('/', 0, pos)
22 if pos == -1:
22 if pos == -1:
23 break
23 break
24 yield path[:pos]
24 yield path[:pos]
25
25
26 class dirstate(object):
26 class dirstate(object):
27
27
28 def __init__(self, opener, ui, root):
28 def __init__(self, opener, ui, root):
29 self._opener = opener
29 self._opener = opener
30 self._root = root
30 self._root = root
31 self._rootdir = os.path.join(root, '')
31 self._rootdir = os.path.join(root, '')
32 self._dirty = False
32 self._dirty = False
33 self._dirtypl = False
33 self._dirtypl = False
34 self._ui = ui
34 self._ui = ui
35
35
36 def __getattr__(self, name):
36 def __getattr__(self, name):
37 if name == '_map':
37 if name == '_map':
38 self._read()
38 self._read()
39 return self._map
39 return self._map
40 elif name == '_copymap':
40 elif name == '_copymap':
41 self._read()
41 self._read()
42 return self._copymap
42 return self._copymap
43 elif name == '_foldmap':
43 elif name == '_foldmap':
44 _foldmap = {}
44 _foldmap = {}
45 for name in self._map:
45 for name in self._map:
46 norm = os.path.normcase(os.path.normpath(name))
46 norm = os.path.normcase(os.path.normpath(name))
47 _foldmap[norm] = name
47 _foldmap[norm] = name
48 self._foldmap = _foldmap
48 self._foldmap = _foldmap
49 return self._foldmap
49 return self._foldmap
50 elif name == '_branch':
50 elif name == '_branch':
51 try:
51 try:
52 self._branch = (self._opener("branch").read().strip()
52 self._branch = (self._opener("branch").read().strip()
53 or "default")
53 or "default")
54 except IOError:
54 except IOError:
55 self._branch = "default"
55 self._branch = "default"
56 return self._branch
56 return self._branch
57 elif name == '_pl':
57 elif name == '_pl':
58 self._pl = [nullid, nullid]
58 self._pl = [nullid, nullid]
59 try:
59 try:
60 st = self._opener("dirstate").read(40)
60 st = self._opener("dirstate").read(40)
61 if len(st) == 40:
61 if len(st) == 40:
62 self._pl = st[:20], st[20:40]
62 self._pl = st[:20], st[20:40]
63 except IOError, err:
63 except IOError, err:
64 if err.errno != errno.ENOENT: raise
64 if err.errno != errno.ENOENT: raise
65 return self._pl
65 return self._pl
66 elif name == '_dirs':
66 elif name == '_dirs':
67 dirs = {}
67 dirs = {}
68 for f,s in self._map.items():
68 for f,s in self._map.items():
69 if s[0] != 'r':
69 if s[0] != 'r':
70 for base in _finddirs(f):
70 for base in _finddirs(f):
71 dirs[base] = dirs.get(base, 0) + 1
71 dirs[base] = dirs.get(base, 0) + 1
72 self._dirs = dirs
72 self._dirs = dirs
73 return self._dirs
73 return self._dirs
74 elif name == '_ignore':
74 elif name == '_ignore':
75 files = [self._join('.hgignore')]
75 files = [self._join('.hgignore')]
76 for name, path in self._ui.configitems("ui"):
76 for name, path in self._ui.configitems("ui"):
77 if name == 'ignore' or name.startswith('ignore.'):
77 if name == 'ignore' or name.startswith('ignore.'):
78 files.append(os.path.expanduser(path))
78 files.append(os.path.expanduser(path))
79 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
79 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
80 return self._ignore
80 return self._ignore
81 elif name == '_slash':
81 elif name == '_slash':
82 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
82 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
83 return self._slash
83 return self._slash
84 elif name == '_checklink':
84 elif name == '_checklink':
85 self._checklink = util.checklink(self._root)
85 self._checklink = util.checklink(self._root)
86 return self._checklink
86 return self._checklink
87 elif name == '_checkexec':
87 elif name == '_checkexec':
88 self._checkexec = util.checkexec(self._root)
88 self._checkexec = util.checkexec(self._root)
89 return self._checkexec
89 return self._checkexec
90 elif name == '_checkcase':
90 elif name == '_checkcase':
91 self._checkcase = not util.checkcase(self._join('.hg'))
91 self._checkcase = not util.checkcase(self._join('.hg'))
92 return self._checkcase
92 return self._checkcase
93 elif name == 'normalize':
93 elif name == 'normalize':
94 if self._checkcase:
94 if self._checkcase:
95 self.normalize = self._normalize
95 self.normalize = self._normalize
96 else:
96 else:
97 self.normalize = lambda x: x
97 self.normalize = lambda x: x
98 return self.normalize
98 return self.normalize
99 else:
99 else:
100 raise AttributeError, name
100 raise AttributeError, name
101
101
102 def _join(self, f):
102 def _join(self, f):
103 # much faster than os.path.join()
103 # much faster than os.path.join()
104 # it's safe because f is always a relative path
104 return self._rootdir + f
105 return self._rootdir + f
105
106
106 def flagfunc(self, fallback):
107 def flagfunc(self, fallback):
107 if self._checklink:
108 if self._checklink:
108 if self._checkexec:
109 if self._checkexec:
109 def f(x):
110 def f(x):
110 p = self._join(x)
111 p = self._join(x)
111 if os.path.islink(p):
112 if os.path.islink(p):
112 return 'l'
113 return 'l'
113 if util.is_exec(p):
114 if util.is_exec(p):
114 return 'x'
115 return 'x'
115 return ''
116 return ''
116 return f
117 return f
117 def f(x):
118 def f(x):
118 if os.path.islink(self._join(x)):
119 if os.path.islink(self._join(x)):
119 return 'l'
120 return 'l'
120 if 'x' in fallback(x):
121 if 'x' in fallback(x):
121 return 'x'
122 return 'x'
122 return ''
123 return ''
123 return f
124 return f
124 if self._checkexec:
125 if self._checkexec:
125 def f(x):
126 def f(x):
126 if 'l' in fallback(x):
127 if 'l' in fallback(x):
127 return 'l'
128 return 'l'
128 if util.is_exec(self._join(x)):
129 if util.is_exec(self._join(x)):
129 return 'x'
130 return 'x'
130 return ''
131 return ''
131 return f
132 return f
132 return fallback
133 return fallback
133
134
134 def getcwd(self):
135 def getcwd(self):
135 cwd = os.getcwd()
136 cwd = os.getcwd()
136 if cwd == self._root: return ''
137 if cwd == self._root: return ''
137 # self._root ends with a path separator if self._root is '/' or 'C:\'
138 # self._root ends with a path separator if self._root is '/' or 'C:\'
138 rootsep = self._root
139 rootsep = self._root
139 if not util.endswithsep(rootsep):
140 if not util.endswithsep(rootsep):
140 rootsep += os.sep
141 rootsep += os.sep
141 if cwd.startswith(rootsep):
142 if cwd.startswith(rootsep):
142 return cwd[len(rootsep):]
143 return cwd[len(rootsep):]
143 else:
144 else:
144 # we're outside the repo. return an absolute path.
145 # we're outside the repo. return an absolute path.
145 return cwd
146 return cwd
146
147
147 def pathto(self, f, cwd=None):
148 def pathto(self, f, cwd=None):
148 if cwd is None:
149 if cwd is None:
149 cwd = self.getcwd()
150 cwd = self.getcwd()
150 path = util.pathto(self._root, cwd, f)
151 path = util.pathto(self._root, cwd, f)
151 if self._slash:
152 if self._slash:
152 return util.normpath(path)
153 return util.normpath(path)
153 return path
154 return path
154
155
155 def __getitem__(self, key):
156 def __getitem__(self, key):
156 ''' current states:
157 ''' current states:
157 n normal
158 n normal
158 m needs merging
159 m needs merging
159 r marked for removal
160 r marked for removal
160 a marked for addition
161 a marked for addition
161 ? not tracked'''
162 ? not tracked'''
162 return self._map.get(key, ("?",))[0]
163 return self._map.get(key, ("?",))[0]
163
164
164 def __contains__(self, key):
165 def __contains__(self, key):
165 return key in self._map
166 return key in self._map
166
167
167 def __iter__(self):
168 def __iter__(self):
168 for x in util.sort(self._map):
169 for x in util.sort(self._map):
169 yield x
170 yield x
170
171
171 def parents(self):
172 def parents(self):
172 return self._pl
173 return self._pl
173
174
174 def branch(self):
175 def branch(self):
175 return self._branch
176 return self._branch
176
177
177 def setparents(self, p1, p2=nullid):
178 def setparents(self, p1, p2=nullid):
178 self._dirty = self._dirtypl = True
179 self._dirty = self._dirtypl = True
179 self._pl = p1, p2
180 self._pl = p1, p2
180
181
181 def setbranch(self, branch):
182 def setbranch(self, branch):
182 self._branch = branch
183 self._branch = branch
183 self._opener("branch", "w").write(branch + '\n')
184 self._opener("branch", "w").write(branch + '\n')
184
185
185 def _read(self):
186 def _read(self):
186 self._map = {}
187 self._map = {}
187 self._copymap = {}
188 self._copymap = {}
188 if not self._dirtypl:
189 if not self._dirtypl:
189 self._pl = [nullid, nullid]
190 self._pl = [nullid, nullid]
190 try:
191 try:
191 st = self._opener("dirstate").read()
192 st = self._opener("dirstate").read()
192 except IOError, err:
193 except IOError, err:
193 if err.errno != errno.ENOENT: raise
194 if err.errno != errno.ENOENT: raise
194 return
195 return
195 if not st:
196 if not st:
196 return
197 return
197
198
198 if not self._dirtypl:
199 if not self._dirtypl:
199 self._pl = [st[:20], st[20: 40]]
200 self._pl = [st[:20], st[20: 40]]
200
201
201 # deref fields so they will be local in loop
202 # deref fields so they will be local in loop
202 dmap = self._map
203 dmap = self._map
203 copymap = self._copymap
204 copymap = self._copymap
204 unpack = struct.unpack
205 unpack = struct.unpack
205 e_size = struct.calcsize(_format)
206 e_size = struct.calcsize(_format)
206 pos1 = 40
207 pos1 = 40
207 l = len(st)
208 l = len(st)
208
209
209 # the inner loop
210 # the inner loop
210 while pos1 < l:
211 while pos1 < l:
211 pos2 = pos1 + e_size
212 pos2 = pos1 + e_size
212 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
213 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
213 pos1 = pos2 + e[4]
214 pos1 = pos2 + e[4]
214 f = st[pos2:pos1]
215 f = st[pos2:pos1]
215 if '\0' in f:
216 if '\0' in f:
216 f, c = f.split('\0')
217 f, c = f.split('\0')
217 copymap[f] = c
218 copymap[f] = c
218 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
219 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
219
220
220 def invalidate(self):
221 def invalidate(self):
221 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
222 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
222 if a in self.__dict__:
223 if a in self.__dict__:
223 delattr(self, a)
224 delattr(self, a)
224 self._dirty = False
225 self._dirty = False
225
226
226 def copy(self, source, dest):
227 def copy(self, source, dest):
227 if source == dest:
228 if source == dest:
228 return
229 return
229 self._dirty = True
230 self._dirty = True
230 self._copymap[dest] = source
231 self._copymap[dest] = source
231
232
232 def copied(self, file):
233 def copied(self, file):
233 return self._copymap.get(file, None)
234 return self._copymap.get(file, None)
234
235
235 def copies(self):
236 def copies(self):
236 return self._copymap
237 return self._copymap
237
238
238 def _droppath(self, f):
239 def _droppath(self, f):
239 if self[f] not in "?r" and "_dirs" in self.__dict__:
240 if self[f] not in "?r" and "_dirs" in self.__dict__:
240 dirs = self._dirs
241 dirs = self._dirs
241 for base in _finddirs(f):
242 for base in _finddirs(f):
242 if dirs[base] == 1:
243 if dirs[base] == 1:
243 del dirs[base]
244 del dirs[base]
244 else:
245 else:
245 dirs[base] -= 1
246 dirs[base] -= 1
246
247
247 def _addpath(self, f, check=False):
248 def _addpath(self, f, check=False):
248 oldstate = self[f]
249 oldstate = self[f]
249 if check or oldstate == "r":
250 if check or oldstate == "r":
250 if '\r' in f or '\n' in f:
251 if '\r' in f or '\n' in f:
251 raise util.Abort(
252 raise util.Abort(
252 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
253 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
253 if f in self._dirs:
254 if f in self._dirs:
254 raise util.Abort(_('directory %r already in dirstate') % f)
255 raise util.Abort(_('directory %r already in dirstate') % f)
255 # shadows
256 # shadows
256 for d in _finddirs(f):
257 for d in _finddirs(f):
257 if d in self._dirs:
258 if d in self._dirs:
258 break
259 break
259 if d in self._map and self[d] != 'r':
260 if d in self._map and self[d] != 'r':
260 raise util.Abort(
261 raise util.Abort(
261 _('file %r in dirstate clashes with %r') % (d, f))
262 _('file %r in dirstate clashes with %r') % (d, f))
262 if oldstate in "?r" and "_dirs" in self.__dict__:
263 if oldstate in "?r" and "_dirs" in self.__dict__:
263 dirs = self._dirs
264 dirs = self._dirs
264 for base in _finddirs(f):
265 for base in _finddirs(f):
265 dirs[base] = dirs.get(base, 0) + 1
266 dirs[base] = dirs.get(base, 0) + 1
266
267
267 def normal(self, f):
268 def normal(self, f):
268 'mark a file normal and clean'
269 'mark a file normal and clean'
269 self._dirty = True
270 self._dirty = True
270 self._addpath(f)
271 self._addpath(f)
271 s = os.lstat(self._join(f))
272 s = os.lstat(self._join(f))
272 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
273 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
273 if f in self._copymap:
274 if f in self._copymap:
274 del self._copymap[f]
275 del self._copymap[f]
275
276
276 def normallookup(self, f):
277 def normallookup(self, f):
277 'mark a file normal, but possibly dirty'
278 'mark a file normal, but possibly dirty'
278 if self._pl[1] != nullid and f in self._map:
279 if self._pl[1] != nullid and f in self._map:
279 # if there is a merge going on and the file was either
280 # if there is a merge going on and the file was either
280 # in state 'm' or dirty before being removed, restore that state.
281 # in state 'm' or dirty before being removed, restore that state.
281 entry = self._map[f]
282 entry = self._map[f]
282 if entry[0] == 'r' and entry[2] in (-1, -2):
283 if entry[0] == 'r' and entry[2] in (-1, -2):
283 source = self._copymap.get(f)
284 source = self._copymap.get(f)
284 if entry[2] == -1:
285 if entry[2] == -1:
285 self.merge(f)
286 self.merge(f)
286 elif entry[2] == -2:
287 elif entry[2] == -2:
287 self.normaldirty(f)
288 self.normaldirty(f)
288 if source:
289 if source:
289 self.copy(source, f)
290 self.copy(source, f)
290 return
291 return
291 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
292 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
292 return
293 return
293 self._dirty = True
294 self._dirty = True
294 self._addpath(f)
295 self._addpath(f)
295 self._map[f] = ('n', 0, -1, -1, 0)
296 self._map[f] = ('n', 0, -1, -1, 0)
296 if f in self._copymap:
297 if f in self._copymap:
297 del self._copymap[f]
298 del self._copymap[f]
298
299
299 def normaldirty(self, f):
300 def normaldirty(self, f):
300 'mark a file normal, but dirty'
301 'mark a file normal, but dirty'
301 self._dirty = True
302 self._dirty = True
302 self._addpath(f)
303 self._addpath(f)
303 self._map[f] = ('n', 0, -2, -1, 0)
304 self._map[f] = ('n', 0, -2, -1, 0)
304 if f in self._copymap:
305 if f in self._copymap:
305 del self._copymap[f]
306 del self._copymap[f]
306
307
307 def add(self, f):
308 def add(self, f):
308 'mark a file added'
309 'mark a file added'
309 self._dirty = True
310 self._dirty = True
310 self._addpath(f, True)
311 self._addpath(f, True)
311 self._map[f] = ('a', 0, -1, -1, 0)
312 self._map[f] = ('a', 0, -1, -1, 0)
312 if f in self._copymap:
313 if f in self._copymap:
313 del self._copymap[f]
314 del self._copymap[f]
314
315
315 def remove(self, f):
316 def remove(self, f):
316 'mark a file removed'
317 'mark a file removed'
317 self._dirty = True
318 self._dirty = True
318 self._droppath(f)
319 self._droppath(f)
319 size = 0
320 size = 0
320 if self._pl[1] != nullid and f in self._map:
321 if self._pl[1] != nullid and f in self._map:
321 entry = self._map[f]
322 entry = self._map[f]
322 if entry[0] == 'm':
323 if entry[0] == 'm':
323 size = -1
324 size = -1
324 elif entry[0] == 'n' and entry[2] == -2:
325 elif entry[0] == 'n' and entry[2] == -2:
325 size = -2
326 size = -2
326 self._map[f] = ('r', 0, size, 0, 0)
327 self._map[f] = ('r', 0, size, 0, 0)
327 if size == 0 and f in self._copymap:
328 if size == 0 and f in self._copymap:
328 del self._copymap[f]
329 del self._copymap[f]
329
330
330 def merge(self, f):
331 def merge(self, f):
331 'mark a file merged'
332 'mark a file merged'
332 self._dirty = True
333 self._dirty = True
333 s = os.lstat(self._join(f))
334 s = os.lstat(self._join(f))
334 self._addpath(f)
335 self._addpath(f)
335 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
336 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
336 if f in self._copymap:
337 if f in self._copymap:
337 del self._copymap[f]
338 del self._copymap[f]
338
339
339 def forget(self, f):
340 def forget(self, f):
340 'forget a file'
341 'forget a file'
341 self._dirty = True
342 self._dirty = True
342 try:
343 try:
343 self._droppath(f)
344 self._droppath(f)
344 del self._map[f]
345 del self._map[f]
345 except KeyError:
346 except KeyError:
346 self._ui.warn(_("not in dirstate: %s\n") % f)
347 self._ui.warn(_("not in dirstate: %s\n") % f)
347
348
348 def _normalize(self, path):
349 def _normalize(self, path):
349 if path not in self._foldmap:
350 if path not in self._foldmap:
350 if not os.path.exists(path):
351 if not os.path.exists(path):
351 return path
352 return path
352 self._foldmap[path] = util.fspath(path, self._root)
353 self._foldmap[path] = util.fspath(path, self._root)
353 return self._foldmap[path]
354 return self._foldmap[path]
354
355
355 def clear(self):
356 def clear(self):
356 self._map = {}
357 self._map = {}
357 if "_dirs" in self.__dict__:
358 if "_dirs" in self.__dict__:
358 delattr(self, "_dirs");
359 delattr(self, "_dirs");
359 self._copymap = {}
360 self._copymap = {}
360 self._pl = [nullid, nullid]
361 self._pl = [nullid, nullid]
361 self._dirty = True
362 self._dirty = True
362
363
363 def rebuild(self, parent, files):
364 def rebuild(self, parent, files):
364 self.clear()
365 self.clear()
365 for f in files:
366 for f in files:
366 if 'x' in files.flags(f):
367 if 'x' in files.flags(f):
367 self._map[f] = ('n', 0777, -1, 0, 0)
368 self._map[f] = ('n', 0777, -1, 0, 0)
368 else:
369 else:
369 self._map[f] = ('n', 0666, -1, 0, 0)
370 self._map[f] = ('n', 0666, -1, 0, 0)
370 self._pl = (parent, nullid)
371 self._pl = (parent, nullid)
371 self._dirty = True
372 self._dirty = True
372
373
373 def write(self):
374 def write(self):
374 if not self._dirty:
375 if not self._dirty:
375 return
376 return
376 st = self._opener("dirstate", "w", atomictemp=True)
377 st = self._opener("dirstate", "w", atomictemp=True)
377
378
378 try:
379 try:
379 gran = int(self._ui.config('dirstate', 'granularity', 1))
380 gran = int(self._ui.config('dirstate', 'granularity', 1))
380 except ValueError:
381 except ValueError:
381 gran = 1
382 gran = 1
382 limit = sys.maxint
383 limit = sys.maxint
383 if gran > 0:
384 if gran > 0:
384 limit = util.fstat(st).st_mtime - gran
385 limit = util.fstat(st).st_mtime - gran
385
386
386 cs = cStringIO.StringIO()
387 cs = cStringIO.StringIO()
387 copymap = self._copymap
388 copymap = self._copymap
388 pack = struct.pack
389 pack = struct.pack
389 write = cs.write
390 write = cs.write
390 write("".join(self._pl))
391 write("".join(self._pl))
391 for f, e in self._map.iteritems():
392 for f, e in self._map.iteritems():
392 if f in copymap:
393 if f in copymap:
393 f = "%s\0%s" % (f, copymap[f])
394 f = "%s\0%s" % (f, copymap[f])
394 if e[3] > limit and e[0] == 'n':
395 if e[3] > limit and e[0] == 'n':
395 e = (e[0], 0, -1, -1, 0)
396 e = (e[0], 0, -1, -1, 0)
396 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
397 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
397 write(e)
398 write(e)
398 write(f)
399 write(f)
399 st.write(cs.getvalue())
400 st.write(cs.getvalue())
400 st.rename()
401 st.rename()
401 self._dirty = self._dirtypl = False
402 self._dirty = self._dirtypl = False
402
403
403 def _dirignore(self, f):
404 def _dirignore(self, f):
404 if f == '.':
405 if f == '.':
405 return False
406 return False
406 if self._ignore(f):
407 if self._ignore(f):
407 return True
408 return True
408 for p in _finddirs(f):
409 for p in _finddirs(f):
409 if self._ignore(p):
410 if self._ignore(p):
410 return True
411 return True
411 return False
412 return False
412
413
413 def walk(self, match, unknown, ignored):
414 def walk(self, match, unknown, ignored):
414 '''
415 '''
415 walk recursively through the directory tree, finding all files
416 walk recursively through the directory tree, finding all files
416 matched by the match function
417 matched by the match function
417
418
418 results are yielded in a tuple (filename, stat), where stat
419 results are yielded in a tuple (filename, stat), where stat
419 and st is the stat result if the file was found in the directory.
420 and st is the stat result if the file was found in the directory.
420 '''
421 '''
421
422
422 def fwarn(f, msg):
423 def fwarn(f, msg):
423 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
424 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
424 return False
425 return False
425 badfn = fwarn
426 badfn = fwarn
426 if hasattr(match, 'bad'):
427 if hasattr(match, 'bad'):
427 badfn = match.bad
428 badfn = match.bad
428
429
429 def badtype(f, mode):
430 def badtype(f, 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 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
437 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
437 % (self.pathto(f), kind))
438 % (self.pathto(f), kind))
438
439
439 ignore = self._ignore
440 ignore = self._ignore
440 dirignore = self._dirignore
441 dirignore = self._dirignore
441 if ignored:
442 if ignored:
442 ignore = util.never
443 ignore = util.never
443 dirignore = util.never
444 dirignore = util.never
444 elif not unknown:
445 elif not unknown:
445 # if unknown and ignored are False, skip step 2
446 # if unknown and ignored are False, skip step 2
446 ignore = util.always
447 ignore = util.always
447 dirignore = util.always
448 dirignore = util.always
448
449
449 matchfn = match.matchfn
450 matchfn = match.matchfn
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 bisect_left = bisect.bisect_left
456 bisect_left = bisect.bisect_left
456 pconvert = util.pconvert
457 pconvert = util.pconvert
457 getkind = stat.S_IFMT
458 getkind = stat.S_IFMT
458 dirkind = stat.S_IFDIR
459 dirkind = stat.S_IFDIR
459 regkind = stat.S_IFREG
460 regkind = stat.S_IFREG
460 lnkkind = stat.S_IFLNK
461 lnkkind = stat.S_IFLNK
461 join = self._join
462 join = self._join
462 work = []
463 work = []
463 wadd = work.append
464 wadd = work.append
464
465
465 files = util.unique(match.files())
466 files = util.unique(match.files())
466 if not files or '.' in files:
467 if not files or '.' in files:
467 files = ['']
468 files = ['']
468 results = {'.hg': None}
469 results = {'.hg': None}
469
470
470 # step 1: find all explicit files
471 # step 1: find all explicit files
471 for ff in util.sort(files):
472 for ff in util.sort(files):
472 nf = normalize(normpath(ff))
473 nf = normalize(normpath(ff))
473 if nf in results:
474 if nf in results:
474 continue
475 continue
475
476
476 try:
477 try:
477 st = lstat(join(nf))
478 st = lstat(join(nf))
478 kind = getkind(st.st_mode)
479 kind = getkind(st.st_mode)
479 if kind == dirkind:
480 if kind == dirkind:
480 if not dirignore(nf):
481 if not dirignore(nf):
481 wadd(nf)
482 wadd(nf)
482 elif kind == regkind or kind == lnkkind:
483 elif kind == regkind or kind == lnkkind:
483 results[nf] = st
484 results[nf] = st
484 else:
485 else:
485 badtype(ff, kind)
486 badtype(ff, kind)
486 if nf in dmap:
487 if nf in dmap:
487 results[nf] = None
488 results[nf] = None
488 except OSError, inst:
489 except OSError, inst:
489 keep = False
490 keep = False
490 prefix = nf + "/"
491 prefix = nf + "/"
491 for fn in dmap:
492 for fn in dmap:
492 if nf == fn or fn.startswith(prefix):
493 if nf == fn or fn.startswith(prefix):
493 keep = True
494 keep = True
494 break
495 break
495 if not keep:
496 if not keep:
496 if inst.errno != errno.ENOENT:
497 if inst.errno != errno.ENOENT:
497 fwarn(ff, inst.strerror)
498 fwarn(ff, inst.strerror)
498 elif badfn(ff, inst.strerror):
499 elif badfn(ff, inst.strerror):
499 if (nf in dmap or not ignore(nf)) and matchfn(nf):
500 if (nf in dmap or not ignore(nf)) and matchfn(nf):
500 results[nf] = None
501 results[nf] = None
501
502
502 # step 2: visit subdirectories
503 # step 2: visit subdirectories
503 while work:
504 while work:
504 nd = work.pop()
505 nd = work.pop()
505 if hasattr(match, 'dir'):
506 if hasattr(match, 'dir'):
506 match.dir(nd)
507 match.dir(nd)
507 entries = listdir(join(nd), stat=True)
508 entries = listdir(join(nd), stat=True)
508 if nd == '.':
509 if nd == '.':
509 nd = ''
510 nd = ''
510 else:
511 else:
511 # do not recurse into a repo contained in this
512 # do not recurse into a repo contained in this
512 # one. use bisect to find .hg directory so speed
513 # one. use bisect to find .hg directory so speed
513 # is good on big directory.
514 # is good on big directory.
514 hg = bisect_left(entries, ('.hg'))
515 hg = bisect_left(entries, ('.hg'))
515 if hg < len(entries) and entries[hg][0] == '.hg' \
516 if hg < len(entries) and entries[hg][0] == '.hg' \
516 and entries[hg][1] == dirkind:
517 and entries[hg][1] == dirkind:
517 continue
518 continue
518 for f, kind, st in entries:
519 for f, kind, st in entries:
519 nf = normalize(nd and (nd + "/" + f) or f)
520 nf = normalize(nd and (nd + "/" + f) or f)
520 if nf not in results:
521 if nf not in results:
521 if kind == dirkind:
522 if kind == dirkind:
522 if not ignore(nf):
523 if not ignore(nf):
523 wadd(nf)
524 wadd(nf)
524 if nf in dmap and matchfn(nf):
525 if nf in dmap and matchfn(nf):
525 results[nf] = None
526 results[nf] = None
526 elif kind == regkind or kind == lnkkind:
527 elif kind == regkind or kind == lnkkind:
527 if nf in dmap:
528 if nf in dmap:
528 if matchfn(nf):
529 if matchfn(nf):
529 results[nf] = st
530 results[nf] = st
530 elif matchfn(nf) and not ignore(nf):
531 elif matchfn(nf) and not ignore(nf):
531 results[nf] = st
532 results[nf] = st
532 elif nf in dmap and matchfn(nf):
533 elif nf in dmap and matchfn(nf):
533 results[nf] = None
534 results[nf] = None
534
535
535 # step 3: report unseen items in the dmap hash
536 # step 3: report unseen items in the dmap hash
536 visit = [f for f in dmap if f not in results and match(f)]
537 visit = [f for f in dmap if f not in results and match(f)]
537 for nf in util.sort(visit):
538 for nf in util.sort(visit):
538 results[nf] = None
539 results[nf] = None
539 try:
540 try:
540 st = lstat(join(nf))
541 st = lstat(join(nf))
541 kind = getkind(st.st_mode)
542 kind = getkind(st.st_mode)
542 if kind == regkind or kind == lnkkind:
543 if kind == regkind or kind == lnkkind:
543 results[nf] = st
544 results[nf] = st
544 except OSError, inst:
545 except OSError, inst:
545 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
546 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
546 raise
547 raise
547
548
548 del results['.hg']
549 del results['.hg']
549 return results
550 return results
550
551
551 def status(self, match, ignored, clean, unknown):
552 def status(self, match, ignored, clean, unknown):
552 listignored, listclean, listunknown = ignored, clean, unknown
553 listignored, listclean, listunknown = ignored, clean, unknown
553 lookup, modified, added, unknown, ignored = [], [], [], [], []
554 lookup, modified, added, unknown, ignored = [], [], [], [], []
554 removed, deleted, clean = [], [], []
555 removed, deleted, clean = [], [], []
555
556
556 _join = self._join
557 _join = self._join
557 lstat = os.lstat
558 lstat = os.lstat
558 cmap = self._copymap
559 cmap = self._copymap
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, foo = dmap[fn]
579 state, mode, size, time, foo = 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