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