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