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