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