##// END OF EJS Templates
dirstate: simplify/optimize path checking...
Matt Mackall -
r6767:80605a81 default
parent child Browse files
Show More
@@ -1,688 +1,670
1 1 """
2 2 dirstate.py - working directory tracking for mercurial
3 3
4 4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5
6 6 This software may be used and distributed according to the terms
7 7 of the GNU General Public License, incorporated herein by reference.
8 8 """
9 9
10 10 from node import nullid
11 11 from i18n import _
12 import struct, os, bisect, stat, strutil, util, errno, ignore
12 import struct, os, bisect, stat, util, errno, ignore
13 13 import cStringIO, osutil, sys
14 14
15 15 _unknown = ('?', 0, 0, 0)
16 16 _format = ">cllll"
17 17
18 def _finddirs(path):
19 pos = len(path)
20 while 1:
21 pos = path.rfind('/', 0, pos)
22 if pos == -1:
23 break
24 yield path[:pos]
25
18 26 class dirstate(object):
19 27
20 28 def __init__(self, opener, ui, root):
21 29 self._opener = opener
22 30 self._root = root
23 31 self._dirty = False
24 32 self._dirtypl = False
25 33 self._ui = ui
26 34
27 35 def __getattr__(self, name):
28 36 if name == '_map':
29 37 self._read()
30 38 return self._map
31 39 elif name == '_copymap':
32 40 self._read()
33 41 return self._copymap
34 42 elif name == '_foldmap':
35 43 _foldmap = {}
36 44 for name in self._map:
37 45 norm = os.path.normcase(os.path.normpath(name))
38 46 _foldmap[norm] = name
39 47 self._foldmap = _foldmap
40 48 return self._foldmap
41 49 elif name == '_branch':
42 50 try:
43 51 self._branch = (self._opener("branch").read().strip()
44 52 or "default")
45 53 except IOError:
46 54 self._branch = "default"
47 55 return self._branch
48 56 elif name == '_pl':
49 57 self._pl = [nullid, nullid]
50 58 try:
51 59 st = self._opener("dirstate").read(40)
52 60 if len(st) == 40:
53 61 self._pl = st[:20], st[20:40]
54 62 except IOError, err:
55 63 if err.errno != errno.ENOENT: raise
56 64 return self._pl
57 65 elif name == '_dirs':
58 self._dirs = {}
59 for f in self._map:
60 if self[f] != 'r':
61 self._incpath(f)
66 dirs = {}
67 for f,s in self._map.items():
68 if s[0] != 'r':
69 for base in _finddirs(f):
70 dirs[base] = dirs.get(base, 0) + 1
71 self._dirs = dirs
62 72 return self._dirs
63 73 elif name == '_ignore':
64 74 files = [self._join('.hgignore')]
65 75 for name, path in self._ui.configitems("ui"):
66 76 if name == 'ignore' or name.startswith('ignore.'):
67 77 files.append(os.path.expanduser(path))
68 78 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
69 79 return self._ignore
70 80 elif name == '_slash':
71 81 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
72 82 return self._slash
73 83 elif name == '_checklink':
74 84 self._checklink = util.checklink(self._root)
75 85 return self._checklink
76 86 elif name == '_checkexec':
77 87 self._checkexec = util.checkexec(self._root)
78 88 return self._checkexec
79 89 elif name == '_checkcase':
80 90 self._checkcase = not util.checkcase(self._join('.hg'))
81 91 return self._checkcase
82 92 elif name == 'normalize':
83 93 if self._checkcase:
84 94 self.normalize = self._normalize
85 95 else:
86 96 self.normalize = lambda x: x
87 97 return self.normalize
88 98 else:
89 99 raise AttributeError, name
90 100
91 101 def _join(self, f):
92 102 return os.path.join(self._root, f)
93 103
94 104 def flagfunc(self, fallback):
95 105 if self._checklink:
96 106 if self._checkexec:
97 107 def f(x):
98 108 p = os.path.join(self._root, x)
99 109 if os.path.islink(p):
100 110 return 'l'
101 111 if util.is_exec(p):
102 112 return 'x'
103 113 return ''
104 114 return f
105 115 def f(x):
106 116 if os.path.islink(os.path.join(self._root, x)):
107 117 return 'l'
108 118 if 'x' in fallback(x):
109 119 return 'x'
110 120 return ''
111 121 return f
112 122 if self._checkexec:
113 123 def f(x):
114 124 if 'l' in fallback(x):
115 125 return 'l'
116 126 if util.is_exec(os.path.join(self._root, x)):
117 127 return 'x'
118 128 return ''
119 129 return f
120 130 return fallback
121 131
122 132 def getcwd(self):
123 133 cwd = os.getcwd()
124 134 if cwd == self._root: return ''
125 135 # self._root ends with a path separator if self._root is '/' or 'C:\'
126 136 rootsep = self._root
127 137 if not util.endswithsep(rootsep):
128 138 rootsep += os.sep
129 139 if cwd.startswith(rootsep):
130 140 return cwd[len(rootsep):]
131 141 else:
132 142 # we're outside the repo. return an absolute path.
133 143 return cwd
134 144
135 145 def pathto(self, f, cwd=None):
136 146 if cwd is None:
137 147 cwd = self.getcwd()
138 148 path = util.pathto(self._root, cwd, f)
139 149 if self._slash:
140 150 return util.normpath(path)
141 151 return path
142 152
143 153 def __getitem__(self, key):
144 154 ''' current states:
145 155 n normal
146 156 m needs merging
147 157 r marked for removal
148 158 a marked for addition
149 159 ? not tracked'''
150 160 return self._map.get(key, ("?",))[0]
151 161
152 162 def __contains__(self, key):
153 163 return key in self._map
154 164
155 165 def __iter__(self):
156 166 for x in util.sort(self._map):
157 167 yield x
158 168
159 169 def parents(self):
160 170 return self._pl
161 171
162 172 def branch(self):
163 173 return self._branch
164 174
165 175 def setparents(self, p1, p2=nullid):
166 176 self._dirty = self._dirtypl = True
167 177 self._pl = p1, p2
168 178
169 179 def setbranch(self, branch):
170 180 self._branch = branch
171 181 self._opener("branch", "w").write(branch + '\n')
172 182
173 183 def _read(self):
174 184 self._map = {}
175 185 self._copymap = {}
176 186 if not self._dirtypl:
177 187 self._pl = [nullid, nullid]
178 188 try:
179 189 st = self._opener("dirstate").read()
180 190 except IOError, err:
181 191 if err.errno != errno.ENOENT: raise
182 192 return
183 193 if not st:
184 194 return
185 195
186 196 if not self._dirtypl:
187 197 self._pl = [st[:20], st[20: 40]]
188 198
189 199 # deref fields so they will be local in loop
190 200 dmap = self._map
191 201 copymap = self._copymap
192 202 unpack = struct.unpack
193 203 e_size = struct.calcsize(_format)
194 204 pos1 = 40
195 205 l = len(st)
196 206
197 207 # the inner loop
198 208 while pos1 < l:
199 209 pos2 = pos1 + e_size
200 210 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
201 211 pos1 = pos2 + e[4]
202 212 f = st[pos2:pos1]
203 213 if '\0' in f:
204 214 f, c = f.split('\0')
205 215 copymap[f] = c
206 216 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
207 217
208 218 def invalidate(self):
209 219 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
210 220 if a in self.__dict__:
211 221 delattr(self, a)
212 222 self._dirty = False
213 223
214 224 def copy(self, source, dest):
215 225 if source == dest:
216 226 return
217 227 self._dirty = True
218 228 self._copymap[dest] = source
219 229
220 230 def copied(self, file):
221 231 return self._copymap.get(file, None)
222 232
223 233 def copies(self):
224 234 return self._copymap
225 235
226 def _incpath(self, path):
227 c = path.rfind('/')
228 if c >= 0:
236 def _droppath(self, f):
237 if self[f] not in "?r" and "_dirs" in self.__dict__:
229 238 dirs = self._dirs
230 base = path[:c]
231 if base not in dirs:
232 self._incpath(base)
233 dirs[base] = 1
234 else:
235 dirs[base] += 1
236
237 def _decpath(self, path):
238 c = path.rfind('/')
239 if c >= 0:
240 base = path[:c]
241 dirs = self._dirs
242 if dirs[base] == 1:
243 del dirs[base]
244 self._decpath(base)
245 else:
246 dirs[base] -= 1
239 for base in _finddirs(f):
240 if dirs[base] == 1:
241 del dirs[base]
242 else:
243 dirs[base] -= 1
247 244
248 def _incpathcheck(self, f):
249 if '\r' in f or '\n' in f:
250 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r")
251 % f)
252 # shadows
253 if f in self._dirs:
254 raise util.Abort(_('directory %r already in dirstate') % f)
255 for c in strutil.rfindall(f, '/'):
256 d = f[:c]
257 if d in self._dirs:
258 break
259 if d in self._map and self[d] != 'r':
260 raise util.Abort(_('file %r in dirstate clashes with %r') %
261 (d, f))
262 self._incpath(f)
263
264 def _changepath(self, f, newstate, relaxed=False):
265 # handle upcoming path changes
245 def _addpath(self, f, check=False):
266 246 oldstate = self[f]
267 if oldstate not in "?r" and newstate in "?r":
268 if "_dirs" in self.__dict__:
269 self._decpath(f)
270 return
271 if oldstate in "?r" and newstate not in "?r":
272 if relaxed and oldstate == '?':
273 # XXX
274 # in relaxed mode we assume the caller knows
275 # what it is doing, workaround for updating
276 # dir-to-file revisions
277 if "_dirs" in self.__dict__:
278 self._incpath(f)
279 return
280 self._incpathcheck(f)
281 return
247 if check or oldstate == "r":
248 if '\r' in f or '\n' in f:
249 raise util.Abort(
250 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
251 if f in self._dirs:
252 raise util.Abort(_('directory %r already in dirstate') % f)
253 # shadows
254 for d in _finddirs(f):
255 if d in self._dirs:
256 break
257 if d in self._map and self[d] != 'r':
258 raise util.Abort(
259 _('file %r in dirstate clashes with %r') % (d, f))
260 if oldstate in "?r" and "_dirs" in self.__dict__:
261 dirs = self._dirs
262 for base in _finddirs(f):
263 dirs[base] = dirs.get(base, 0) + 1
282 264
283 265 def normal(self, f):
284 266 'mark a file normal and clean'
285 267 self._dirty = True
286 self._changepath(f, 'n', True)
268 self._addpath(f)
287 269 s = os.lstat(self._join(f))
288 270 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
289 271 if f in self._copymap:
290 272 del self._copymap[f]
291 273
292 274 def normallookup(self, f):
293 275 'mark a file normal, but possibly dirty'
294 276 if self._pl[1] != nullid and f in self._map:
295 277 # if there is a merge going on and the file was either
296 278 # in state 'm' or dirty before being removed, restore that state.
297 279 entry = self._map[f]
298 280 if entry[0] == 'r' and entry[2] in (-1, -2):
299 281 source = self._copymap.get(f)
300 282 if entry[2] == -1:
301 283 self.merge(f)
302 284 elif entry[2] == -2:
303 285 self.normaldirty(f)
304 286 if source:
305 287 self.copy(source, f)
306 288 return
307 289 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
308 290 return
309 291 self._dirty = True
310 self._changepath(f, 'n', True)
292 self._addpath(f)
311 293 self._map[f] = ('n', 0, -1, -1, 0)
312 294 if f in self._copymap:
313 295 del self._copymap[f]
314 296
315 297 def normaldirty(self, f):
316 298 'mark a file normal, but dirty'
317 299 self._dirty = True
318 self._changepath(f, 'n', True)
300 self._addpath(f)
319 301 self._map[f] = ('n', 0, -2, -1, 0)
320 302 if f in self._copymap:
321 303 del self._copymap[f]
322 304
323 305 def add(self, f):
324 306 'mark a file added'
325 307 self._dirty = True
326 self._changepath(f, 'a')
308 self._addpath(f, True)
327 309 self._map[f] = ('a', 0, -1, -1, 0)
328 310 if f in self._copymap:
329 311 del self._copymap[f]
330 312
331 313 def remove(self, f):
332 314 'mark a file removed'
333 315 self._dirty = True
334 self._changepath(f, 'r')
316 self._droppath(f)
335 317 size = 0
336 318 if self._pl[1] != nullid and f in self._map:
337 319 entry = self._map[f]
338 320 if entry[0] == 'm':
339 321 size = -1
340 322 elif entry[0] == 'n' and entry[2] == -2:
341 323 size = -2
342 324 self._map[f] = ('r', 0, size, 0, 0)
343 325 if size == 0 and f in self._copymap:
344 326 del self._copymap[f]
345 327
346 328 def merge(self, f):
347 329 'mark a file merged'
348 330 self._dirty = True
349 331 s = os.lstat(self._join(f))
350 self._changepath(f, 'm', True)
332 self._addpath(f)
351 333 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
352 334 if f in self._copymap:
353 335 del self._copymap[f]
354 336
355 337 def forget(self, f):
356 338 'forget a file'
357 339 self._dirty = True
358 340 try:
359 self._changepath(f, '?')
341 self._droppath('?')
360 342 del self._map[f]
361 343 except KeyError:
362 344 self._ui.warn(_("not in dirstate: %s\n") % f)
363 345
364 346 def _normalize(self, path):
365 347 normpath = os.path.normcase(os.path.normpath(path))
366 348 if normpath in self._foldmap:
367 349 return self._foldmap[normpath]
368 350 elif os.path.exists(path):
369 351 self._foldmap[normpath] = util.fspath(path, self._root)
370 352 return self._foldmap[normpath]
371 353 else:
372 354 return path
373 355
374 356 def clear(self):
375 357 self._map = {}
376 358 if "_dirs" in self.__dict__:
377 359 delattr(self, "_dirs");
378 360 self._copymap = {}
379 361 self._pl = [nullid, nullid]
380 362 self._dirty = True
381 363
382 364 def rebuild(self, parent, files):
383 365 self.clear()
384 366 for f in files:
385 367 if 'x' in files.flags(f):
386 368 self._map[f] = ('n', 0777, -1, 0, 0)
387 369 else:
388 370 self._map[f] = ('n', 0666, -1, 0, 0)
389 371 self._pl = (parent, nullid)
390 372 self._dirty = True
391 373
392 374 def write(self):
393 375 if not self._dirty:
394 376 return
395 377 st = self._opener("dirstate", "w", atomictemp=True)
396 378
397 379 try:
398 380 gran = int(self._ui.config('dirstate', 'granularity', 1))
399 381 except ValueError:
400 382 gran = 1
401 383 limit = sys.maxint
402 384 if gran > 0:
403 385 limit = util.fstat(st).st_mtime - gran
404 386
405 387 cs = cStringIO.StringIO()
406 388 copymap = self._copymap
407 389 pack = struct.pack
408 390 write = cs.write
409 391 write("".join(self._pl))
410 392 for f, e in self._map.iteritems():
411 393 if f in copymap:
412 394 f = "%s\0%s" % (f, copymap[f])
413 395 if e[3] > limit and e[0] == 'n':
414 396 e = (e[0], 0, -1, -1, 0)
415 397 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
416 398 write(e)
417 399 write(f)
418 400 st.write(cs.getvalue())
419 401 st.rename()
420 402 self._dirty = self._dirtypl = False
421 403
422 404 def _filter(self, files):
423 405 ret = {}
424 406 unknown = []
425 407
426 408 for x in files:
427 409 if x == '.':
428 410 return self._map.copy()
429 411 if x not in self._map:
430 412 unknown.append(x)
431 413 else:
432 414 ret[x] = self._map[x]
433 415
434 416 if not unknown:
435 417 return ret
436 418
437 419 b = util.sort(self._map)
438 420 blen = len(b)
439 421
440 422 for x in unknown:
441 423 bs = bisect.bisect(b, "%s%s" % (x, '/'))
442 424 while bs < blen:
443 425 s = b[bs]
444 426 if len(s) > len(x) and s.startswith(x):
445 427 ret[s] = self._map[s]
446 428 else:
447 429 break
448 430 bs += 1
449 431 return ret
450 432
451 433 def _supported(self, f, mode, verbose=False):
452 434 if stat.S_ISREG(mode) or stat.S_ISLNK(mode):
453 435 return True
454 436 if verbose:
455 437 kind = 'unknown'
456 438 if stat.S_ISCHR(mode): kind = _('character device')
457 439 elif stat.S_ISBLK(mode): kind = _('block device')
458 440 elif stat.S_ISFIFO(mode): kind = _('fifo')
459 441 elif stat.S_ISSOCK(mode): kind = _('socket')
460 442 elif stat.S_ISDIR(mode): kind = _('directory')
461 443 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
462 444 % (self.pathto(f), kind))
463 445 return False
464 446
465 447 def _dirignore(self, f):
466 448 if f == '.':
467 449 return False
468 450 if self._ignore(f):
469 451 return True
470 for c in strutil.findall(f, '/'):
471 if self._ignore(f[:c]):
452 for p in _finddirs(f):
453 if self._ignore(p):
472 454 return True
473 455 return False
474 456
475 457 def walk(self, match, unknown, ignored):
476 458 '''
477 459 walk recursively through the directory tree, finding all files
478 460 matched by the match function
479 461
480 462 results are yielded in a tuple (src, filename, st), where src
481 463 is one of:
482 464 'f' the file was found in the directory tree
483 465 'm' the file was only in the dirstate and not in the tree
484 466
485 467 and st is the stat result if the file was found in the directory.
486 468 '''
487 469
488 470 def fwarn(f, msg):
489 471 self._ui.warn('%s: %s\n' % (self.pathto(ff), msg))
490 472 return False
491 473 badfn = fwarn
492 474 if hasattr(match, 'bad'):
493 475 badfn = match.bad
494 476
495 477 # walk all files by default
496 478 files = match.files()
497 479 if not files:
498 480 files = ['.']
499 481 dc = self._map.copy()
500 482 else:
501 483 files = util.unique(files)
502 484 dc = self._filter(files)
503 485
504 486 def imatch(file_):
505 487 if file_ not in dc and self._ignore(file_):
506 488 return False
507 489 return match(file_)
508 490
509 491 # TODO: don't walk unknown directories if unknown and ignored are False
510 492 ignore = self._ignore
511 493 dirignore = self._dirignore
512 494 if ignored:
513 495 imatch = match
514 496 ignore = util.never
515 497 dirignore = util.never
516 498
517 499 # self._root may end with a path separator when self._root == '/'
518 500 common_prefix_len = len(self._root)
519 501 if not util.endswithsep(self._root):
520 502 common_prefix_len += 1
521 503
522 504 normpath = util.normpath
523 505 listdir = osutil.listdir
524 506 lstat = os.lstat
525 507 bisect_left = bisect.bisect_left
526 508 isdir = os.path.isdir
527 509 pconvert = util.pconvert
528 510 join = os.path.join
529 511 s_isdir = stat.S_ISDIR
530 512 supported = self._supported
531 513 _join = self._join
532 514 known = {'.hg': 1}
533 515
534 516 # recursion free walker, faster than os.walk.
535 517 def findfiles(s):
536 518 work = [s]
537 519 wadd = work.append
538 520 found = []
539 521 add = found.append
540 522 if hasattr(match, 'dir'):
541 523 match.dir(normpath(s[common_prefix_len:]))
542 524 while work:
543 525 top = work.pop()
544 526 entries = listdir(top, stat=True)
545 527 # nd is the top of the repository dir tree
546 528 nd = normpath(top[common_prefix_len:])
547 529 if nd == '.':
548 530 nd = ''
549 531 else:
550 532 # do not recurse into a repo contained in this
551 533 # one. use bisect to find .hg directory so speed
552 534 # is good on big directory.
553 535 names = [e[0] for e in entries]
554 536 hg = bisect_left(names, '.hg')
555 537 if hg < len(names) and names[hg] == '.hg':
556 538 if isdir(join(top, '.hg')):
557 539 continue
558 540 for f, kind, st in entries:
559 541 np = pconvert(join(nd, f))
560 542 nn = self.normalize(np)
561 543 if np in known:
562 544 continue
563 545 known[nn] = 1
564 546 p = join(top, f)
565 547 # don't trip over symlinks
566 548 if kind == stat.S_IFDIR:
567 549 if not ignore(np):
568 550 wadd(p)
569 551 if hasattr(match, 'dir'):
570 552 match.dir(np)
571 553 if np in dc and match(np):
572 554 add((nn, 'm', st))
573 555 elif imatch(np):
574 556 if supported(np, st.st_mode):
575 557 add((nn, 'f', st))
576 558 elif np in dc:
577 559 add((nn, 'm', st))
578 560 return util.sort(found)
579 561
580 562 # step one, find all files that match our criteria
581 563 for ff in util.sort(files):
582 564 nf = normpath(ff)
583 565 nn = self.normalize(nf)
584 566 f = _join(ff)
585 567 try:
586 568 st = lstat(f)
587 569 except OSError, inst:
588 570 found = False
589 571 for fn in dc:
590 572 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
591 573 found = True
592 574 break
593 575 if not found:
594 576 if inst.errno != errno.ENOENT:
595 577 fwarn(ff, inst.strerror)
596 578 elif badfn(ff, inst.strerror) and imatch(nf):
597 579 yield 'f', ff, None
598 580 continue
599 581 if s_isdir(st.st_mode):
600 582 if not dirignore(nf):
601 583 for f, src, st in findfiles(f):
602 584 yield src, f, st
603 585 else:
604 586 if nn in known:
605 587 continue
606 588 known[nn] = 1
607 589 if match(nf):
608 590 if supported(ff, st.st_mode, verbose=True):
609 591 yield 'f', self.normalize(nf), st
610 592 elif ff in dc:
611 593 yield 'm', nf, st
612 594
613 595 # step two run through anything left in the dc hash and yield
614 596 # if we haven't already seen it
615 597 for k in util.sort(dc):
616 598 if k in known:
617 599 continue
618 600 known[k] = 1
619 601 if imatch(k):
620 602 yield 'm', k, None
621 603
622 604 def status(self, match, ignored, clean, unknown):
623 605 listignored, listclean, listunknown = ignored, clean, unknown
624 606 lookup, modified, added, unknown, ignored = [], [], [], [], []
625 607 removed, deleted, clean = [], [], []
626 608
627 609 _join = self._join
628 610 lstat = os.lstat
629 611 cmap = self._copymap
630 612 dmap = self._map
631 613 ladd = lookup.append
632 614 madd = modified.append
633 615 aadd = added.append
634 616 uadd = unknown.append
635 617 iadd = ignored.append
636 618 radd = removed.append
637 619 dadd = deleted.append
638 620 cadd = clean.append
639 621
640 622 for src, fn, st in self.walk(match, listunknown, listignored):
641 623 if fn not in dmap:
642 624 if (listignored or match.exact(fn)) and self._dirignore(fn):
643 625 if listignored:
644 626 iadd(fn)
645 627 elif listunknown:
646 628 uadd(fn)
647 629 continue
648 630
649 631 state, mode, size, time, foo = dmap[fn]
650 632
651 633 if src == 'm':
652 634 nonexistent = True
653 635 if not st:
654 636 try:
655 637 st = lstat(_join(fn))
656 638 except OSError, inst:
657 639 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
658 640 raise
659 641 st = None
660 642 # We need to re-check that it is a valid file
661 643 if st and self._supported(fn, st.st_mode):
662 644 nonexistent = False
663 645 if nonexistent and state in "nma":
664 646 dadd(fn)
665 647 continue
666 648 # check the common case first
667 649 if state == 'n':
668 650 if not st:
669 651 st = lstat(_join(fn))
670 652 if (size >= 0 and
671 653 (size != st.st_size
672 654 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
673 655 or size == -2
674 656 or fn in self._copymap):
675 657 madd(fn)
676 658 elif time != int(st.st_mtime):
677 659 ladd(fn)
678 660 elif listclean:
679 661 cadd(fn)
680 662 elif state == 'm':
681 663 madd(fn)
682 664 elif state == 'a':
683 665 aadd(fn)
684 666 elif state == 'r':
685 667 radd(fn)
686 668
687 669 return (lookup, modified, added, removed, deleted, unknown, ignored,
688 670 clean)
General Comments 0
You need to be logged in to leave comments. Login now