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