|
1 # dirstate.py - working directory tracking for mercurial |
|
2 # |
|
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms of the |
|
6 # GNU General Public License version 2 or any later version. |
|
7 |
|
8 from node import nullid |
|
9 from i18n import _ |
|
10 import util, ignore, osutil, parsers |
|
11 import struct, os, stat, errno |
|
12 import cStringIO |
|
13 |
|
14 _format = ">cllll" |
|
15 propertycache = util.propertycache |
|
16 |
|
17 def _finddirs(path): |
|
18 pos = path.rfind('/') |
|
19 while pos != -1: |
|
20 yield path[:pos] |
|
21 pos = path.rfind('/', 0, pos) |
|
22 |
|
23 def _incdirs(dirs, path): |
|
24 for base in _finddirs(path): |
|
25 if base in dirs: |
|
26 dirs[base] += 1 |
|
27 return |
|
28 dirs[base] = 1 |
|
29 |
|
30 def _decdirs(dirs, path): |
|
31 for base in _finddirs(path): |
|
32 if dirs[base] > 1: |
|
33 dirs[base] -= 1 |
|
34 return |
|
35 del dirs[base] |
|
36 |
|
37 class dirstate(object): |
|
38 |
|
39 def __init__(self, opener, ui, root): |
|
40 '''Create a new dirstate object. |
|
41 |
|
42 opener is an open()-like callable that can be used to open the |
|
43 dirstate file; root is the root of the directory tracked by |
|
44 the dirstate. |
|
45 ''' |
|
46 self._opener = opener |
|
47 self._root = root |
|
48 self._rootdir = os.path.join(root, '') |
|
49 self._dirty = False |
|
50 self._dirtypl = False |
|
51 self._ui = ui |
|
52 |
|
53 @propertycache |
|
54 def _map(self): |
|
55 '''Return the dirstate contents as a map from filename to |
|
56 (state, mode, size, time).''' |
|
57 self._read() |
|
58 return self._map |
|
59 |
|
60 @propertycache |
|
61 def _copymap(self): |
|
62 self._read() |
|
63 return self._copymap |
|
64 |
|
65 @propertycache |
|
66 def _foldmap(self): |
|
67 f = {} |
|
68 for name in self._map: |
|
69 f[os.path.normcase(name)] = name |
|
70 return f |
|
71 |
|
72 @propertycache |
|
73 def _branch(self): |
|
74 try: |
|
75 return self._opener("branch").read().strip() or "default" |
|
76 except IOError: |
|
77 return "default" |
|
78 |
|
79 @propertycache |
|
80 def _pl(self): |
|
81 try: |
|
82 st = self._opener("dirstate").read(40) |
|
83 l = len(st) |
|
84 if l == 40: |
|
85 return st[:20], st[20:40] |
|
86 elif l > 0 and l < 40: |
|
87 raise util.Abort(_('working directory state appears damaged!')) |
|
88 except IOError, err: |
|
89 if err.errno != errno.ENOENT: |
|
90 raise |
|
91 return [nullid, nullid] |
|
92 |
|
93 @propertycache |
|
94 def _dirs(self): |
|
95 dirs = {} |
|
96 for f, s in self._map.iteritems(): |
|
97 if s[0] != 'r': |
|
98 _incdirs(dirs, f) |
|
99 return dirs |
|
100 |
|
101 @propertycache |
|
102 def _ignore(self): |
|
103 files = [self._join('.hgignore')] |
|
104 for name, path in self._ui.configitems("ui"): |
|
105 if name == 'ignore' or name.startswith('ignore.'): |
|
106 files.append(util.expandpath(path)) |
|
107 return ignore.ignore(self._root, files, self._ui.warn) |
|
108 |
|
109 @propertycache |
|
110 def _slash(self): |
|
111 return self._ui.configbool('ui', 'slash') and os.sep != '/' |
|
112 |
|
113 @propertycache |
|
114 def _checklink(self): |
|
115 return util.checklink(self._root) |
|
116 |
|
117 @propertycache |
|
118 def _checkexec(self): |
|
119 return util.checkexec(self._root) |
|
120 |
|
121 @propertycache |
|
122 def _checkcase(self): |
|
123 return not util.checkcase(self._join('.hg')) |
|
124 |
|
125 def _join(self, f): |
|
126 # much faster than os.path.join() |
|
127 # it's safe because f is always a relative path |
|
128 return self._rootdir + f |
|
129 |
|
130 def flagfunc(self, fallback): |
|
131 if self._checklink: |
|
132 if self._checkexec: |
|
133 def f(x): |
|
134 p = self._join(x) |
|
135 if os.path.islink(p): |
|
136 return 'l' |
|
137 if util.is_exec(p): |
|
138 return 'x' |
|
139 return '' |
|
140 return f |
|
141 def f(x): |
|
142 if os.path.islink(self._join(x)): |
|
143 return 'l' |
|
144 if 'x' in fallback(x): |
|
145 return 'x' |
|
146 return '' |
|
147 return f |
|
148 if self._checkexec: |
|
149 def f(x): |
|
150 if 'l' in fallback(x): |
|
151 return 'l' |
|
152 if util.is_exec(self._join(x)): |
|
153 return 'x' |
|
154 return '' |
|
155 return f |
|
156 return fallback |
|
157 |
|
158 def getcwd(self): |
|
159 cwd = os.getcwd() |
|
160 if cwd == self._root: |
|
161 return '' |
|
162 # self._root ends with a path separator if self._root is '/' or 'C:\' |
|
163 rootsep = self._root |
|
164 if not util.endswithsep(rootsep): |
|
165 rootsep += os.sep |
|
166 if cwd.startswith(rootsep): |
|
167 return cwd[len(rootsep):] |
|
168 else: |
|
169 # we're outside the repo. return an absolute path. |
|
170 return cwd |
|
171 |
|
172 def pathto(self, f, cwd=None): |
|
173 if cwd is None: |
|
174 cwd = self.getcwd() |
|
175 path = util.pathto(self._root, cwd, f) |
|
176 if self._slash: |
|
177 return util.normpath(path) |
|
178 return path |
|
179 |
|
180 def __getitem__(self, key): |
|
181 '''Return the current state of key (a filename) in the dirstate. |
|
182 |
|
183 States are: |
|
184 n normal |
|
185 m needs merging |
|
186 r marked for removal |
|
187 a marked for addition |
|
188 ? not tracked |
|
189 ''' |
|
190 return self._map.get(key, ("?",))[0] |
|
191 |
|
192 def __contains__(self, key): |
|
193 return key in self._map |
|
194 |
|
195 def __iter__(self): |
|
196 for x in sorted(self._map): |
|
197 yield x |
|
198 |
|
199 def parents(self): |
|
200 return self._pl |
|
201 |
|
202 def branch(self): |
|
203 return self._branch |
|
204 |
|
205 def setparents(self, p1, p2=nullid): |
|
206 self._dirty = self._dirtypl = True |
|
207 self._pl = p1, p2 |
|
208 |
|
209 def setbranch(self, branch): |
|
210 if branch in ['tip', '.', 'null']: |
|
211 raise util.Abort(_('the name \'%s\' is reserved') % branch) |
|
212 self._branch = branch |
|
213 self._opener("branch", "w").write(branch + '\n') |
|
214 |
|
215 def _read(self): |
|
216 self._map = {} |
|
217 self._copymap = {} |
|
218 try: |
|
219 st = self._opener("dirstate").read() |
|
220 except IOError, err: |
|
221 if err.errno != errno.ENOENT: |
|
222 raise |
|
223 return |
|
224 if not st: |
|
225 return |
|
226 |
|
227 p = parsers.parse_dirstate(self._map, self._copymap, st) |
|
228 if not self._dirtypl: |
|
229 self._pl = p |
|
230 |
|
231 def invalidate(self): |
|
232 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split(): |
|
233 if a in self.__dict__: |
|
234 delattr(self, a) |
|
235 self._dirty = False |
|
236 |
|
237 def copy(self, source, dest): |
|
238 """Mark dest as a copy of source. Unmark dest if source is None.""" |
|
239 if source == dest: |
|
240 return |
|
241 self._dirty = True |
|
242 if source is not None: |
|
243 self._copymap[dest] = source |
|
244 elif dest in self._copymap: |
|
245 del self._copymap[dest] |
|
246 |
|
247 def copied(self, file): |
|
248 return self._copymap.get(file, None) |
|
249 |
|
250 def copies(self): |
|
251 return self._copymap |
|
252 |
|
253 def _droppath(self, f): |
|
254 if self[f] not in "?r" and "_dirs" in self.__dict__: |
|
255 _decdirs(self._dirs, f) |
|
256 |
|
257 def _addpath(self, f, check=False): |
|
258 oldstate = self[f] |
|
259 if check or oldstate == "r": |
|
260 if '\r' in f or '\n' in f: |
|
261 raise util.Abort( |
|
262 _("'\\n' and '\\r' disallowed in filenames: %r") % f) |
|
263 if f in self._dirs: |
|
264 raise util.Abort(_('directory %r already in dirstate') % f) |
|
265 # shadows |
|
266 for d in _finddirs(f): |
|
267 if d in self._dirs: |
|
268 break |
|
269 if d in self._map and self[d] != 'r': |
|
270 raise util.Abort( |
|
271 _('file %r in dirstate clashes with %r') % (d, f)) |
|
272 if oldstate in "?r" and "_dirs" in self.__dict__: |
|
273 _incdirs(self._dirs, f) |
|
274 |
|
275 def normal(self, f): |
|
276 '''Mark a file normal and clean.''' |
|
277 self._dirty = True |
|
278 self._addpath(f) |
|
279 s = os.lstat(self._join(f)) |
|
280 self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime)) |
|
281 if f in self._copymap: |
|
282 del self._copymap[f] |
|
283 |
|
284 def normallookup(self, f): |
|
285 '''Mark a file normal, but possibly dirty.''' |
|
286 if self._pl[1] != nullid and f in self._map: |
|
287 # if there is a merge going on and the file was either |
|
288 # in state 'm' (-1) or coming from other parent (-2) before |
|
289 # being removed, restore that state. |
|
290 entry = self._map[f] |
|
291 if entry[0] == 'r' and entry[2] in (-1, -2): |
|
292 source = self._copymap.get(f) |
|
293 if entry[2] == -1: |
|
294 self.merge(f) |
|
295 elif entry[2] == -2: |
|
296 self.otherparent(f) |
|
297 if source: |
|
298 self.copy(source, f) |
|
299 return |
|
300 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2: |
|
301 return |
|
302 self._dirty = True |
|
303 self._addpath(f) |
|
304 self._map[f] = ('n', 0, -1, -1) |
|
305 if f in self._copymap: |
|
306 del self._copymap[f] |
|
307 |
|
308 def otherparent(self, f): |
|
309 '''Mark as coming from the other parent, always dirty.''' |
|
310 if self._pl[1] == nullid: |
|
311 raise util.Abort(_("setting %r to other parent " |
|
312 "only allowed in merges") % f) |
|
313 self._dirty = True |
|
314 self._addpath(f) |
|
315 self._map[f] = ('n', 0, -2, -1) |
|
316 if f in self._copymap: |
|
317 del self._copymap[f] |
|
318 |
|
319 def add(self, f): |
|
320 '''Mark a file added.''' |
|
321 self._dirty = True |
|
322 self._addpath(f, True) |
|
323 self._map[f] = ('a', 0, -1, -1) |
|
324 if f in self._copymap: |
|
325 del self._copymap[f] |
|
326 |
|
327 def remove(self, f): |
|
328 '''Mark a file removed.''' |
|
329 self._dirty = True |
|
330 self._droppath(f) |
|
331 size = 0 |
|
332 if self._pl[1] != nullid and f in self._map: |
|
333 # backup the previous state |
|
334 entry = self._map[f] |
|
335 if entry[0] == 'm': # merge |
|
336 size = -1 |
|
337 elif entry[0] == 'n' and entry[2] == -2: # other parent |
|
338 size = -2 |
|
339 self._map[f] = ('r', 0, size, 0) |
|
340 if size == 0 and f in self._copymap: |
|
341 del self._copymap[f] |
|
342 |
|
343 def merge(self, f): |
|
344 '''Mark a file merged.''' |
|
345 self._dirty = True |
|
346 s = os.lstat(self._join(f)) |
|
347 self._addpath(f) |
|
348 self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime)) |
|
349 if f in self._copymap: |
|
350 del self._copymap[f] |
|
351 |
|
352 def forget(self, f): |
|
353 '''Forget a file.''' |
|
354 self._dirty = True |
|
355 try: |
|
356 self._droppath(f) |
|
357 del self._map[f] |
|
358 except KeyError: |
|
359 self._ui.warn(_("not in dirstate: %s\n") % f) |
|
360 |
|
361 def _normalize(self, path, knownpath): |
|
362 norm_path = os.path.normcase(path) |
|
363 fold_path = self._foldmap.get(norm_path, None) |
|
364 if fold_path is None: |
|
365 if knownpath or not os.path.lexists(os.path.join(self._root, path)): |
|
366 fold_path = path |
|
367 else: |
|
368 fold_path = self._foldmap.setdefault(norm_path, |
|
369 util.fspath(path, self._root)) |
|
370 return fold_path |
|
371 |
|
372 def clear(self): |
|
373 self._map = {} |
|
374 if "_dirs" in self.__dict__: |
|
375 delattr(self, "_dirs") |
|
376 self._copymap = {} |
|
377 self._pl = [nullid, nullid] |
|
378 self._dirty = True |
|
379 |
|
380 def rebuild(self, parent, files): |
|
381 self.clear() |
|
382 for f in files: |
|
383 if 'x' in files.flags(f): |
|
384 self._map[f] = ('n', 0777, -1, 0) |
|
385 else: |
|
386 self._map[f] = ('n', 0666, -1, 0) |
|
387 self._pl = (parent, nullid) |
|
388 self._dirty = True |
|
389 |
|
390 def write(self): |
|
391 if not self._dirty: |
|
392 return |
|
393 st = self._opener("dirstate", "w", atomictemp=True) |
|
394 |
|
395 # use the modification time of the newly created temporary file as the |
|
396 # filesystem's notion of 'now' |
|
397 now = int(util.fstat(st).st_mtime) |
|
398 |
|
399 cs = cStringIO.StringIO() |
|
400 copymap = self._copymap |
|
401 pack = struct.pack |
|
402 write = cs.write |
|
403 write("".join(self._pl)) |
|
404 for f, e in self._map.iteritems(): |
|
405 if e[0] == 'n' and e[3] == now: |
|
406 # The file was last modified "simultaneously" with the current |
|
407 # write to dirstate (i.e. within the same second for file- |
|
408 # systems with a granularity of 1 sec). This commonly happens |
|
409 # for at least a couple of files on 'update'. |
|
410 # The user could change the file without changing its size |
|
411 # within the same second. Invalidate the file's stat data in |
|
412 # dirstate, forcing future 'status' calls to compare the |
|
413 # contents of the file. This prevents mistakenly treating such |
|
414 # files as clean. |
|
415 e = (e[0], 0, -1, -1) # mark entry as 'unset' |
|
416 self._map[f] = e |
|
417 |
|
418 if f in copymap: |
|
419 f = "%s\0%s" % (f, copymap[f]) |
|
420 e = pack(_format, e[0], e[1], e[2], e[3], len(f)) |
|
421 write(e) |
|
422 write(f) |
|
423 st.write(cs.getvalue()) |
|
424 st.rename() |
|
425 self._dirty = self._dirtypl = False |
|
426 |
|
427 def _dirignore(self, f): |
|
428 if f == '.': |
|
429 return False |
|
430 if self._ignore(f): |
|
431 return True |
|
432 for p in _finddirs(f): |
|
433 if self._ignore(p): |
|
434 return True |
|
435 return False |
|
436 |
|
437 def walk(self, match, subrepos, unknown, ignored): |
|
438 ''' |
|
439 Walk recursively through the directory tree, finding all files |
|
440 matched by match. |
|
441 |
|
442 Return a dict mapping filename to stat-like object (either |
|
443 mercurial.osutil.stat instance or return value of os.stat()). |
|
444 ''' |
|
445 |
|
446 def fwarn(f, msg): |
|
447 self._ui.warn('%s: %s\n' % (self.pathto(f), msg)) |
|
448 return False |
|
449 |
|
450 def badtype(mode): |
|
451 kind = _('unknown') |
|
452 if stat.S_ISCHR(mode): |
|
453 kind = _('character device') |
|
454 elif stat.S_ISBLK(mode): |
|
455 kind = _('block device') |
|
456 elif stat.S_ISFIFO(mode): |
|
457 kind = _('fifo') |
|
458 elif stat.S_ISSOCK(mode): |
|
459 kind = _('socket') |
|
460 elif stat.S_ISDIR(mode): |
|
461 kind = _('directory') |
|
462 return _('unsupported file type (type is %s)') % kind |
|
463 |
|
464 ignore = self._ignore |
|
465 dirignore = self._dirignore |
|
466 if ignored: |
|
467 ignore = util.never |
|
468 dirignore = util.never |
|
469 elif not unknown: |
|
470 # if unknown and ignored are False, skip step 2 |
|
471 ignore = util.always |
|
472 dirignore = util.always |
|
473 |
|
474 matchfn = match.matchfn |
|
475 badfn = match.bad |
|
476 dmap = self._map |
|
477 normpath = util.normpath |
|
478 listdir = osutil.listdir |
|
479 lstat = os.lstat |
|
480 getkind = stat.S_IFMT |
|
481 dirkind = stat.S_IFDIR |
|
482 regkind = stat.S_IFREG |
|
483 lnkkind = stat.S_IFLNK |
|
484 join = self._join |
|
485 work = [] |
|
486 wadd = work.append |
|
487 |
|
488 exact = skipstep3 = False |
|
489 if matchfn == match.exact: # match.exact |
|
490 exact = True |
|
491 dirignore = util.always # skip step 2 |
|
492 elif match.files() and not match.anypats(): # match.match, no patterns |
|
493 skipstep3 = True |
|
494 |
|
495 if self._checkcase: |
|
496 normalize = self._normalize |
|
497 skipstep3 = False |
|
498 else: |
|
499 normalize = lambda x, y: x |
|
500 |
|
501 files = sorted(match.files()) |
|
502 subrepos.sort() |
|
503 i, j = 0, 0 |
|
504 while i < len(files) and j < len(subrepos): |
|
505 subpath = subrepos[j] + "/" |
|
506 if not files[i].startswith(subpath): |
|
507 i += 1 |
|
508 continue |
|
509 while files and files[i].startswith(subpath): |
|
510 del files[i] |
|
511 j += 1 |
|
512 |
|
513 if not files or '.' in files: |
|
514 files = [''] |
|
515 results = dict.fromkeys(subrepos) |
|
516 results['.hg'] = None |
|
517 |
|
518 # step 1: find all explicit files |
|
519 for ff in files: |
|
520 nf = normalize(normpath(ff), False) |
|
521 if nf in results: |
|
522 continue |
|
523 |
|
524 try: |
|
525 st = lstat(join(nf)) |
|
526 kind = getkind(st.st_mode) |
|
527 if kind == dirkind: |
|
528 skipstep3 = False |
|
529 if nf in dmap: |
|
530 #file deleted on disk but still in dirstate |
|
531 results[nf] = None |
|
532 match.dir(nf) |
|
533 if not dirignore(nf): |
|
534 wadd(nf) |
|
535 elif kind == regkind or kind == lnkkind: |
|
536 results[nf] = st |
|
537 else: |
|
538 badfn(ff, badtype(kind)) |
|
539 if nf in dmap: |
|
540 results[nf] = None |
|
541 except OSError, inst: |
|
542 if nf in dmap: # does it exactly match a file? |
|
543 results[nf] = None |
|
544 else: # does it match a directory? |
|
545 prefix = nf + "/" |
|
546 for fn in dmap: |
|
547 if fn.startswith(prefix): |
|
548 match.dir(nf) |
|
549 skipstep3 = False |
|
550 break |
|
551 else: |
|
552 badfn(ff, inst.strerror) |
|
553 |
|
554 # step 2: visit subdirectories |
|
555 while work: |
|
556 nd = work.pop() |
|
557 skip = None |
|
558 if nd == '.': |
|
559 nd = '' |
|
560 else: |
|
561 skip = '.hg' |
|
562 try: |
|
563 entries = listdir(join(nd), stat=True, skip=skip) |
|
564 except OSError, inst: |
|
565 if inst.errno == errno.EACCES: |
|
566 fwarn(nd, inst.strerror) |
|
567 continue |
|
568 raise |
|
569 for f, kind, st in entries: |
|
570 nf = normalize(nd and (nd + "/" + f) or f, True) |
|
571 if nf not in results: |
|
572 if kind == dirkind: |
|
573 if not ignore(nf): |
|
574 match.dir(nf) |
|
575 wadd(nf) |
|
576 if nf in dmap and matchfn(nf): |
|
577 results[nf] = None |
|
578 elif kind == regkind or kind == lnkkind: |
|
579 if nf in dmap: |
|
580 if matchfn(nf): |
|
581 results[nf] = st |
|
582 elif matchfn(nf) and not ignore(nf): |
|
583 results[nf] = st |
|
584 elif nf in dmap and matchfn(nf): |
|
585 results[nf] = None |
|
586 |
|
587 # step 3: report unseen items in the dmap hash |
|
588 if not skipstep3 and not exact: |
|
589 visit = sorted([f for f in dmap if f not in results and matchfn(f)]) |
|
590 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])): |
|
591 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind): |
|
592 st = None |
|
593 results[nf] = st |
|
594 for s in subrepos: |
|
595 del results[s] |
|
596 del results['.hg'] |
|
597 return results |
|
598 |
|
599 def status(self, match, subrepos, ignored, clean, unknown): |
|
600 '''Determine the status of the working copy relative to the |
|
601 dirstate and return a tuple of lists (unsure, modified, added, |
|
602 removed, deleted, unknown, ignored, clean), where: |
|
603 |
|
604 unsure: |
|
605 files that might have been modified since the dirstate was |
|
606 written, but need to be read to be sure (size is the same |
|
607 but mtime differs) |
|
608 modified: |
|
609 files that have definitely been modified since the dirstate |
|
610 was written (different size or mode) |
|
611 added: |
|
612 files that have been explicitly added with hg add |
|
613 removed: |
|
614 files that have been explicitly removed with hg remove |
|
615 deleted: |
|
616 files that have been deleted through other means ("missing") |
|
617 unknown: |
|
618 files not in the dirstate that are not ignored |
|
619 ignored: |
|
620 files not in the dirstate that are ignored |
|
621 (by _dirignore()) |
|
622 clean: |
|
623 files that have definitely not been modified since the |
|
624 dirstate was written |
|
625 ''' |
|
626 listignored, listclean, listunknown = ignored, clean, unknown |
|
627 lookup, modified, added, unknown, ignored = [], [], [], [], [] |
|
628 removed, deleted, clean = [], [], [] |
|
629 |
|
630 dmap = self._map |
|
631 ladd = lookup.append # aka "unsure" |
|
632 madd = modified.append |
|
633 aadd = added.append |
|
634 uadd = unknown.append |
|
635 iadd = ignored.append |
|
636 radd = removed.append |
|
637 dadd = deleted.append |
|
638 cadd = clean.append |
|
639 |
|
640 lnkkind = stat.S_IFLNK |
|
641 |
|
642 for fn, st in self.walk(match, subrepos, listunknown, |
|
643 listignored).iteritems(): |
|
644 if fn not in dmap: |
|
645 if (listignored or match.exact(fn)) and self._dirignore(fn): |
|
646 if listignored: |
|
647 iadd(fn) |
|
648 elif listunknown: |
|
649 uadd(fn) |
|
650 continue |
|
651 |
|
652 state, mode, size, time = dmap[fn] |
|
653 |
|
654 if not st and state in "nma": |
|
655 dadd(fn) |
|
656 elif state == 'n': |
|
657 # The "mode & lnkkind != lnkkind or self._checklink" |
|
658 # lines are an expansion of "islink => checklink" |
|
659 # where islink means "is this a link?" and checklink |
|
660 # means "can we check links?". |
|
661 if (size >= 0 and |
|
662 (size != st.st_size |
|
663 or ((mode ^ st.st_mode) & 0100 and self._checkexec)) |
|
664 and (mode & lnkkind != lnkkind or self._checklink) |
|
665 or size == -2 # other parent |
|
666 or fn in self._copymap): |
|
667 madd(fn) |
|
668 elif (time != int(st.st_mtime) |
|
669 and (mode & lnkkind != lnkkind or self._checklink)): |
|
670 ladd(fn) |
|
671 elif listclean: |
|
672 cadd(fn) |
|
673 elif state == 'm': |
|
674 madd(fn) |
|
675 elif state == 'a': |
|
676 aadd(fn) |
|
677 elif state == 'r': |
|
678 radd(fn) |
|
679 |
|
680 return (lookup, modified, added, removed, deleted, unknown, ignored, |
|
681 clean) |