##// END OF EJS Templates
match: document that visitchildrenset might return files...
Kyle Lippincott -
r39296:27946fca default
parent child Browse files
Show More
@@ -1,1501 +1,1504 b''
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import contextlib
11 import contextlib
12 import errno
12 import errno
13 import os
13 import os
14 import stat
14 import stat
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import nullid
17 from .node import nullid
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 match as matchmod,
21 match as matchmod,
22 pathutil,
22 pathutil,
23 policy,
23 policy,
24 pycompat,
24 pycompat,
25 scmutil,
25 scmutil,
26 txnutil,
26 txnutil,
27 util,
27 util,
28 )
28 )
29
29
30 parsers = policy.importmod(r'parsers')
30 parsers = policy.importmod(r'parsers')
31
31
32 propertycache = util.propertycache
32 propertycache = util.propertycache
33 filecache = scmutil.filecache
33 filecache = scmutil.filecache
34 _rangemask = 0x7fffffff
34 _rangemask = 0x7fffffff
35
35
36 dirstatetuple = parsers.dirstatetuple
36 dirstatetuple = parsers.dirstatetuple
37
37
38 class repocache(filecache):
38 class repocache(filecache):
39 """filecache for files in .hg/"""
39 """filecache for files in .hg/"""
40 def join(self, obj, fname):
40 def join(self, obj, fname):
41 return obj._opener.join(fname)
41 return obj._opener.join(fname)
42
42
43 class rootcache(filecache):
43 class rootcache(filecache):
44 """filecache for files in the repository root"""
44 """filecache for files in the repository root"""
45 def join(self, obj, fname):
45 def join(self, obj, fname):
46 return obj._join(fname)
46 return obj._join(fname)
47
47
48 def _getfsnow(vfs):
48 def _getfsnow(vfs):
49 '''Get "now" timestamp on filesystem'''
49 '''Get "now" timestamp on filesystem'''
50 tmpfd, tmpname = vfs.mkstemp()
50 tmpfd, tmpname = vfs.mkstemp()
51 try:
51 try:
52 return os.fstat(tmpfd)[stat.ST_MTIME]
52 return os.fstat(tmpfd)[stat.ST_MTIME]
53 finally:
53 finally:
54 os.close(tmpfd)
54 os.close(tmpfd)
55 vfs.unlink(tmpname)
55 vfs.unlink(tmpname)
56
56
57 class dirstate(object):
57 class dirstate(object):
58
58
59 def __init__(self, opener, ui, root, validate, sparsematchfn):
59 def __init__(self, opener, ui, root, validate, sparsematchfn):
60 '''Create a new dirstate object.
60 '''Create a new dirstate object.
61
61
62 opener is an open()-like callable that can be used to open the
62 opener is an open()-like callable that can be used to open the
63 dirstate file; root is the root of the directory tracked by
63 dirstate file; root is the root of the directory tracked by
64 the dirstate.
64 the dirstate.
65 '''
65 '''
66 self._opener = opener
66 self._opener = opener
67 self._validate = validate
67 self._validate = validate
68 self._root = root
68 self._root = root
69 self._sparsematchfn = sparsematchfn
69 self._sparsematchfn = sparsematchfn
70 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
70 # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is
71 # UNC path pointing to root share (issue4557)
71 # UNC path pointing to root share (issue4557)
72 self._rootdir = pathutil.normasprefix(root)
72 self._rootdir = pathutil.normasprefix(root)
73 self._dirty = False
73 self._dirty = False
74 self._lastnormaltime = 0
74 self._lastnormaltime = 0
75 self._ui = ui
75 self._ui = ui
76 self._filecache = {}
76 self._filecache = {}
77 self._parentwriters = 0
77 self._parentwriters = 0
78 self._filename = 'dirstate'
78 self._filename = 'dirstate'
79 self._pendingfilename = '%s.pending' % self._filename
79 self._pendingfilename = '%s.pending' % self._filename
80 self._plchangecallbacks = {}
80 self._plchangecallbacks = {}
81 self._origpl = None
81 self._origpl = None
82 self._updatedfiles = set()
82 self._updatedfiles = set()
83 self._mapcls = dirstatemap
83 self._mapcls = dirstatemap
84
84
85 @contextlib.contextmanager
85 @contextlib.contextmanager
86 def parentchange(self):
86 def parentchange(self):
87 '''Context manager for handling dirstate parents.
87 '''Context manager for handling dirstate parents.
88
88
89 If an exception occurs in the scope of the context manager,
89 If an exception occurs in the scope of the context manager,
90 the incoherent dirstate won't be written when wlock is
90 the incoherent dirstate won't be written when wlock is
91 released.
91 released.
92 '''
92 '''
93 self._parentwriters += 1
93 self._parentwriters += 1
94 yield
94 yield
95 # Typically we want the "undo" step of a context manager in a
95 # Typically we want the "undo" step of a context manager in a
96 # finally block so it happens even when an exception
96 # finally block so it happens even when an exception
97 # occurs. In this case, however, we only want to decrement
97 # occurs. In this case, however, we only want to decrement
98 # parentwriters if the code in the with statement exits
98 # parentwriters if the code in the with statement exits
99 # normally, so we don't have a try/finally here on purpose.
99 # normally, so we don't have a try/finally here on purpose.
100 self._parentwriters -= 1
100 self._parentwriters -= 1
101
101
102 def pendingparentchange(self):
102 def pendingparentchange(self):
103 '''Returns true if the dirstate is in the middle of a set of changes
103 '''Returns true if the dirstate is in the middle of a set of changes
104 that modify the dirstate parent.
104 that modify the dirstate parent.
105 '''
105 '''
106 return self._parentwriters > 0
106 return self._parentwriters > 0
107
107
108 @propertycache
108 @propertycache
109 def _map(self):
109 def _map(self):
110 """Return the dirstate contents (see documentation for dirstatemap)."""
110 """Return the dirstate contents (see documentation for dirstatemap)."""
111 self._map = self._mapcls(self._ui, self._opener, self._root)
111 self._map = self._mapcls(self._ui, self._opener, self._root)
112 return self._map
112 return self._map
113
113
114 @property
114 @property
115 def _sparsematcher(self):
115 def _sparsematcher(self):
116 """The matcher for the sparse checkout.
116 """The matcher for the sparse checkout.
117
117
118 The working directory may not include every file from a manifest. The
118 The working directory may not include every file from a manifest. The
119 matcher obtained by this property will match a path if it is to be
119 matcher obtained by this property will match a path if it is to be
120 included in the working directory.
120 included in the working directory.
121 """
121 """
122 # TODO there is potential to cache this property. For now, the matcher
122 # TODO there is potential to cache this property. For now, the matcher
123 # is resolved on every access. (But the called function does use a
123 # is resolved on every access. (But the called function does use a
124 # cache to keep the lookup fast.)
124 # cache to keep the lookup fast.)
125 return self._sparsematchfn()
125 return self._sparsematchfn()
126
126
127 @repocache('branch')
127 @repocache('branch')
128 def _branch(self):
128 def _branch(self):
129 try:
129 try:
130 return self._opener.read("branch").strip() or "default"
130 return self._opener.read("branch").strip() or "default"
131 except IOError as inst:
131 except IOError as inst:
132 if inst.errno != errno.ENOENT:
132 if inst.errno != errno.ENOENT:
133 raise
133 raise
134 return "default"
134 return "default"
135
135
136 @property
136 @property
137 def _pl(self):
137 def _pl(self):
138 return self._map.parents()
138 return self._map.parents()
139
139
140 def hasdir(self, d):
140 def hasdir(self, d):
141 return self._map.hastrackeddir(d)
141 return self._map.hastrackeddir(d)
142
142
143 @rootcache('.hgignore')
143 @rootcache('.hgignore')
144 def _ignore(self):
144 def _ignore(self):
145 files = self._ignorefiles()
145 files = self._ignorefiles()
146 if not files:
146 if not files:
147 return matchmod.never(self._root, '')
147 return matchmod.never(self._root, '')
148
148
149 pats = ['include:%s' % f for f in files]
149 pats = ['include:%s' % f for f in files]
150 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
150 return matchmod.match(self._root, '', [], pats, warn=self._ui.warn)
151
151
152 @propertycache
152 @propertycache
153 def _slash(self):
153 def _slash(self):
154 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
154 return self._ui.configbool('ui', 'slash') and pycompat.ossep != '/'
155
155
156 @propertycache
156 @propertycache
157 def _checklink(self):
157 def _checklink(self):
158 return util.checklink(self._root)
158 return util.checklink(self._root)
159
159
160 @propertycache
160 @propertycache
161 def _checkexec(self):
161 def _checkexec(self):
162 return util.checkexec(self._root)
162 return util.checkexec(self._root)
163
163
164 @propertycache
164 @propertycache
165 def _checkcase(self):
165 def _checkcase(self):
166 return not util.fscasesensitive(self._join('.hg'))
166 return not util.fscasesensitive(self._join('.hg'))
167
167
168 def _join(self, f):
168 def _join(self, f):
169 # much faster than os.path.join()
169 # much faster than os.path.join()
170 # it's safe because f is always a relative path
170 # it's safe because f is always a relative path
171 return self._rootdir + f
171 return self._rootdir + f
172
172
173 def flagfunc(self, buildfallback):
173 def flagfunc(self, buildfallback):
174 if self._checklink and self._checkexec:
174 if self._checklink and self._checkexec:
175 def f(x):
175 def f(x):
176 try:
176 try:
177 st = os.lstat(self._join(x))
177 st = os.lstat(self._join(x))
178 if util.statislink(st):
178 if util.statislink(st):
179 return 'l'
179 return 'l'
180 if util.statisexec(st):
180 if util.statisexec(st):
181 return 'x'
181 return 'x'
182 except OSError:
182 except OSError:
183 pass
183 pass
184 return ''
184 return ''
185 return f
185 return f
186
186
187 fallback = buildfallback()
187 fallback = buildfallback()
188 if self._checklink:
188 if self._checklink:
189 def f(x):
189 def f(x):
190 if os.path.islink(self._join(x)):
190 if os.path.islink(self._join(x)):
191 return 'l'
191 return 'l'
192 if 'x' in fallback(x):
192 if 'x' in fallback(x):
193 return 'x'
193 return 'x'
194 return ''
194 return ''
195 return f
195 return f
196 if self._checkexec:
196 if self._checkexec:
197 def f(x):
197 def f(x):
198 if 'l' in fallback(x):
198 if 'l' in fallback(x):
199 return 'l'
199 return 'l'
200 if util.isexec(self._join(x)):
200 if util.isexec(self._join(x)):
201 return 'x'
201 return 'x'
202 return ''
202 return ''
203 return f
203 return f
204 else:
204 else:
205 return fallback
205 return fallback
206
206
207 @propertycache
207 @propertycache
208 def _cwd(self):
208 def _cwd(self):
209 # internal config: ui.forcecwd
209 # internal config: ui.forcecwd
210 forcecwd = self._ui.config('ui', 'forcecwd')
210 forcecwd = self._ui.config('ui', 'forcecwd')
211 if forcecwd:
211 if forcecwd:
212 return forcecwd
212 return forcecwd
213 return pycompat.getcwd()
213 return pycompat.getcwd()
214
214
215 def getcwd(self):
215 def getcwd(self):
216 '''Return the path from which a canonical path is calculated.
216 '''Return the path from which a canonical path is calculated.
217
217
218 This path should be used to resolve file patterns or to convert
218 This path should be used to resolve file patterns or to convert
219 canonical paths back to file paths for display. It shouldn't be
219 canonical paths back to file paths for display. It shouldn't be
220 used to get real file paths. Use vfs functions instead.
220 used to get real file paths. Use vfs functions instead.
221 '''
221 '''
222 cwd = self._cwd
222 cwd = self._cwd
223 if cwd == self._root:
223 if cwd == self._root:
224 return ''
224 return ''
225 # self._root ends with a path separator if self._root is '/' or 'C:\'
225 # self._root ends with a path separator if self._root is '/' or 'C:\'
226 rootsep = self._root
226 rootsep = self._root
227 if not util.endswithsep(rootsep):
227 if not util.endswithsep(rootsep):
228 rootsep += pycompat.ossep
228 rootsep += pycompat.ossep
229 if cwd.startswith(rootsep):
229 if cwd.startswith(rootsep):
230 return cwd[len(rootsep):]
230 return cwd[len(rootsep):]
231 else:
231 else:
232 # we're outside the repo. return an absolute path.
232 # we're outside the repo. return an absolute path.
233 return cwd
233 return cwd
234
234
235 def pathto(self, f, cwd=None):
235 def pathto(self, f, cwd=None):
236 if cwd is None:
236 if cwd is None:
237 cwd = self.getcwd()
237 cwd = self.getcwd()
238 path = util.pathto(self._root, cwd, f)
238 path = util.pathto(self._root, cwd, f)
239 if self._slash:
239 if self._slash:
240 return util.pconvert(path)
240 return util.pconvert(path)
241 return path
241 return path
242
242
243 def __getitem__(self, key):
243 def __getitem__(self, key):
244 '''Return the current state of key (a filename) in the dirstate.
244 '''Return the current state of key (a filename) in the dirstate.
245
245
246 States are:
246 States are:
247 n normal
247 n normal
248 m needs merging
248 m needs merging
249 r marked for removal
249 r marked for removal
250 a marked for addition
250 a marked for addition
251 ? not tracked
251 ? not tracked
252 '''
252 '''
253 return self._map.get(key, ("?",))[0]
253 return self._map.get(key, ("?",))[0]
254
254
255 def __contains__(self, key):
255 def __contains__(self, key):
256 return key in self._map
256 return key in self._map
257
257
258 def __iter__(self):
258 def __iter__(self):
259 return iter(sorted(self._map))
259 return iter(sorted(self._map))
260
260
261 def items(self):
261 def items(self):
262 return self._map.iteritems()
262 return self._map.iteritems()
263
263
264 iteritems = items
264 iteritems = items
265
265
266 def parents(self):
266 def parents(self):
267 return [self._validate(p) for p in self._pl]
267 return [self._validate(p) for p in self._pl]
268
268
269 def p1(self):
269 def p1(self):
270 return self._validate(self._pl[0])
270 return self._validate(self._pl[0])
271
271
272 def p2(self):
272 def p2(self):
273 return self._validate(self._pl[1])
273 return self._validate(self._pl[1])
274
274
275 def branch(self):
275 def branch(self):
276 return encoding.tolocal(self._branch)
276 return encoding.tolocal(self._branch)
277
277
278 def setparents(self, p1, p2=nullid):
278 def setparents(self, p1, p2=nullid):
279 """Set dirstate parents to p1 and p2.
279 """Set dirstate parents to p1 and p2.
280
280
281 When moving from two parents to one, 'm' merged entries a
281 When moving from two parents to one, 'm' merged entries a
282 adjusted to normal and previous copy records discarded and
282 adjusted to normal and previous copy records discarded and
283 returned by the call.
283 returned by the call.
284
284
285 See localrepo.setparents()
285 See localrepo.setparents()
286 """
286 """
287 if self._parentwriters == 0:
287 if self._parentwriters == 0:
288 raise ValueError("cannot set dirstate parent without "
288 raise ValueError("cannot set dirstate parent without "
289 "calling dirstate.beginparentchange")
289 "calling dirstate.beginparentchange")
290
290
291 self._dirty = True
291 self._dirty = True
292 oldp2 = self._pl[1]
292 oldp2 = self._pl[1]
293 if self._origpl is None:
293 if self._origpl is None:
294 self._origpl = self._pl
294 self._origpl = self._pl
295 self._map.setparents(p1, p2)
295 self._map.setparents(p1, p2)
296 copies = {}
296 copies = {}
297 if oldp2 != nullid and p2 == nullid:
297 if oldp2 != nullid and p2 == nullid:
298 candidatefiles = self._map.nonnormalset.union(
298 candidatefiles = self._map.nonnormalset.union(
299 self._map.otherparentset)
299 self._map.otherparentset)
300 for f in candidatefiles:
300 for f in candidatefiles:
301 s = self._map.get(f)
301 s = self._map.get(f)
302 if s is None:
302 if s is None:
303 continue
303 continue
304
304
305 # Discard 'm' markers when moving away from a merge state
305 # Discard 'm' markers when moving away from a merge state
306 if s[0] == 'm':
306 if s[0] == 'm':
307 source = self._map.copymap.get(f)
307 source = self._map.copymap.get(f)
308 if source:
308 if source:
309 copies[f] = source
309 copies[f] = source
310 self.normallookup(f)
310 self.normallookup(f)
311 # Also fix up otherparent markers
311 # Also fix up otherparent markers
312 elif s[0] == 'n' and s[2] == -2:
312 elif s[0] == 'n' and s[2] == -2:
313 source = self._map.copymap.get(f)
313 source = self._map.copymap.get(f)
314 if source:
314 if source:
315 copies[f] = source
315 copies[f] = source
316 self.add(f)
316 self.add(f)
317 return copies
317 return copies
318
318
319 def setbranch(self, branch):
319 def setbranch(self, branch):
320 self._branch = encoding.fromlocal(branch)
320 self._branch = encoding.fromlocal(branch)
321 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
321 f = self._opener('branch', 'w', atomictemp=True, checkambig=True)
322 try:
322 try:
323 f.write(self._branch + '\n')
323 f.write(self._branch + '\n')
324 f.close()
324 f.close()
325
325
326 # make sure filecache has the correct stat info for _branch after
326 # make sure filecache has the correct stat info for _branch after
327 # replacing the underlying file
327 # replacing the underlying file
328 ce = self._filecache['_branch']
328 ce = self._filecache['_branch']
329 if ce:
329 if ce:
330 ce.refresh()
330 ce.refresh()
331 except: # re-raises
331 except: # re-raises
332 f.discard()
332 f.discard()
333 raise
333 raise
334
334
335 def invalidate(self):
335 def invalidate(self):
336 '''Causes the next access to reread the dirstate.
336 '''Causes the next access to reread the dirstate.
337
337
338 This is different from localrepo.invalidatedirstate() because it always
338 This is different from localrepo.invalidatedirstate() because it always
339 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
339 rereads the dirstate. Use localrepo.invalidatedirstate() if you want to
340 check whether the dirstate has changed before rereading it.'''
340 check whether the dirstate has changed before rereading it.'''
341
341
342 for a in (r"_map", r"_branch", r"_ignore"):
342 for a in (r"_map", r"_branch", r"_ignore"):
343 if a in self.__dict__:
343 if a in self.__dict__:
344 delattr(self, a)
344 delattr(self, a)
345 self._lastnormaltime = 0
345 self._lastnormaltime = 0
346 self._dirty = False
346 self._dirty = False
347 self._updatedfiles.clear()
347 self._updatedfiles.clear()
348 self._parentwriters = 0
348 self._parentwriters = 0
349 self._origpl = None
349 self._origpl = None
350
350
351 def copy(self, source, dest):
351 def copy(self, source, dest):
352 """Mark dest as a copy of source. Unmark dest if source is None."""
352 """Mark dest as a copy of source. Unmark dest if source is None."""
353 if source == dest:
353 if source == dest:
354 return
354 return
355 self._dirty = True
355 self._dirty = True
356 if source is not None:
356 if source is not None:
357 self._map.copymap[dest] = source
357 self._map.copymap[dest] = source
358 self._updatedfiles.add(source)
358 self._updatedfiles.add(source)
359 self._updatedfiles.add(dest)
359 self._updatedfiles.add(dest)
360 elif self._map.copymap.pop(dest, None):
360 elif self._map.copymap.pop(dest, None):
361 self._updatedfiles.add(dest)
361 self._updatedfiles.add(dest)
362
362
363 def copied(self, file):
363 def copied(self, file):
364 return self._map.copymap.get(file, None)
364 return self._map.copymap.get(file, None)
365
365
366 def copies(self):
366 def copies(self):
367 return self._map.copymap
367 return self._map.copymap
368
368
369 def _addpath(self, f, state, mode, size, mtime):
369 def _addpath(self, f, state, mode, size, mtime):
370 oldstate = self[f]
370 oldstate = self[f]
371 if state == 'a' or oldstate == 'r':
371 if state == 'a' or oldstate == 'r':
372 scmutil.checkfilename(f)
372 scmutil.checkfilename(f)
373 if self._map.hastrackeddir(f):
373 if self._map.hastrackeddir(f):
374 raise error.Abort(_('directory %r already in dirstate') %
374 raise error.Abort(_('directory %r already in dirstate') %
375 pycompat.bytestr(f))
375 pycompat.bytestr(f))
376 # shadows
376 # shadows
377 for d in util.finddirs(f):
377 for d in util.finddirs(f):
378 if self._map.hastrackeddir(d):
378 if self._map.hastrackeddir(d):
379 break
379 break
380 entry = self._map.get(d)
380 entry = self._map.get(d)
381 if entry is not None and entry[0] != 'r':
381 if entry is not None and entry[0] != 'r':
382 raise error.Abort(
382 raise error.Abort(
383 _('file %r in dirstate clashes with %r') %
383 _('file %r in dirstate clashes with %r') %
384 (pycompat.bytestr(d), pycompat.bytestr(f)))
384 (pycompat.bytestr(d), pycompat.bytestr(f)))
385 self._dirty = True
385 self._dirty = True
386 self._updatedfiles.add(f)
386 self._updatedfiles.add(f)
387 self._map.addfile(f, oldstate, state, mode, size, mtime)
387 self._map.addfile(f, oldstate, state, mode, size, mtime)
388
388
389 def normal(self, f):
389 def normal(self, f):
390 '''Mark a file normal and clean.'''
390 '''Mark a file normal and clean.'''
391 s = os.lstat(self._join(f))
391 s = os.lstat(self._join(f))
392 mtime = s[stat.ST_MTIME]
392 mtime = s[stat.ST_MTIME]
393 self._addpath(f, 'n', s.st_mode,
393 self._addpath(f, 'n', s.st_mode,
394 s.st_size & _rangemask, mtime & _rangemask)
394 s.st_size & _rangemask, mtime & _rangemask)
395 self._map.copymap.pop(f, None)
395 self._map.copymap.pop(f, None)
396 if f in self._map.nonnormalset:
396 if f in self._map.nonnormalset:
397 self._map.nonnormalset.remove(f)
397 self._map.nonnormalset.remove(f)
398 if mtime > self._lastnormaltime:
398 if mtime > self._lastnormaltime:
399 # Remember the most recent modification timeslot for status(),
399 # Remember the most recent modification timeslot for status(),
400 # to make sure we won't miss future size-preserving file content
400 # to make sure we won't miss future size-preserving file content
401 # modifications that happen within the same timeslot.
401 # modifications that happen within the same timeslot.
402 self._lastnormaltime = mtime
402 self._lastnormaltime = mtime
403
403
404 def normallookup(self, f):
404 def normallookup(self, f):
405 '''Mark a file normal, but possibly dirty.'''
405 '''Mark a file normal, but possibly dirty.'''
406 if self._pl[1] != nullid:
406 if self._pl[1] != nullid:
407 # if there is a merge going on and the file was either
407 # if there is a merge going on and the file was either
408 # in state 'm' (-1) or coming from other parent (-2) before
408 # in state 'm' (-1) or coming from other parent (-2) before
409 # being removed, restore that state.
409 # being removed, restore that state.
410 entry = self._map.get(f)
410 entry = self._map.get(f)
411 if entry is not None:
411 if entry is not None:
412 if entry[0] == 'r' and entry[2] in (-1, -2):
412 if entry[0] == 'r' and entry[2] in (-1, -2):
413 source = self._map.copymap.get(f)
413 source = self._map.copymap.get(f)
414 if entry[2] == -1:
414 if entry[2] == -1:
415 self.merge(f)
415 self.merge(f)
416 elif entry[2] == -2:
416 elif entry[2] == -2:
417 self.otherparent(f)
417 self.otherparent(f)
418 if source:
418 if source:
419 self.copy(source, f)
419 self.copy(source, f)
420 return
420 return
421 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
421 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
422 return
422 return
423 self._addpath(f, 'n', 0, -1, -1)
423 self._addpath(f, 'n', 0, -1, -1)
424 self._map.copymap.pop(f, None)
424 self._map.copymap.pop(f, None)
425
425
426 def otherparent(self, f):
426 def otherparent(self, f):
427 '''Mark as coming from the other parent, always dirty.'''
427 '''Mark as coming from the other parent, always dirty.'''
428 if self._pl[1] == nullid:
428 if self._pl[1] == nullid:
429 raise error.Abort(_("setting %r to other parent "
429 raise error.Abort(_("setting %r to other parent "
430 "only allowed in merges") % f)
430 "only allowed in merges") % f)
431 if f in self and self[f] == 'n':
431 if f in self and self[f] == 'n':
432 # merge-like
432 # merge-like
433 self._addpath(f, 'm', 0, -2, -1)
433 self._addpath(f, 'm', 0, -2, -1)
434 else:
434 else:
435 # add-like
435 # add-like
436 self._addpath(f, 'n', 0, -2, -1)
436 self._addpath(f, 'n', 0, -2, -1)
437 self._map.copymap.pop(f, None)
437 self._map.copymap.pop(f, None)
438
438
439 def add(self, f):
439 def add(self, f):
440 '''Mark a file added.'''
440 '''Mark a file added.'''
441 self._addpath(f, 'a', 0, -1, -1)
441 self._addpath(f, 'a', 0, -1, -1)
442 self._map.copymap.pop(f, None)
442 self._map.copymap.pop(f, None)
443
443
444 def remove(self, f):
444 def remove(self, f):
445 '''Mark a file removed.'''
445 '''Mark a file removed.'''
446 self._dirty = True
446 self._dirty = True
447 oldstate = self[f]
447 oldstate = self[f]
448 size = 0
448 size = 0
449 if self._pl[1] != nullid:
449 if self._pl[1] != nullid:
450 entry = self._map.get(f)
450 entry = self._map.get(f)
451 if entry is not None:
451 if entry is not None:
452 # backup the previous state
452 # backup the previous state
453 if entry[0] == 'm': # merge
453 if entry[0] == 'm': # merge
454 size = -1
454 size = -1
455 elif entry[0] == 'n' and entry[2] == -2: # other parent
455 elif entry[0] == 'n' and entry[2] == -2: # other parent
456 size = -2
456 size = -2
457 self._map.otherparentset.add(f)
457 self._map.otherparentset.add(f)
458 self._updatedfiles.add(f)
458 self._updatedfiles.add(f)
459 self._map.removefile(f, oldstate, size)
459 self._map.removefile(f, oldstate, size)
460 if size == 0:
460 if size == 0:
461 self._map.copymap.pop(f, None)
461 self._map.copymap.pop(f, None)
462
462
463 def merge(self, f):
463 def merge(self, f):
464 '''Mark a file merged.'''
464 '''Mark a file merged.'''
465 if self._pl[1] == nullid:
465 if self._pl[1] == nullid:
466 return self.normallookup(f)
466 return self.normallookup(f)
467 return self.otherparent(f)
467 return self.otherparent(f)
468
468
469 def drop(self, f):
469 def drop(self, f):
470 '''Drop a file from the dirstate'''
470 '''Drop a file from the dirstate'''
471 oldstate = self[f]
471 oldstate = self[f]
472 if self._map.dropfile(f, oldstate):
472 if self._map.dropfile(f, oldstate):
473 self._dirty = True
473 self._dirty = True
474 self._updatedfiles.add(f)
474 self._updatedfiles.add(f)
475 self._map.copymap.pop(f, None)
475 self._map.copymap.pop(f, None)
476
476
477 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
477 def _discoverpath(self, path, normed, ignoremissing, exists, storemap):
478 if exists is None:
478 if exists is None:
479 exists = os.path.lexists(os.path.join(self._root, path))
479 exists = os.path.lexists(os.path.join(self._root, path))
480 if not exists:
480 if not exists:
481 # Maybe a path component exists
481 # Maybe a path component exists
482 if not ignoremissing and '/' in path:
482 if not ignoremissing and '/' in path:
483 d, f = path.rsplit('/', 1)
483 d, f = path.rsplit('/', 1)
484 d = self._normalize(d, False, ignoremissing, None)
484 d = self._normalize(d, False, ignoremissing, None)
485 folded = d + "/" + f
485 folded = d + "/" + f
486 else:
486 else:
487 # No path components, preserve original case
487 # No path components, preserve original case
488 folded = path
488 folded = path
489 else:
489 else:
490 # recursively normalize leading directory components
490 # recursively normalize leading directory components
491 # against dirstate
491 # against dirstate
492 if '/' in normed:
492 if '/' in normed:
493 d, f = normed.rsplit('/', 1)
493 d, f = normed.rsplit('/', 1)
494 d = self._normalize(d, False, ignoremissing, True)
494 d = self._normalize(d, False, ignoremissing, True)
495 r = self._root + "/" + d
495 r = self._root + "/" + d
496 folded = d + "/" + util.fspath(f, r)
496 folded = d + "/" + util.fspath(f, r)
497 else:
497 else:
498 folded = util.fspath(normed, self._root)
498 folded = util.fspath(normed, self._root)
499 storemap[normed] = folded
499 storemap[normed] = folded
500
500
501 return folded
501 return folded
502
502
503 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
503 def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
504 normed = util.normcase(path)
504 normed = util.normcase(path)
505 folded = self._map.filefoldmap.get(normed, None)
505 folded = self._map.filefoldmap.get(normed, None)
506 if folded is None:
506 if folded is None:
507 if isknown:
507 if isknown:
508 folded = path
508 folded = path
509 else:
509 else:
510 folded = self._discoverpath(path, normed, ignoremissing, exists,
510 folded = self._discoverpath(path, normed, ignoremissing, exists,
511 self._map.filefoldmap)
511 self._map.filefoldmap)
512 return folded
512 return folded
513
513
514 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
514 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
515 normed = util.normcase(path)
515 normed = util.normcase(path)
516 folded = self._map.filefoldmap.get(normed, None)
516 folded = self._map.filefoldmap.get(normed, None)
517 if folded is None:
517 if folded is None:
518 folded = self._map.dirfoldmap.get(normed, None)
518 folded = self._map.dirfoldmap.get(normed, None)
519 if folded is None:
519 if folded is None:
520 if isknown:
520 if isknown:
521 folded = path
521 folded = path
522 else:
522 else:
523 # store discovered result in dirfoldmap so that future
523 # store discovered result in dirfoldmap so that future
524 # normalizefile calls don't start matching directories
524 # normalizefile calls don't start matching directories
525 folded = self._discoverpath(path, normed, ignoremissing, exists,
525 folded = self._discoverpath(path, normed, ignoremissing, exists,
526 self._map.dirfoldmap)
526 self._map.dirfoldmap)
527 return folded
527 return folded
528
528
529 def normalize(self, path, isknown=False, ignoremissing=False):
529 def normalize(self, path, isknown=False, ignoremissing=False):
530 '''
530 '''
531 normalize the case of a pathname when on a casefolding filesystem
531 normalize the case of a pathname when on a casefolding filesystem
532
532
533 isknown specifies whether the filename came from walking the
533 isknown specifies whether the filename came from walking the
534 disk, to avoid extra filesystem access.
534 disk, to avoid extra filesystem access.
535
535
536 If ignoremissing is True, missing path are returned
536 If ignoremissing is True, missing path are returned
537 unchanged. Otherwise, we try harder to normalize possibly
537 unchanged. Otherwise, we try harder to normalize possibly
538 existing path components.
538 existing path components.
539
539
540 The normalized case is determined based on the following precedence:
540 The normalized case is determined based on the following precedence:
541
541
542 - version of name already stored in the dirstate
542 - version of name already stored in the dirstate
543 - version of name stored on disk
543 - version of name stored on disk
544 - version provided via command arguments
544 - version provided via command arguments
545 '''
545 '''
546
546
547 if self._checkcase:
547 if self._checkcase:
548 return self._normalize(path, isknown, ignoremissing)
548 return self._normalize(path, isknown, ignoremissing)
549 return path
549 return path
550
550
551 def clear(self):
551 def clear(self):
552 self._map.clear()
552 self._map.clear()
553 self._lastnormaltime = 0
553 self._lastnormaltime = 0
554 self._updatedfiles.clear()
554 self._updatedfiles.clear()
555 self._dirty = True
555 self._dirty = True
556
556
557 def rebuild(self, parent, allfiles, changedfiles=None):
557 def rebuild(self, parent, allfiles, changedfiles=None):
558 if changedfiles is None:
558 if changedfiles is None:
559 # Rebuild entire dirstate
559 # Rebuild entire dirstate
560 changedfiles = allfiles
560 changedfiles = allfiles
561 lastnormaltime = self._lastnormaltime
561 lastnormaltime = self._lastnormaltime
562 self.clear()
562 self.clear()
563 self._lastnormaltime = lastnormaltime
563 self._lastnormaltime = lastnormaltime
564
564
565 if self._origpl is None:
565 if self._origpl is None:
566 self._origpl = self._pl
566 self._origpl = self._pl
567 self._map.setparents(parent, nullid)
567 self._map.setparents(parent, nullid)
568 for f in changedfiles:
568 for f in changedfiles:
569 if f in allfiles:
569 if f in allfiles:
570 self.normallookup(f)
570 self.normallookup(f)
571 else:
571 else:
572 self.drop(f)
572 self.drop(f)
573
573
574 self._dirty = True
574 self._dirty = True
575
575
576 def identity(self):
576 def identity(self):
577 '''Return identity of dirstate itself to detect changing in storage
577 '''Return identity of dirstate itself to detect changing in storage
578
578
579 If identity of previous dirstate is equal to this, writing
579 If identity of previous dirstate is equal to this, writing
580 changes based on the former dirstate out can keep consistency.
580 changes based on the former dirstate out can keep consistency.
581 '''
581 '''
582 return self._map.identity
582 return self._map.identity
583
583
584 def write(self, tr):
584 def write(self, tr):
585 if not self._dirty:
585 if not self._dirty:
586 return
586 return
587
587
588 filename = self._filename
588 filename = self._filename
589 if tr:
589 if tr:
590 # 'dirstate.write()' is not only for writing in-memory
590 # 'dirstate.write()' is not only for writing in-memory
591 # changes out, but also for dropping ambiguous timestamp.
591 # changes out, but also for dropping ambiguous timestamp.
592 # delayed writing re-raise "ambiguous timestamp issue".
592 # delayed writing re-raise "ambiguous timestamp issue".
593 # See also the wiki page below for detail:
593 # See also the wiki page below for detail:
594 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
594 # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan
595
595
596 # emulate dropping timestamp in 'parsers.pack_dirstate'
596 # emulate dropping timestamp in 'parsers.pack_dirstate'
597 now = _getfsnow(self._opener)
597 now = _getfsnow(self._opener)
598 self._map.clearambiguoustimes(self._updatedfiles, now)
598 self._map.clearambiguoustimes(self._updatedfiles, now)
599
599
600 # emulate that all 'dirstate.normal' results are written out
600 # emulate that all 'dirstate.normal' results are written out
601 self._lastnormaltime = 0
601 self._lastnormaltime = 0
602 self._updatedfiles.clear()
602 self._updatedfiles.clear()
603
603
604 # delay writing in-memory changes out
604 # delay writing in-memory changes out
605 tr.addfilegenerator('dirstate', (self._filename,),
605 tr.addfilegenerator('dirstate', (self._filename,),
606 self._writedirstate, location='plain')
606 self._writedirstate, location='plain')
607 return
607 return
608
608
609 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
609 st = self._opener(filename, "w", atomictemp=True, checkambig=True)
610 self._writedirstate(st)
610 self._writedirstate(st)
611
611
612 def addparentchangecallback(self, category, callback):
612 def addparentchangecallback(self, category, callback):
613 """add a callback to be called when the wd parents are changed
613 """add a callback to be called when the wd parents are changed
614
614
615 Callback will be called with the following arguments:
615 Callback will be called with the following arguments:
616 dirstate, (oldp1, oldp2), (newp1, newp2)
616 dirstate, (oldp1, oldp2), (newp1, newp2)
617
617
618 Category is a unique identifier to allow overwriting an old callback
618 Category is a unique identifier to allow overwriting an old callback
619 with a newer callback.
619 with a newer callback.
620 """
620 """
621 self._plchangecallbacks[category] = callback
621 self._plchangecallbacks[category] = callback
622
622
623 def _writedirstate(self, st):
623 def _writedirstate(self, st):
624 # notify callbacks about parents change
624 # notify callbacks about parents change
625 if self._origpl is not None and self._origpl != self._pl:
625 if self._origpl is not None and self._origpl != self._pl:
626 for c, callback in sorted(self._plchangecallbacks.iteritems()):
626 for c, callback in sorted(self._plchangecallbacks.iteritems()):
627 callback(self, self._origpl, self._pl)
627 callback(self, self._origpl, self._pl)
628 self._origpl = None
628 self._origpl = None
629 # use the modification time of the newly created temporary file as the
629 # use the modification time of the newly created temporary file as the
630 # filesystem's notion of 'now'
630 # filesystem's notion of 'now'
631 now = util.fstat(st)[stat.ST_MTIME] & _rangemask
631 now = util.fstat(st)[stat.ST_MTIME] & _rangemask
632
632
633 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
633 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
634 # timestamp of each entries in dirstate, because of 'now > mtime'
634 # timestamp of each entries in dirstate, because of 'now > mtime'
635 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite')
635 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite')
636 if delaywrite > 0:
636 if delaywrite > 0:
637 # do we have any files to delay for?
637 # do we have any files to delay for?
638 for f, e in self._map.iteritems():
638 for f, e in self._map.iteritems():
639 if e[0] == 'n' and e[3] == now:
639 if e[0] == 'n' and e[3] == now:
640 import time # to avoid useless import
640 import time # to avoid useless import
641 # rather than sleep n seconds, sleep until the next
641 # rather than sleep n seconds, sleep until the next
642 # multiple of n seconds
642 # multiple of n seconds
643 clock = time.time()
643 clock = time.time()
644 start = int(clock) - (int(clock) % delaywrite)
644 start = int(clock) - (int(clock) % delaywrite)
645 end = start + delaywrite
645 end = start + delaywrite
646 time.sleep(end - clock)
646 time.sleep(end - clock)
647 now = end # trust our estimate that the end is near now
647 now = end # trust our estimate that the end is near now
648 break
648 break
649
649
650 self._map.write(st, now)
650 self._map.write(st, now)
651 self._lastnormaltime = 0
651 self._lastnormaltime = 0
652 self._dirty = False
652 self._dirty = False
653
653
654 def _dirignore(self, f):
654 def _dirignore(self, f):
655 if f == '.':
655 if f == '.':
656 return False
656 return False
657 if self._ignore(f):
657 if self._ignore(f):
658 return True
658 return True
659 for p in util.finddirs(f):
659 for p in util.finddirs(f):
660 if self._ignore(p):
660 if self._ignore(p):
661 return True
661 return True
662 return False
662 return False
663
663
664 def _ignorefiles(self):
664 def _ignorefiles(self):
665 files = []
665 files = []
666 if os.path.exists(self._join('.hgignore')):
666 if os.path.exists(self._join('.hgignore')):
667 files.append(self._join('.hgignore'))
667 files.append(self._join('.hgignore'))
668 for name, path in self._ui.configitems("ui"):
668 for name, path in self._ui.configitems("ui"):
669 if name == 'ignore' or name.startswith('ignore.'):
669 if name == 'ignore' or name.startswith('ignore.'):
670 # we need to use os.path.join here rather than self._join
670 # we need to use os.path.join here rather than self._join
671 # because path is arbitrary and user-specified
671 # because path is arbitrary and user-specified
672 files.append(os.path.join(self._rootdir, util.expandpath(path)))
672 files.append(os.path.join(self._rootdir, util.expandpath(path)))
673 return files
673 return files
674
674
675 def _ignorefileandline(self, f):
675 def _ignorefileandline(self, f):
676 files = collections.deque(self._ignorefiles())
676 files = collections.deque(self._ignorefiles())
677 visited = set()
677 visited = set()
678 while files:
678 while files:
679 i = files.popleft()
679 i = files.popleft()
680 patterns = matchmod.readpatternfile(i, self._ui.warn,
680 patterns = matchmod.readpatternfile(i, self._ui.warn,
681 sourceinfo=True)
681 sourceinfo=True)
682 for pattern, lineno, line in patterns:
682 for pattern, lineno, line in patterns:
683 kind, p = matchmod._patsplit(pattern, 'glob')
683 kind, p = matchmod._patsplit(pattern, 'glob')
684 if kind == "subinclude":
684 if kind == "subinclude":
685 if p not in visited:
685 if p not in visited:
686 files.append(p)
686 files.append(p)
687 continue
687 continue
688 m = matchmod.match(self._root, '', [], [pattern],
688 m = matchmod.match(self._root, '', [], [pattern],
689 warn=self._ui.warn)
689 warn=self._ui.warn)
690 if m(f):
690 if m(f):
691 return (i, lineno, line)
691 return (i, lineno, line)
692 visited.add(i)
692 visited.add(i)
693 return (None, -1, "")
693 return (None, -1, "")
694
694
695 def _walkexplicit(self, match, subrepos):
695 def _walkexplicit(self, match, subrepos):
696 '''Get stat data about the files explicitly specified by match.
696 '''Get stat data about the files explicitly specified by match.
697
697
698 Return a triple (results, dirsfound, dirsnotfound).
698 Return a triple (results, dirsfound, dirsnotfound).
699 - results is a mapping from filename to stat result. It also contains
699 - results is a mapping from filename to stat result. It also contains
700 listings mapping subrepos and .hg to None.
700 listings mapping subrepos and .hg to None.
701 - dirsfound is a list of files found to be directories.
701 - dirsfound is a list of files found to be directories.
702 - dirsnotfound is a list of files that the dirstate thinks are
702 - dirsnotfound is a list of files that the dirstate thinks are
703 directories and that were not found.'''
703 directories and that were not found.'''
704
704
705 def badtype(mode):
705 def badtype(mode):
706 kind = _('unknown')
706 kind = _('unknown')
707 if stat.S_ISCHR(mode):
707 if stat.S_ISCHR(mode):
708 kind = _('character device')
708 kind = _('character device')
709 elif stat.S_ISBLK(mode):
709 elif stat.S_ISBLK(mode):
710 kind = _('block device')
710 kind = _('block device')
711 elif stat.S_ISFIFO(mode):
711 elif stat.S_ISFIFO(mode):
712 kind = _('fifo')
712 kind = _('fifo')
713 elif stat.S_ISSOCK(mode):
713 elif stat.S_ISSOCK(mode):
714 kind = _('socket')
714 kind = _('socket')
715 elif stat.S_ISDIR(mode):
715 elif stat.S_ISDIR(mode):
716 kind = _('directory')
716 kind = _('directory')
717 return _('unsupported file type (type is %s)') % kind
717 return _('unsupported file type (type is %s)') % kind
718
718
719 matchedir = match.explicitdir
719 matchedir = match.explicitdir
720 badfn = match.bad
720 badfn = match.bad
721 dmap = self._map
721 dmap = self._map
722 lstat = os.lstat
722 lstat = os.lstat
723 getkind = stat.S_IFMT
723 getkind = stat.S_IFMT
724 dirkind = stat.S_IFDIR
724 dirkind = stat.S_IFDIR
725 regkind = stat.S_IFREG
725 regkind = stat.S_IFREG
726 lnkkind = stat.S_IFLNK
726 lnkkind = stat.S_IFLNK
727 join = self._join
727 join = self._join
728 dirsfound = []
728 dirsfound = []
729 foundadd = dirsfound.append
729 foundadd = dirsfound.append
730 dirsnotfound = []
730 dirsnotfound = []
731 notfoundadd = dirsnotfound.append
731 notfoundadd = dirsnotfound.append
732
732
733 if not match.isexact() and self._checkcase:
733 if not match.isexact() and self._checkcase:
734 normalize = self._normalize
734 normalize = self._normalize
735 else:
735 else:
736 normalize = None
736 normalize = None
737
737
738 files = sorted(match.files())
738 files = sorted(match.files())
739 subrepos.sort()
739 subrepos.sort()
740 i, j = 0, 0
740 i, j = 0, 0
741 while i < len(files) and j < len(subrepos):
741 while i < len(files) and j < len(subrepos):
742 subpath = subrepos[j] + "/"
742 subpath = subrepos[j] + "/"
743 if files[i] < subpath:
743 if files[i] < subpath:
744 i += 1
744 i += 1
745 continue
745 continue
746 while i < len(files) and files[i].startswith(subpath):
746 while i < len(files) and files[i].startswith(subpath):
747 del files[i]
747 del files[i]
748 j += 1
748 j += 1
749
749
750 if not files or '.' in files:
750 if not files or '.' in files:
751 files = ['.']
751 files = ['.']
752 results = dict.fromkeys(subrepos)
752 results = dict.fromkeys(subrepos)
753 results['.hg'] = None
753 results['.hg'] = None
754
754
755 for ff in files:
755 for ff in files:
756 # constructing the foldmap is expensive, so don't do it for the
756 # constructing the foldmap is expensive, so don't do it for the
757 # common case where files is ['.']
757 # common case where files is ['.']
758 if normalize and ff != '.':
758 if normalize and ff != '.':
759 nf = normalize(ff, False, True)
759 nf = normalize(ff, False, True)
760 else:
760 else:
761 nf = ff
761 nf = ff
762 if nf in results:
762 if nf in results:
763 continue
763 continue
764
764
765 try:
765 try:
766 st = lstat(join(nf))
766 st = lstat(join(nf))
767 kind = getkind(st.st_mode)
767 kind = getkind(st.st_mode)
768 if kind == dirkind:
768 if kind == dirkind:
769 if nf in dmap:
769 if nf in dmap:
770 # file replaced by dir on disk but still in dirstate
770 # file replaced by dir on disk but still in dirstate
771 results[nf] = None
771 results[nf] = None
772 if matchedir:
772 if matchedir:
773 matchedir(nf)
773 matchedir(nf)
774 foundadd((nf, ff))
774 foundadd((nf, ff))
775 elif kind == regkind or kind == lnkkind:
775 elif kind == regkind or kind == lnkkind:
776 results[nf] = st
776 results[nf] = st
777 else:
777 else:
778 badfn(ff, badtype(kind))
778 badfn(ff, badtype(kind))
779 if nf in dmap:
779 if nf in dmap:
780 results[nf] = None
780 results[nf] = None
781 except OSError as inst: # nf not found on disk - it is dirstate only
781 except OSError as inst: # nf not found on disk - it is dirstate only
782 if nf in dmap: # does it exactly match a missing file?
782 if nf in dmap: # does it exactly match a missing file?
783 results[nf] = None
783 results[nf] = None
784 else: # does it match a missing directory?
784 else: # does it match a missing directory?
785 if self._map.hasdir(nf):
785 if self._map.hasdir(nf):
786 if matchedir:
786 if matchedir:
787 matchedir(nf)
787 matchedir(nf)
788 notfoundadd(nf)
788 notfoundadd(nf)
789 else:
789 else:
790 badfn(ff, encoding.strtolocal(inst.strerror))
790 badfn(ff, encoding.strtolocal(inst.strerror))
791
791
792 # match.files() may contain explicitly-specified paths that shouldn't
792 # match.files() may contain explicitly-specified paths that shouldn't
793 # be taken; drop them from the list of files found. dirsfound/notfound
793 # be taken; drop them from the list of files found. dirsfound/notfound
794 # aren't filtered here because they will be tested later.
794 # aren't filtered here because they will be tested later.
795 if match.anypats():
795 if match.anypats():
796 for f in list(results):
796 for f in list(results):
797 if f == '.hg' or f in subrepos:
797 if f == '.hg' or f in subrepos:
798 # keep sentinel to disable further out-of-repo walks
798 # keep sentinel to disable further out-of-repo walks
799 continue
799 continue
800 if not match(f):
800 if not match(f):
801 del results[f]
801 del results[f]
802
802
803 # Case insensitive filesystems cannot rely on lstat() failing to detect
803 # Case insensitive filesystems cannot rely on lstat() failing to detect
804 # a case-only rename. Prune the stat object for any file that does not
804 # a case-only rename. Prune the stat object for any file that does not
805 # match the case in the filesystem, if there are multiple files that
805 # match the case in the filesystem, if there are multiple files that
806 # normalize to the same path.
806 # normalize to the same path.
807 if match.isexact() and self._checkcase:
807 if match.isexact() and self._checkcase:
808 normed = {}
808 normed = {}
809
809
810 for f, st in results.iteritems():
810 for f, st in results.iteritems():
811 if st is None:
811 if st is None:
812 continue
812 continue
813
813
814 nc = util.normcase(f)
814 nc = util.normcase(f)
815 paths = normed.get(nc)
815 paths = normed.get(nc)
816
816
817 if paths is None:
817 if paths is None:
818 paths = set()
818 paths = set()
819 normed[nc] = paths
819 normed[nc] = paths
820
820
821 paths.add(f)
821 paths.add(f)
822
822
823 for norm, paths in normed.iteritems():
823 for norm, paths in normed.iteritems():
824 if len(paths) > 1:
824 if len(paths) > 1:
825 for path in paths:
825 for path in paths:
826 folded = self._discoverpath(path, norm, True, None,
826 folded = self._discoverpath(path, norm, True, None,
827 self._map.dirfoldmap)
827 self._map.dirfoldmap)
828 if path != folded:
828 if path != folded:
829 results[path] = None
829 results[path] = None
830
830
831 return results, dirsfound, dirsnotfound
831 return results, dirsfound, dirsnotfound
832
832
833 def walk(self, match, subrepos, unknown, ignored, full=True):
833 def walk(self, match, subrepos, unknown, ignored, full=True):
834 '''
834 '''
835 Walk recursively through the directory tree, finding all files
835 Walk recursively through the directory tree, finding all files
836 matched by match.
836 matched by match.
837
837
838 If full is False, maybe skip some known-clean files.
838 If full is False, maybe skip some known-clean files.
839
839
840 Return a dict mapping filename to stat-like object (either
840 Return a dict mapping filename to stat-like object (either
841 mercurial.osutil.stat instance or return value of os.stat()).
841 mercurial.osutil.stat instance or return value of os.stat()).
842
842
843 '''
843 '''
844 # full is a flag that extensions that hook into walk can use -- this
844 # full is a flag that extensions that hook into walk can use -- this
845 # implementation doesn't use it at all. This satisfies the contract
845 # implementation doesn't use it at all. This satisfies the contract
846 # because we only guarantee a "maybe".
846 # because we only guarantee a "maybe".
847
847
848 if ignored:
848 if ignored:
849 ignore = util.never
849 ignore = util.never
850 dirignore = util.never
850 dirignore = util.never
851 elif unknown:
851 elif unknown:
852 ignore = self._ignore
852 ignore = self._ignore
853 dirignore = self._dirignore
853 dirignore = self._dirignore
854 else:
854 else:
855 # if not unknown and not ignored, drop dir recursion and step 2
855 # if not unknown and not ignored, drop dir recursion and step 2
856 ignore = util.always
856 ignore = util.always
857 dirignore = util.always
857 dirignore = util.always
858
858
859 matchfn = match.matchfn
859 matchfn = match.matchfn
860 matchalways = match.always()
860 matchalways = match.always()
861 matchtdir = match.traversedir
861 matchtdir = match.traversedir
862 dmap = self._map
862 dmap = self._map
863 listdir = util.listdir
863 listdir = util.listdir
864 lstat = os.lstat
864 lstat = os.lstat
865 dirkind = stat.S_IFDIR
865 dirkind = stat.S_IFDIR
866 regkind = stat.S_IFREG
866 regkind = stat.S_IFREG
867 lnkkind = stat.S_IFLNK
867 lnkkind = stat.S_IFLNK
868 join = self._join
868 join = self._join
869
869
870 exact = skipstep3 = False
870 exact = skipstep3 = False
871 if match.isexact(): # match.exact
871 if match.isexact(): # match.exact
872 exact = True
872 exact = True
873 dirignore = util.always # skip step 2
873 dirignore = util.always # skip step 2
874 elif match.prefix(): # match.match, no patterns
874 elif match.prefix(): # match.match, no patterns
875 skipstep3 = True
875 skipstep3 = True
876
876
877 if not exact and self._checkcase:
877 if not exact and self._checkcase:
878 normalize = self._normalize
878 normalize = self._normalize
879 normalizefile = self._normalizefile
879 normalizefile = self._normalizefile
880 skipstep3 = False
880 skipstep3 = False
881 else:
881 else:
882 normalize = self._normalize
882 normalize = self._normalize
883 normalizefile = None
883 normalizefile = None
884
884
885 # step 1: find all explicit files
885 # step 1: find all explicit files
886 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
886 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
887
887
888 skipstep3 = skipstep3 and not (work or dirsnotfound)
888 skipstep3 = skipstep3 and not (work or dirsnotfound)
889 work = [d for d in work if not dirignore(d[0])]
889 work = [d for d in work if not dirignore(d[0])]
890
890
891 # step 2: visit subdirectories
891 # step 2: visit subdirectories
892 def traverse(work, alreadynormed):
892 def traverse(work, alreadynormed):
893 wadd = work.append
893 wadd = work.append
894 while work:
894 while work:
895 nd = work.pop()
895 nd = work.pop()
896 visitentries = match.visitchildrenset(nd)
896 visitentries = match.visitchildrenset(nd)
897 if not visitentries:
897 if not visitentries:
898 continue
898 continue
899 if visitentries == 'this' or visitentries == 'all':
899 if visitentries == 'this' or visitentries == 'all':
900 visitentries = None
900 visitentries = None
901 skip = None
901 skip = None
902 if nd == '.':
902 if nd == '.':
903 nd = ''
903 nd = ''
904 else:
904 else:
905 skip = '.hg'
905 skip = '.hg'
906 try:
906 try:
907 entries = listdir(join(nd), stat=True, skip=skip)
907 entries = listdir(join(nd), stat=True, skip=skip)
908 except OSError as inst:
908 except OSError as inst:
909 if inst.errno in (errno.EACCES, errno.ENOENT):
909 if inst.errno in (errno.EACCES, errno.ENOENT):
910 match.bad(self.pathto(nd),
910 match.bad(self.pathto(nd),
911 encoding.strtolocal(inst.strerror))
911 encoding.strtolocal(inst.strerror))
912 continue
912 continue
913 raise
913 raise
914 for f, kind, st in entries:
914 for f, kind, st in entries:
915 # If we needed to inspect any files, visitentries would have
915 # Some matchers may return files in the visitentries set,
916 # been 'this' or 'all', and we would have set it to None
916 # instead of 'this', if the matcher explicitly mentions them
917 # above. If we have visitentries populated here, we don't
917 # and is not an exactmatcher. This is acceptable; we do not
918 # care about any files in this directory, so no need to
918 # make any hard assumptions about file-or-directory below
919 # check the type of `f`.
919 # based on the presence of `f` in visitentries. If
920 # visitchildrenset returned a set, we can always skip the
921 # entries *not* in the set it provided regardless of whether
922 # they're actually a file or a directory.
920 if visitentries and f not in visitentries:
923 if visitentries and f not in visitentries:
921 continue
924 continue
922 if normalizefile:
925 if normalizefile:
923 # even though f might be a directory, we're only
926 # even though f might be a directory, we're only
924 # interested in comparing it to files currently in the
927 # interested in comparing it to files currently in the
925 # dmap -- therefore normalizefile is enough
928 # dmap -- therefore normalizefile is enough
926 nf = normalizefile(nd and (nd + "/" + f) or f, True,
929 nf = normalizefile(nd and (nd + "/" + f) or f, True,
927 True)
930 True)
928 else:
931 else:
929 nf = nd and (nd + "/" + f) or f
932 nf = nd and (nd + "/" + f) or f
930 if nf not in results:
933 if nf not in results:
931 if kind == dirkind:
934 if kind == dirkind:
932 if not ignore(nf):
935 if not ignore(nf):
933 if matchtdir:
936 if matchtdir:
934 matchtdir(nf)
937 matchtdir(nf)
935 wadd(nf)
938 wadd(nf)
936 if nf in dmap and (matchalways or matchfn(nf)):
939 if nf in dmap and (matchalways or matchfn(nf)):
937 results[nf] = None
940 results[nf] = None
938 elif kind == regkind or kind == lnkkind:
941 elif kind == regkind or kind == lnkkind:
939 if nf in dmap:
942 if nf in dmap:
940 if matchalways or matchfn(nf):
943 if matchalways or matchfn(nf):
941 results[nf] = st
944 results[nf] = st
942 elif ((matchalways or matchfn(nf))
945 elif ((matchalways or matchfn(nf))
943 and not ignore(nf)):
946 and not ignore(nf)):
944 # unknown file -- normalize if necessary
947 # unknown file -- normalize if necessary
945 if not alreadynormed:
948 if not alreadynormed:
946 nf = normalize(nf, False, True)
949 nf = normalize(nf, False, True)
947 results[nf] = st
950 results[nf] = st
948 elif nf in dmap and (matchalways or matchfn(nf)):
951 elif nf in dmap and (matchalways or matchfn(nf)):
949 results[nf] = None
952 results[nf] = None
950
953
951 for nd, d in work:
954 for nd, d in work:
952 # alreadynormed means that processwork doesn't have to do any
955 # alreadynormed means that processwork doesn't have to do any
953 # expensive directory normalization
956 # expensive directory normalization
954 alreadynormed = not normalize or nd == d
957 alreadynormed = not normalize or nd == d
955 traverse([d], alreadynormed)
958 traverse([d], alreadynormed)
956
959
957 for s in subrepos:
960 for s in subrepos:
958 del results[s]
961 del results[s]
959 del results['.hg']
962 del results['.hg']
960
963
961 # step 3: visit remaining files from dmap
964 # step 3: visit remaining files from dmap
962 if not skipstep3 and not exact:
965 if not skipstep3 and not exact:
963 # If a dmap file is not in results yet, it was either
966 # If a dmap file is not in results yet, it was either
964 # a) not matching matchfn b) ignored, c) missing, or d) under a
967 # a) not matching matchfn b) ignored, c) missing, or d) under a
965 # symlink directory.
968 # symlink directory.
966 if not results and matchalways:
969 if not results and matchalways:
967 visit = [f for f in dmap]
970 visit = [f for f in dmap]
968 else:
971 else:
969 visit = [f for f in dmap if f not in results and matchfn(f)]
972 visit = [f for f in dmap if f not in results and matchfn(f)]
970 visit.sort()
973 visit.sort()
971
974
972 if unknown:
975 if unknown:
973 # unknown == True means we walked all dirs under the roots
976 # unknown == True means we walked all dirs under the roots
974 # that wasn't ignored, and everything that matched was stat'ed
977 # that wasn't ignored, and everything that matched was stat'ed
975 # and is already in results.
978 # and is already in results.
976 # The rest must thus be ignored or under a symlink.
979 # The rest must thus be ignored or under a symlink.
977 audit_path = pathutil.pathauditor(self._root, cached=True)
980 audit_path = pathutil.pathauditor(self._root, cached=True)
978
981
979 for nf in iter(visit):
982 for nf in iter(visit):
980 # If a stat for the same file was already added with a
983 # If a stat for the same file was already added with a
981 # different case, don't add one for this, since that would
984 # different case, don't add one for this, since that would
982 # make it appear as if the file exists under both names
985 # make it appear as if the file exists under both names
983 # on disk.
986 # on disk.
984 if (normalizefile and
987 if (normalizefile and
985 normalizefile(nf, True, True) in results):
988 normalizefile(nf, True, True) in results):
986 results[nf] = None
989 results[nf] = None
987 # Report ignored items in the dmap as long as they are not
990 # Report ignored items in the dmap as long as they are not
988 # under a symlink directory.
991 # under a symlink directory.
989 elif audit_path.check(nf):
992 elif audit_path.check(nf):
990 try:
993 try:
991 results[nf] = lstat(join(nf))
994 results[nf] = lstat(join(nf))
992 # file was just ignored, no links, and exists
995 # file was just ignored, no links, and exists
993 except OSError:
996 except OSError:
994 # file doesn't exist
997 # file doesn't exist
995 results[nf] = None
998 results[nf] = None
996 else:
999 else:
997 # It's either missing or under a symlink directory
1000 # It's either missing or under a symlink directory
998 # which we in this case report as missing
1001 # which we in this case report as missing
999 results[nf] = None
1002 results[nf] = None
1000 else:
1003 else:
1001 # We may not have walked the full directory tree above,
1004 # We may not have walked the full directory tree above,
1002 # so stat and check everything we missed.
1005 # so stat and check everything we missed.
1003 iv = iter(visit)
1006 iv = iter(visit)
1004 for st in util.statfiles([join(i) for i in visit]):
1007 for st in util.statfiles([join(i) for i in visit]):
1005 results[next(iv)] = st
1008 results[next(iv)] = st
1006 return results
1009 return results
1007
1010
1008 def status(self, match, subrepos, ignored, clean, unknown):
1011 def status(self, match, subrepos, ignored, clean, unknown):
1009 '''Determine the status of the working copy relative to the
1012 '''Determine the status of the working copy relative to the
1010 dirstate and return a pair of (unsure, status), where status is of type
1013 dirstate and return a pair of (unsure, status), where status is of type
1011 scmutil.status and:
1014 scmutil.status and:
1012
1015
1013 unsure:
1016 unsure:
1014 files that might have been modified since the dirstate was
1017 files that might have been modified since the dirstate was
1015 written, but need to be read to be sure (size is the same
1018 written, but need to be read to be sure (size is the same
1016 but mtime differs)
1019 but mtime differs)
1017 status.modified:
1020 status.modified:
1018 files that have definitely been modified since the dirstate
1021 files that have definitely been modified since the dirstate
1019 was written (different size or mode)
1022 was written (different size or mode)
1020 status.clean:
1023 status.clean:
1021 files that have definitely not been modified since the
1024 files that have definitely not been modified since the
1022 dirstate was written
1025 dirstate was written
1023 '''
1026 '''
1024 listignored, listclean, listunknown = ignored, clean, unknown
1027 listignored, listclean, listunknown = ignored, clean, unknown
1025 lookup, modified, added, unknown, ignored = [], [], [], [], []
1028 lookup, modified, added, unknown, ignored = [], [], [], [], []
1026 removed, deleted, clean = [], [], []
1029 removed, deleted, clean = [], [], []
1027
1030
1028 dmap = self._map
1031 dmap = self._map
1029 dmap.preload()
1032 dmap.preload()
1030 dcontains = dmap.__contains__
1033 dcontains = dmap.__contains__
1031 dget = dmap.__getitem__
1034 dget = dmap.__getitem__
1032 ladd = lookup.append # aka "unsure"
1035 ladd = lookup.append # aka "unsure"
1033 madd = modified.append
1036 madd = modified.append
1034 aadd = added.append
1037 aadd = added.append
1035 uadd = unknown.append
1038 uadd = unknown.append
1036 iadd = ignored.append
1039 iadd = ignored.append
1037 radd = removed.append
1040 radd = removed.append
1038 dadd = deleted.append
1041 dadd = deleted.append
1039 cadd = clean.append
1042 cadd = clean.append
1040 mexact = match.exact
1043 mexact = match.exact
1041 dirignore = self._dirignore
1044 dirignore = self._dirignore
1042 checkexec = self._checkexec
1045 checkexec = self._checkexec
1043 copymap = self._map.copymap
1046 copymap = self._map.copymap
1044 lastnormaltime = self._lastnormaltime
1047 lastnormaltime = self._lastnormaltime
1045
1048
1046 # We need to do full walks when either
1049 # We need to do full walks when either
1047 # - we're listing all clean files, or
1050 # - we're listing all clean files, or
1048 # - match.traversedir does something, because match.traversedir should
1051 # - match.traversedir does something, because match.traversedir should
1049 # be called for every dir in the working dir
1052 # be called for every dir in the working dir
1050 full = listclean or match.traversedir is not None
1053 full = listclean or match.traversedir is not None
1051 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1054 for fn, st in self.walk(match, subrepos, listunknown, listignored,
1052 full=full).iteritems():
1055 full=full).iteritems():
1053 if not dcontains(fn):
1056 if not dcontains(fn):
1054 if (listignored or mexact(fn)) and dirignore(fn):
1057 if (listignored or mexact(fn)) and dirignore(fn):
1055 if listignored:
1058 if listignored:
1056 iadd(fn)
1059 iadd(fn)
1057 else:
1060 else:
1058 uadd(fn)
1061 uadd(fn)
1059 continue
1062 continue
1060
1063
1061 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1064 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
1062 # written like that for performance reasons. dmap[fn] is not a
1065 # written like that for performance reasons. dmap[fn] is not a
1063 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1066 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
1064 # opcode has fast paths when the value to be unpacked is a tuple or
1067 # opcode has fast paths when the value to be unpacked is a tuple or
1065 # a list, but falls back to creating a full-fledged iterator in
1068 # a list, but falls back to creating a full-fledged iterator in
1066 # general. That is much slower than simply accessing and storing the
1069 # general. That is much slower than simply accessing and storing the
1067 # tuple members one by one.
1070 # tuple members one by one.
1068 t = dget(fn)
1071 t = dget(fn)
1069 state = t[0]
1072 state = t[0]
1070 mode = t[1]
1073 mode = t[1]
1071 size = t[2]
1074 size = t[2]
1072 time = t[3]
1075 time = t[3]
1073
1076
1074 if not st and state in "nma":
1077 if not st and state in "nma":
1075 dadd(fn)
1078 dadd(fn)
1076 elif state == 'n':
1079 elif state == 'n':
1077 if (size >= 0 and
1080 if (size >= 0 and
1078 ((size != st.st_size and size != st.st_size & _rangemask)
1081 ((size != st.st_size and size != st.st_size & _rangemask)
1079 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1082 or ((mode ^ st.st_mode) & 0o100 and checkexec))
1080 or size == -2 # other parent
1083 or size == -2 # other parent
1081 or fn in copymap):
1084 or fn in copymap):
1082 madd(fn)
1085 madd(fn)
1083 elif (time != st[stat.ST_MTIME]
1086 elif (time != st[stat.ST_MTIME]
1084 and time != st[stat.ST_MTIME] & _rangemask):
1087 and time != st[stat.ST_MTIME] & _rangemask):
1085 ladd(fn)
1088 ladd(fn)
1086 elif st[stat.ST_MTIME] == lastnormaltime:
1089 elif st[stat.ST_MTIME] == lastnormaltime:
1087 # fn may have just been marked as normal and it may have
1090 # fn may have just been marked as normal and it may have
1088 # changed in the same second without changing its size.
1091 # changed in the same second without changing its size.
1089 # This can happen if we quickly do multiple commits.
1092 # This can happen if we quickly do multiple commits.
1090 # Force lookup, so we don't miss such a racy file change.
1093 # Force lookup, so we don't miss such a racy file change.
1091 ladd(fn)
1094 ladd(fn)
1092 elif listclean:
1095 elif listclean:
1093 cadd(fn)
1096 cadd(fn)
1094 elif state == 'm':
1097 elif state == 'm':
1095 madd(fn)
1098 madd(fn)
1096 elif state == 'a':
1099 elif state == 'a':
1097 aadd(fn)
1100 aadd(fn)
1098 elif state == 'r':
1101 elif state == 'r':
1099 radd(fn)
1102 radd(fn)
1100
1103
1101 return (lookup, scmutil.status(modified, added, removed, deleted,
1104 return (lookup, scmutil.status(modified, added, removed, deleted,
1102 unknown, ignored, clean))
1105 unknown, ignored, clean))
1103
1106
1104 def matches(self, match):
1107 def matches(self, match):
1105 '''
1108 '''
1106 return files in the dirstate (in whatever state) filtered by match
1109 return files in the dirstate (in whatever state) filtered by match
1107 '''
1110 '''
1108 dmap = self._map
1111 dmap = self._map
1109 if match.always():
1112 if match.always():
1110 return dmap.keys()
1113 return dmap.keys()
1111 files = match.files()
1114 files = match.files()
1112 if match.isexact():
1115 if match.isexact():
1113 # fast path -- filter the other way around, since typically files is
1116 # fast path -- filter the other way around, since typically files is
1114 # much smaller than dmap
1117 # much smaller than dmap
1115 return [f for f in files if f in dmap]
1118 return [f for f in files if f in dmap]
1116 if match.prefix() and all(fn in dmap for fn in files):
1119 if match.prefix() and all(fn in dmap for fn in files):
1117 # fast path -- all the values are known to be files, so just return
1120 # fast path -- all the values are known to be files, so just return
1118 # that
1121 # that
1119 return list(files)
1122 return list(files)
1120 return [f for f in dmap if match(f)]
1123 return [f for f in dmap if match(f)]
1121
1124
1122 def _actualfilename(self, tr):
1125 def _actualfilename(self, tr):
1123 if tr:
1126 if tr:
1124 return self._pendingfilename
1127 return self._pendingfilename
1125 else:
1128 else:
1126 return self._filename
1129 return self._filename
1127
1130
1128 def savebackup(self, tr, backupname):
1131 def savebackup(self, tr, backupname):
1129 '''Save current dirstate into backup file'''
1132 '''Save current dirstate into backup file'''
1130 filename = self._actualfilename(tr)
1133 filename = self._actualfilename(tr)
1131 assert backupname != filename
1134 assert backupname != filename
1132
1135
1133 # use '_writedirstate' instead of 'write' to write changes certainly,
1136 # use '_writedirstate' instead of 'write' to write changes certainly,
1134 # because the latter omits writing out if transaction is running.
1137 # because the latter omits writing out if transaction is running.
1135 # output file will be used to create backup of dirstate at this point.
1138 # output file will be used to create backup of dirstate at this point.
1136 if self._dirty or not self._opener.exists(filename):
1139 if self._dirty or not self._opener.exists(filename):
1137 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1140 self._writedirstate(self._opener(filename, "w", atomictemp=True,
1138 checkambig=True))
1141 checkambig=True))
1139
1142
1140 if tr:
1143 if tr:
1141 # ensure that subsequent tr.writepending returns True for
1144 # ensure that subsequent tr.writepending returns True for
1142 # changes written out above, even if dirstate is never
1145 # changes written out above, even if dirstate is never
1143 # changed after this
1146 # changed after this
1144 tr.addfilegenerator('dirstate', (self._filename,),
1147 tr.addfilegenerator('dirstate', (self._filename,),
1145 self._writedirstate, location='plain')
1148 self._writedirstate, location='plain')
1146
1149
1147 # ensure that pending file written above is unlinked at
1150 # ensure that pending file written above is unlinked at
1148 # failure, even if tr.writepending isn't invoked until the
1151 # failure, even if tr.writepending isn't invoked until the
1149 # end of this transaction
1152 # end of this transaction
1150 tr.registertmp(filename, location='plain')
1153 tr.registertmp(filename, location='plain')
1151
1154
1152 self._opener.tryunlink(backupname)
1155 self._opener.tryunlink(backupname)
1153 # hardlink backup is okay because _writedirstate is always called
1156 # hardlink backup is okay because _writedirstate is always called
1154 # with an "atomictemp=True" file.
1157 # with an "atomictemp=True" file.
1155 util.copyfile(self._opener.join(filename),
1158 util.copyfile(self._opener.join(filename),
1156 self._opener.join(backupname), hardlink=True)
1159 self._opener.join(backupname), hardlink=True)
1157
1160
1158 def restorebackup(self, tr, backupname):
1161 def restorebackup(self, tr, backupname):
1159 '''Restore dirstate by backup file'''
1162 '''Restore dirstate by backup file'''
1160 # this "invalidate()" prevents "wlock.release()" from writing
1163 # this "invalidate()" prevents "wlock.release()" from writing
1161 # changes of dirstate out after restoring from backup file
1164 # changes of dirstate out after restoring from backup file
1162 self.invalidate()
1165 self.invalidate()
1163 filename = self._actualfilename(tr)
1166 filename = self._actualfilename(tr)
1164 o = self._opener
1167 o = self._opener
1165 if util.samefile(o.join(backupname), o.join(filename)):
1168 if util.samefile(o.join(backupname), o.join(filename)):
1166 o.unlink(backupname)
1169 o.unlink(backupname)
1167 else:
1170 else:
1168 o.rename(backupname, filename, checkambig=True)
1171 o.rename(backupname, filename, checkambig=True)
1169
1172
1170 def clearbackup(self, tr, backupname):
1173 def clearbackup(self, tr, backupname):
1171 '''Clear backup file'''
1174 '''Clear backup file'''
1172 self._opener.unlink(backupname)
1175 self._opener.unlink(backupname)
1173
1176
1174 class dirstatemap(object):
1177 class dirstatemap(object):
1175 """Map encapsulating the dirstate's contents.
1178 """Map encapsulating the dirstate's contents.
1176
1179
1177 The dirstate contains the following state:
1180 The dirstate contains the following state:
1178
1181
1179 - `identity` is the identity of the dirstate file, which can be used to
1182 - `identity` is the identity of the dirstate file, which can be used to
1180 detect when changes have occurred to the dirstate file.
1183 detect when changes have occurred to the dirstate file.
1181
1184
1182 - `parents` is a pair containing the parents of the working copy. The
1185 - `parents` is a pair containing the parents of the working copy. The
1183 parents are updated by calling `setparents`.
1186 parents are updated by calling `setparents`.
1184
1187
1185 - the state map maps filenames to tuples of (state, mode, size, mtime),
1188 - the state map maps filenames to tuples of (state, mode, size, mtime),
1186 where state is a single character representing 'normal', 'added',
1189 where state is a single character representing 'normal', 'added',
1187 'removed', or 'merged'. It is read by treating the dirstate as a
1190 'removed', or 'merged'. It is read by treating the dirstate as a
1188 dict. File state is updated by calling the `addfile`, `removefile` and
1191 dict. File state is updated by calling the `addfile`, `removefile` and
1189 `dropfile` methods.
1192 `dropfile` methods.
1190
1193
1191 - `copymap` maps destination filenames to their source filename.
1194 - `copymap` maps destination filenames to their source filename.
1192
1195
1193 The dirstate also provides the following views onto the state:
1196 The dirstate also provides the following views onto the state:
1194
1197
1195 - `nonnormalset` is a set of the filenames that have state other
1198 - `nonnormalset` is a set of the filenames that have state other
1196 than 'normal', or are normal but have an mtime of -1 ('normallookup').
1199 than 'normal', or are normal but have an mtime of -1 ('normallookup').
1197
1200
1198 - `otherparentset` is a set of the filenames that are marked as coming
1201 - `otherparentset` is a set of the filenames that are marked as coming
1199 from the second parent when the dirstate is currently being merged.
1202 from the second parent when the dirstate is currently being merged.
1200
1203
1201 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
1204 - `filefoldmap` is a dict mapping normalized filenames to the denormalized
1202 form that they appear as in the dirstate.
1205 form that they appear as in the dirstate.
1203
1206
1204 - `dirfoldmap` is a dict mapping normalized directory names to the
1207 - `dirfoldmap` is a dict mapping normalized directory names to the
1205 denormalized form that they appear as in the dirstate.
1208 denormalized form that they appear as in the dirstate.
1206 """
1209 """
1207
1210
1208 def __init__(self, ui, opener, root):
1211 def __init__(self, ui, opener, root):
1209 self._ui = ui
1212 self._ui = ui
1210 self._opener = opener
1213 self._opener = opener
1211 self._root = root
1214 self._root = root
1212 self._filename = 'dirstate'
1215 self._filename = 'dirstate'
1213
1216
1214 self._parents = None
1217 self._parents = None
1215 self._dirtyparents = False
1218 self._dirtyparents = False
1216
1219
1217 # for consistent view between _pl() and _read() invocations
1220 # for consistent view between _pl() and _read() invocations
1218 self._pendingmode = None
1221 self._pendingmode = None
1219
1222
1220 @propertycache
1223 @propertycache
1221 def _map(self):
1224 def _map(self):
1222 self._map = {}
1225 self._map = {}
1223 self.read()
1226 self.read()
1224 return self._map
1227 return self._map
1225
1228
1226 @propertycache
1229 @propertycache
1227 def copymap(self):
1230 def copymap(self):
1228 self.copymap = {}
1231 self.copymap = {}
1229 self._map
1232 self._map
1230 return self.copymap
1233 return self.copymap
1231
1234
1232 def clear(self):
1235 def clear(self):
1233 self._map.clear()
1236 self._map.clear()
1234 self.copymap.clear()
1237 self.copymap.clear()
1235 self.setparents(nullid, nullid)
1238 self.setparents(nullid, nullid)
1236 util.clearcachedproperty(self, "_dirs")
1239 util.clearcachedproperty(self, "_dirs")
1237 util.clearcachedproperty(self, "_alldirs")
1240 util.clearcachedproperty(self, "_alldirs")
1238 util.clearcachedproperty(self, "filefoldmap")
1241 util.clearcachedproperty(self, "filefoldmap")
1239 util.clearcachedproperty(self, "dirfoldmap")
1242 util.clearcachedproperty(self, "dirfoldmap")
1240 util.clearcachedproperty(self, "nonnormalset")
1243 util.clearcachedproperty(self, "nonnormalset")
1241 util.clearcachedproperty(self, "otherparentset")
1244 util.clearcachedproperty(self, "otherparentset")
1242
1245
1243 def items(self):
1246 def items(self):
1244 return self._map.iteritems()
1247 return self._map.iteritems()
1245
1248
1246 # forward for python2,3 compat
1249 # forward for python2,3 compat
1247 iteritems = items
1250 iteritems = items
1248
1251
1249 def __len__(self):
1252 def __len__(self):
1250 return len(self._map)
1253 return len(self._map)
1251
1254
1252 def __iter__(self):
1255 def __iter__(self):
1253 return iter(self._map)
1256 return iter(self._map)
1254
1257
1255 def get(self, key, default=None):
1258 def get(self, key, default=None):
1256 return self._map.get(key, default)
1259 return self._map.get(key, default)
1257
1260
1258 def __contains__(self, key):
1261 def __contains__(self, key):
1259 return key in self._map
1262 return key in self._map
1260
1263
1261 def __getitem__(self, key):
1264 def __getitem__(self, key):
1262 return self._map[key]
1265 return self._map[key]
1263
1266
1264 def keys(self):
1267 def keys(self):
1265 return self._map.keys()
1268 return self._map.keys()
1266
1269
1267 def preload(self):
1270 def preload(self):
1268 """Loads the underlying data, if it's not already loaded"""
1271 """Loads the underlying data, if it's not already loaded"""
1269 self._map
1272 self._map
1270
1273
1271 def addfile(self, f, oldstate, state, mode, size, mtime):
1274 def addfile(self, f, oldstate, state, mode, size, mtime):
1272 """Add a tracked file to the dirstate."""
1275 """Add a tracked file to the dirstate."""
1273 if oldstate in "?r" and r"_dirs" in self.__dict__:
1276 if oldstate in "?r" and r"_dirs" in self.__dict__:
1274 self._dirs.addpath(f)
1277 self._dirs.addpath(f)
1275 if oldstate == "?" and r"_alldirs" in self.__dict__:
1278 if oldstate == "?" and r"_alldirs" in self.__dict__:
1276 self._alldirs.addpath(f)
1279 self._alldirs.addpath(f)
1277 self._map[f] = dirstatetuple(state, mode, size, mtime)
1280 self._map[f] = dirstatetuple(state, mode, size, mtime)
1278 if state != 'n' or mtime == -1:
1281 if state != 'n' or mtime == -1:
1279 self.nonnormalset.add(f)
1282 self.nonnormalset.add(f)
1280 if size == -2:
1283 if size == -2:
1281 self.otherparentset.add(f)
1284 self.otherparentset.add(f)
1282
1285
1283 def removefile(self, f, oldstate, size):
1286 def removefile(self, f, oldstate, size):
1284 """
1287 """
1285 Mark a file as removed in the dirstate.
1288 Mark a file as removed in the dirstate.
1286
1289
1287 The `size` parameter is used to store sentinel values that indicate
1290 The `size` parameter is used to store sentinel values that indicate
1288 the file's previous state. In the future, we should refactor this
1291 the file's previous state. In the future, we should refactor this
1289 to be more explicit about what that state is.
1292 to be more explicit about what that state is.
1290 """
1293 """
1291 if oldstate not in "?r" and r"_dirs" in self.__dict__:
1294 if oldstate not in "?r" and r"_dirs" in self.__dict__:
1292 self._dirs.delpath(f)
1295 self._dirs.delpath(f)
1293 if oldstate == "?" and r"_alldirs" in self.__dict__:
1296 if oldstate == "?" and r"_alldirs" in self.__dict__:
1294 self._alldirs.addpath(f)
1297 self._alldirs.addpath(f)
1295 if r"filefoldmap" in self.__dict__:
1298 if r"filefoldmap" in self.__dict__:
1296 normed = util.normcase(f)
1299 normed = util.normcase(f)
1297 self.filefoldmap.pop(normed, None)
1300 self.filefoldmap.pop(normed, None)
1298 self._map[f] = dirstatetuple('r', 0, size, 0)
1301 self._map[f] = dirstatetuple('r', 0, size, 0)
1299 self.nonnormalset.add(f)
1302 self.nonnormalset.add(f)
1300
1303
1301 def dropfile(self, f, oldstate):
1304 def dropfile(self, f, oldstate):
1302 """
1305 """
1303 Remove a file from the dirstate. Returns True if the file was
1306 Remove a file from the dirstate. Returns True if the file was
1304 previously recorded.
1307 previously recorded.
1305 """
1308 """
1306 exists = self._map.pop(f, None) is not None
1309 exists = self._map.pop(f, None) is not None
1307 if exists:
1310 if exists:
1308 if oldstate != "r" and r"_dirs" in self.__dict__:
1311 if oldstate != "r" and r"_dirs" in self.__dict__:
1309 self._dirs.delpath(f)
1312 self._dirs.delpath(f)
1310 if r"_alldirs" in self.__dict__:
1313 if r"_alldirs" in self.__dict__:
1311 self._alldirs.delpath(f)
1314 self._alldirs.delpath(f)
1312 if r"filefoldmap" in self.__dict__:
1315 if r"filefoldmap" in self.__dict__:
1313 normed = util.normcase(f)
1316 normed = util.normcase(f)
1314 self.filefoldmap.pop(normed, None)
1317 self.filefoldmap.pop(normed, None)
1315 self.nonnormalset.discard(f)
1318 self.nonnormalset.discard(f)
1316 return exists
1319 return exists
1317
1320
1318 def clearambiguoustimes(self, files, now):
1321 def clearambiguoustimes(self, files, now):
1319 for f in files:
1322 for f in files:
1320 e = self.get(f)
1323 e = self.get(f)
1321 if e is not None and e[0] == 'n' and e[3] == now:
1324 if e is not None and e[0] == 'n' and e[3] == now:
1322 self._map[f] = dirstatetuple(e[0], e[1], e[2], -1)
1325 self._map[f] = dirstatetuple(e[0], e[1], e[2], -1)
1323 self.nonnormalset.add(f)
1326 self.nonnormalset.add(f)
1324
1327
1325 def nonnormalentries(self):
1328 def nonnormalentries(self):
1326 '''Compute the nonnormal dirstate entries from the dmap'''
1329 '''Compute the nonnormal dirstate entries from the dmap'''
1327 try:
1330 try:
1328 return parsers.nonnormalotherparententries(self._map)
1331 return parsers.nonnormalotherparententries(self._map)
1329 except AttributeError:
1332 except AttributeError:
1330 nonnorm = set()
1333 nonnorm = set()
1331 otherparent = set()
1334 otherparent = set()
1332 for fname, e in self._map.iteritems():
1335 for fname, e in self._map.iteritems():
1333 if e[0] != 'n' or e[3] == -1:
1336 if e[0] != 'n' or e[3] == -1:
1334 nonnorm.add(fname)
1337 nonnorm.add(fname)
1335 if e[0] == 'n' and e[2] == -2:
1338 if e[0] == 'n' and e[2] == -2:
1336 otherparent.add(fname)
1339 otherparent.add(fname)
1337 return nonnorm, otherparent
1340 return nonnorm, otherparent
1338
1341
1339 @propertycache
1342 @propertycache
1340 def filefoldmap(self):
1343 def filefoldmap(self):
1341 """Returns a dictionary mapping normalized case paths to their
1344 """Returns a dictionary mapping normalized case paths to their
1342 non-normalized versions.
1345 non-normalized versions.
1343 """
1346 """
1344 try:
1347 try:
1345 makefilefoldmap = parsers.make_file_foldmap
1348 makefilefoldmap = parsers.make_file_foldmap
1346 except AttributeError:
1349 except AttributeError:
1347 pass
1350 pass
1348 else:
1351 else:
1349 return makefilefoldmap(self._map, util.normcasespec,
1352 return makefilefoldmap(self._map, util.normcasespec,
1350 util.normcasefallback)
1353 util.normcasefallback)
1351
1354
1352 f = {}
1355 f = {}
1353 normcase = util.normcase
1356 normcase = util.normcase
1354 for name, s in self._map.iteritems():
1357 for name, s in self._map.iteritems():
1355 if s[0] != 'r':
1358 if s[0] != 'r':
1356 f[normcase(name)] = name
1359 f[normcase(name)] = name
1357 f['.'] = '.' # prevents useless util.fspath() invocation
1360 f['.'] = '.' # prevents useless util.fspath() invocation
1358 return f
1361 return f
1359
1362
1360 def hastrackeddir(self, d):
1363 def hastrackeddir(self, d):
1361 """
1364 """
1362 Returns True if the dirstate contains a tracked (not removed) file
1365 Returns True if the dirstate contains a tracked (not removed) file
1363 in this directory.
1366 in this directory.
1364 """
1367 """
1365 return d in self._dirs
1368 return d in self._dirs
1366
1369
1367 def hasdir(self, d):
1370 def hasdir(self, d):
1368 """
1371 """
1369 Returns True if the dirstate contains a file (tracked or removed)
1372 Returns True if the dirstate contains a file (tracked or removed)
1370 in this directory.
1373 in this directory.
1371 """
1374 """
1372 return d in self._alldirs
1375 return d in self._alldirs
1373
1376
1374 @propertycache
1377 @propertycache
1375 def _dirs(self):
1378 def _dirs(self):
1376 return util.dirs(self._map, 'r')
1379 return util.dirs(self._map, 'r')
1377
1380
1378 @propertycache
1381 @propertycache
1379 def _alldirs(self):
1382 def _alldirs(self):
1380 return util.dirs(self._map)
1383 return util.dirs(self._map)
1381
1384
1382 def _opendirstatefile(self):
1385 def _opendirstatefile(self):
1383 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
1386 fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
1384 if self._pendingmode is not None and self._pendingmode != mode:
1387 if self._pendingmode is not None and self._pendingmode != mode:
1385 fp.close()
1388 fp.close()
1386 raise error.Abort(_('working directory state may be '
1389 raise error.Abort(_('working directory state may be '
1387 'changed parallelly'))
1390 'changed parallelly'))
1388 self._pendingmode = mode
1391 self._pendingmode = mode
1389 return fp
1392 return fp
1390
1393
1391 def parents(self):
1394 def parents(self):
1392 if not self._parents:
1395 if not self._parents:
1393 try:
1396 try:
1394 fp = self._opendirstatefile()
1397 fp = self._opendirstatefile()
1395 st = fp.read(40)
1398 st = fp.read(40)
1396 fp.close()
1399 fp.close()
1397 except IOError as err:
1400 except IOError as err:
1398 if err.errno != errno.ENOENT:
1401 if err.errno != errno.ENOENT:
1399 raise
1402 raise
1400 # File doesn't exist, so the current state is empty
1403 # File doesn't exist, so the current state is empty
1401 st = ''
1404 st = ''
1402
1405
1403 l = len(st)
1406 l = len(st)
1404 if l == 40:
1407 if l == 40:
1405 self._parents = st[:20], st[20:40]
1408 self._parents = st[:20], st[20:40]
1406 elif l == 0:
1409 elif l == 0:
1407 self._parents = [nullid, nullid]
1410 self._parents = [nullid, nullid]
1408 else:
1411 else:
1409 raise error.Abort(_('working directory state appears '
1412 raise error.Abort(_('working directory state appears '
1410 'damaged!'))
1413 'damaged!'))
1411
1414
1412 return self._parents
1415 return self._parents
1413
1416
1414 def setparents(self, p1, p2):
1417 def setparents(self, p1, p2):
1415 self._parents = (p1, p2)
1418 self._parents = (p1, p2)
1416 self._dirtyparents = True
1419 self._dirtyparents = True
1417
1420
1418 def read(self):
1421 def read(self):
1419 # ignore HG_PENDING because identity is used only for writing
1422 # ignore HG_PENDING because identity is used only for writing
1420 self.identity = util.filestat.frompath(
1423 self.identity = util.filestat.frompath(
1421 self._opener.join(self._filename))
1424 self._opener.join(self._filename))
1422
1425
1423 try:
1426 try:
1424 fp = self._opendirstatefile()
1427 fp = self._opendirstatefile()
1425 try:
1428 try:
1426 st = fp.read()
1429 st = fp.read()
1427 finally:
1430 finally:
1428 fp.close()
1431 fp.close()
1429 except IOError as err:
1432 except IOError as err:
1430 if err.errno != errno.ENOENT:
1433 if err.errno != errno.ENOENT:
1431 raise
1434 raise
1432 return
1435 return
1433 if not st:
1436 if not st:
1434 return
1437 return
1435
1438
1436 if util.safehasattr(parsers, 'dict_new_presized'):
1439 if util.safehasattr(parsers, 'dict_new_presized'):
1437 # Make an estimate of the number of files in the dirstate based on
1440 # Make an estimate of the number of files in the dirstate based on
1438 # its size. From a linear regression on a set of real-world repos,
1441 # its size. From a linear regression on a set of real-world repos,
1439 # all over 10,000 files, the size of a dirstate entry is 85
1442 # all over 10,000 files, the size of a dirstate entry is 85
1440 # bytes. The cost of resizing is significantly higher than the cost
1443 # bytes. The cost of resizing is significantly higher than the cost
1441 # of filling in a larger presized dict, so subtract 20% from the
1444 # of filling in a larger presized dict, so subtract 20% from the
1442 # size.
1445 # size.
1443 #
1446 #
1444 # This heuristic is imperfect in many ways, so in a future dirstate
1447 # This heuristic is imperfect in many ways, so in a future dirstate
1445 # format update it makes sense to just record the number of entries
1448 # format update it makes sense to just record the number of entries
1446 # on write.
1449 # on write.
1447 self._map = parsers.dict_new_presized(len(st) // 71)
1450 self._map = parsers.dict_new_presized(len(st) // 71)
1448
1451
1449 # Python's garbage collector triggers a GC each time a certain number
1452 # Python's garbage collector triggers a GC each time a certain number
1450 # of container objects (the number being defined by
1453 # of container objects (the number being defined by
1451 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
1454 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
1452 # for each file in the dirstate. The C version then immediately marks
1455 # for each file in the dirstate. The C version then immediately marks
1453 # them as not to be tracked by the collector. However, this has no
1456 # them as not to be tracked by the collector. However, this has no
1454 # effect on when GCs are triggered, only on what objects the GC looks
1457 # effect on when GCs are triggered, only on what objects the GC looks
1455 # into. This means that O(number of files) GCs are unavoidable.
1458 # into. This means that O(number of files) GCs are unavoidable.
1456 # Depending on when in the process's lifetime the dirstate is parsed,
1459 # Depending on when in the process's lifetime the dirstate is parsed,
1457 # this can get very expensive. As a workaround, disable GC while
1460 # this can get very expensive. As a workaround, disable GC while
1458 # parsing the dirstate.
1461 # parsing the dirstate.
1459 #
1462 #
1460 # (we cannot decorate the function directly since it is in a C module)
1463 # (we cannot decorate the function directly since it is in a C module)
1461 parse_dirstate = util.nogc(parsers.parse_dirstate)
1464 parse_dirstate = util.nogc(parsers.parse_dirstate)
1462 p = parse_dirstate(self._map, self.copymap, st)
1465 p = parse_dirstate(self._map, self.copymap, st)
1463 if not self._dirtyparents:
1466 if not self._dirtyparents:
1464 self.setparents(*p)
1467 self.setparents(*p)
1465
1468
1466 # Avoid excess attribute lookups by fast pathing certain checks
1469 # Avoid excess attribute lookups by fast pathing certain checks
1467 self.__contains__ = self._map.__contains__
1470 self.__contains__ = self._map.__contains__
1468 self.__getitem__ = self._map.__getitem__
1471 self.__getitem__ = self._map.__getitem__
1469 self.get = self._map.get
1472 self.get = self._map.get
1470
1473
1471 def write(self, st, now):
1474 def write(self, st, now):
1472 st.write(parsers.pack_dirstate(self._map, self.copymap,
1475 st.write(parsers.pack_dirstate(self._map, self.copymap,
1473 self.parents(), now))
1476 self.parents(), now))
1474 st.close()
1477 st.close()
1475 self._dirtyparents = False
1478 self._dirtyparents = False
1476 self.nonnormalset, self.otherparentset = self.nonnormalentries()
1479 self.nonnormalset, self.otherparentset = self.nonnormalentries()
1477
1480
1478 @propertycache
1481 @propertycache
1479 def nonnormalset(self):
1482 def nonnormalset(self):
1480 nonnorm, otherparents = self.nonnormalentries()
1483 nonnorm, otherparents = self.nonnormalentries()
1481 self.otherparentset = otherparents
1484 self.otherparentset = otherparents
1482 return nonnorm
1485 return nonnorm
1483
1486
1484 @propertycache
1487 @propertycache
1485 def otherparentset(self):
1488 def otherparentset(self):
1486 nonnorm, otherparents = self.nonnormalentries()
1489 nonnorm, otherparents = self.nonnormalentries()
1487 self.nonnormalset = nonnorm
1490 self.nonnormalset = nonnorm
1488 return otherparents
1491 return otherparents
1489
1492
1490 @propertycache
1493 @propertycache
1491 def identity(self):
1494 def identity(self):
1492 self._map
1495 self._map
1493 return self.identity
1496 return self.identity
1494
1497
1495 @propertycache
1498 @propertycache
1496 def dirfoldmap(self):
1499 def dirfoldmap(self):
1497 f = {}
1500 f = {}
1498 normcase = util.normcase
1501 normcase = util.normcase
1499 for name in self._dirs:
1502 for name in self._dirs:
1500 f[normcase(name)] = name
1503 f[normcase(name)] = name
1501 return f
1504 return f
@@ -1,1321 +1,1332 b''
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import copy
10 import copy
11 import itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14
14
15 from .i18n import _
15 from .i18n import _
16 from . import (
16 from . import (
17 encoding,
17 encoding,
18 error,
18 error,
19 pathutil,
19 pathutil,
20 pycompat,
20 pycompat,
21 util,
21 util,
22 )
22 )
23 from .utils import (
23 from .utils import (
24 stringutil,
24 stringutil,
25 )
25 )
26
26
27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
28 'listfile', 'listfile0', 'set', 'include', 'subinclude',
28 'listfile', 'listfile0', 'set', 'include', 'subinclude',
29 'rootfilesin')
29 'rootfilesin')
30 cwdrelativepatternkinds = ('relpath', 'glob')
30 cwdrelativepatternkinds = ('relpath', 'glob')
31
31
32 propertycache = util.propertycache
32 propertycache = util.propertycache
33
33
34 def _rematcher(regex):
34 def _rematcher(regex):
35 '''compile the regexp with the best available regexp engine and return a
35 '''compile the regexp with the best available regexp engine and return a
36 matcher function'''
36 matcher function'''
37 m = util.re.compile(regex)
37 m = util.re.compile(regex)
38 try:
38 try:
39 # slightly faster, provided by facebook's re2 bindings
39 # slightly faster, provided by facebook's re2 bindings
40 return m.test_match
40 return m.test_match
41 except AttributeError:
41 except AttributeError:
42 return m.match
42 return m.match
43
43
44 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
44 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
45 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
45 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
46 matchers = []
46 matchers = []
47 other = []
47 other = []
48
48
49 for kind, pat, source in kindpats:
49 for kind, pat, source in kindpats:
50 if kind == 'set':
50 if kind == 'set':
51 if not ctx:
51 if not ctx:
52 raise error.ProgrammingError("fileset expression with no "
52 raise error.ProgrammingError("fileset expression with no "
53 "context")
53 "context")
54 matchers.append(ctx.matchfileset(pat, badfn=badfn))
54 matchers.append(ctx.matchfileset(pat, badfn=badfn))
55
55
56 if listsubrepos:
56 if listsubrepos:
57 for subpath in ctx.substate:
57 for subpath in ctx.substate:
58 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
58 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
59 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
59 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
60 matchers.append(pm)
60 matchers.append(pm)
61
61
62 continue
62 continue
63 other.append((kind, pat, source))
63 other.append((kind, pat, source))
64 return matchers, other
64 return matchers, other
65
65
66 def _expandsubinclude(kindpats, root):
66 def _expandsubinclude(kindpats, root):
67 '''Returns the list of subinclude matcher args and the kindpats without the
67 '''Returns the list of subinclude matcher args and the kindpats without the
68 subincludes in it.'''
68 subincludes in it.'''
69 relmatchers = []
69 relmatchers = []
70 other = []
70 other = []
71
71
72 for kind, pat, source in kindpats:
72 for kind, pat, source in kindpats:
73 if kind == 'subinclude':
73 if kind == 'subinclude':
74 sourceroot = pathutil.dirname(util.normpath(source))
74 sourceroot = pathutil.dirname(util.normpath(source))
75 pat = util.pconvert(pat)
75 pat = util.pconvert(pat)
76 path = pathutil.join(sourceroot, pat)
76 path = pathutil.join(sourceroot, pat)
77
77
78 newroot = pathutil.dirname(path)
78 newroot = pathutil.dirname(path)
79 matcherargs = (newroot, '', [], ['include:%s' % path])
79 matcherargs = (newroot, '', [], ['include:%s' % path])
80
80
81 prefix = pathutil.canonpath(root, root, newroot)
81 prefix = pathutil.canonpath(root, root, newroot)
82 if prefix:
82 if prefix:
83 prefix += '/'
83 prefix += '/'
84 relmatchers.append((prefix, matcherargs))
84 relmatchers.append((prefix, matcherargs))
85 else:
85 else:
86 other.append((kind, pat, source))
86 other.append((kind, pat, source))
87
87
88 return relmatchers, other
88 return relmatchers, other
89
89
90 def _kindpatsalwaysmatch(kindpats):
90 def _kindpatsalwaysmatch(kindpats):
91 """"Checks whether the kindspats match everything, as e.g.
91 """"Checks whether the kindspats match everything, as e.g.
92 'relpath:.' does.
92 'relpath:.' does.
93 """
93 """
94 for kind, pat, source in kindpats:
94 for kind, pat, source in kindpats:
95 if pat != '' or kind not in ['relpath', 'glob']:
95 if pat != '' or kind not in ['relpath', 'glob']:
96 return False
96 return False
97 return True
97 return True
98
98
99 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
99 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
100 listsubrepos=False, badfn=None):
100 listsubrepos=False, badfn=None):
101 matchers = []
101 matchers = []
102 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
102 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
103 listsubrepos=listsubrepos, badfn=badfn)
103 listsubrepos=listsubrepos, badfn=badfn)
104 if kindpats:
104 if kindpats:
105 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
105 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
106 badfn=badfn)
106 badfn=badfn)
107 matchers.append(m)
107 matchers.append(m)
108 if fms:
108 if fms:
109 matchers.extend(fms)
109 matchers.extend(fms)
110 if not matchers:
110 if not matchers:
111 return nevermatcher(root, cwd, badfn=badfn)
111 return nevermatcher(root, cwd, badfn=badfn)
112 if len(matchers) == 1:
112 if len(matchers) == 1:
113 return matchers[0]
113 return matchers[0]
114 return unionmatcher(matchers)
114 return unionmatcher(matchers)
115
115
116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
118 badfn=None, icasefs=False):
118 badfn=None, icasefs=False):
119 """build an object to match a set of file patterns
119 """build an object to match a set of file patterns
120
120
121 arguments:
121 arguments:
122 root - the canonical root of the tree you're matching against
122 root - the canonical root of the tree you're matching against
123 cwd - the current working directory, if relevant
123 cwd - the current working directory, if relevant
124 patterns - patterns to find
124 patterns - patterns to find
125 include - patterns to include (unless they are excluded)
125 include - patterns to include (unless they are excluded)
126 exclude - patterns to exclude (even if they are included)
126 exclude - patterns to exclude (even if they are included)
127 default - if a pattern in patterns has no explicit type, assume this one
127 default - if a pattern in patterns has no explicit type, assume this one
128 exact - patterns are actually filenames (include/exclude still apply)
128 exact - patterns are actually filenames (include/exclude still apply)
129 warn - optional function used for printing warnings
129 warn - optional function used for printing warnings
130 badfn - optional bad() callback for this matcher instead of the default
130 badfn - optional bad() callback for this matcher instead of the default
131 icasefs - make a matcher for wdir on case insensitive filesystems, which
131 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 normalizes the given patterns to the case in the filesystem
132 normalizes the given patterns to the case in the filesystem
133
133
134 a pattern is one of:
134 a pattern is one of:
135 'glob:<glob>' - a glob relative to cwd
135 'glob:<glob>' - a glob relative to cwd
136 're:<regexp>' - a regular expression
136 're:<regexp>' - a regular expression
137 'path:<path>' - a path relative to repository root, which is matched
137 'path:<path>' - a path relative to repository root, which is matched
138 recursively
138 recursively
139 'rootfilesin:<path>' - a path relative to repository root, which is
139 'rootfilesin:<path>' - a path relative to repository root, which is
140 matched non-recursively (will not match subdirectories)
140 matched non-recursively (will not match subdirectories)
141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 'relpath:<path>' - a path relative to cwd
142 'relpath:<path>' - a path relative to cwd
143 'relre:<regexp>' - a regexp that needn't match the start of a name
143 'relre:<regexp>' - a regexp that needn't match the start of a name
144 'set:<fileset>' - a fileset expression
144 'set:<fileset>' - a fileset expression
145 'include:<path>' - a file of patterns to read and include
145 'include:<path>' - a file of patterns to read and include
146 'subinclude:<path>' - a file of patterns to match against files under
146 'subinclude:<path>' - a file of patterns to match against files under
147 the same directory
147 the same directory
148 '<something>' - a pattern of the specified default type
148 '<something>' - a pattern of the specified default type
149 """
149 """
150 normalize = _donormalize
150 normalize = _donormalize
151 if icasefs:
151 if icasefs:
152 if exact:
152 if exact:
153 raise error.ProgrammingError("a case-insensitive exact matcher "
153 raise error.ProgrammingError("a case-insensitive exact matcher "
154 "doesn't make sense")
154 "doesn't make sense")
155 dirstate = ctx.repo().dirstate
155 dirstate = ctx.repo().dirstate
156 dsnormalize = dirstate.normalize
156 dsnormalize = dirstate.normalize
157
157
158 def normalize(patterns, default, root, cwd, auditor, warn):
158 def normalize(patterns, default, root, cwd, auditor, warn):
159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
160 kindpats = []
160 kindpats = []
161 for kind, pats, source in kp:
161 for kind, pats, source in kp:
162 if kind not in ('re', 'relre'): # regex can't be normalized
162 if kind not in ('re', 'relre'): # regex can't be normalized
163 p = pats
163 p = pats
164 pats = dsnormalize(pats)
164 pats = dsnormalize(pats)
165
165
166 # Preserve the original to handle a case only rename.
166 # Preserve the original to handle a case only rename.
167 if p != pats and p in dirstate:
167 if p != pats and p in dirstate:
168 kindpats.append((kind, p, source))
168 kindpats.append((kind, p, source))
169
169
170 kindpats.append((kind, pats, source))
170 kindpats.append((kind, pats, source))
171 return kindpats
171 return kindpats
172
172
173 if exact:
173 if exact:
174 m = exactmatcher(root, cwd, patterns, badfn)
174 m = exactmatcher(root, cwd, patterns, badfn)
175 elif patterns:
175 elif patterns:
176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
177 if _kindpatsalwaysmatch(kindpats):
177 if _kindpatsalwaysmatch(kindpats):
178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
179 else:
179 else:
180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
181 ctx=ctx, listsubrepos=listsubrepos,
181 ctx=ctx, listsubrepos=listsubrepos,
182 badfn=badfn)
182 badfn=badfn)
183 else:
183 else:
184 # It's a little strange that no patterns means to match everything.
184 # It's a little strange that no patterns means to match everything.
185 # Consider changing this to match nothing (probably using nevermatcher).
185 # Consider changing this to match nothing (probably using nevermatcher).
186 m = alwaysmatcher(root, cwd, badfn)
186 m = alwaysmatcher(root, cwd, badfn)
187
187
188 if include:
188 if include:
189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 listsubrepos=listsubrepos, badfn=None)
191 listsubrepos=listsubrepos, badfn=None)
192 m = intersectmatchers(m, im)
192 m = intersectmatchers(m, im)
193 if exclude:
193 if exclude:
194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
196 listsubrepos=listsubrepos, badfn=None)
196 listsubrepos=listsubrepos, badfn=None)
197 m = differencematcher(m, em)
197 m = differencematcher(m, em)
198 return m
198 return m
199
199
200 def exact(root, cwd, files, badfn=None):
200 def exact(root, cwd, files, badfn=None):
201 return exactmatcher(root, cwd, files, badfn=badfn)
201 return exactmatcher(root, cwd, files, badfn=badfn)
202
202
203 def always(root, cwd):
203 def always(root, cwd):
204 return alwaysmatcher(root, cwd)
204 return alwaysmatcher(root, cwd)
205
205
206 def never(root, cwd):
206 def never(root, cwd):
207 return nevermatcher(root, cwd)
207 return nevermatcher(root, cwd)
208
208
209 def badmatch(match, badfn):
209 def badmatch(match, badfn):
210 """Make a copy of the given matcher, replacing its bad method with the given
210 """Make a copy of the given matcher, replacing its bad method with the given
211 one.
211 one.
212 """
212 """
213 m = copy.copy(match)
213 m = copy.copy(match)
214 m.bad = badfn
214 m.bad = badfn
215 return m
215 return m
216
216
217 def _donormalize(patterns, default, root, cwd, auditor, warn):
217 def _donormalize(patterns, default, root, cwd, auditor, warn):
218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
219 normalized and rooted patterns and with listfiles expanded.'''
219 normalized and rooted patterns and with listfiles expanded.'''
220 kindpats = []
220 kindpats = []
221 for kind, pat in [_patsplit(p, default) for p in patterns]:
221 for kind, pat in [_patsplit(p, default) for p in patterns]:
222 if kind in cwdrelativepatternkinds:
222 if kind in cwdrelativepatternkinds:
223 pat = pathutil.canonpath(root, cwd, pat, auditor)
223 pat = pathutil.canonpath(root, cwd, pat, auditor)
224 elif kind in ('relglob', 'path', 'rootfilesin'):
224 elif kind in ('relglob', 'path', 'rootfilesin'):
225 pat = util.normpath(pat)
225 pat = util.normpath(pat)
226 elif kind in ('listfile', 'listfile0'):
226 elif kind in ('listfile', 'listfile0'):
227 try:
227 try:
228 files = util.readfile(pat)
228 files = util.readfile(pat)
229 if kind == 'listfile0':
229 if kind == 'listfile0':
230 files = files.split('\0')
230 files = files.split('\0')
231 else:
231 else:
232 files = files.splitlines()
232 files = files.splitlines()
233 files = [f for f in files if f]
233 files = [f for f in files if f]
234 except EnvironmentError:
234 except EnvironmentError:
235 raise error.Abort(_("unable to read file list (%s)") % pat)
235 raise error.Abort(_("unable to read file list (%s)") % pat)
236 for k, p, source in _donormalize(files, default, root, cwd,
236 for k, p, source in _donormalize(files, default, root, cwd,
237 auditor, warn):
237 auditor, warn):
238 kindpats.append((k, p, pat))
238 kindpats.append((k, p, pat))
239 continue
239 continue
240 elif kind == 'include':
240 elif kind == 'include':
241 try:
241 try:
242 fullpath = os.path.join(root, util.localpath(pat))
242 fullpath = os.path.join(root, util.localpath(pat))
243 includepats = readpatternfile(fullpath, warn)
243 includepats = readpatternfile(fullpath, warn)
244 for k, p, source in _donormalize(includepats, default,
244 for k, p, source in _donormalize(includepats, default,
245 root, cwd, auditor, warn):
245 root, cwd, auditor, warn):
246 kindpats.append((k, p, source or pat))
246 kindpats.append((k, p, source or pat))
247 except error.Abort as inst:
247 except error.Abort as inst:
248 raise error.Abort('%s: %s' % (pat, inst[0]))
248 raise error.Abort('%s: %s' % (pat, inst[0]))
249 except IOError as inst:
249 except IOError as inst:
250 if warn:
250 if warn:
251 warn(_("skipping unreadable pattern file '%s': %s\n") %
251 warn(_("skipping unreadable pattern file '%s': %s\n") %
252 (pat, stringutil.forcebytestr(inst.strerror)))
252 (pat, stringutil.forcebytestr(inst.strerror)))
253 continue
253 continue
254 # else: re or relre - which cannot be normalized
254 # else: re or relre - which cannot be normalized
255 kindpats.append((kind, pat, ''))
255 kindpats.append((kind, pat, ''))
256 return kindpats
256 return kindpats
257
257
258 class basematcher(object):
258 class basematcher(object):
259
259
260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
261 self._root = root
261 self._root = root
262 self._cwd = cwd
262 self._cwd = cwd
263 if badfn is not None:
263 if badfn is not None:
264 self.bad = badfn
264 self.bad = badfn
265 self._relativeuipath = relativeuipath
265 self._relativeuipath = relativeuipath
266
266
267 def __call__(self, fn):
267 def __call__(self, fn):
268 return self.matchfn(fn)
268 return self.matchfn(fn)
269 def __iter__(self):
269 def __iter__(self):
270 for f in self._files:
270 for f in self._files:
271 yield f
271 yield f
272 # Callbacks related to how the matcher is used by dirstate.walk.
272 # Callbacks related to how the matcher is used by dirstate.walk.
273 # Subscribers to these events must monkeypatch the matcher object.
273 # Subscribers to these events must monkeypatch the matcher object.
274 def bad(self, f, msg):
274 def bad(self, f, msg):
275 '''Callback from dirstate.walk for each explicit file that can't be
275 '''Callback from dirstate.walk for each explicit file that can't be
276 found/accessed, with an error message.'''
276 found/accessed, with an error message.'''
277
277
278 # If an explicitdir is set, it will be called when an explicitly listed
278 # If an explicitdir is set, it will be called when an explicitly listed
279 # directory is visited.
279 # directory is visited.
280 explicitdir = None
280 explicitdir = None
281
281
282 # If an traversedir is set, it will be called when a directory discovered
282 # If an traversedir is set, it will be called when a directory discovered
283 # by recursive traversal is visited.
283 # by recursive traversal is visited.
284 traversedir = None
284 traversedir = None
285
285
286 def abs(self, f):
286 def abs(self, f):
287 '''Convert a repo path back to path that is relative to the root of the
287 '''Convert a repo path back to path that is relative to the root of the
288 matcher.'''
288 matcher.'''
289 return f
289 return f
290
290
291 def rel(self, f):
291 def rel(self, f):
292 '''Convert repo path back to path that is relative to cwd of matcher.'''
292 '''Convert repo path back to path that is relative to cwd of matcher.'''
293 return util.pathto(self._root, self._cwd, f)
293 return util.pathto(self._root, self._cwd, f)
294
294
295 def uipath(self, f):
295 def uipath(self, f):
296 '''Convert repo path to a display path. If patterns or -I/-X were used
296 '''Convert repo path to a display path. If patterns or -I/-X were used
297 to create this matcher, the display path will be relative to cwd.
297 to create this matcher, the display path will be relative to cwd.
298 Otherwise it is relative to the root of the repo.'''
298 Otherwise it is relative to the root of the repo.'''
299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
300
300
301 @propertycache
301 @propertycache
302 def _files(self):
302 def _files(self):
303 return []
303 return []
304
304
305 def files(self):
305 def files(self):
306 '''Explicitly listed files or patterns or roots:
306 '''Explicitly listed files or patterns or roots:
307 if no patterns or .always(): empty list,
307 if no patterns or .always(): empty list,
308 if exact: list exact files,
308 if exact: list exact files,
309 if not .anypats(): list all files and dirs,
309 if not .anypats(): list all files and dirs,
310 else: optimal roots'''
310 else: optimal roots'''
311 return self._files
311 return self._files
312
312
313 @propertycache
313 @propertycache
314 def _fileset(self):
314 def _fileset(self):
315 return set(self._files)
315 return set(self._files)
316
316
317 def exact(self, f):
317 def exact(self, f):
318 '''Returns True if f is in .files().'''
318 '''Returns True if f is in .files().'''
319 return f in self._fileset
319 return f in self._fileset
320
320
321 def matchfn(self, f):
321 def matchfn(self, f):
322 return False
322 return False
323
323
324 def visitdir(self, dir):
324 def visitdir(self, dir):
325 '''Decides whether a directory should be visited based on whether it
325 '''Decides whether a directory should be visited based on whether it
326 has potential matches in it or one of its subdirectories. This is
326 has potential matches in it or one of its subdirectories. This is
327 based on the match's primary, included, and excluded patterns.
327 based on the match's primary, included, and excluded patterns.
328
328
329 Returns the string 'all' if the given directory and all subdirectories
329 Returns the string 'all' if the given directory and all subdirectories
330 should be visited. Otherwise returns True or False indicating whether
330 should be visited. Otherwise returns True or False indicating whether
331 the given directory should be visited.
331 the given directory should be visited.
332 '''
332 '''
333 return True
333 return True
334
334
335 def visitchildrenset(self, dir):
335 def visitchildrenset(self, dir):
336 '''Decides whether a directory should be visited based on whether it
336 '''Decides whether a directory should be visited based on whether it
337 has potential matches in it or one of its subdirectories, and
337 has potential matches in it or one of its subdirectories, and
338 potentially lists which subdirectories of that directory should be
338 potentially lists which subdirectories of that directory should be
339 visited. This is based on the match's primary, included, and excluded
339 visited. This is based on the match's primary, included, and excluded
340 patterns.
340 patterns.
341
341
342 This function is very similar to 'visitdir', and the following mapping
342 This function is very similar to 'visitdir', and the following mapping
343 can be applied:
343 can be applied:
344
344
345 visitdir | visitchildrenlist
345 visitdir | visitchildrenlist
346 ----------+-------------------
346 ----------+-------------------
347 False | set()
347 False | set()
348 'all' | 'all'
348 'all' | 'all'
349 True | 'this' OR non-empty set of subdirs to visit
349 True | 'this' OR non-empty set of subdirs -or files- to visit
350
350
351 Example:
351 Example:
352 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
352 Assume matchers ['path:foo/bar', 'rootfilesin:qux'], we would return
353 the following values (assuming the implementation of visitchildrenset
353 the following values (assuming the implementation of visitchildrenset
354 is capable of recognizing this; some implementations are not).
354 is capable of recognizing this; some implementations are not).
355
355
356 '.' -> {'foo', 'qux'}
356 '.' -> {'foo', 'qux'}
357 'baz' -> set()
357 'baz' -> set()
358 'foo' -> {'bar'}
358 'foo' -> {'bar'}
359 # Ideally this would be 'all', but since the prefix nature of matchers
359 # Ideally this would be 'all', but since the prefix nature of matchers
360 # is applied to the entire matcher, we have to downgrade to this
360 # is applied to the entire matcher, we have to downgrade this to
361 # 'this' due to the non-prefix 'rootfilesin'-kind matcher.
361 # 'this' due to the non-prefix 'rootfilesin'-kind matcher being mixed
362 # in.
362 'foo/bar' -> 'this'
363 'foo/bar' -> 'this'
363 'qux' -> 'this'
364 'qux' -> 'this'
365
366 Important:
367 Most matchers do not know if they're representing files or
368 directories. They see ['path:dir/f'] and don't know whether 'f' is a
369 file or a directory, so visitchildrenset('dir') for most matchers will
370 return {'f'}, but if the matcher knows it's a file (like exactmatcher
371 does), it may return 'this'. Do not rely on the return being a set
372 indicating that there are no files in this dir to investigate (or
373 equivalently that if there are files to investigate in 'dir' that it
374 will always return 'this').
364 '''
375 '''
365 return 'this'
376 return 'this'
366
377
367 def always(self):
378 def always(self):
368 '''Matcher will match everything and .files() will be empty --
379 '''Matcher will match everything and .files() will be empty --
369 optimization might be possible.'''
380 optimization might be possible.'''
370 return False
381 return False
371
382
372 def isexact(self):
383 def isexact(self):
373 '''Matcher will match exactly the list of files in .files() --
384 '''Matcher will match exactly the list of files in .files() --
374 optimization might be possible.'''
385 optimization might be possible.'''
375 return False
386 return False
376
387
377 def prefix(self):
388 def prefix(self):
378 '''Matcher will match the paths in .files() recursively --
389 '''Matcher will match the paths in .files() recursively --
379 optimization might be possible.'''
390 optimization might be possible.'''
380 return False
391 return False
381
392
382 def anypats(self):
393 def anypats(self):
383 '''None of .always(), .isexact(), and .prefix() is true --
394 '''None of .always(), .isexact(), and .prefix() is true --
384 optimizations will be difficult.'''
395 optimizations will be difficult.'''
385 return not self.always() and not self.isexact() and not self.prefix()
396 return not self.always() and not self.isexact() and not self.prefix()
386
397
387 class alwaysmatcher(basematcher):
398 class alwaysmatcher(basematcher):
388 '''Matches everything.'''
399 '''Matches everything.'''
389
400
390 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
401 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
391 super(alwaysmatcher, self).__init__(root, cwd, badfn,
402 super(alwaysmatcher, self).__init__(root, cwd, badfn,
392 relativeuipath=relativeuipath)
403 relativeuipath=relativeuipath)
393
404
394 def always(self):
405 def always(self):
395 return True
406 return True
396
407
397 def matchfn(self, f):
408 def matchfn(self, f):
398 return True
409 return True
399
410
400 def visitdir(self, dir):
411 def visitdir(self, dir):
401 return 'all'
412 return 'all'
402
413
403 def visitchildrenset(self, dir):
414 def visitchildrenset(self, dir):
404 return 'all'
415 return 'all'
405
416
406 def __repr__(self):
417 def __repr__(self):
407 return r'<alwaysmatcher>'
418 return r'<alwaysmatcher>'
408
419
409 class nevermatcher(basematcher):
420 class nevermatcher(basematcher):
410 '''Matches nothing.'''
421 '''Matches nothing.'''
411
422
412 def __init__(self, root, cwd, badfn=None):
423 def __init__(self, root, cwd, badfn=None):
413 super(nevermatcher, self).__init__(root, cwd, badfn)
424 super(nevermatcher, self).__init__(root, cwd, badfn)
414
425
415 # It's a little weird to say that the nevermatcher is an exact matcher
426 # It's a little weird to say that the nevermatcher is an exact matcher
416 # or a prefix matcher, but it seems to make sense to let callers take
427 # or a prefix matcher, but it seems to make sense to let callers take
417 # fast paths based on either. There will be no exact matches, nor any
428 # fast paths based on either. There will be no exact matches, nor any
418 # prefixes (files() returns []), so fast paths iterating over them should
429 # prefixes (files() returns []), so fast paths iterating over them should
419 # be efficient (and correct).
430 # be efficient (and correct).
420 def isexact(self):
431 def isexact(self):
421 return True
432 return True
422
433
423 def prefix(self):
434 def prefix(self):
424 return True
435 return True
425
436
426 def visitdir(self, dir):
437 def visitdir(self, dir):
427 return False
438 return False
428
439
429 def visitchildrenset(self, dir):
440 def visitchildrenset(self, dir):
430 return set()
441 return set()
431
442
432 def __repr__(self):
443 def __repr__(self):
433 return r'<nevermatcher>'
444 return r'<nevermatcher>'
434
445
435 class predicatematcher(basematcher):
446 class predicatematcher(basematcher):
436 """A matcher adapter for a simple boolean function"""
447 """A matcher adapter for a simple boolean function"""
437
448
438 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
449 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
439 super(predicatematcher, self).__init__(root, cwd, badfn)
450 super(predicatematcher, self).__init__(root, cwd, badfn)
440 self.matchfn = predfn
451 self.matchfn = predfn
441 self._predrepr = predrepr
452 self._predrepr = predrepr
442
453
443 @encoding.strmethod
454 @encoding.strmethod
444 def __repr__(self):
455 def __repr__(self):
445 s = (stringutil.buildrepr(self._predrepr)
456 s = (stringutil.buildrepr(self._predrepr)
446 or pycompat.byterepr(self.matchfn))
457 or pycompat.byterepr(self.matchfn))
447 return '<predicatenmatcher pred=%s>' % s
458 return '<predicatenmatcher pred=%s>' % s
448
459
449 class patternmatcher(basematcher):
460 class patternmatcher(basematcher):
450
461
451 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
462 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
452 super(patternmatcher, self).__init__(root, cwd, badfn)
463 super(patternmatcher, self).__init__(root, cwd, badfn)
453
464
454 self._files = _explicitfiles(kindpats)
465 self._files = _explicitfiles(kindpats)
455 self._prefix = _prefix(kindpats)
466 self._prefix = _prefix(kindpats)
456 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
467 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
457 root)
468 root)
458
469
459 @propertycache
470 @propertycache
460 def _dirs(self):
471 def _dirs(self):
461 return set(util.dirs(self._fileset)) | {'.'}
472 return set(util.dirs(self._fileset)) | {'.'}
462
473
463 def visitdir(self, dir):
474 def visitdir(self, dir):
464 if self._prefix and dir in self._fileset:
475 if self._prefix and dir in self._fileset:
465 return 'all'
476 return 'all'
466 return ('.' in self._fileset or
477 return ('.' in self._fileset or
467 dir in self._fileset or
478 dir in self._fileset or
468 dir in self._dirs or
479 dir in self._dirs or
469 any(parentdir in self._fileset
480 any(parentdir in self._fileset
470 for parentdir in util.finddirs(dir)))
481 for parentdir in util.finddirs(dir)))
471
482
472 def visitchildrenset(self, dir):
483 def visitchildrenset(self, dir):
473 ret = self.visitdir(dir)
484 ret = self.visitdir(dir)
474 if ret is True:
485 if ret is True:
475 return 'this'
486 return 'this'
476 elif not ret:
487 elif not ret:
477 return set()
488 return set()
478 assert ret == 'all'
489 assert ret == 'all'
479 return 'all'
490 return 'all'
480
491
481 def prefix(self):
492 def prefix(self):
482 return self._prefix
493 return self._prefix
483
494
484 @encoding.strmethod
495 @encoding.strmethod
485 def __repr__(self):
496 def __repr__(self):
486 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
497 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
487
498
488 class includematcher(basematcher):
499 class includematcher(basematcher):
489
500
490 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
501 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
491 super(includematcher, self).__init__(root, cwd, badfn)
502 super(includematcher, self).__init__(root, cwd, badfn)
492
503
493 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
504 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
494 listsubrepos, root)
505 listsubrepos, root)
495 self._prefix = _prefix(kindpats)
506 self._prefix = _prefix(kindpats)
496 roots, dirs, parents = _rootsdirsandparents(kindpats)
507 roots, dirs, parents = _rootsdirsandparents(kindpats)
497 # roots are directories which are recursively included.
508 # roots are directories which are recursively included.
498 self._roots = set(roots)
509 self._roots = set(roots)
499 # dirs are directories which are non-recursively included.
510 # dirs are directories which are non-recursively included.
500 self._dirs = set(dirs)
511 self._dirs = set(dirs)
501 # parents are directories which are non-recursively included because
512 # parents are directories which are non-recursively included because
502 # they are needed to get to items in _dirs or _roots.
513 # they are needed to get to items in _dirs or _roots.
503 self._parents = set(parents)
514 self._parents = set(parents)
504
515
505 def visitdir(self, dir):
516 def visitdir(self, dir):
506 if self._prefix and dir in self._roots:
517 if self._prefix and dir in self._roots:
507 return 'all'
518 return 'all'
508 return ('.' in self._roots or
519 return ('.' in self._roots or
509 dir in self._roots or
520 dir in self._roots or
510 dir in self._dirs or
521 dir in self._dirs or
511 dir in self._parents or
522 dir in self._parents or
512 any(parentdir in self._roots
523 any(parentdir in self._roots
513 for parentdir in util.finddirs(dir)))
524 for parentdir in util.finddirs(dir)))
514
525
515 def visitchildrenset(self, dir):
526 def visitchildrenset(self, dir):
516 if self._prefix and dir in self._roots:
527 if self._prefix and dir in self._roots:
517 return 'all'
528 return 'all'
518 # Note: this does *not* include the 'dir in self._parents' case from
529 # Note: this does *not* include the 'dir in self._parents' case from
519 # visitdir, that's handled below.
530 # visitdir, that's handled below.
520 if ('.' in self._roots or
531 if ('.' in self._roots or
521 dir in self._roots or
532 dir in self._roots or
522 dir in self._dirs or
533 dir in self._dirs or
523 any(parentdir in self._roots
534 any(parentdir in self._roots
524 for parentdir in util.finddirs(dir))):
535 for parentdir in util.finddirs(dir))):
525 return 'this'
536 return 'this'
526
537
527 ret = set()
538 ret = set()
528 if dir in self._parents:
539 if dir in self._parents:
529 # We add a '/' on to `dir` so that we don't return items that are
540 # We add a '/' on to `dir` so that we don't return items that are
530 # prefixed by `dir` but are actually siblings of `dir`.
541 # prefixed by `dir` but are actually siblings of `dir`.
531 suffixeddir = dir + '/' if dir != '.' else ''
542 suffixeddir = dir + '/' if dir != '.' else ''
532 # Look in all _roots, _dirs, and _parents for things that start with
543 # Look in all _roots, _dirs, and _parents for things that start with
533 # 'suffixeddir'.
544 # 'suffixeddir'.
534 for d in [q for q in
545 for d in [q for q in
535 itertools.chain(self._roots, self._dirs, self._parents) if
546 itertools.chain(self._roots, self._dirs, self._parents) if
536 q.startswith(suffixeddir)]:
547 q.startswith(suffixeddir)]:
537 # Don't emit '.' in the response for the root directory
548 # Don't emit '.' in the response for the root directory
538 if not suffixeddir and d == '.':
549 if not suffixeddir and d == '.':
539 continue
550 continue
540
551
541 # We return the item name without the `suffixeddir` prefix or a
552 # We return the item name without the `suffixeddir` prefix or a
542 # slash suffix
553 # slash suffix
543 d = d[len(suffixeddir):]
554 d = d[len(suffixeddir):]
544 if '/' in d:
555 if '/' in d:
545 # This is a subdirectory-of-a-subdirectory, i.e.
556 # This is a subdirectory-of-a-subdirectory, i.e.
546 # suffixeddir='foo/', d was 'foo/bar/baz' before removing
557 # suffixeddir='foo/', d was 'foo/bar/baz' before removing
547 # 'foo/'.
558 # 'foo/'.
548 d = d[:d.index('/')]
559 d = d[:d.index('/')]
549 ret.add(d)
560 ret.add(d)
550 return ret
561 return ret
551
562
552 @encoding.strmethod
563 @encoding.strmethod
553 def __repr__(self):
564 def __repr__(self):
554 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
565 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
555
566
556 class exactmatcher(basematcher):
567 class exactmatcher(basematcher):
557 '''Matches the input files exactly. They are interpreted as paths, not
568 '''Matches the input files exactly. They are interpreted as paths, not
558 patterns (so no kind-prefixes).
569 patterns (so no kind-prefixes).
559 '''
570 '''
560
571
561 def __init__(self, root, cwd, files, badfn=None):
572 def __init__(self, root, cwd, files, badfn=None):
562 super(exactmatcher, self).__init__(root, cwd, badfn)
573 super(exactmatcher, self).__init__(root, cwd, badfn)
563
574
564 if isinstance(files, list):
575 if isinstance(files, list):
565 self._files = files
576 self._files = files
566 else:
577 else:
567 self._files = list(files)
578 self._files = list(files)
568
579
569 matchfn = basematcher.exact
580 matchfn = basematcher.exact
570
581
571 @propertycache
582 @propertycache
572 def _dirs(self):
583 def _dirs(self):
573 return set(util.dirs(self._fileset)) | {'.'}
584 return set(util.dirs(self._fileset)) | {'.'}
574
585
575 def visitdir(self, dir):
586 def visitdir(self, dir):
576 return dir in self._dirs
587 return dir in self._dirs
577
588
578 def visitchildrenset(self, dir):
589 def visitchildrenset(self, dir):
579 if dir in self._dirs:
590 if dir in self._dirs:
580 candidates = self._dirs - {'.'}
591 candidates = self._dirs - {'.'}
581 if dir != '.':
592 if dir != '.':
582 d = dir + '/'
593 d = dir + '/'
583 candidates = set(c[len(d):] for c in candidates if
594 candidates = set(c[len(d):] for c in candidates if
584 c.startswith(d))
595 c.startswith(d))
585 # self._dirs includes all of the directories, recursively, so if
596 # self._dirs includes all of the directories, recursively, so if
586 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
597 # we're attempting to match foo/bar/baz.txt, it'll have '.', 'foo',
587 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
598 # 'foo/bar' in it. Thus we can safely ignore a candidate that has a
588 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
599 # '/' in it, indicating a it's for a subdir-of-a-subdir; the
589 # immediate subdir will be in there without a slash.
600 # immediate subdir will be in there without a slash.
590 ret = set(c for c in candidates if '/' not in c)
601 ret = set(c for c in candidates if '/' not in c)
591 # We need to emit 'this' for foo/bar, not set(), not {'baz.txt'}.
602 # We need to emit 'this' for foo/bar, not set(), not {'baz.txt'}.
592 if not ret:
603 if not ret:
593 return 'this'
604 return 'this'
594 return ret
605 return ret
595 return set()
606 return set()
596
607
597 def isexact(self):
608 def isexact(self):
598 return True
609 return True
599
610
600 @encoding.strmethod
611 @encoding.strmethod
601 def __repr__(self):
612 def __repr__(self):
602 return ('<exactmatcher files=%r>' % self._files)
613 return ('<exactmatcher files=%r>' % self._files)
603
614
604 class differencematcher(basematcher):
615 class differencematcher(basematcher):
605 '''Composes two matchers by matching if the first matches and the second
616 '''Composes two matchers by matching if the first matches and the second
606 does not.
617 does not.
607
618
608 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
619 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
609 traversedir) are ignored.
620 traversedir) are ignored.
610 '''
621 '''
611 def __init__(self, m1, m2):
622 def __init__(self, m1, m2):
612 super(differencematcher, self).__init__(m1._root, m1._cwd)
623 super(differencematcher, self).__init__(m1._root, m1._cwd)
613 self._m1 = m1
624 self._m1 = m1
614 self._m2 = m2
625 self._m2 = m2
615 self.bad = m1.bad
626 self.bad = m1.bad
616 self.explicitdir = m1.explicitdir
627 self.explicitdir = m1.explicitdir
617 self.traversedir = m1.traversedir
628 self.traversedir = m1.traversedir
618
629
619 def matchfn(self, f):
630 def matchfn(self, f):
620 return self._m1(f) and not self._m2(f)
631 return self._m1(f) and not self._m2(f)
621
632
622 @propertycache
633 @propertycache
623 def _files(self):
634 def _files(self):
624 if self.isexact():
635 if self.isexact():
625 return [f for f in self._m1.files() if self(f)]
636 return [f for f in self._m1.files() if self(f)]
626 # If m1 is not an exact matcher, we can't easily figure out the set of
637 # If m1 is not an exact matcher, we can't easily figure out the set of
627 # files, because its files() are not always files. For example, if
638 # files, because its files() are not always files. For example, if
628 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
639 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
629 # want to remove "dir" from the set even though it would match m2,
640 # want to remove "dir" from the set even though it would match m2,
630 # because the "dir" in m1 may not be a file.
641 # because the "dir" in m1 may not be a file.
631 return self._m1.files()
642 return self._m1.files()
632
643
633 def visitdir(self, dir):
644 def visitdir(self, dir):
634 if self._m2.visitdir(dir) == 'all':
645 if self._m2.visitdir(dir) == 'all':
635 return False
646 return False
636 return bool(self._m1.visitdir(dir))
647 return bool(self._m1.visitdir(dir))
637
648
638 def visitchildrenset(self, dir):
649 def visitchildrenset(self, dir):
639 m2_set = self._m2.visitchildrenset(dir)
650 m2_set = self._m2.visitchildrenset(dir)
640 if m2_set == 'all':
651 if m2_set == 'all':
641 return set()
652 return set()
642 m1_set = self._m1.visitchildrenset(dir)
653 m1_set = self._m1.visitchildrenset(dir)
643 # Possible values for m1: 'all', 'this', set(...), set()
654 # Possible values for m1: 'all', 'this', set(...), set()
644 # Possible values for m2: 'this', set(...), set()
655 # Possible values for m2: 'this', set(...), set()
645 # If m2 has nothing under here that we care about, return m1, even if
656 # If m2 has nothing under here that we care about, return m1, even if
646 # it's 'all'. This is a change in behavior from visitdir, which would
657 # it's 'all'. This is a change in behavior from visitdir, which would
647 # return True, not 'all', for some reason.
658 # return True, not 'all', for some reason.
648 if not m2_set:
659 if not m2_set:
649 return m1_set
660 return m1_set
650 if m1_set in ['all', 'this']:
661 if m1_set in ['all', 'this']:
651 # Never return 'all' here if m2_set is any kind of non-empty (either
662 # Never return 'all' here if m2_set is any kind of non-empty (either
652 # 'this' or set(foo)), since m2 might return set() for a
663 # 'this' or set(foo)), since m2 might return set() for a
653 # subdirectory.
664 # subdirectory.
654 return 'this'
665 return 'this'
655 # Possible values for m1: set(...), set()
666 # Possible values for m1: set(...), set()
656 # Possible values for m2: 'this', set(...)
667 # Possible values for m2: 'this', set(...)
657 # We ignore m2's set results. They're possibly incorrect:
668 # We ignore m2's set results. They're possibly incorrect:
658 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
669 # m1 = path:dir/subdir, m2=rootfilesin:dir, visitchildrenset('.'):
659 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
670 # m1 returns {'dir'}, m2 returns {'dir'}, if we subtracted we'd
660 # return set(), which is *not* correct, we still need to visit 'dir'!
671 # return set(), which is *not* correct, we still need to visit 'dir'!
661 return m1_set
672 return m1_set
662
673
663 def isexact(self):
674 def isexact(self):
664 return self._m1.isexact()
675 return self._m1.isexact()
665
676
666 @encoding.strmethod
677 @encoding.strmethod
667 def __repr__(self):
678 def __repr__(self):
668 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
679 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
669
680
670 def intersectmatchers(m1, m2):
681 def intersectmatchers(m1, m2):
671 '''Composes two matchers by matching if both of them match.
682 '''Composes two matchers by matching if both of them match.
672
683
673 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
684 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
674 traversedir) are ignored.
685 traversedir) are ignored.
675 '''
686 '''
676 if m1 is None or m2 is None:
687 if m1 is None or m2 is None:
677 return m1 or m2
688 return m1 or m2
678 if m1.always():
689 if m1.always():
679 m = copy.copy(m2)
690 m = copy.copy(m2)
680 # TODO: Consider encapsulating these things in a class so there's only
691 # TODO: Consider encapsulating these things in a class so there's only
681 # one thing to copy from m1.
692 # one thing to copy from m1.
682 m.bad = m1.bad
693 m.bad = m1.bad
683 m.explicitdir = m1.explicitdir
694 m.explicitdir = m1.explicitdir
684 m.traversedir = m1.traversedir
695 m.traversedir = m1.traversedir
685 m.abs = m1.abs
696 m.abs = m1.abs
686 m.rel = m1.rel
697 m.rel = m1.rel
687 m._relativeuipath |= m1._relativeuipath
698 m._relativeuipath |= m1._relativeuipath
688 return m
699 return m
689 if m2.always():
700 if m2.always():
690 m = copy.copy(m1)
701 m = copy.copy(m1)
691 m._relativeuipath |= m2._relativeuipath
702 m._relativeuipath |= m2._relativeuipath
692 return m
703 return m
693 return intersectionmatcher(m1, m2)
704 return intersectionmatcher(m1, m2)
694
705
695 class intersectionmatcher(basematcher):
706 class intersectionmatcher(basematcher):
696 def __init__(self, m1, m2):
707 def __init__(self, m1, m2):
697 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
708 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
698 self._m1 = m1
709 self._m1 = m1
699 self._m2 = m2
710 self._m2 = m2
700 self.bad = m1.bad
711 self.bad = m1.bad
701 self.explicitdir = m1.explicitdir
712 self.explicitdir = m1.explicitdir
702 self.traversedir = m1.traversedir
713 self.traversedir = m1.traversedir
703
714
704 @propertycache
715 @propertycache
705 def _files(self):
716 def _files(self):
706 if self.isexact():
717 if self.isexact():
707 m1, m2 = self._m1, self._m2
718 m1, m2 = self._m1, self._m2
708 if not m1.isexact():
719 if not m1.isexact():
709 m1, m2 = m2, m1
720 m1, m2 = m2, m1
710 return [f for f in m1.files() if m2(f)]
721 return [f for f in m1.files() if m2(f)]
711 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
722 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
712 # the set of files, because their files() are not always files. For
723 # the set of files, because their files() are not always files. For
713 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
724 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
714 # "path:dir2", we don't want to remove "dir2" from the set.
725 # "path:dir2", we don't want to remove "dir2" from the set.
715 return self._m1.files() + self._m2.files()
726 return self._m1.files() + self._m2.files()
716
727
717 def matchfn(self, f):
728 def matchfn(self, f):
718 return self._m1(f) and self._m2(f)
729 return self._m1(f) and self._m2(f)
719
730
720 def visitdir(self, dir):
731 def visitdir(self, dir):
721 visit1 = self._m1.visitdir(dir)
732 visit1 = self._m1.visitdir(dir)
722 if visit1 == 'all':
733 if visit1 == 'all':
723 return self._m2.visitdir(dir)
734 return self._m2.visitdir(dir)
724 # bool() because visit1=True + visit2='all' should not be 'all'
735 # bool() because visit1=True + visit2='all' should not be 'all'
725 return bool(visit1 and self._m2.visitdir(dir))
736 return bool(visit1 and self._m2.visitdir(dir))
726
737
727 def visitchildrenset(self, dir):
738 def visitchildrenset(self, dir):
728 m1_set = self._m1.visitchildrenset(dir)
739 m1_set = self._m1.visitchildrenset(dir)
729 if not m1_set:
740 if not m1_set:
730 return set()
741 return set()
731 m2_set = self._m2.visitchildrenset(dir)
742 m2_set = self._m2.visitchildrenset(dir)
732 if not m2_set:
743 if not m2_set:
733 return set()
744 return set()
734
745
735 if m1_set == 'all':
746 if m1_set == 'all':
736 return m2_set
747 return m2_set
737 elif m2_set == 'all':
748 elif m2_set == 'all':
738 return m1_set
749 return m1_set
739
750
740 if m1_set == 'this' or m2_set == 'this':
751 if m1_set == 'this' or m2_set == 'this':
741 return 'this'
752 return 'this'
742
753
743 assert isinstance(m1_set, set) and isinstance(m2_set, set)
754 assert isinstance(m1_set, set) and isinstance(m2_set, set)
744 return m1_set.intersection(m2_set)
755 return m1_set.intersection(m2_set)
745
756
746 def always(self):
757 def always(self):
747 return self._m1.always() and self._m2.always()
758 return self._m1.always() and self._m2.always()
748
759
749 def isexact(self):
760 def isexact(self):
750 return self._m1.isexact() or self._m2.isexact()
761 return self._m1.isexact() or self._m2.isexact()
751
762
752 @encoding.strmethod
763 @encoding.strmethod
753 def __repr__(self):
764 def __repr__(self):
754 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
765 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
755
766
756 class subdirmatcher(basematcher):
767 class subdirmatcher(basematcher):
757 """Adapt a matcher to work on a subdirectory only.
768 """Adapt a matcher to work on a subdirectory only.
758
769
759 The paths are remapped to remove/insert the path as needed:
770 The paths are remapped to remove/insert the path as needed:
760
771
761 >>> from . import pycompat
772 >>> from . import pycompat
762 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
773 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
763 >>> m2 = subdirmatcher(b'sub', m1)
774 >>> m2 = subdirmatcher(b'sub', m1)
764 >>> bool(m2(b'a.txt'))
775 >>> bool(m2(b'a.txt'))
765 False
776 False
766 >>> bool(m2(b'b.txt'))
777 >>> bool(m2(b'b.txt'))
767 True
778 True
768 >>> bool(m2.matchfn(b'a.txt'))
779 >>> bool(m2.matchfn(b'a.txt'))
769 False
780 False
770 >>> bool(m2.matchfn(b'b.txt'))
781 >>> bool(m2.matchfn(b'b.txt'))
771 True
782 True
772 >>> m2.files()
783 >>> m2.files()
773 ['b.txt']
784 ['b.txt']
774 >>> m2.exact(b'b.txt')
785 >>> m2.exact(b'b.txt')
775 True
786 True
776 >>> util.pconvert(m2.rel(b'b.txt'))
787 >>> util.pconvert(m2.rel(b'b.txt'))
777 'sub/b.txt'
788 'sub/b.txt'
778 >>> def bad(f, msg):
789 >>> def bad(f, msg):
779 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
790 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
780 >>> m1.bad = bad
791 >>> m1.bad = bad
781 >>> m2.bad(b'x.txt', b'No such file')
792 >>> m2.bad(b'x.txt', b'No such file')
782 sub/x.txt: No such file
793 sub/x.txt: No such file
783 >>> m2.abs(b'c.txt')
794 >>> m2.abs(b'c.txt')
784 'sub/c.txt'
795 'sub/c.txt'
785 """
796 """
786
797
787 def __init__(self, path, matcher):
798 def __init__(self, path, matcher):
788 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
799 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
789 self._path = path
800 self._path = path
790 self._matcher = matcher
801 self._matcher = matcher
791 self._always = matcher.always()
802 self._always = matcher.always()
792
803
793 self._files = [f[len(path) + 1:] for f in matcher._files
804 self._files = [f[len(path) + 1:] for f in matcher._files
794 if f.startswith(path + "/")]
805 if f.startswith(path + "/")]
795
806
796 # If the parent repo had a path to this subrepo and the matcher is
807 # If the parent repo had a path to this subrepo and the matcher is
797 # a prefix matcher, this submatcher always matches.
808 # a prefix matcher, this submatcher always matches.
798 if matcher.prefix():
809 if matcher.prefix():
799 self._always = any(f == path for f in matcher._files)
810 self._always = any(f == path for f in matcher._files)
800
811
801 def bad(self, f, msg):
812 def bad(self, f, msg):
802 self._matcher.bad(self._path + "/" + f, msg)
813 self._matcher.bad(self._path + "/" + f, msg)
803
814
804 def abs(self, f):
815 def abs(self, f):
805 return self._matcher.abs(self._path + "/" + f)
816 return self._matcher.abs(self._path + "/" + f)
806
817
807 def rel(self, f):
818 def rel(self, f):
808 return self._matcher.rel(self._path + "/" + f)
819 return self._matcher.rel(self._path + "/" + f)
809
820
810 def uipath(self, f):
821 def uipath(self, f):
811 return self._matcher.uipath(self._path + "/" + f)
822 return self._matcher.uipath(self._path + "/" + f)
812
823
813 def matchfn(self, f):
824 def matchfn(self, f):
814 # Some information is lost in the superclass's constructor, so we
825 # Some information is lost in the superclass's constructor, so we
815 # can not accurately create the matching function for the subdirectory
826 # can not accurately create the matching function for the subdirectory
816 # from the inputs. Instead, we override matchfn() and visitdir() to
827 # from the inputs. Instead, we override matchfn() and visitdir() to
817 # call the original matcher with the subdirectory path prepended.
828 # call the original matcher with the subdirectory path prepended.
818 return self._matcher.matchfn(self._path + "/" + f)
829 return self._matcher.matchfn(self._path + "/" + f)
819
830
820 def visitdir(self, dir):
831 def visitdir(self, dir):
821 if dir == '.':
832 if dir == '.':
822 dir = self._path
833 dir = self._path
823 else:
834 else:
824 dir = self._path + "/" + dir
835 dir = self._path + "/" + dir
825 return self._matcher.visitdir(dir)
836 return self._matcher.visitdir(dir)
826
837
827 def visitchildrenset(self, dir):
838 def visitchildrenset(self, dir):
828 if dir == '.':
839 if dir == '.':
829 dir = self._path
840 dir = self._path
830 else:
841 else:
831 dir = self._path + "/" + dir
842 dir = self._path + "/" + dir
832 return self._matcher.visitchildrenset(dir)
843 return self._matcher.visitchildrenset(dir)
833
844
834 def always(self):
845 def always(self):
835 return self._always
846 return self._always
836
847
837 def prefix(self):
848 def prefix(self):
838 return self._matcher.prefix() and not self._always
849 return self._matcher.prefix() and not self._always
839
850
840 @encoding.strmethod
851 @encoding.strmethod
841 def __repr__(self):
852 def __repr__(self):
842 return ('<subdirmatcher path=%r, matcher=%r>' %
853 return ('<subdirmatcher path=%r, matcher=%r>' %
843 (self._path, self._matcher))
854 (self._path, self._matcher))
844
855
845 class prefixdirmatcher(basematcher):
856 class prefixdirmatcher(basematcher):
846 """Adapt a matcher to work on a parent directory.
857 """Adapt a matcher to work on a parent directory.
847
858
848 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
859 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
849 traversedir) are ignored.
860 traversedir) are ignored.
850
861
851 The prefix path should usually be the relative path from the root of
862 The prefix path should usually be the relative path from the root of
852 this matcher to the root of the wrapped matcher.
863 this matcher to the root of the wrapped matcher.
853
864
854 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
865 >>> m1 = match(util.localpath(b'root/d/e'), b'f', [b'../a.txt', b'b.txt'])
855 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
866 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
856 >>> bool(m2(b'a.txt'),)
867 >>> bool(m2(b'a.txt'),)
857 False
868 False
858 >>> bool(m2(b'd/e/a.txt'))
869 >>> bool(m2(b'd/e/a.txt'))
859 True
870 True
860 >>> bool(m2(b'd/e/b.txt'))
871 >>> bool(m2(b'd/e/b.txt'))
861 False
872 False
862 >>> m2.files()
873 >>> m2.files()
863 ['d/e/a.txt', 'd/e/f/b.txt']
874 ['d/e/a.txt', 'd/e/f/b.txt']
864 >>> m2.exact(b'd/e/a.txt')
875 >>> m2.exact(b'd/e/a.txt')
865 True
876 True
866 >>> m2.visitdir(b'd')
877 >>> m2.visitdir(b'd')
867 True
878 True
868 >>> m2.visitdir(b'd/e')
879 >>> m2.visitdir(b'd/e')
869 True
880 True
870 >>> m2.visitdir(b'd/e/f')
881 >>> m2.visitdir(b'd/e/f')
871 True
882 True
872 >>> m2.visitdir(b'd/e/g')
883 >>> m2.visitdir(b'd/e/g')
873 False
884 False
874 >>> m2.visitdir(b'd/ef')
885 >>> m2.visitdir(b'd/ef')
875 False
886 False
876 """
887 """
877
888
878 def __init__(self, root, cwd, path, matcher, badfn=None):
889 def __init__(self, root, cwd, path, matcher, badfn=None):
879 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
890 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
880 if not path:
891 if not path:
881 raise error.ProgrammingError('prefix path must not be empty')
892 raise error.ProgrammingError('prefix path must not be empty')
882 self._path = path
893 self._path = path
883 self._pathprefix = path + '/'
894 self._pathprefix = path + '/'
884 self._matcher = matcher
895 self._matcher = matcher
885
896
886 @propertycache
897 @propertycache
887 def _files(self):
898 def _files(self):
888 return [self._pathprefix + f for f in self._matcher._files]
899 return [self._pathprefix + f for f in self._matcher._files]
889
900
890 def matchfn(self, f):
901 def matchfn(self, f):
891 if not f.startswith(self._pathprefix):
902 if not f.startswith(self._pathprefix):
892 return False
903 return False
893 return self._matcher.matchfn(f[len(self._pathprefix):])
904 return self._matcher.matchfn(f[len(self._pathprefix):])
894
905
895 @propertycache
906 @propertycache
896 def _pathdirs(self):
907 def _pathdirs(self):
897 return set(util.finddirs(self._path)) | {'.'}
908 return set(util.finddirs(self._path)) | {'.'}
898
909
899 def visitdir(self, dir):
910 def visitdir(self, dir):
900 if dir == self._path:
911 if dir == self._path:
901 return self._matcher.visitdir('.')
912 return self._matcher.visitdir('.')
902 if dir.startswith(self._pathprefix):
913 if dir.startswith(self._pathprefix):
903 return self._matcher.visitdir(dir[len(self._pathprefix):])
914 return self._matcher.visitdir(dir[len(self._pathprefix):])
904 return dir in self._pathdirs
915 return dir in self._pathdirs
905
916
906 def visitchildrenset(self, dir):
917 def visitchildrenset(self, dir):
907 if dir == self._path:
918 if dir == self._path:
908 return self._matcher.visitchildrenset('.')
919 return self._matcher.visitchildrenset('.')
909 if dir.startswith(self._pathprefix):
920 if dir.startswith(self._pathprefix):
910 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
921 return self._matcher.visitchildrenset(dir[len(self._pathprefix):])
911 if dir in self._pathdirs:
922 if dir in self._pathdirs:
912 return 'this'
923 return 'this'
913 return set()
924 return set()
914
925
915 def isexact(self):
926 def isexact(self):
916 return self._matcher.isexact()
927 return self._matcher.isexact()
917
928
918 def prefix(self):
929 def prefix(self):
919 return self._matcher.prefix()
930 return self._matcher.prefix()
920
931
921 @encoding.strmethod
932 @encoding.strmethod
922 def __repr__(self):
933 def __repr__(self):
923 return ('<prefixdirmatcher path=%r, matcher=%r>'
934 return ('<prefixdirmatcher path=%r, matcher=%r>'
924 % (pycompat.bytestr(self._path), self._matcher))
935 % (pycompat.bytestr(self._path), self._matcher))
925
936
926 class unionmatcher(basematcher):
937 class unionmatcher(basematcher):
927 """A matcher that is the union of several matchers.
938 """A matcher that is the union of several matchers.
928
939
929 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
940 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
930 taken from the first matcher.
941 taken from the first matcher.
931 """
942 """
932
943
933 def __init__(self, matchers):
944 def __init__(self, matchers):
934 m1 = matchers[0]
945 m1 = matchers[0]
935 super(unionmatcher, self).__init__(m1._root, m1._cwd)
946 super(unionmatcher, self).__init__(m1._root, m1._cwd)
936 self.explicitdir = m1.explicitdir
947 self.explicitdir = m1.explicitdir
937 self.traversedir = m1.traversedir
948 self.traversedir = m1.traversedir
938 self._matchers = matchers
949 self._matchers = matchers
939
950
940 def matchfn(self, f):
951 def matchfn(self, f):
941 for match in self._matchers:
952 for match in self._matchers:
942 if match(f):
953 if match(f):
943 return True
954 return True
944 return False
955 return False
945
956
946 def visitdir(self, dir):
957 def visitdir(self, dir):
947 r = False
958 r = False
948 for m in self._matchers:
959 for m in self._matchers:
949 v = m.visitdir(dir)
960 v = m.visitdir(dir)
950 if v == 'all':
961 if v == 'all':
951 return v
962 return v
952 r |= v
963 r |= v
953 return r
964 return r
954
965
955 def visitchildrenset(self, dir):
966 def visitchildrenset(self, dir):
956 r = set()
967 r = set()
957 this = False
968 this = False
958 for m in self._matchers:
969 for m in self._matchers:
959 v = m.visitchildrenset(dir)
970 v = m.visitchildrenset(dir)
960 if not v:
971 if not v:
961 continue
972 continue
962 if v == 'all':
973 if v == 'all':
963 return v
974 return v
964 if this or v == 'this':
975 if this or v == 'this':
965 this = True
976 this = True
966 # don't break, we might have an 'all' in here.
977 # don't break, we might have an 'all' in here.
967 continue
978 continue
968 assert isinstance(v, set)
979 assert isinstance(v, set)
969 r = r.union(v)
980 r = r.union(v)
970 if this:
981 if this:
971 return 'this'
982 return 'this'
972 return r
983 return r
973
984
974 @encoding.strmethod
985 @encoding.strmethod
975 def __repr__(self):
986 def __repr__(self):
976 return ('<unionmatcher matchers=%r>' % self._matchers)
987 return ('<unionmatcher matchers=%r>' % self._matchers)
977
988
978 def patkind(pattern, default=None):
989 def patkind(pattern, default=None):
979 '''If pattern is 'kind:pat' with a known kind, return kind.'''
990 '''If pattern is 'kind:pat' with a known kind, return kind.'''
980 return _patsplit(pattern, default)[0]
991 return _patsplit(pattern, default)[0]
981
992
982 def _patsplit(pattern, default):
993 def _patsplit(pattern, default):
983 """Split a string into the optional pattern kind prefix and the actual
994 """Split a string into the optional pattern kind prefix and the actual
984 pattern."""
995 pattern."""
985 if ':' in pattern:
996 if ':' in pattern:
986 kind, pat = pattern.split(':', 1)
997 kind, pat = pattern.split(':', 1)
987 if kind in allpatternkinds:
998 if kind in allpatternkinds:
988 return kind, pat
999 return kind, pat
989 return default, pattern
1000 return default, pattern
990
1001
991 def _globre(pat):
1002 def _globre(pat):
992 r'''Convert an extended glob string to a regexp string.
1003 r'''Convert an extended glob string to a regexp string.
993
1004
994 >>> from . import pycompat
1005 >>> from . import pycompat
995 >>> def bprint(s):
1006 >>> def bprint(s):
996 ... print(pycompat.sysstr(s))
1007 ... print(pycompat.sysstr(s))
997 >>> bprint(_globre(br'?'))
1008 >>> bprint(_globre(br'?'))
998 .
1009 .
999 >>> bprint(_globre(br'*'))
1010 >>> bprint(_globre(br'*'))
1000 [^/]*
1011 [^/]*
1001 >>> bprint(_globre(br'**'))
1012 >>> bprint(_globre(br'**'))
1002 .*
1013 .*
1003 >>> bprint(_globre(br'**/a'))
1014 >>> bprint(_globre(br'**/a'))
1004 (?:.*/)?a
1015 (?:.*/)?a
1005 >>> bprint(_globre(br'a/**/b'))
1016 >>> bprint(_globre(br'a/**/b'))
1006 a/(?:.*/)?b
1017 a/(?:.*/)?b
1007 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1018 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
1008 [a*?!^][\^b][^c]
1019 [a*?!^][\^b][^c]
1009 >>> bprint(_globre(br'{a,b}'))
1020 >>> bprint(_globre(br'{a,b}'))
1010 (?:a|b)
1021 (?:a|b)
1011 >>> bprint(_globre(br'.\*\?'))
1022 >>> bprint(_globre(br'.\*\?'))
1012 \.\*\?
1023 \.\*\?
1013 '''
1024 '''
1014 i, n = 0, len(pat)
1025 i, n = 0, len(pat)
1015 res = ''
1026 res = ''
1016 group = 0
1027 group = 0
1017 escape = util.stringutil.reescape
1028 escape = util.stringutil.reescape
1018 def peek():
1029 def peek():
1019 return i < n and pat[i:i + 1]
1030 return i < n and pat[i:i + 1]
1020 while i < n:
1031 while i < n:
1021 c = pat[i:i + 1]
1032 c = pat[i:i + 1]
1022 i += 1
1033 i += 1
1023 if c not in '*?[{},\\':
1034 if c not in '*?[{},\\':
1024 res += escape(c)
1035 res += escape(c)
1025 elif c == '*':
1036 elif c == '*':
1026 if peek() == '*':
1037 if peek() == '*':
1027 i += 1
1038 i += 1
1028 if peek() == '/':
1039 if peek() == '/':
1029 i += 1
1040 i += 1
1030 res += '(?:.*/)?'
1041 res += '(?:.*/)?'
1031 else:
1042 else:
1032 res += '.*'
1043 res += '.*'
1033 else:
1044 else:
1034 res += '[^/]*'
1045 res += '[^/]*'
1035 elif c == '?':
1046 elif c == '?':
1036 res += '.'
1047 res += '.'
1037 elif c == '[':
1048 elif c == '[':
1038 j = i
1049 j = i
1039 if j < n and pat[j:j + 1] in '!]':
1050 if j < n and pat[j:j + 1] in '!]':
1040 j += 1
1051 j += 1
1041 while j < n and pat[j:j + 1] != ']':
1052 while j < n and pat[j:j + 1] != ']':
1042 j += 1
1053 j += 1
1043 if j >= n:
1054 if j >= n:
1044 res += '\\['
1055 res += '\\['
1045 else:
1056 else:
1046 stuff = pat[i:j].replace('\\','\\\\')
1057 stuff = pat[i:j].replace('\\','\\\\')
1047 i = j + 1
1058 i = j + 1
1048 if stuff[0:1] == '!':
1059 if stuff[0:1] == '!':
1049 stuff = '^' + stuff[1:]
1060 stuff = '^' + stuff[1:]
1050 elif stuff[0:1] == '^':
1061 elif stuff[0:1] == '^':
1051 stuff = '\\' + stuff
1062 stuff = '\\' + stuff
1052 res = '%s[%s]' % (res, stuff)
1063 res = '%s[%s]' % (res, stuff)
1053 elif c == '{':
1064 elif c == '{':
1054 group += 1
1065 group += 1
1055 res += '(?:'
1066 res += '(?:'
1056 elif c == '}' and group:
1067 elif c == '}' and group:
1057 res += ')'
1068 res += ')'
1058 group -= 1
1069 group -= 1
1059 elif c == ',' and group:
1070 elif c == ',' and group:
1060 res += '|'
1071 res += '|'
1061 elif c == '\\':
1072 elif c == '\\':
1062 p = peek()
1073 p = peek()
1063 if p:
1074 if p:
1064 i += 1
1075 i += 1
1065 res += escape(p)
1076 res += escape(p)
1066 else:
1077 else:
1067 res += escape(c)
1078 res += escape(c)
1068 else:
1079 else:
1069 res += escape(c)
1080 res += escape(c)
1070 return res
1081 return res
1071
1082
1072 def _regex(kind, pat, globsuffix):
1083 def _regex(kind, pat, globsuffix):
1073 '''Convert a (normalized) pattern of any kind into a regular expression.
1084 '''Convert a (normalized) pattern of any kind into a regular expression.
1074 globsuffix is appended to the regexp of globs.'''
1085 globsuffix is appended to the regexp of globs.'''
1075 if not pat:
1086 if not pat:
1076 return ''
1087 return ''
1077 if kind == 're':
1088 if kind == 're':
1078 return pat
1089 return pat
1079 if kind in ('path', 'relpath'):
1090 if kind in ('path', 'relpath'):
1080 if pat == '.':
1091 if pat == '.':
1081 return ''
1092 return ''
1082 return util.stringutil.reescape(pat) + '(?:/|$)'
1093 return util.stringutil.reescape(pat) + '(?:/|$)'
1083 if kind == 'rootfilesin':
1094 if kind == 'rootfilesin':
1084 if pat == '.':
1095 if pat == '.':
1085 escaped = ''
1096 escaped = ''
1086 else:
1097 else:
1087 # Pattern is a directory name.
1098 # Pattern is a directory name.
1088 escaped = util.stringutil.reescape(pat) + '/'
1099 escaped = util.stringutil.reescape(pat) + '/'
1089 # Anything after the pattern must be a non-directory.
1100 # Anything after the pattern must be a non-directory.
1090 return escaped + '[^/]+$'
1101 return escaped + '[^/]+$'
1091 if kind == 'relglob':
1102 if kind == 'relglob':
1092 return '(?:|.*/)' + _globre(pat) + globsuffix
1103 return '(?:|.*/)' + _globre(pat) + globsuffix
1093 if kind == 'relre':
1104 if kind == 'relre':
1094 if pat.startswith('^'):
1105 if pat.startswith('^'):
1095 return pat
1106 return pat
1096 return '.*' + pat
1107 return '.*' + pat
1097 if kind == 'glob':
1108 if kind == 'glob':
1098 return _globre(pat) + globsuffix
1109 return _globre(pat) + globsuffix
1099 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1110 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
1100
1111
1101 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1112 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
1102 '''Return regexp string and a matcher function for kindpats.
1113 '''Return regexp string and a matcher function for kindpats.
1103 globsuffix is appended to the regexp of globs.'''
1114 globsuffix is appended to the regexp of globs.'''
1104 matchfuncs = []
1115 matchfuncs = []
1105
1116
1106 subincludes, kindpats = _expandsubinclude(kindpats, root)
1117 subincludes, kindpats = _expandsubinclude(kindpats, root)
1107 if subincludes:
1118 if subincludes:
1108 submatchers = {}
1119 submatchers = {}
1109 def matchsubinclude(f):
1120 def matchsubinclude(f):
1110 for prefix, matcherargs in subincludes:
1121 for prefix, matcherargs in subincludes:
1111 if f.startswith(prefix):
1122 if f.startswith(prefix):
1112 mf = submatchers.get(prefix)
1123 mf = submatchers.get(prefix)
1113 if mf is None:
1124 if mf is None:
1114 mf = match(*matcherargs)
1125 mf = match(*matcherargs)
1115 submatchers[prefix] = mf
1126 submatchers[prefix] = mf
1116
1127
1117 if mf(f[len(prefix):]):
1128 if mf(f[len(prefix):]):
1118 return True
1129 return True
1119 return False
1130 return False
1120 matchfuncs.append(matchsubinclude)
1131 matchfuncs.append(matchsubinclude)
1121
1132
1122 regex = ''
1133 regex = ''
1123 if kindpats:
1134 if kindpats:
1124 regex, mf = _buildregexmatch(kindpats, globsuffix)
1135 regex, mf = _buildregexmatch(kindpats, globsuffix)
1125 matchfuncs.append(mf)
1136 matchfuncs.append(mf)
1126
1137
1127 if len(matchfuncs) == 1:
1138 if len(matchfuncs) == 1:
1128 return regex, matchfuncs[0]
1139 return regex, matchfuncs[0]
1129 else:
1140 else:
1130 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1141 return regex, lambda f: any(mf(f) for mf in matchfuncs)
1131
1142
1132 def _buildregexmatch(kindpats, globsuffix):
1143 def _buildregexmatch(kindpats, globsuffix):
1133 """Build a match function from a list of kinds and kindpats,
1144 """Build a match function from a list of kinds and kindpats,
1134 return regexp string and a matcher function."""
1145 return regexp string and a matcher function."""
1135 try:
1146 try:
1136 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
1147 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
1137 for (k, p, s) in kindpats])
1148 for (k, p, s) in kindpats])
1138 if len(regex) > 20000:
1149 if len(regex) > 20000:
1139 raise OverflowError
1150 raise OverflowError
1140 return regex, _rematcher(regex)
1151 return regex, _rematcher(regex)
1141 except OverflowError:
1152 except OverflowError:
1142 # We're using a Python with a tiny regex engine and we
1153 # We're using a Python with a tiny regex engine and we
1143 # made it explode, so we'll divide the pattern list in two
1154 # made it explode, so we'll divide the pattern list in two
1144 # until it works
1155 # until it works
1145 l = len(kindpats)
1156 l = len(kindpats)
1146 if l < 2:
1157 if l < 2:
1147 raise
1158 raise
1148 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
1159 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
1149 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
1160 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
1150 return regex, lambda s: a(s) or b(s)
1161 return regex, lambda s: a(s) or b(s)
1151 except re.error:
1162 except re.error:
1152 for k, p, s in kindpats:
1163 for k, p, s in kindpats:
1153 try:
1164 try:
1154 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
1165 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
1155 except re.error:
1166 except re.error:
1156 if s:
1167 if s:
1157 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1168 raise error.Abort(_("%s: invalid pattern (%s): %s") %
1158 (s, k, p))
1169 (s, k, p))
1159 else:
1170 else:
1160 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1171 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
1161 raise error.Abort(_("invalid pattern"))
1172 raise error.Abort(_("invalid pattern"))
1162
1173
1163 def _patternrootsanddirs(kindpats):
1174 def _patternrootsanddirs(kindpats):
1164 '''Returns roots and directories corresponding to each pattern.
1175 '''Returns roots and directories corresponding to each pattern.
1165
1176
1166 This calculates the roots and directories exactly matching the patterns and
1177 This calculates the roots and directories exactly matching the patterns and
1167 returns a tuple of (roots, dirs) for each. It does not return other
1178 returns a tuple of (roots, dirs) for each. It does not return other
1168 directories which may also need to be considered, like the parent
1179 directories which may also need to be considered, like the parent
1169 directories.
1180 directories.
1170 '''
1181 '''
1171 r = []
1182 r = []
1172 d = []
1183 d = []
1173 for kind, pat, source in kindpats:
1184 for kind, pat, source in kindpats:
1174 if kind == 'glob': # find the non-glob prefix
1185 if kind == 'glob': # find the non-glob prefix
1175 root = []
1186 root = []
1176 for p in pat.split('/'):
1187 for p in pat.split('/'):
1177 if '[' in p or '{' in p or '*' in p or '?' in p:
1188 if '[' in p or '{' in p or '*' in p or '?' in p:
1178 break
1189 break
1179 root.append(p)
1190 root.append(p)
1180 r.append('/'.join(root) or '.')
1191 r.append('/'.join(root) or '.')
1181 elif kind in ('relpath', 'path'):
1192 elif kind in ('relpath', 'path'):
1182 r.append(pat or '.')
1193 r.append(pat or '.')
1183 elif kind in ('rootfilesin',):
1194 elif kind in ('rootfilesin',):
1184 d.append(pat or '.')
1195 d.append(pat or '.')
1185 else: # relglob, re, relre
1196 else: # relglob, re, relre
1186 r.append('.')
1197 r.append('.')
1187 return r, d
1198 return r, d
1188
1199
1189 def _roots(kindpats):
1200 def _roots(kindpats):
1190 '''Returns root directories to match recursively from the given patterns.'''
1201 '''Returns root directories to match recursively from the given patterns.'''
1191 roots, dirs = _patternrootsanddirs(kindpats)
1202 roots, dirs = _patternrootsanddirs(kindpats)
1192 return roots
1203 return roots
1193
1204
1194 def _rootsdirsandparents(kindpats):
1205 def _rootsdirsandparents(kindpats):
1195 '''Returns roots and exact directories from patterns.
1206 '''Returns roots and exact directories from patterns.
1196
1207
1197 `roots` are directories to match recursively, `dirs` should
1208 `roots` are directories to match recursively, `dirs` should
1198 be matched non-recursively, and `parents` are the implicitly required
1209 be matched non-recursively, and `parents` are the implicitly required
1199 directories to walk to items in either roots or dirs.
1210 directories to walk to items in either roots or dirs.
1200
1211
1201 Returns a tuple of (roots, dirs, parents).
1212 Returns a tuple of (roots, dirs, parents).
1202
1213
1203 >>> _rootsdirsandparents(
1214 >>> _rootsdirsandparents(
1204 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1215 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1205 ... (b'glob', b'g*', b'')])
1216 ... (b'glob', b'g*', b'')])
1206 (['g/h', 'g/h', '.'], [], ['g', '.'])
1217 (['g/h', 'g/h', '.'], [], ['g', '.'])
1207 >>> _rootsdirsandparents(
1218 >>> _rootsdirsandparents(
1208 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1219 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1209 ([], ['g/h', '.'], ['g', '.'])
1220 ([], ['g/h', '.'], ['g', '.'])
1210 >>> _rootsdirsandparents(
1221 >>> _rootsdirsandparents(
1211 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1222 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1212 ... (b'path', b'', b'')])
1223 ... (b'path', b'', b'')])
1213 (['r', 'p/p', '.'], [], ['p', '.'])
1224 (['r', 'p/p', '.'], [], ['p', '.'])
1214 >>> _rootsdirsandparents(
1225 >>> _rootsdirsandparents(
1215 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1226 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1216 ... (b'relre', b'rr', b'')])
1227 ... (b'relre', b'rr', b'')])
1217 (['.', '.', '.'], [], ['.'])
1228 (['.', '.', '.'], [], ['.'])
1218 '''
1229 '''
1219 r, d = _patternrootsanddirs(kindpats)
1230 r, d = _patternrootsanddirs(kindpats)
1220
1231
1221 p = []
1232 p = []
1222 # Append the parents as non-recursive/exact directories, since they must be
1233 # Append the parents as non-recursive/exact directories, since they must be
1223 # scanned to get to either the roots or the other exact directories.
1234 # scanned to get to either the roots or the other exact directories.
1224 p.extend(util.dirs(d))
1235 p.extend(util.dirs(d))
1225 p.extend(util.dirs(r))
1236 p.extend(util.dirs(r))
1226 # util.dirs() does not include the root directory, so add it manually
1237 # util.dirs() does not include the root directory, so add it manually
1227 p.append('.')
1238 p.append('.')
1228
1239
1229 return r, d, p
1240 return r, d, p
1230
1241
1231 def _explicitfiles(kindpats):
1242 def _explicitfiles(kindpats):
1232 '''Returns the potential explicit filenames from the patterns.
1243 '''Returns the potential explicit filenames from the patterns.
1233
1244
1234 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1245 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1235 ['foo/bar']
1246 ['foo/bar']
1236 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1247 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1237 []
1248 []
1238 '''
1249 '''
1239 # Keep only the pattern kinds where one can specify filenames (vs only
1250 # Keep only the pattern kinds where one can specify filenames (vs only
1240 # directory names).
1251 # directory names).
1241 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1252 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1242 return _roots(filable)
1253 return _roots(filable)
1243
1254
1244 def _prefix(kindpats):
1255 def _prefix(kindpats):
1245 '''Whether all the patterns match a prefix (i.e. recursively)'''
1256 '''Whether all the patterns match a prefix (i.e. recursively)'''
1246 for kind, pat, source in kindpats:
1257 for kind, pat, source in kindpats:
1247 if kind not in ('path', 'relpath'):
1258 if kind not in ('path', 'relpath'):
1248 return False
1259 return False
1249 return True
1260 return True
1250
1261
1251 _commentre = None
1262 _commentre = None
1252
1263
1253 def readpatternfile(filepath, warn, sourceinfo=False):
1264 def readpatternfile(filepath, warn, sourceinfo=False):
1254 '''parse a pattern file, returning a list of
1265 '''parse a pattern file, returning a list of
1255 patterns. These patterns should be given to compile()
1266 patterns. These patterns should be given to compile()
1256 to be validated and converted into a match function.
1267 to be validated and converted into a match function.
1257
1268
1258 trailing white space is dropped.
1269 trailing white space is dropped.
1259 the escape character is backslash.
1270 the escape character is backslash.
1260 comments start with #.
1271 comments start with #.
1261 empty lines are skipped.
1272 empty lines are skipped.
1262
1273
1263 lines can be of the following formats:
1274 lines can be of the following formats:
1264
1275
1265 syntax: regexp # defaults following lines to non-rooted regexps
1276 syntax: regexp # defaults following lines to non-rooted regexps
1266 syntax: glob # defaults following lines to non-rooted globs
1277 syntax: glob # defaults following lines to non-rooted globs
1267 re:pattern # non-rooted regular expression
1278 re:pattern # non-rooted regular expression
1268 glob:pattern # non-rooted glob
1279 glob:pattern # non-rooted glob
1269 pattern # pattern of the current default type
1280 pattern # pattern of the current default type
1270
1281
1271 if sourceinfo is set, returns a list of tuples:
1282 if sourceinfo is set, returns a list of tuples:
1272 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1283 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1273 '''
1284 '''
1274
1285
1275 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1286 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1276 'include': 'include', 'subinclude': 'subinclude'}
1287 'include': 'include', 'subinclude': 'subinclude'}
1277 syntax = 'relre:'
1288 syntax = 'relre:'
1278 patterns = []
1289 patterns = []
1279
1290
1280 fp = open(filepath, 'rb')
1291 fp = open(filepath, 'rb')
1281 for lineno, line in enumerate(util.iterfile(fp), start=1):
1292 for lineno, line in enumerate(util.iterfile(fp), start=1):
1282 if "#" in line:
1293 if "#" in line:
1283 global _commentre
1294 global _commentre
1284 if not _commentre:
1295 if not _commentre:
1285 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1296 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1286 # remove comments prefixed by an even number of escapes
1297 # remove comments prefixed by an even number of escapes
1287 m = _commentre.search(line)
1298 m = _commentre.search(line)
1288 if m:
1299 if m:
1289 line = line[:m.end(1)]
1300 line = line[:m.end(1)]
1290 # fixup properly escaped comments that survived the above
1301 # fixup properly escaped comments that survived the above
1291 line = line.replace("\\#", "#")
1302 line = line.replace("\\#", "#")
1292 line = line.rstrip()
1303 line = line.rstrip()
1293 if not line:
1304 if not line:
1294 continue
1305 continue
1295
1306
1296 if line.startswith('syntax:'):
1307 if line.startswith('syntax:'):
1297 s = line[7:].strip()
1308 s = line[7:].strip()
1298 try:
1309 try:
1299 syntax = syntaxes[s]
1310 syntax = syntaxes[s]
1300 except KeyError:
1311 except KeyError:
1301 if warn:
1312 if warn:
1302 warn(_("%s: ignoring invalid syntax '%s'\n") %
1313 warn(_("%s: ignoring invalid syntax '%s'\n") %
1303 (filepath, s))
1314 (filepath, s))
1304 continue
1315 continue
1305
1316
1306 linesyntax = syntax
1317 linesyntax = syntax
1307 for s, rels in syntaxes.iteritems():
1318 for s, rels in syntaxes.iteritems():
1308 if line.startswith(rels):
1319 if line.startswith(rels):
1309 linesyntax = rels
1320 linesyntax = rels
1310 line = line[len(rels):]
1321 line = line[len(rels):]
1311 break
1322 break
1312 elif line.startswith(s+':'):
1323 elif line.startswith(s+':'):
1313 linesyntax = rels
1324 linesyntax = rels
1314 line = line[len(s) + 1:]
1325 line = line[len(s) + 1:]
1315 break
1326 break
1316 if sourceinfo:
1327 if sourceinfo:
1317 patterns.append((linesyntax + line, lineno, line))
1328 patterns.append((linesyntax + line, lineno, line))
1318 else:
1329 else:
1319 patterns.append(linesyntax + line)
1330 patterns.append(linesyntax + line)
1320 fp.close()
1331 fp.close()
1321 return patterns
1332 return patterns
General Comments 0
You need to be logged in to leave comments. Login now