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