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