##// END OF EJS Templates
Optimize dirstate walking...
mason@suse.com -
r1183:d9e85a75 default
parent child Browse files
Show More
@@ -22,6 +22,7 b' class dirstate:'
22 self.pl = None
22 self.pl = None
23 self.copies = {}
23 self.copies = {}
24 self.ignorefunc = None
24 self.ignorefunc = None
25 self.blockignore = False
25
26
26 def wjoin(self, f):
27 def wjoin(self, f):
27 return os.path.join(self.root, f)
28 return os.path.join(self.root, f)
@@ -32,6 +33,8 b' class dirstate:'
32 return cwd[len(self.root) + 1:]
33 return cwd[len(self.root) + 1:]
33
34
34 def ignore(self, f):
35 def ignore(self, f):
36 if self.blockignore:
37 return False
35 if not self.ignorefunc:
38 if not self.ignorefunc:
36 bigpat = []
39 bigpat = []
37 try:
40 try:
@@ -214,11 +217,55 b' class dirstate:'
214 elif not dc:
217 elif not dc:
215 dc = self.filterfiles(files)
218 dc = self.filterfiles(files)
216
219
220 def statmatch(file, stat):
221 if file not in dc and self.ignore(file):
222 return False
223 return match(file)
224 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
225
226 # walk recursively through the directory tree, finding all files
227 # matched by the statmatch function
228 #
229 # results are yielded in a tuple (src, filename), where src is one of:
230 # 'f' the file was found in the directory tree
231 # 'm' the file was only in the dirstate and not in the tree
232 #
233 # dc is an optional arg for the current dirstate. dc is not modified
234 # directly by this function, but might be modified by your statmatch call.
235 #
236 def walkhelper(self, files, statmatch, dc):
237 # recursion free walker, faster than os.walk.
238 def findfiles(s):
239 retfiles = []
240 work = [s]
241 while work:
242 top = work.pop()
243 names = os.listdir(top)
244 names.sort()
245 # nd is the top of the repository dir tree
246 nd = util.normpath(top[len(self.root) + 1:])
247 if nd == '.': nd = ''
248 for f in names:
249 np = os.path.join(nd, f)
250 if seen(np):
251 continue
252 p = os.path.join(top, f)
253 st = os.stat(p)
254 if stat.S_ISDIR(st.st_mode):
255 ds = os.path.join(nd, f +'/')
256 if statmatch(ds, st):
257 work.append(p)
258 else:
259 if statmatch(np, st):
260 yield np
261
217 known = {'.hg': 1}
262 known = {'.hg': 1}
218 def seen(fn):
263 def seen(fn):
219 if fn in known: return True
264 if fn in known: return True
220 known[fn] = 1
265 known[fn] = 1
221 def traverse():
266
267 # step one, find all files that match our criteria
268 files.sort()
222 for ff in util.unique(files):
269 for ff in util.unique(files):
223 f = os.path.join(self.root, ff)
270 f = os.path.join(self.root, ff)
224 try:
271 try:
@@ -229,23 +276,20 b' class dirstate:'
229 inst.strerror))
276 inst.strerror))
230 continue
277 continue
231 if stat.S_ISDIR(st.st_mode):
278 if stat.S_ISDIR(st.st_mode):
232 for dir, subdirs, fl in os.walk(f):
279 sorted = [ x for x in findfiles(f) ]
233 d = dir[len(self.root) + 1:]
280 sorted.sort()
234 nd = util.normpath(d)
281 for fl in sorted:
235 if nd == '.': nd = ''
282 yield 'f', fl
236 if seen(nd):
283 elif stat.S_ISREG(st.st_mode):
237 subdirs[:] = []
284 ff = util.normpath(ff)
285 if seen(ff):
238 continue
286 continue
239 for sd in subdirs:
287 found = False
240 ds = os.path.join(nd, sd +'/')
288 self.blockignore = True
241 if self.ignore(ds) or not match(ds):
289 if statmatch(ff, st):
242 subdirs.remove(sd)
290 found = True
243 subdirs.sort()
291 self.blockignore = False
244 fl.sort()
292 if found:
245 for fn in fl:
246 fn = util.pconvert(os.path.join(d, fn))
247 yield 'f', fn
248 elif stat.S_ISREG(st.st_mode):
249 yield 'f', ff
293 yield 'f', ff
250 else:
294 else:
251 kind = 'unknown'
295 kind = 'unknown'
@@ -258,54 +302,68 b' class dirstate:'
258 util.pathto(self.getcwd(), ff),
302 util.pathto(self.getcwd(), ff),
259 kind))
303 kind))
260
304
305 # step two run through anything left in the dc hash and yield
306 # if we haven't already seen it
261 ks = dc.keys()
307 ks = dc.keys()
262 ks.sort()
308 ks.sort()
263 for k in ks:
309 for k in ks:
310 if not seen(k) and (statmatch(k, None)):
264 yield 'm', k
311 yield 'm', k
265
312
266 # yield only files that match: all in dirstate, others only if
267 # not in .hgignore
268
269 for src, fn in util.unique(traverse()):
270 fn = util.normpath(fn)
271 if seen(fn): continue
272 if fn not in dc and self.ignore(fn):
273 continue
274 if match(fn):
275 yield src, fn
276
277 def changes(self, files=None, match=util.always):
313 def changes(self, files=None, match=util.always):
278 self.read()
314 self.read()
279 if not files:
315 if not files:
316 files = [self.root]
280 dc = self.map.copy()
317 dc = self.map.copy()
281 else:
318 else:
282 dc = self.filterfiles(files)
319 dc = self.filterfiles(files)
283 lookup, modified, added, unknown = [], [], [], []
320 lookup, modified, added, unknown = [], [], [], []
284 removed, deleted = [], []
321 removed, deleted = [], []
285
322
286 for src, fn in self.walk(files, match, dc=dc):
323 # statmatch function to eliminate entries from the dirstate copy
287 try:
324 # and put files into the appropriate array. This gets passed
288 s = os.stat(os.path.join(self.root, fn))
325 # to the walking code
289 except OSError:
326 def statmatch(fn, s):
290 continue
327 def checkappend(l, fn):
328 if match is util.always or match(fn):
329 l.append(fn)
330
331 if not s or stat.S_ISDIR(s.st_mode):
332 return self.ignore(fn) and False or match(fn)
333
291 if not stat.S_ISREG(s.st_mode):
334 if not stat.S_ISREG(s.st_mode):
292 continue
335 return False
293 c = dc.get(fn)
336 c = dc.pop(fn, None)
294 if c:
337 if c:
295 del dc[fn]
338 type, mode, size, time = c
296 if c[0] == 'm':
339 # check the common case first
297 modified.append(fn)
340 if type == 'n':
298 elif c[0] == 'a':
341 if size != s.st_size or (mode ^ s.st_mode) & 0100:
299 added.append(fn)
342 checkappend(modified, fn)
300 elif c[0] == 'r':
343 elif time != s.st_mtime:
344 checkappend(lookup, fn)
345 elif type == 'm':
346 checkappend(modified, fn)
347 elif type == 'a':
348 checkappend(added, fn)
349 elif type == 'r':
350 checkappend(unknown, fn)
351 else:
352 if not self.ignore(fn) and match(fn):
301 unknown.append(fn)
353 unknown.append(fn)
302 elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100:
354 # return false because we've already handled all cases above.
303 modified.append(fn)
355 # there's no need for the walking code to process the file
304 elif c[3] != s.st_mtime:
356 # any further.
305 lookup.append(fn)
357 return False
306 else:
307 unknown.append(fn)
308
358
359 # because our statmatch always returns false, self.walk will only
360 # return files in the dirstate map that are not present in the FS.
361 # But, we still need to iterate through the results to force the
362 # walk to complete
363 for src, fn in self.walkhelper(files, statmatch, dc):
364 pass
365
366 # anything left in dc didn't exist in the filesystem
309 for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]:
367 for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]:
310 if c[0] == 'r':
368 if c[0] == 'r':
311 removed.append(fn)
369 removed.append(fn)
General Comments 0
You need to be logged in to leave comments. Login now