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