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