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