Show More
@@ -382,7 +382,7 b' def overridewalk(orig, self, match, subr' | |||||
382 | visit.update(f for f in copymap |
|
382 | visit.update(f for f in copymap | |
383 | if f not in results and matchfn(f)) |
|
383 | if f not in results and matchfn(f)) | |
384 |
|
384 | |||
385 | audit = pathutil.pathauditor(self._root).check |
|
385 | audit = pathutil.pathauditor(self._root, cached=True).check | |
386 | auditpass = [f for f in visit if audit(f)] |
|
386 | auditpass = [f for f in visit if audit(f)] | |
387 | auditpass.sort() |
|
387 | auditpass.sort() | |
388 | auditfail = visit.difference(auditpass) |
|
388 | auditfail = visit.difference(auditpass) |
@@ -3538,7 +3538,7 b' def _performrevert(repo, parents, ctx, a' | |||||
3538 | pass |
|
3538 | pass | |
3539 | repo.dirstate.remove(f) |
|
3539 | repo.dirstate.remove(f) | |
3540 |
|
3540 | |||
3541 | audit_path = pathutil.pathauditor(repo.root) |
|
3541 | audit_path = pathutil.pathauditor(repo.root, cached=True) | |
3542 | for f in actions['forget'][0]: |
|
3542 | for f in actions['forget'][0]: | |
3543 | if interactive: |
|
3543 | if interactive: | |
3544 | choice = repo.ui.promptchoice( |
|
3544 | choice = repo.ui.promptchoice( |
@@ -1153,7 +1153,7 b' class dirstate(object):' | |||||
1153 | # that wasn't ignored, and everything that matched was stat'ed |
|
1153 | # that wasn't ignored, and everything that matched was stat'ed | |
1154 | # and is already in results. |
|
1154 | # and is already in results. | |
1155 | # The rest must thus be ignored or under a symlink. |
|
1155 | # The rest must thus be ignored or under a symlink. | |
1156 | audit_path = pathutil.pathauditor(self._root) |
|
1156 | audit_path = pathutil.pathauditor(self._root, cached=True) | |
1157 |
|
1157 | |||
1158 | for nf in iter(visit): |
|
1158 | for nf in iter(visit): | |
1159 | # If a stat for the same file was already added with a |
|
1159 | # If a stat for the same file was already added with a |
@@ -339,11 +339,11 b' class localrepository(object):' | |||||
339 | # only used when writing this comment: basectx.match |
|
339 | # only used when writing this comment: basectx.match | |
340 | self.auditor = pathutil.pathauditor(self.root, self._checknested) |
|
340 | self.auditor = pathutil.pathauditor(self.root, self._checknested) | |
341 | self.nofsauditor = pathutil.pathauditor(self.root, self._checknested, |
|
341 | self.nofsauditor = pathutil.pathauditor(self.root, self._checknested, | |
342 | realfs=False) |
|
342 | realfs=False, cached=True) | |
343 | self.baseui = baseui |
|
343 | self.baseui = baseui | |
344 | self.ui = baseui.copy() |
|
344 | self.ui = baseui.copy() | |
345 | self.ui.copy = baseui.copy # prevent copying repo configuration |
|
345 | self.ui.copy = baseui.copy # prevent copying repo configuration | |
346 | self.vfs = vfsmod.vfs(self.path) |
|
346 | self.vfs = vfsmod.vfs(self.path, cacheaudited=True) | |
347 | if (self.ui.configbool('devel', 'all-warnings') or |
|
347 | if (self.ui.configbool('devel', 'all-warnings') or | |
348 | self.ui.configbool('devel', 'check-locks')): |
|
348 | self.ui.configbool('devel', 'check-locks')): | |
349 | self.vfs.audit = self._getvfsward(self.vfs.audit) |
|
349 | self.vfs.audit = self._getvfsward(self.vfs.audit) | |
@@ -426,12 +426,13 b' class localrepository(object):' | |||||
426 | '"sparse" extensions to access')) |
|
426 | '"sparse" extensions to access')) | |
427 |
|
427 | |||
428 | self.store = store.store( |
|
428 | self.store = store.store( | |
429 |
|
|
429 | self.requirements, self.sharedpath, | |
|
430 | lambda base: vfsmod.vfs(base, cacheaudited=True)) | |||
430 | self.spath = self.store.path |
|
431 | self.spath = self.store.path | |
431 | self.svfs = self.store.vfs |
|
432 | self.svfs = self.store.vfs | |
432 | self.sjoin = self.store.join |
|
433 | self.sjoin = self.store.join | |
433 | self.vfs.createmode = self.store.createmode |
|
434 | self.vfs.createmode = self.store.createmode | |
434 | self.cachevfs = vfsmod.vfs(cachepath) |
|
435 | self.cachevfs = vfsmod.vfs(cachepath, cacheaudited=True) | |
435 | self.cachevfs.createmode = self.store.createmode |
|
436 | self.cachevfs.createmode = self.store.createmode | |
436 | if (self.ui.configbool('devel', 'all-warnings') or |
|
437 | if (self.ui.configbool('devel', 'all-warnings') or | |
437 | self.ui.configbool('devel', 'check-locks')): |
|
438 | self.ui.configbool('devel', 'check-locks')): |
@@ -33,13 +33,18 b' class pathauditor(object):' | |||||
33 | The file system checks are only done when 'realfs' is set to True (the |
|
33 | The file system checks are only done when 'realfs' is set to True (the | |
34 | default). They should be disable then we are auditing path for operation on |
|
34 | default). They should be disable then we are auditing path for operation on | |
35 | stored history. |
|
35 | stored history. | |
|
36 | ||||
|
37 | If 'cached' is set to True, audited paths and sub-directories are cached. | |||
|
38 | Be careful to not keep the cache of unmanaged directories for long because | |||
|
39 | audited paths may be replaced with symlinks. | |||
36 | ''' |
|
40 | ''' | |
37 |
|
41 | |||
38 | def __init__(self, root, callback=None, realfs=True): |
|
42 | def __init__(self, root, callback=None, realfs=True, cached=False): | |
39 | self.audited = set() |
|
43 | self.audited = set() | |
40 | self.auditeddir = set() |
|
44 | self.auditeddir = set() | |
41 | self.root = root |
|
45 | self.root = root | |
42 | self._realfs = realfs |
|
46 | self._realfs = realfs | |
|
47 | self._cached = cached | |||
43 | self.callback = callback |
|
48 | self.callback = callback | |
44 | if os.path.lexists(root) and not util.fscasesensitive(root): |
|
49 | if os.path.lexists(root) and not util.fscasesensitive(root): | |
45 | self.normcase = util.normcase |
|
50 | self.normcase = util.normcase | |
@@ -96,10 +101,11 b' class pathauditor(object):' | |||||
96 | self._checkfs(prefix, path) |
|
101 | self._checkfs(prefix, path) | |
97 | prefixes.append(normprefix) |
|
102 | prefixes.append(normprefix) | |
98 |
|
103 | |||
99 | self.audited.add(normpath) |
|
104 | if self._cached: | |
100 | # only add prefixes to the cache after checking everything: we don't |
|
105 | self.audited.add(normpath) | |
101 | # want to add "foo/bar/baz" before checking if there's a "foo/.hg" |
|
106 | # only add prefixes to the cache after checking everything: we don't | |
102 | self.auditeddir.update(prefixes) |
|
107 | # want to add "foo/bar/baz" before checking if there's a "foo/.hg" | |
|
108 | self.auditeddir.update(prefixes) | |||
103 |
|
109 | |||
104 | def _checkfs(self, prefix, path): |
|
110 | def _checkfs(self, prefix, path): | |
105 | """raise exception if a file system backed check fails""" |
|
111 | """raise exception if a file system backed check fails""" |
@@ -738,7 +738,7 b' def _interestingfiles(repo, matcher):' | |||||
738 | This is different from dirstate.status because it doesn't care about |
|
738 | This is different from dirstate.status because it doesn't care about | |
739 | whether files are modified or clean.''' |
|
739 | whether files are modified or clean.''' | |
740 | added, unknown, deleted, removed, forgotten = [], [], [], [], [] |
|
740 | added, unknown, deleted, removed, forgotten = [], [], [], [], [] | |
741 | audit_path = pathutil.pathauditor(repo.root) |
|
741 | audit_path = pathutil.pathauditor(repo.root, cached=True) | |
742 |
|
742 | |||
743 | ctx = repo[None] |
|
743 | ctx = repo[None] | |
744 | dirstate = repo.dirstate |
|
744 | dirstate = repo.dirstate |
@@ -295,8 +295,13 b' class vfs(abstractvfs):' | |||||
295 |
|
295 | |||
296 | This class is used to hide the details of COW semantics and |
|
296 | This class is used to hide the details of COW semantics and | |
297 | remote file access from higher level code. |
|
297 | remote file access from higher level code. | |
|
298 | ||||
|
299 | 'cacheaudited' should be enabled only if (a) vfs object is short-lived, or | |||
|
300 | (b) the base directory is managed by hg and considered sort-of append-only. | |||
|
301 | See pathutil.pathauditor() for details. | |||
298 | ''' |
|
302 | ''' | |
299 |
def __init__(self, base, audit=True, expandpath=False, |
|
303 | def __init__(self, base, audit=True, cacheaudited=False, expandpath=False, | |
|
304 | realpath=False): | |||
300 | if expandpath: |
|
305 | if expandpath: | |
301 | base = util.expandpath(base) |
|
306 | base = util.expandpath(base) | |
302 | if realpath: |
|
307 | if realpath: | |
@@ -304,7 +309,7 b' class vfs(abstractvfs):' | |||||
304 | self.base = base |
|
309 | self.base = base | |
305 | self._audit = audit |
|
310 | self._audit = audit | |
306 | if audit: |
|
311 | if audit: | |
307 | self.audit = pathutil.pathauditor(self.base) |
|
312 | self.audit = pathutil.pathauditor(self.base, cached=cacheaudited) | |
308 | else: |
|
313 | else: | |
309 | self.audit = (lambda path, mode=None: True) |
|
314 | self.audit = (lambda path, mode=None: True) | |
310 | self.createmode = None |
|
315 | self.createmode = None |
@@ -169,9 +169,9 b' and the rebase should fail (issue5628)' | |||||
169 | $ hg up -qC 2 |
|
169 | $ hg up -qC 2 | |
170 | $ hg rebase -s 2 -d 1 --config extensions.rebase= |
|
170 | $ hg rebase -s 2 -d 1 --config extensions.rebase= | |
171 | rebasing 2:e73c21d6b244 "file a/poisoned" (tip) |
|
171 | rebasing 2:e73c21d6b244 "file a/poisoned" (tip) | |
172 | saved backup bundle to * (glob) |
|
172 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
|
173 | [255] | |||
173 | $ ls ../merge-symlink-out |
|
174 | $ ls ../merge-symlink-out | |
174 | poisoned |
|
|||
175 |
|
175 | |||
176 | $ cd .. |
|
176 | $ cd .. | |
177 |
|
177 | |||
@@ -211,10 +211,9 b' audited first by calculateupdates(), whe' | |||||
211 |
|
211 | |||
212 | $ hg up -qC null |
|
212 | $ hg up -qC null | |
213 | $ hg up 1 |
|
213 | $ hg up 1 | |
214 | 2 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
214 | abort: path 'a/b' traverses symbolic link 'a' | |
|
215 | [255] | |||
215 | $ ls ../update-symlink-out |
|
216 | $ ls ../update-symlink-out | |
216 | b |
|
|||
217 | $ rm ../update-symlink-out/b |
|
|||
218 |
|
217 | |||
219 | try branch update replacing directory with symlink, and its content: the |
|
218 | try branch update replacing directory with symlink, and its content: the | |
220 | path 'a' is audited as a directory first, which should be audited again as |
|
219 | path 'a' is audited as a directory first, which should be audited again as | |
@@ -223,9 +222,9 b' a symlink.' | |||||
223 | $ rm -f a |
|
222 | $ rm -f a | |
224 | $ hg up -qC 2 |
|
223 | $ hg up -qC 2 | |
225 | $ hg up 1 |
|
224 | $ hg up 1 | |
226 | 2 files updated, 0 files merged, 1 files removed, 0 files unresolved |
|
225 | abort: path 'a/b' traverses symbolic link 'a' | |
|
226 | [255] | |||
227 | $ ls ../update-symlink-out |
|
227 | $ ls ../update-symlink-out | |
228 | b |
|
|||
229 |
|
228 | |||
230 | $ cd .. |
|
229 | $ cd .. | |
231 |
|
230 |
@@ -953,10 +953,9 b' and the merge should fail (issue5628)' | |||||
953 | *** runcommand up -qC 2 |
|
953 | *** runcommand up -qC 2 | |
954 | *** runcommand up -qC 1 |
|
954 | *** runcommand up -qC 1 | |
955 | *** runcommand merge 2 |
|
955 | *** runcommand merge 2 | |
956 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved |
|
956 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
957 | (branch merge, don't forget to commit) |
|
957 | [255] | |
958 | $ ls ../merge-symlink-out |
|
958 | $ ls ../merge-symlink-out | |
959 | poisoned |
|
|||
960 |
|
959 | |||
961 | cache of repo.auditor should be discarded, so matcher would never traverse |
|
960 | cache of repo.auditor should be discarded, so matcher would never traverse | |
962 | symlinks: |
|
961 | symlinks: | |
@@ -980,7 +979,8 b' symlinks:' | |||||
980 | *** runcommand up -qC 0 |
|
979 | *** runcommand up -qC 0 | |
981 | *** runcommand up -qC 1 |
|
980 | *** runcommand up -qC 1 | |
982 | *** runcommand files a/poisoned |
|
981 | *** runcommand files a/poisoned | |
983 | [1] |
|
982 | abort: path 'a/poisoned' traverses symbolic link 'a' | |
|
983 | [255] | |||
984 |
|
984 | |||
985 | $ cd .. |
|
985 | $ cd .. | |
986 |
|
986 |
General Comments 0
You need to be logged in to leave comments.
Login now