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