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