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