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