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