72 |
72 |
73 |
73 |
74 @interfaceutil.implementer(intdirstate.idirstate) |
74 @interfaceutil.implementer(intdirstate.idirstate) |
75 class dirstate(object): |
75 class dirstate(object): |
76 def __init__(self, opener, ui, root, validate, sparsematchfn): |
76 def __init__(self, opener, ui, root, validate, sparsematchfn): |
77 '''Create a new dirstate object. |
77 """Create a new dirstate object. |
78 |
78 |
79 opener is an open()-like callable that can be used to open the |
79 opener is an open()-like callable that can be used to open the |
80 dirstate file; root is the root of the directory tracked by |
80 dirstate file; root is the root of the directory tracked by |
81 the dirstate. |
81 the dirstate. |
82 ''' |
82 """ |
83 self._opener = opener |
83 self._opener = opener |
84 self._validate = validate |
84 self._validate = validate |
85 self._root = root |
85 self._root = root |
86 self._sparsematchfn = sparsematchfn |
86 self._sparsematchfn = sparsematchfn |
87 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is |
87 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is |
110 """ |
110 """ |
111 self._pl |
111 self._pl |
112 |
112 |
113 @contextlib.contextmanager |
113 @contextlib.contextmanager |
114 def parentchange(self): |
114 def parentchange(self): |
115 '''Context manager for handling dirstate parents. |
115 """Context manager for handling dirstate parents. |
116 |
116 |
117 If an exception occurs in the scope of the context manager, |
117 If an exception occurs in the scope of the context manager, |
118 the incoherent dirstate won't be written when wlock is |
118 the incoherent dirstate won't be written when wlock is |
119 released. |
119 released. |
120 ''' |
120 """ |
121 self._parentwriters += 1 |
121 self._parentwriters += 1 |
122 yield |
122 yield |
123 # Typically we want the "undo" step of a context manager in a |
123 # Typically we want the "undo" step of a context manager in a |
124 # finally block so it happens even when an exception |
124 # finally block so it happens even when an exception |
125 # occurs. In this case, however, we only want to decrement |
125 # occurs. In this case, however, we only want to decrement |
126 # parentwriters if the code in the with statement exits |
126 # parentwriters if the code in the with statement exits |
127 # normally, so we don't have a try/finally here on purpose. |
127 # normally, so we don't have a try/finally here on purpose. |
128 self._parentwriters -= 1 |
128 self._parentwriters -= 1 |
129 |
129 |
130 def pendingparentchange(self): |
130 def pendingparentchange(self): |
131 '''Returns true if the dirstate is in the middle of a set of changes |
131 """Returns true if the dirstate is in the middle of a set of changes |
132 that modify the dirstate parent. |
132 that modify the dirstate parent. |
133 ''' |
133 """ |
134 return self._parentwriters > 0 |
134 return self._parentwriters > 0 |
135 |
135 |
136 @propertycache |
136 @propertycache |
137 def _map(self): |
137 def _map(self): |
138 """Return the dirstate contents (see documentation for dirstatemap).""" |
138 """Return the dirstate contents (see documentation for dirstatemap).""" |
245 if forcecwd: |
245 if forcecwd: |
246 return forcecwd |
246 return forcecwd |
247 return encoding.getcwd() |
247 return encoding.getcwd() |
248 |
248 |
249 def getcwd(self): |
249 def getcwd(self): |
250 '''Return the path from which a canonical path is calculated. |
250 """Return the path from which a canonical path is calculated. |
251 |
251 |
252 This path should be used to resolve file patterns or to convert |
252 This path should be used to resolve file patterns or to convert |
253 canonical paths back to file paths for display. It shouldn't be |
253 canonical paths back to file paths for display. It shouldn't be |
254 used to get real file paths. Use vfs functions instead. |
254 used to get real file paths. Use vfs functions instead. |
255 ''' |
255 """ |
256 cwd = self._cwd |
256 cwd = self._cwd |
257 if cwd == self._root: |
257 if cwd == self._root: |
258 return b'' |
258 return b'' |
259 # self._root ends with a path separator if self._root is '/' or 'C:\' |
259 # self._root ends with a path separator if self._root is '/' or 'C:\' |
260 rootsep = self._root |
260 rootsep = self._root |
273 if self._slash: |
273 if self._slash: |
274 return util.pconvert(path) |
274 return util.pconvert(path) |
275 return path |
275 return path |
276 |
276 |
277 def __getitem__(self, key): |
277 def __getitem__(self, key): |
278 '''Return the current state of key (a filename) in the dirstate. |
278 """Return the current state of key (a filename) in the dirstate. |
279 |
279 |
280 States are: |
280 States are: |
281 n normal |
281 n normal |
282 m needs merging |
282 m needs merging |
283 r marked for removal |
283 r marked for removal |
284 a marked for addition |
284 a marked for addition |
285 ? not tracked |
285 ? not tracked |
286 ''' |
286 """ |
287 return self._map.get(key, (b"?",))[0] |
287 return self._map.get(key, (b"?",))[0] |
288 |
288 |
289 def __contains__(self, key): |
289 def __contains__(self, key): |
290 return key in self._map |
290 return key in self._map |
291 |
291 |
368 except: # re-raises |
368 except: # re-raises |
369 f.discard() |
369 f.discard() |
370 raise |
370 raise |
371 |
371 |
372 def invalidate(self): |
372 def invalidate(self): |
373 '''Causes the next access to reread the dirstate. |
373 """Causes the next access to reread the dirstate. |
374 |
374 |
375 This is different from localrepo.invalidatedirstate() because it always |
375 This is different from localrepo.invalidatedirstate() because it always |
376 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to |
376 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to |
377 check whether the dirstate has changed before rereading it.''' |
377 check whether the dirstate has changed before rereading it.""" |
378 |
378 |
379 for a in ("_map", "_branch", "_ignore"): |
379 for a in ("_map", "_branch", "_ignore"): |
380 if a in self.__dict__: |
380 if a in self.__dict__: |
381 delattr(self, a) |
381 delattr(self, a) |
382 self._lastnormaltime = 0 |
382 self._lastnormaltime = 0 |
424 self._dirty = True |
424 self._dirty = True |
425 self._updatedfiles.add(f) |
425 self._updatedfiles.add(f) |
426 self._map.addfile(f, oldstate, state, mode, size, mtime) |
426 self._map.addfile(f, oldstate, state, mode, size, mtime) |
427 |
427 |
428 def normal(self, f, parentfiledata=None): |
428 def normal(self, f, parentfiledata=None): |
429 '''Mark a file normal and clean. |
429 """Mark a file normal and clean. |
430 |
430 |
431 parentfiledata: (mode, size, mtime) of the clean file |
431 parentfiledata: (mode, size, mtime) of the clean file |
432 |
432 |
433 parentfiledata should be computed from memory (for mode, |
433 parentfiledata should be computed from memory (for mode, |
434 size), as or close as possible from the point where we |
434 size), as or close as possible from the point where we |
435 determined the file was clean, to limit the risk of the |
435 determined the file was clean, to limit the risk of the |
436 file having been changed by an external process between the |
436 file having been changed by an external process between the |
437 moment where the file was determined to be clean and now.''' |
437 moment where the file was determined to be clean and now.""" |
438 if parentfiledata: |
438 if parentfiledata: |
439 (mode, size, mtime) = parentfiledata |
439 (mode, size, mtime) = parentfiledata |
440 else: |
440 else: |
441 s = os.lstat(self._join(f)) |
441 s = os.lstat(self._join(f)) |
442 mode = s.st_mode |
442 mode = s.st_mode |
579 path, normed, ignoremissing, exists, self._map.dirfoldmap |
579 path, normed, ignoremissing, exists, self._map.dirfoldmap |
580 ) |
580 ) |
581 return folded |
581 return folded |
582 |
582 |
583 def normalize(self, path, isknown=False, ignoremissing=False): |
583 def normalize(self, path, isknown=False, ignoremissing=False): |
584 ''' |
584 """ |
585 normalize the case of a pathname when on a casefolding filesystem |
585 normalize the case of a pathname when on a casefolding filesystem |
586 |
586 |
587 isknown specifies whether the filename came from walking the |
587 isknown specifies whether the filename came from walking the |
588 disk, to avoid extra filesystem access. |
588 disk, to avoid extra filesystem access. |
589 |
589 |
594 The normalized case is determined based on the following precedence: |
594 The normalized case is determined based on the following precedence: |
595 |
595 |
596 - version of name already stored in the dirstate |
596 - version of name already stored in the dirstate |
597 - version of name stored on disk |
597 - version of name stored on disk |
598 - version provided via command arguments |
598 - version provided via command arguments |
599 ''' |
599 """ |
600 |
600 |
601 if self._checkcase: |
601 if self._checkcase: |
602 return self._normalize(path, isknown, ignoremissing) |
602 return self._normalize(path, isknown, ignoremissing) |
603 return path |
603 return path |
604 |
604 |
767 return (i, lineno, line) |
767 return (i, lineno, line) |
768 visited.add(i) |
768 visited.add(i) |
769 return (None, -1, b"") |
769 return (None, -1, b"") |
770 |
770 |
771 def _walkexplicit(self, match, subrepos): |
771 def _walkexplicit(self, match, subrepos): |
772 '''Get stat data about the files explicitly specified by match. |
772 """Get stat data about the files explicitly specified by match. |
773 |
773 |
774 Return a triple (results, dirsfound, dirsnotfound). |
774 Return a triple (results, dirsfound, dirsnotfound). |
775 - results is a mapping from filename to stat result. It also contains |
775 - results is a mapping from filename to stat result. It also contains |
776 listings mapping subrepos and .hg to None. |
776 listings mapping subrepos and .hg to None. |
777 - dirsfound is a list of files found to be directories. |
777 - dirsfound is a list of files found to be directories. |
778 - dirsnotfound is a list of files that the dirstate thinks are |
778 - dirsnotfound is a list of files that the dirstate thinks are |
779 directories and that were not found.''' |
779 directories and that were not found.""" |
780 |
780 |
781 def badtype(mode): |
781 def badtype(mode): |
782 kind = _(b'unknown') |
782 kind = _(b'unknown') |
783 if stat.S_ISCHR(mode): |
783 if stat.S_ISCHR(mode): |
784 kind = _(b'character device') |
784 kind = _(b'character device') |
902 results[path] = None |
902 results[path] = None |
903 |
903 |
904 return results, dirsfound, dirsnotfound |
904 return results, dirsfound, dirsnotfound |
905 |
905 |
906 def walk(self, match, subrepos, unknown, ignored, full=True): |
906 def walk(self, match, subrepos, unknown, ignored, full=True): |
907 ''' |
907 """ |
908 Walk recursively through the directory tree, finding all files |
908 Walk recursively through the directory tree, finding all files |
909 matched by match. |
909 matched by match. |
910 |
910 |
911 If full is False, maybe skip some known-clean files. |
911 If full is False, maybe skip some known-clean files. |
912 |
912 |
913 Return a dict mapping filename to stat-like object (either |
913 Return a dict mapping filename to stat-like object (either |
914 mercurial.osutil.stat instance or return value of os.stat()). |
914 mercurial.osutil.stat instance or return value of os.stat()). |
915 |
915 |
916 ''' |
916 """ |
917 # full is a flag that extensions that hook into walk can use -- this |
917 # full is a flag that extensions that hook into walk can use -- this |
918 # implementation doesn't use it at all. This satisfies the contract |
918 # implementation doesn't use it at all. This satisfies the contract |
919 # because we only guarantee a "maybe". |
919 # because we only guarantee a "maybe". |
920 |
920 |
921 if ignored: |
921 if ignored: |
1166 clean=clean, |
1166 clean=clean, |
1167 ) |
1167 ) |
1168 return (lookup, status) |
1168 return (lookup, status) |
1169 |
1169 |
1170 def status(self, match, subrepos, ignored, clean, unknown): |
1170 def status(self, match, subrepos, ignored, clean, unknown): |
1171 '''Determine the status of the working copy relative to the |
1171 """Determine the status of the working copy relative to the |
1172 dirstate and return a pair of (unsure, status), where status is of type |
1172 dirstate and return a pair of (unsure, status), where status is of type |
1173 scmutil.status and: |
1173 scmutil.status and: |
1174 |
1174 |
1175 unsure: |
1175 unsure: |
1176 files that might have been modified since the dirstate was |
1176 files that might have been modified since the dirstate was |
1180 files that have definitely been modified since the dirstate |
1180 files that have definitely been modified since the dirstate |
1181 was written (different size or mode) |
1181 was written (different size or mode) |
1182 status.clean: |
1182 status.clean: |
1183 files that have definitely not been modified since the |
1183 files that have definitely not been modified since the |
1184 dirstate was written |
1184 dirstate was written |
1185 ''' |
1185 """ |
1186 listignored, listclean, listunknown = ignored, clean, unknown |
1186 listignored, listclean, listunknown = ignored, clean, unknown |
1187 lookup, modified, added, unknown, ignored = [], [], [], [], [] |
1187 lookup, modified, added, unknown, ignored = [], [], [], [], [] |
1188 removed, deleted, clean = [], [], [] |
1188 removed, deleted, clean = [], [], [] |
1189 |
1189 |
1190 dmap = self._map |
1190 dmap = self._map |
1303 modified, added, removed, deleted, unknown, ignored, clean |
1303 modified, added, removed, deleted, unknown, ignored, clean |
1304 ) |
1304 ) |
1305 return (lookup, status) |
1305 return (lookup, status) |
1306 |
1306 |
1307 def matches(self, match): |
1307 def matches(self, match): |
1308 ''' |
1308 """ |
1309 return files in the dirstate (in whatever state) filtered by match |
1309 return files in the dirstate (in whatever state) filtered by match |
1310 ''' |
1310 """ |
1311 dmap = self._map |
1311 dmap = self._map |
1312 if rustmod is not None: |
1312 if rustmod is not None: |
1313 dmap = self._map._rustmap |
1313 dmap = self._map._rustmap |
1314 |
1314 |
1315 if match.always(): |
1315 if match.always(): |