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