35 parsers = policy.importmod(r'parsers') |
35 parsers = policy.importmod(r'parsers') |
36 rustmod = policy.importrust(r'dirstate') |
36 rustmod = policy.importrust(r'dirstate') |
37 |
37 |
38 propertycache = util.propertycache |
38 propertycache = util.propertycache |
39 filecache = scmutil.filecache |
39 filecache = scmutil.filecache |
40 _rangemask = 0x7fffffff |
40 _rangemask = 0x7FFFFFFF |
41 |
41 |
42 dirstatetuple = parsers.dirstatetuple |
42 dirstatetuple = parsers.dirstatetuple |
|
43 |
43 |
44 |
44 class repocache(filecache): |
45 class repocache(filecache): |
45 """filecache for files in .hg/""" |
46 """filecache for files in .hg/""" |
|
47 |
46 def join(self, obj, fname): |
48 def join(self, obj, fname): |
47 return obj._opener.join(fname) |
49 return obj._opener.join(fname) |
48 |
50 |
|
51 |
49 class rootcache(filecache): |
52 class rootcache(filecache): |
50 """filecache for files in the repository root""" |
53 """filecache for files in the repository root""" |
|
54 |
51 def join(self, obj, fname): |
55 def join(self, obj, fname): |
52 return obj._join(fname) |
56 return obj._join(fname) |
|
57 |
53 |
58 |
54 def _getfsnow(vfs): |
59 def _getfsnow(vfs): |
55 '''Get "now" timestamp on filesystem''' |
60 '''Get "now" timestamp on filesystem''' |
56 tmpfd, tmpname = vfs.mkstemp() |
61 tmpfd, tmpname = vfs.mkstemp() |
57 try: |
62 try: |
58 return os.fstat(tmpfd)[stat.ST_MTIME] |
63 return os.fstat(tmpfd)[stat.ST_MTIME] |
59 finally: |
64 finally: |
60 os.close(tmpfd) |
65 os.close(tmpfd) |
61 vfs.unlink(tmpname) |
66 vfs.unlink(tmpname) |
62 |
67 |
|
68 |
63 @interfaceutil.implementer(intdirstate.idirstate) |
69 @interfaceutil.implementer(intdirstate.idirstate) |
64 class dirstate(object): |
70 class dirstate(object): |
65 |
|
66 def __init__(self, opener, ui, root, validate, sparsematchfn): |
71 def __init__(self, opener, ui, root, validate, sparsematchfn): |
67 '''Create a new dirstate object. |
72 '''Create a new dirstate object. |
68 |
73 |
69 opener is an open()-like callable that can be used to open the |
74 opener is an open()-like callable that can be used to open the |
70 dirstate file; root is the root of the directory tracked by |
75 dirstate file; root is the root of the directory tracked by |
181 # it's safe because f is always a relative path |
186 # it's safe because f is always a relative path |
182 return self._rootdir + f |
187 return self._rootdir + f |
183 |
188 |
184 def flagfunc(self, buildfallback): |
189 def flagfunc(self, buildfallback): |
185 if self._checklink and self._checkexec: |
190 if self._checklink and self._checkexec: |
|
191 |
186 def f(x): |
192 def f(x): |
187 try: |
193 try: |
188 st = os.lstat(self._join(x)) |
194 st = os.lstat(self._join(x)) |
189 if util.statislink(st): |
195 if util.statislink(st): |
190 return 'l' |
196 return 'l' |
191 if util.statisexec(st): |
197 if util.statisexec(st): |
192 return 'x' |
198 return 'x' |
193 except OSError: |
199 except OSError: |
194 pass |
200 pass |
195 return '' |
201 return '' |
|
202 |
196 return f |
203 return f |
197 |
204 |
198 fallback = buildfallback() |
205 fallback = buildfallback() |
199 if self._checklink: |
206 if self._checklink: |
|
207 |
200 def f(x): |
208 def f(x): |
201 if os.path.islink(self._join(x)): |
209 if os.path.islink(self._join(x)): |
202 return 'l' |
210 return 'l' |
203 if 'x' in fallback(x): |
211 if 'x' in fallback(x): |
204 return 'x' |
212 return 'x' |
205 return '' |
213 return '' |
|
214 |
206 return f |
215 return f |
207 if self._checkexec: |
216 if self._checkexec: |
|
217 |
208 def f(x): |
218 def f(x): |
209 if 'l' in fallback(x): |
219 if 'l' in fallback(x): |
210 return 'l' |
220 return 'l' |
211 if util.isexec(self._join(x)): |
221 if util.isexec(self._join(x)): |
212 return 'x' |
222 return 'x' |
213 return '' |
223 return '' |
|
224 |
214 return f |
225 return f |
215 else: |
226 else: |
216 return fallback |
227 return fallback |
217 |
228 |
218 @propertycache |
229 @propertycache |
236 # self._root ends with a path separator if self._root is '/' or 'C:\' |
247 # self._root ends with a path separator if self._root is '/' or 'C:\' |
237 rootsep = self._root |
248 rootsep = self._root |
238 if not util.endswithsep(rootsep): |
249 if not util.endswithsep(rootsep): |
239 rootsep += pycompat.ossep |
250 rootsep += pycompat.ossep |
240 if cwd.startswith(rootsep): |
251 if cwd.startswith(rootsep): |
241 return cwd[len(rootsep):] |
252 return cwd[len(rootsep) :] |
242 else: |
253 else: |
243 # we're outside the repo. return an absolute path. |
254 # we're outside the repo. return an absolute path. |
244 return cwd |
255 return cwd |
245 |
256 |
246 def pathto(self, f, cwd=None): |
257 def pathto(self, f, cwd=None): |
294 returned by the call. |
305 returned by the call. |
295 |
306 |
296 See localrepo.setparents() |
307 See localrepo.setparents() |
297 """ |
308 """ |
298 if self._parentwriters == 0: |
309 if self._parentwriters == 0: |
299 raise ValueError("cannot set dirstate parent outside of " |
310 raise ValueError( |
300 "dirstate.parentchange context manager") |
311 "cannot set dirstate parent outside of " |
|
312 "dirstate.parentchange context manager" |
|
313 ) |
301 |
314 |
302 self._dirty = True |
315 self._dirty = True |
303 oldp2 = self._pl[1] |
316 oldp2 = self._pl[1] |
304 if self._origpl is None: |
317 if self._origpl is None: |
305 self._origpl = self._pl |
318 self._origpl = self._pl |
306 self._map.setparents(p1, p2) |
319 self._map.setparents(p1, p2) |
307 copies = {} |
320 copies = {} |
308 if oldp2 != nullid and p2 == nullid: |
321 if oldp2 != nullid and p2 == nullid: |
309 candidatefiles = self._map.nonnormalset.union( |
322 candidatefiles = self._map.nonnormalset.union( |
310 self._map.otherparentset) |
323 self._map.otherparentset |
|
324 ) |
311 for f in candidatefiles: |
325 for f in candidatefiles: |
312 s = self._map.get(f) |
326 s = self._map.get(f) |
313 if s is None: |
327 if s is None: |
314 continue |
328 continue |
315 |
329 |
380 def _addpath(self, f, state, mode, size, mtime): |
394 def _addpath(self, f, state, mode, size, mtime): |
381 oldstate = self[f] |
395 oldstate = self[f] |
382 if state == 'a' or oldstate == 'r': |
396 if state == 'a' or oldstate == 'r': |
383 scmutil.checkfilename(f) |
397 scmutil.checkfilename(f) |
384 if self._map.hastrackeddir(f): |
398 if self._map.hastrackeddir(f): |
385 raise error.Abort(_('directory %r already in dirstate') % |
399 raise error.Abort( |
386 pycompat.bytestr(f)) |
400 _('directory %r already in dirstate') % pycompat.bytestr(f) |
|
401 ) |
387 # shadows |
402 # shadows |
388 for d in util.finddirs(f): |
403 for d in util.finddirs(f): |
389 if self._map.hastrackeddir(d): |
404 if self._map.hastrackeddir(d): |
390 break |
405 break |
391 entry = self._map.get(d) |
406 entry = self._map.get(d) |
392 if entry is not None and entry[0] != 'r': |
407 if entry is not None and entry[0] != 'r': |
393 raise error.Abort( |
408 raise error.Abort( |
394 _('file %r in dirstate clashes with %r') % |
409 _('file %r in dirstate clashes with %r') |
395 (pycompat.bytestr(d), pycompat.bytestr(f))) |
410 % (pycompat.bytestr(d), pycompat.bytestr(f)) |
|
411 ) |
396 self._dirty = True |
412 self._dirty = True |
397 self._updatedfiles.add(f) |
413 self._updatedfiles.add(f) |
398 self._map.addfile(f, oldstate, state, mode, size, mtime) |
414 self._map.addfile(f, oldstate, state, mode, size, mtime) |
399 |
415 |
400 def normal(self, f, parentfiledata=None): |
416 def normal(self, f, parentfiledata=None): |
447 self._map.copymap.pop(f, None) |
463 self._map.copymap.pop(f, None) |
448 |
464 |
449 def otherparent(self, f): |
465 def otherparent(self, f): |
450 '''Mark as coming from the other parent, always dirty.''' |
466 '''Mark as coming from the other parent, always dirty.''' |
451 if self._pl[1] == nullid: |
467 if self._pl[1] == nullid: |
452 raise error.Abort(_("setting %r to other parent " |
468 raise error.Abort( |
453 "only allowed in merges") % f) |
469 _("setting %r to other parent " "only allowed in merges") % f |
|
470 ) |
454 if f in self and self[f] == 'n': |
471 if f in self and self[f] == 'n': |
455 # merge-like |
472 # merge-like |
456 self._addpath(f, 'm', 0, -2, -1) |
473 self._addpath(f, 'm', 0, -2, -1) |
457 else: |
474 else: |
458 # add-like |
475 # add-like |
471 size = 0 |
488 size = 0 |
472 if self._pl[1] != nullid: |
489 if self._pl[1] != nullid: |
473 entry = self._map.get(f) |
490 entry = self._map.get(f) |
474 if entry is not None: |
491 if entry is not None: |
475 # backup the previous state |
492 # backup the previous state |
476 if entry[0] == 'm': # merge |
493 if entry[0] == 'm': # merge |
477 size = -1 |
494 size = -1 |
478 elif entry[0] == 'n' and entry[2] == -2: # other parent |
495 elif entry[0] == 'n' and entry[2] == -2: # other parent |
479 size = -2 |
496 size = -2 |
480 self._map.otherparentset.add(f) |
497 self._map.otherparentset.add(f) |
481 self._updatedfiles.add(f) |
498 self._updatedfiles.add(f) |
482 self._map.removefile(f, oldstate, size) |
499 self._map.removefile(f, oldstate, size) |
483 if size == 0: |
500 if size == 0: |
528 folded = self._map.filefoldmap.get(normed, None) |
545 folded = self._map.filefoldmap.get(normed, None) |
529 if folded is None: |
546 if folded is None: |
530 if isknown: |
547 if isknown: |
531 folded = path |
548 folded = path |
532 else: |
549 else: |
533 folded = self._discoverpath(path, normed, ignoremissing, exists, |
550 folded = self._discoverpath( |
534 self._map.filefoldmap) |
551 path, normed, ignoremissing, exists, self._map.filefoldmap |
|
552 ) |
535 return folded |
553 return folded |
536 |
554 |
537 def _normalize(self, path, isknown, ignoremissing=False, exists=None): |
555 def _normalize(self, path, isknown, ignoremissing=False, exists=None): |
538 normed = util.normcase(path) |
556 normed = util.normcase(path) |
539 folded = self._map.filefoldmap.get(normed, None) |
557 folded = self._map.filefoldmap.get(normed, None) |
543 if isknown: |
561 if isknown: |
544 folded = path |
562 folded = path |
545 else: |
563 else: |
546 # store discovered result in dirfoldmap so that future |
564 # store discovered result in dirfoldmap so that future |
547 # normalizefile calls don't start matching directories |
565 # normalizefile calls don't start matching directories |
548 folded = self._discoverpath(path, normed, ignoremissing, exists, |
566 folded = self._discoverpath( |
549 self._map.dirfoldmap) |
567 path, normed, ignoremissing, exists, self._map.dirfoldmap |
|
568 ) |
550 return folded |
569 return folded |
551 |
570 |
552 def normalize(self, path, isknown=False, ignoremissing=False): |
571 def normalize(self, path, isknown=False, ignoremissing=False): |
553 ''' |
572 ''' |
554 normalize the case of a pathname when on a casefolding filesystem |
573 normalize the case of a pathname when on a casefolding filesystem |
623 # emulate that all 'dirstate.normal' results are written out |
642 # emulate that all 'dirstate.normal' results are written out |
624 self._lastnormaltime = 0 |
643 self._lastnormaltime = 0 |
625 self._updatedfiles.clear() |
644 self._updatedfiles.clear() |
626 |
645 |
627 # delay writing in-memory changes out |
646 # delay writing in-memory changes out |
628 tr.addfilegenerator('dirstate', (self._filename,), |
647 tr.addfilegenerator( |
629 self._writedirstate, location='plain') |
648 'dirstate', |
|
649 (self._filename,), |
|
650 self._writedirstate, |
|
651 location='plain', |
|
652 ) |
630 return |
653 return |
631 |
654 |
632 st = self._opener(filename, "w", atomictemp=True, checkambig=True) |
655 st = self._opener(filename, "w", atomictemp=True, checkambig=True) |
633 self._writedirstate(st) |
656 self._writedirstate(st) |
634 |
657 |
659 if delaywrite > 0: |
682 if delaywrite > 0: |
660 # do we have any files to delay for? |
683 # do we have any files to delay for? |
661 items = self._map.iteritems() |
684 items = self._map.iteritems() |
662 for f, e in items: |
685 for f, e in items: |
663 if e[0] == 'n' and e[3] == now: |
686 if e[0] == 'n' and e[3] == now: |
664 import time # to avoid useless import |
687 import time # to avoid useless import |
|
688 |
665 # rather than sleep n seconds, sleep until the next |
689 # rather than sleep n seconds, sleep until the next |
666 # multiple of n seconds |
690 # multiple of n seconds |
667 clock = time.time() |
691 clock = time.time() |
668 start = int(clock) - (int(clock) % delaywrite) |
692 start = int(clock) - (int(clock) % delaywrite) |
669 end = start + delaywrite |
693 end = start + delaywrite |
670 time.sleep(end - clock) |
694 time.sleep(end - clock) |
671 now = end # trust our estimate that the end is near now |
695 now = end # trust our estimate that the end is near now |
672 break |
696 break |
673 # since the iterator is potentially not deleted, |
697 # since the iterator is potentially not deleted, |
674 # delete the iterator to release the reference for the Rust |
698 # delete the iterator to release the reference for the Rust |
675 # implementation. |
699 # implementation. |
676 # TODO make the Rust implementation behave like Python |
700 # TODO make the Rust implementation behave like Python |
703 def _ignorefileandline(self, f): |
727 def _ignorefileandline(self, f): |
704 files = collections.deque(self._ignorefiles()) |
728 files = collections.deque(self._ignorefiles()) |
705 visited = set() |
729 visited = set() |
706 while files: |
730 while files: |
707 i = files.popleft() |
731 i = files.popleft() |
708 patterns = matchmod.readpatternfile(i, self._ui.warn, |
732 patterns = matchmod.readpatternfile( |
709 sourceinfo=True) |
733 i, self._ui.warn, sourceinfo=True |
|
734 ) |
710 for pattern, lineno, line in patterns: |
735 for pattern, lineno, line in patterns: |
711 kind, p = matchmod._patsplit(pattern, 'glob') |
736 kind, p = matchmod._patsplit(pattern, 'glob') |
712 if kind == "subinclude": |
737 if kind == "subinclude": |
713 if p not in visited: |
738 if p not in visited: |
714 files.append(p) |
739 files.append(p) |
715 continue |
740 continue |
716 m = matchmod.match(self._root, '', [], [pattern], |
741 m = matchmod.match( |
717 warn=self._ui.warn) |
742 self._root, '', [], [pattern], warn=self._ui.warn |
|
743 ) |
718 if m(f): |
744 if m(f): |
719 return (i, lineno, line) |
745 return (i, lineno, line) |
720 visited.add(i) |
746 visited.add(i) |
721 return (None, -1, "") |
747 return (None, -1, "") |
722 |
748 |
850 paths.add(f) |
876 paths.add(f) |
851 |
877 |
852 for norm, paths in normed.iteritems(): |
878 for norm, paths in normed.iteritems(): |
853 if len(paths) > 1: |
879 if len(paths) > 1: |
854 for path in paths: |
880 for path in paths: |
855 folded = self._discoverpath(path, norm, True, None, |
881 folded = self._discoverpath( |
856 self._map.dirfoldmap) |
882 path, norm, True, None, self._map.dirfoldmap |
|
883 ) |
857 if path != folded: |
884 if path != folded: |
858 results[path] = None |
885 results[path] = None |
859 |
886 |
860 return results, dirsfound, dirsnotfound |
887 return results, dirsfound, dirsnotfound |
861 |
888 |
895 regkind = stat.S_IFREG |
922 regkind = stat.S_IFREG |
896 lnkkind = stat.S_IFLNK |
923 lnkkind = stat.S_IFLNK |
897 join = self._join |
924 join = self._join |
898 |
925 |
899 exact = skipstep3 = False |
926 exact = skipstep3 = False |
900 if match.isexact(): # match.exact |
927 if match.isexact(): # match.exact |
901 exact = True |
928 exact = True |
902 dirignore = util.always # skip step 2 |
929 dirignore = util.always # skip step 2 |
903 elif match.prefix(): # match.match, no patterns |
930 elif match.prefix(): # match.match, no patterns |
904 skipstep3 = True |
931 skipstep3 = True |
905 |
932 |
906 if not exact and self._checkcase: |
933 if not exact and self._checkcase: |
907 normalize = self._normalize |
934 normalize = self._normalize |
908 normalizefile = self._normalizefile |
935 normalizefile = self._normalizefile |
932 skip = '.hg' |
959 skip = '.hg' |
933 try: |
960 try: |
934 entries = listdir(join(nd), stat=True, skip=skip) |
961 entries = listdir(join(nd), stat=True, skip=skip) |
935 except OSError as inst: |
962 except OSError as inst: |
936 if inst.errno in (errno.EACCES, errno.ENOENT): |
963 if inst.errno in (errno.EACCES, errno.ENOENT): |
937 match.bad(self.pathto(nd), |
964 match.bad( |
938 encoding.strtolocal(inst.strerror)) |
965 self.pathto(nd), encoding.strtolocal(inst.strerror) |
|
966 ) |
939 continue |
967 continue |
940 raise |
968 raise |
941 for f, kind, st in entries: |
969 for f, kind, st in entries: |
942 # Some matchers may return files in the visitentries set, |
970 # Some matchers may return files in the visitentries set, |
943 # instead of 'this', if the matcher explicitly mentions them |
971 # instead of 'this', if the matcher explicitly mentions them |
951 continue |
979 continue |
952 if normalizefile: |
980 if normalizefile: |
953 # even though f might be a directory, we're only |
981 # even though f might be a directory, we're only |
954 # interested in comparing it to files currently in the |
982 # interested in comparing it to files currently in the |
955 # dmap -- therefore normalizefile is enough |
983 # dmap -- therefore normalizefile is enough |
956 nf = normalizefile(nd and (nd + "/" + f) or f, True, |
984 nf = normalizefile( |
957 True) |
985 nd and (nd + "/" + f) or f, True, True |
|
986 ) |
958 else: |
987 else: |
959 nf = nd and (nd + "/" + f) or f |
988 nf = nd and (nd + "/" + f) or f |
960 if nf not in results: |
989 if nf not in results: |
961 if kind == dirkind: |
990 if kind == dirkind: |
962 if not ignore(nf): |
991 if not ignore(nf): |
967 results[nf] = None |
996 results[nf] = None |
968 elif kind == regkind or kind == lnkkind: |
997 elif kind == regkind or kind == lnkkind: |
969 if nf in dmap: |
998 if nf in dmap: |
970 if matchalways or matchfn(nf): |
999 if matchalways or matchfn(nf): |
971 results[nf] = st |
1000 results[nf] = st |
972 elif ((matchalways or matchfn(nf)) |
1001 elif (matchalways or matchfn(nf)) and not ignore( |
973 and not ignore(nf)): |
1002 nf |
|
1003 ): |
974 # unknown file -- normalize if necessary |
1004 # unknown file -- normalize if necessary |
975 if not alreadynormed: |
1005 if not alreadynormed: |
976 nf = normalize(nf, False, True) |
1006 nf = normalize(nf, False, True) |
977 results[nf] = st |
1007 results[nf] = st |
978 elif nf in dmap and (matchalways or matchfn(nf)): |
1008 elif nf in dmap and (matchalways or matchfn(nf)): |
1009 for nf in iter(visit): |
1039 for nf in iter(visit): |
1010 # If a stat for the same file was already added with a |
1040 # If a stat for the same file was already added with a |
1011 # different case, don't add one for this, since that would |
1041 # different case, don't add one for this, since that would |
1012 # make it appear as if the file exists under both names |
1042 # make it appear as if the file exists under both names |
1013 # on disk. |
1043 # on disk. |
1014 if (normalizefile and |
1044 if ( |
1015 normalizefile(nf, True, True) in results): |
1045 normalizefile |
|
1046 and normalizefile(nf, True, True) in results |
|
1047 ): |
1016 results[nf] = None |
1048 results[nf] = None |
1017 # Report ignored items in the dmap as long as they are not |
1049 # Report ignored items in the dmap as long as they are not |
1018 # under a symlink directory. |
1050 # under a symlink directory. |
1019 elif audit_path.check(nf): |
1051 elif audit_path.check(nf): |
1020 try: |
1052 try: |
1057 |
1089 |
1058 dmap = self._map |
1090 dmap = self._map |
1059 dmap.preload() |
1091 dmap.preload() |
1060 dcontains = dmap.__contains__ |
1092 dcontains = dmap.__contains__ |
1061 dget = dmap.__getitem__ |
1093 dget = dmap.__getitem__ |
1062 ladd = lookup.append # aka "unsure" |
1094 ladd = lookup.append # aka "unsure" |
1063 madd = modified.append |
1095 madd = modified.append |
1064 aadd = added.append |
1096 aadd = added.append |
1065 uadd = unknown.append |
1097 uadd = unknown.append |
1066 iadd = ignored.append |
1098 iadd = ignored.append |
1067 radd = removed.append |
1099 radd = removed.append |
1076 # We need to do full walks when either |
1108 # We need to do full walks when either |
1077 # - we're listing all clean files, or |
1109 # - we're listing all clean files, or |
1078 # - match.traversedir does something, because match.traversedir should |
1110 # - match.traversedir does something, because match.traversedir should |
1079 # be called for every dir in the working dir |
1111 # be called for every dir in the working dir |
1080 full = listclean or match.traversedir is not None |
1112 full = listclean or match.traversedir is not None |
1081 for fn, st in self.walk(match, subrepos, listunknown, listignored, |
1113 for fn, st in self.walk( |
1082 full=full).iteritems(): |
1114 match, subrepos, listunknown, listignored, full=full |
|
1115 ).iteritems(): |
1083 if not dcontains(fn): |
1116 if not dcontains(fn): |
1084 if (listignored or mexact(fn)) and dirignore(fn): |
1117 if (listignored or mexact(fn)) and dirignore(fn): |
1085 if listignored: |
1118 if listignored: |
1086 iadd(fn) |
1119 iadd(fn) |
1087 else: |
1120 else: |
1102 time = t[3] |
1135 time = t[3] |
1103 |
1136 |
1104 if not st and state in "nma": |
1137 if not st and state in "nma": |
1105 dadd(fn) |
1138 dadd(fn) |
1106 elif state == 'n': |
1139 elif state == 'n': |
1107 if (size >= 0 and |
1140 if ( |
1108 ((size != st.st_size and size != st.st_size & _rangemask) |
1141 size >= 0 |
1109 or ((mode ^ st.st_mode) & 0o100 and checkexec)) |
1142 and ( |
1110 or size == -2 # other parent |
1143 (size != st.st_size and size != st.st_size & _rangemask) |
1111 or fn in copymap): |
1144 or ((mode ^ st.st_mode) & 0o100 and checkexec) |
|
1145 ) |
|
1146 or size == -2 # other parent |
|
1147 or fn in copymap |
|
1148 ): |
1112 madd(fn) |
1149 madd(fn) |
1113 elif (time != st[stat.ST_MTIME] |
1150 elif ( |
1114 and time != st[stat.ST_MTIME] & _rangemask): |
1151 time != st[stat.ST_MTIME] |
|
1152 and time != st[stat.ST_MTIME] & _rangemask |
|
1153 ): |
1115 ladd(fn) |
1154 ladd(fn) |
1116 elif st[stat.ST_MTIME] == lastnormaltime: |
1155 elif st[stat.ST_MTIME] == lastnormaltime: |
1117 # fn may have just been marked as normal and it may have |
1156 # fn may have just been marked as normal and it may have |
1118 # changed in the same second without changing its size. |
1157 # changed in the same second without changing its size. |
1119 # This can happen if we quickly do multiple commits. |
1158 # This can happen if we quickly do multiple commits. |
1126 elif state == 'a': |
1165 elif state == 'a': |
1127 aadd(fn) |
1166 aadd(fn) |
1128 elif state == 'r': |
1167 elif state == 'r': |
1129 radd(fn) |
1168 radd(fn) |
1130 |
1169 |
1131 return (lookup, scmutil.status(modified, added, removed, deleted, |
1170 return ( |
1132 unknown, ignored, clean)) |
1171 lookup, |
|
1172 scmutil.status( |
|
1173 modified, added, removed, deleted, unknown, ignored, clean |
|
1174 ), |
|
1175 ) |
1133 |
1176 |
1134 def matches(self, match): |
1177 def matches(self, match): |
1135 ''' |
1178 ''' |
1136 return files in the dirstate (in whatever state) filtered by match |
1179 return files in the dirstate (in whatever state) filtered by match |
1137 ''' |
1180 ''' |
1162 |
1205 |
1163 # use '_writedirstate' instead of 'write' to write changes certainly, |
1206 # use '_writedirstate' instead of 'write' to write changes certainly, |
1164 # because the latter omits writing out if transaction is running. |
1207 # because the latter omits writing out if transaction is running. |
1165 # output file will be used to create backup of dirstate at this point. |
1208 # output file will be used to create backup of dirstate at this point. |
1166 if self._dirty or not self._opener.exists(filename): |
1209 if self._dirty or not self._opener.exists(filename): |
1167 self._writedirstate(self._opener(filename, "w", atomictemp=True, |
1210 self._writedirstate( |
1168 checkambig=True)) |
1211 self._opener(filename, "w", atomictemp=True, checkambig=True) |
|
1212 ) |
1169 |
1213 |
1170 if tr: |
1214 if tr: |
1171 # ensure that subsequent tr.writepending returns True for |
1215 # ensure that subsequent tr.writepending returns True for |
1172 # changes written out above, even if dirstate is never |
1216 # changes written out above, even if dirstate is never |
1173 # changed after this |
1217 # changed after this |
1174 tr.addfilegenerator('dirstate', (self._filename,), |
1218 tr.addfilegenerator( |
1175 self._writedirstate, location='plain') |
1219 'dirstate', |
|
1220 (self._filename,), |
|
1221 self._writedirstate, |
|
1222 location='plain', |
|
1223 ) |
1176 |
1224 |
1177 # ensure that pending file written above is unlinked at |
1225 # ensure that pending file written above is unlinked at |
1178 # failure, even if tr.writepending isn't invoked until the |
1226 # failure, even if tr.writepending isn't invoked until the |
1179 # end of this transaction |
1227 # end of this transaction |
1180 tr.registertmp(filename, location='plain') |
1228 tr.registertmp(filename, location='plain') |
1181 |
1229 |
1182 self._opener.tryunlink(backupname) |
1230 self._opener.tryunlink(backupname) |
1183 # hardlink backup is okay because _writedirstate is always called |
1231 # hardlink backup is okay because _writedirstate is always called |
1184 # with an "atomictemp=True" file. |
1232 # with an "atomictemp=True" file. |
1185 util.copyfile(self._opener.join(filename), |
1233 util.copyfile( |
1186 self._opener.join(backupname), hardlink=True) |
1234 self._opener.join(filename), |
|
1235 self._opener.join(backupname), |
|
1236 hardlink=True, |
|
1237 ) |
1187 |
1238 |
1188 def restorebackup(self, tr, backupname): |
1239 def restorebackup(self, tr, backupname): |
1189 '''Restore dirstate by backup file''' |
1240 '''Restore dirstate by backup file''' |
1190 # this "invalidate()" prevents "wlock.release()" from writing |
1241 # this "invalidate()" prevents "wlock.release()" from writing |
1191 # changes of dirstate out after restoring from backup file |
1242 # changes of dirstate out after restoring from backup file |
1198 o.rename(backupname, filename, checkambig=True) |
1249 o.rename(backupname, filename, checkambig=True) |
1199 |
1250 |
1200 def clearbackup(self, tr, backupname): |
1251 def clearbackup(self, tr, backupname): |
1201 '''Clear backup file''' |
1252 '''Clear backup file''' |
1202 self._opener.unlink(backupname) |
1253 self._opener.unlink(backupname) |
|
1254 |
1203 |
1255 |
1204 class dirstatemap(object): |
1256 class dirstatemap(object): |
1205 """Map encapsulating the dirstate's contents. |
1257 """Map encapsulating the dirstate's contents. |
1206 |
1258 |
1207 The dirstate contains the following state: |
1259 The dirstate contains the following state: |
1374 try: |
1426 try: |
1375 makefilefoldmap = parsers.make_file_foldmap |
1427 makefilefoldmap = parsers.make_file_foldmap |
1376 except AttributeError: |
1428 except AttributeError: |
1377 pass |
1429 pass |
1378 else: |
1430 else: |
1379 return makefilefoldmap(self._map, util.normcasespec, |
1431 return makefilefoldmap( |
1380 util.normcasefallback) |
1432 self._map, util.normcasespec, util.normcasefallback |
|
1433 ) |
1381 |
1434 |
1382 f = {} |
1435 f = {} |
1383 normcase = util.normcase |
1436 normcase = util.normcase |
1384 for name, s in self._map.iteritems(): |
1437 for name, s in self._map.iteritems(): |
1385 if s[0] != 'r': |
1438 if s[0] != 'r': |
1386 f[normcase(name)] = name |
1439 f[normcase(name)] = name |
1387 f['.'] = '.' # prevents useless util.fspath() invocation |
1440 f['.'] = '.' # prevents useless util.fspath() invocation |
1388 return f |
1441 return f |
1389 |
1442 |
1390 def hastrackeddir(self, d): |
1443 def hastrackeddir(self, d): |
1391 """ |
1444 """ |
1392 Returns True if the dirstate contains a tracked (not removed) file |
1445 Returns True if the dirstate contains a tracked (not removed) file |
1411 |
1464 |
1412 def _opendirstatefile(self): |
1465 def _opendirstatefile(self): |
1413 fp, mode = txnutil.trypending(self._root, self._opener, self._filename) |
1466 fp, mode = txnutil.trypending(self._root, self._opener, self._filename) |
1414 if self._pendingmode is not None and self._pendingmode != mode: |
1467 if self._pendingmode is not None and self._pendingmode != mode: |
1415 fp.close() |
1468 fp.close() |
1416 raise error.Abort(_('working directory state may be ' |
1469 raise error.Abort( |
1417 'changed parallelly')) |
1470 _('working directory state may be ' 'changed parallelly') |
|
1471 ) |
1418 self._pendingmode = mode |
1472 self._pendingmode = mode |
1419 return fp |
1473 return fp |
1420 |
1474 |
1421 def parents(self): |
1475 def parents(self): |
1422 if not self._parents: |
1476 if not self._parents: |
1434 if l == 40: |
1488 if l == 40: |
1435 self._parents = (st[:20], st[20:40]) |
1489 self._parents = (st[:20], st[20:40]) |
1436 elif l == 0: |
1490 elif l == 0: |
1437 self._parents = (nullid, nullid) |
1491 self._parents = (nullid, nullid) |
1438 else: |
1492 else: |
1439 raise error.Abort(_('working directory state appears ' |
1493 raise error.Abort( |
1440 'damaged!')) |
1494 _('working directory state appears ' 'damaged!') |
|
1495 ) |
1441 |
1496 |
1442 return self._parents |
1497 return self._parents |
1443 |
1498 |
1444 def setparents(self, p1, p2): |
1499 def setparents(self, p1, p2): |
1445 self._parents = (p1, p2) |
1500 self._parents = (p1, p2) |
1446 self._dirtyparents = True |
1501 self._dirtyparents = True |
1447 |
1502 |
1448 def read(self): |
1503 def read(self): |
1449 # ignore HG_PENDING because identity is used only for writing |
1504 # ignore HG_PENDING because identity is used only for writing |
1450 self.identity = util.filestat.frompath( |
1505 self.identity = util.filestat.frompath( |
1451 self._opener.join(self._filename)) |
1506 self._opener.join(self._filename) |
|
1507 ) |
1452 |
1508 |
1453 try: |
1509 try: |
1454 fp = self._opendirstatefile() |
1510 fp = self._opendirstatefile() |
1455 try: |
1511 try: |
1456 st = fp.read() |
1512 st = fp.read() |
1497 self.__contains__ = self._map.__contains__ |
1553 self.__contains__ = self._map.__contains__ |
1498 self.__getitem__ = self._map.__getitem__ |
1554 self.__getitem__ = self._map.__getitem__ |
1499 self.get = self._map.get |
1555 self.get = self._map.get |
1500 |
1556 |
1501 def write(self, st, now): |
1557 def write(self, st, now): |
1502 st.write(parsers.pack_dirstate(self._map, self.copymap, |
1558 st.write( |
1503 self.parents(), now)) |
1559 parsers.pack_dirstate(self._map, self.copymap, self.parents(), now) |
|
1560 ) |
1504 st.close() |
1561 st.close() |
1505 self._dirtyparents = False |
1562 self._dirtyparents = False |
1506 self.nonnormalset, self.otherparentset = self.nonnormalentries() |
1563 self.nonnormalset, self.otherparentset = self.nonnormalentries() |
1507 |
1564 |
1508 @propertycache |
1565 @propertycache |
1602 |
1660 |
1603 # forward for python2,3 compat |
1661 # forward for python2,3 compat |
1604 iteritems = items |
1662 iteritems = items |
1605 |
1663 |
1606 def _opendirstatefile(self): |
1664 def _opendirstatefile(self): |
1607 fp, mode = txnutil.trypending(self._root, self._opener, |
1665 fp, mode = txnutil.trypending( |
1608 self._filename) |
1666 self._root, self._opener, self._filename |
|
1667 ) |
1609 if self._pendingmode is not None and self._pendingmode != mode: |
1668 if self._pendingmode is not None and self._pendingmode != mode: |
1610 fp.close() |
1669 fp.close() |
1611 raise error.Abort(_('working directory state may be ' |
1670 raise error.Abort( |
1612 'changed parallelly')) |
1671 _('working directory state may be ' 'changed parallelly') |
|
1672 ) |
1613 self._pendingmode = mode |
1673 self._pendingmode = mode |
1614 return fp |
1674 return fp |
1615 |
1675 |
1616 def setparents(self, p1, p2): |
1676 def setparents(self, p1, p2): |
1617 self._rustmap.setparents(p1, p2) |
1677 self._rustmap.setparents(p1, p2) |
1631 st = '' |
1691 st = '' |
1632 |
1692 |
1633 try: |
1693 try: |
1634 self._parents = self._rustmap.parents(st) |
1694 self._parents = self._rustmap.parents(st) |
1635 except ValueError: |
1695 except ValueError: |
1636 raise error.Abort(_('working directory state appears ' |
1696 raise error.Abort( |
1637 'damaged!')) |
1697 _('working directory state appears ' 'damaged!') |
|
1698 ) |
1638 |
1699 |
1639 return self._parents |
1700 return self._parents |
1640 |
1701 |
1641 def read(self): |
1702 def read(self): |
1642 # ignore HG_PENDING because identity is used only for writing |
1703 # ignore HG_PENDING because identity is used only for writing |
1643 self.identity = util.filestat.frompath( |
1704 self.identity = util.filestat.frompath( |
1644 self._opener.join(self._filename)) |
1705 self._opener.join(self._filename) |
|
1706 ) |
1645 |
1707 |
1646 try: |
1708 try: |
1647 fp = self._opendirstatefile() |
1709 fp = self._opendirstatefile() |
1648 try: |
1710 try: |
1649 st = fp.read() |
1711 st = fp.read() |
1673 non-normalized versions. |
1735 non-normalized versions. |
1674 """ |
1736 """ |
1675 return self._rustmap.filefoldmapasdict() |
1737 return self._rustmap.filefoldmapasdict() |
1676 |
1738 |
1677 def hastrackeddir(self, d): |
1739 def hastrackeddir(self, d): |
1678 self._dirs # Trigger Python's propertycache |
1740 self._dirs # Trigger Python's propertycache |
1679 return self._rustmap.hastrackeddir(d) |
1741 return self._rustmap.hastrackeddir(d) |
1680 |
1742 |
1681 def hasdir(self, d): |
1743 def hasdir(self, d): |
1682 self._dirs # Trigger Python's propertycache |
1744 self._dirs # Trigger Python's propertycache |
1683 return self._rustmap.hasdir(d) |
1745 return self._rustmap.hasdir(d) |
1684 |
1746 |
1685 @propertycache |
1747 @propertycache |
1686 def _dirs(self): |
1748 def _dirs(self): |
1687 return self._rustmap.getdirs() |
1749 return self._rustmap.getdirs() |