##// END OF EJS Templates
match: don't print explicitly listed files with wrong case (BC)...
Martin von Zweigbergk -
r32397:2dac9d6a default
parent child Browse files
Show More
@@ -1,816 +1,808 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
8 from __future__ import absolute_import
9
9
10 import copy
10 import copy
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 error,
16 error,
17 pathutil,
17 pathutil,
18 util,
18 util,
19 )
19 )
20
20
21 propertycache = util.propertycache
21 propertycache = util.propertycache
22
22
23 def _rematcher(regex):
23 def _rematcher(regex):
24 '''compile the regexp with the best available regexp engine and return a
24 '''compile the regexp with the best available regexp engine and return a
25 matcher function'''
25 matcher function'''
26 m = util.re.compile(regex)
26 m = util.re.compile(regex)
27 try:
27 try:
28 # slightly faster, provided by facebook's re2 bindings
28 # slightly faster, provided by facebook's re2 bindings
29 return m.test_match
29 return m.test_match
30 except AttributeError:
30 except AttributeError:
31 return m.match
31 return m.match
32
32
33 def _expandsets(kindpats, ctx, listsubrepos):
33 def _expandsets(kindpats, ctx, listsubrepos):
34 '''Returns the kindpats list with the 'set' patterns expanded.'''
34 '''Returns the kindpats list with the 'set' patterns expanded.'''
35 fset = set()
35 fset = set()
36 other = []
36 other = []
37
37
38 for kind, pat, source in kindpats:
38 for kind, pat, source in kindpats:
39 if kind == 'set':
39 if kind == 'set':
40 if not ctx:
40 if not ctx:
41 raise error.Abort(_("fileset expression with no context"))
41 raise error.Abort(_("fileset expression with no context"))
42 s = ctx.getfileset(pat)
42 s = ctx.getfileset(pat)
43 fset.update(s)
43 fset.update(s)
44
44
45 if listsubrepos:
45 if listsubrepos:
46 for subpath in ctx.substate:
46 for subpath in ctx.substate:
47 s = ctx.sub(subpath).getfileset(pat)
47 s = ctx.sub(subpath).getfileset(pat)
48 fset.update(subpath + '/' + f for f in s)
48 fset.update(subpath + '/' + f for f in s)
49
49
50 continue
50 continue
51 other.append((kind, pat, source))
51 other.append((kind, pat, source))
52 return fset, other
52 return fset, other
53
53
54 def _expandsubinclude(kindpats, root):
54 def _expandsubinclude(kindpats, root):
55 '''Returns the list of subinclude matcher args and the kindpats without the
55 '''Returns the list of subinclude matcher args and the kindpats without the
56 subincludes in it.'''
56 subincludes in it.'''
57 relmatchers = []
57 relmatchers = []
58 other = []
58 other = []
59
59
60 for kind, pat, source in kindpats:
60 for kind, pat, source in kindpats:
61 if kind == 'subinclude':
61 if kind == 'subinclude':
62 sourceroot = pathutil.dirname(util.normpath(source))
62 sourceroot = pathutil.dirname(util.normpath(source))
63 pat = util.pconvert(pat)
63 pat = util.pconvert(pat)
64 path = pathutil.join(sourceroot, pat)
64 path = pathutil.join(sourceroot, pat)
65
65
66 newroot = pathutil.dirname(path)
66 newroot = pathutil.dirname(path)
67 matcherargs = (newroot, '', [], ['include:%s' % path])
67 matcherargs = (newroot, '', [], ['include:%s' % path])
68
68
69 prefix = pathutil.canonpath(root, root, newroot)
69 prefix = pathutil.canonpath(root, root, newroot)
70 if prefix:
70 if prefix:
71 prefix += '/'
71 prefix += '/'
72 relmatchers.append((prefix, matcherargs))
72 relmatchers.append((prefix, matcherargs))
73 else:
73 else:
74 other.append((kind, pat, source))
74 other.append((kind, pat, source))
75
75
76 return relmatchers, other
76 return relmatchers, other
77
77
78 def _kindpatsalwaysmatch(kindpats):
78 def _kindpatsalwaysmatch(kindpats):
79 """"Checks whether the kindspats match everything, as e.g.
79 """"Checks whether the kindspats match everything, as e.g.
80 'relpath:.' does.
80 'relpath:.' does.
81 """
81 """
82 for kind, pat, source in kindpats:
82 for kind, pat, source in kindpats:
83 if pat != '' or kind not in ['relpath', 'glob']:
83 if pat != '' or kind not in ['relpath', 'glob']:
84 return False
84 return False
85 return True
85 return True
86
86
87 def match(root, cwd, patterns, include=None, exclude=None, default='glob',
87 def match(root, cwd, patterns, include=None, exclude=None, default='glob',
88 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
88 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
89 badfn=None):
89 badfn=None):
90 """build an object to match a set of file patterns
90 """build an object to match a set of file patterns
91
91
92 arguments:
92 arguments:
93 root - the canonical root of the tree you're matching against
93 root - the canonical root of the tree you're matching against
94 cwd - the current working directory, if relevant
94 cwd - the current working directory, if relevant
95 patterns - patterns to find
95 patterns - patterns to find
96 include - patterns to include (unless they are excluded)
96 include - patterns to include (unless they are excluded)
97 exclude - patterns to exclude (even if they are included)
97 exclude - patterns to exclude (even if they are included)
98 default - if a pattern in patterns has no explicit type, assume this one
98 default - if a pattern in patterns has no explicit type, assume this one
99 exact - patterns are actually filenames (include/exclude still apply)
99 exact - patterns are actually filenames (include/exclude still apply)
100 warn - optional function used for printing warnings
100 warn - optional function used for printing warnings
101 badfn - optional bad() callback for this matcher instead of the default
101 badfn - optional bad() callback for this matcher instead of the default
102
102
103 a pattern is one of:
103 a pattern is one of:
104 'glob:<glob>' - a glob relative to cwd
104 'glob:<glob>' - a glob relative to cwd
105 're:<regexp>' - a regular expression
105 're:<regexp>' - a regular expression
106 'path:<path>' - a path relative to repository root, which is matched
106 'path:<path>' - a path relative to repository root, which is matched
107 recursively
107 recursively
108 'rootfilesin:<path>' - a path relative to repository root, which is
108 'rootfilesin:<path>' - a path relative to repository root, which is
109 matched non-recursively (will not match subdirectories)
109 matched non-recursively (will not match subdirectories)
110 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
110 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
111 'relpath:<path>' - a path relative to cwd
111 'relpath:<path>' - a path relative to cwd
112 'relre:<regexp>' - a regexp that needn't match the start of a name
112 'relre:<regexp>' - a regexp that needn't match the start of a name
113 'set:<fileset>' - a fileset expression
113 'set:<fileset>' - a fileset expression
114 'include:<path>' - a file of patterns to read and include
114 'include:<path>' - a file of patterns to read and include
115 'subinclude:<path>' - a file of patterns to match against files under
115 'subinclude:<path>' - a file of patterns to match against files under
116 the same directory
116 the same directory
117 '<something>' - a pattern of the specified default type
117 '<something>' - a pattern of the specified default type
118 """
118 """
119 return matcher(root, cwd, patterns, include=include, exclude=exclude,
119 return matcher(root, cwd, patterns, include=include, exclude=exclude,
120 default=default, exact=exact, auditor=auditor, ctx=ctx,
120 default=default, exact=exact, auditor=auditor, ctx=ctx,
121 listsubrepos=listsubrepos, warn=warn, badfn=badfn)
121 listsubrepos=listsubrepos, warn=warn, badfn=badfn)
122
122
123 def icasefsmatch(root, cwd, patterns, include=None, exclude=None,
123 def icasefsmatch(root, cwd, patterns, include=None, exclude=None,
124 default='glob', auditor=None, ctx=None,
124 default='glob', auditor=None, ctx=None,
125 listsubrepos=False, badfn=None):
125 listsubrepos=False, badfn=None):
126 """A matcher for wdir on case insensitive filesystems, which normalizes the
126 """A matcher for wdir on case insensitive filesystems, which normalizes the
127 given patterns to the case in the filesystem.
127 given patterns to the case in the filesystem.
128 """
128 """
129 return icasefsmatcher(root, cwd, patterns, include=include, exclude=exclude,
129 return icasefsmatcher(root, cwd, patterns, include=include, exclude=exclude,
130 default=default, auditor=auditor, ctx=ctx,
130 default=default, auditor=auditor, ctx=ctx,
131 listsubrepos=listsubrepos, badfn=badfn)
131 listsubrepos=listsubrepos, badfn=badfn)
132
132
133 def exact(root, cwd, files, badfn=None):
133 def exact(root, cwd, files, badfn=None):
134 return match(root, cwd, files, exact=True, badfn=badfn)
134 return match(root, cwd, files, exact=True, badfn=badfn)
135
135
136 def always(root, cwd):
136 def always(root, cwd):
137 return match(root, cwd, [])
137 return match(root, cwd, [])
138
138
139 def badmatch(match, badfn):
139 def badmatch(match, badfn):
140 """Make a copy of the given matcher, replacing its bad method with the given
140 """Make a copy of the given matcher, replacing its bad method with the given
141 one.
141 one.
142 """
142 """
143 m = copy.copy(match)
143 m = copy.copy(match)
144 m.bad = badfn
144 m.bad = badfn
145 return m
145 return m
146
146
147 def _donormalize(patterns, default, root, cwd, auditor, warn):
147 def _donormalize(patterns, default, root, cwd, auditor, warn):
148 '''Convert 'kind:pat' from the patterns list to tuples with kind and
148 '''Convert 'kind:pat' from the patterns list to tuples with kind and
149 normalized and rooted patterns and with listfiles expanded.'''
149 normalized and rooted patterns and with listfiles expanded.'''
150 kindpats = []
150 kindpats = []
151 for kind, pat in [_patsplit(p, default) for p in patterns]:
151 for kind, pat in [_patsplit(p, default) for p in patterns]:
152 if kind in ('glob', 'relpath'):
152 if kind in ('glob', 'relpath'):
153 pat = pathutil.canonpath(root, cwd, pat, auditor)
153 pat = pathutil.canonpath(root, cwd, pat, auditor)
154 elif kind in ('relglob', 'path', 'rootfilesin'):
154 elif kind in ('relglob', 'path', 'rootfilesin'):
155 pat = util.normpath(pat)
155 pat = util.normpath(pat)
156 elif kind in ('listfile', 'listfile0'):
156 elif kind in ('listfile', 'listfile0'):
157 try:
157 try:
158 files = util.readfile(pat)
158 files = util.readfile(pat)
159 if kind == 'listfile0':
159 if kind == 'listfile0':
160 files = files.split('\0')
160 files = files.split('\0')
161 else:
161 else:
162 files = files.splitlines()
162 files = files.splitlines()
163 files = [f for f in files if f]
163 files = [f for f in files if f]
164 except EnvironmentError:
164 except EnvironmentError:
165 raise error.Abort(_("unable to read file list (%s)") % pat)
165 raise error.Abort(_("unable to read file list (%s)") % pat)
166 for k, p, source in _donormalize(files, default, root, cwd,
166 for k, p, source in _donormalize(files, default, root, cwd,
167 auditor, warn):
167 auditor, warn):
168 kindpats.append((k, p, pat))
168 kindpats.append((k, p, pat))
169 continue
169 continue
170 elif kind == 'include':
170 elif kind == 'include':
171 try:
171 try:
172 fullpath = os.path.join(root, util.localpath(pat))
172 fullpath = os.path.join(root, util.localpath(pat))
173 includepats = readpatternfile(fullpath, warn)
173 includepats = readpatternfile(fullpath, warn)
174 for k, p, source in _donormalize(includepats, default,
174 for k, p, source in _donormalize(includepats, default,
175 root, cwd, auditor, warn):
175 root, cwd, auditor, warn):
176 kindpats.append((k, p, source or pat))
176 kindpats.append((k, p, source or pat))
177 except error.Abort as inst:
177 except error.Abort as inst:
178 raise error.Abort('%s: %s' % (pat, inst[0]))
178 raise error.Abort('%s: %s' % (pat, inst[0]))
179 except IOError as inst:
179 except IOError as inst:
180 if warn:
180 if warn:
181 warn(_("skipping unreadable pattern file '%s': %s\n") %
181 warn(_("skipping unreadable pattern file '%s': %s\n") %
182 (pat, inst.strerror))
182 (pat, inst.strerror))
183 continue
183 continue
184 # else: re or relre - which cannot be normalized
184 # else: re or relre - which cannot be normalized
185 kindpats.append((kind, pat, ''))
185 kindpats.append((kind, pat, ''))
186 return kindpats
186 return kindpats
187
187
188 class matcher(object):
188 class matcher(object):
189
189
190 def __init__(self, root, cwd, patterns, include=None, exclude=None,
190 def __init__(self, root, cwd, patterns, include=None, exclude=None,
191 default='glob', exact=False, auditor=None, ctx=None,
191 default='glob', exact=False, auditor=None, ctx=None,
192 listsubrepos=False, warn=None, badfn=None):
192 listsubrepos=False, warn=None, badfn=None):
193 if include is None:
193 if include is None:
194 include = []
194 include = []
195 if exclude is None:
195 if exclude is None:
196 exclude = []
196 exclude = []
197
197
198 self._root = root
198 self._root = root
199 self._cwd = cwd
199 self._cwd = cwd
200 self._files = [] # exact files and roots of patterns
200 self._files = [] # exact files and roots of patterns
201 self._anypats = bool(include or exclude)
201 self._anypats = bool(include or exclude)
202 self._always = False
202 self._always = False
203 self._pathrestricted = bool(include or exclude or patterns)
203 self._pathrestricted = bool(include or exclude or patterns)
204
204
205 # roots are directories which are recursively included/excluded.
205 # roots are directories which are recursively included/excluded.
206 self._includeroots = set()
206 self._includeroots = set()
207 self._excluderoots = set()
207 self._excluderoots = set()
208 # dirs are directories which are non-recursively included.
208 # dirs are directories which are non-recursively included.
209 self._includedirs = set()
209 self._includedirs = set()
210
210
211 if badfn is not None:
211 if badfn is not None:
212 self.bad = badfn
212 self.bad = badfn
213
213
214 matchfns = []
214 matchfns = []
215 if include:
215 if include:
216 kindpats = self._normalize(include, 'glob', root, cwd, auditor,
216 kindpats = self._normalize(include, 'glob', root, cwd, auditor,
217 warn)
217 warn)
218 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
218 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
219 listsubrepos, root)
219 listsubrepos, root)
220 roots, dirs = _rootsanddirs(kindpats)
220 roots, dirs = _rootsanddirs(kindpats)
221 self._includeroots.update(roots)
221 self._includeroots.update(roots)
222 self._includedirs.update(dirs)
222 self._includedirs.update(dirs)
223 matchfns.append(im)
223 matchfns.append(im)
224 if exclude:
224 if exclude:
225 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor,
225 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor,
226 warn)
226 warn)
227 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
227 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
228 listsubrepos, root)
228 listsubrepos, root)
229 if not _anypats(kindpats):
229 if not _anypats(kindpats):
230 # Only consider recursive excludes as such - if a non-recursive
230 # Only consider recursive excludes as such - if a non-recursive
231 # exclude is used, we must still recurse into the excluded
231 # exclude is used, we must still recurse into the excluded
232 # directory, at least to find subdirectories. In such a case,
232 # directory, at least to find subdirectories. In such a case,
233 # the regex still won't match the non-recursively-excluded
233 # the regex still won't match the non-recursively-excluded
234 # files.
234 # files.
235 self._excluderoots.update(_roots(kindpats))
235 self._excluderoots.update(_roots(kindpats))
236 matchfns.append(lambda f: not em(f))
236 matchfns.append(lambda f: not em(f))
237 if exact:
237 if exact:
238 if isinstance(patterns, list):
238 if isinstance(patterns, list):
239 self._files = patterns
239 self._files = patterns
240 else:
240 else:
241 self._files = list(patterns)
241 self._files = list(patterns)
242 matchfns.append(self.exact)
242 matchfns.append(self.exact)
243 elif patterns:
243 elif patterns:
244 kindpats = self._normalize(patterns, default, root, cwd, auditor,
244 kindpats = self._normalize(patterns, default, root, cwd, auditor,
245 warn)
245 warn)
246 if not _kindpatsalwaysmatch(kindpats):
246 if not _kindpatsalwaysmatch(kindpats):
247 self._files = _explicitfiles(kindpats)
247 self._files = _explicitfiles(kindpats)
248 self._anypats = self._anypats or _anypats(kindpats)
248 self._anypats = self._anypats or _anypats(kindpats)
249 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
249 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
250 listsubrepos, root)
250 listsubrepos, root)
251 matchfns.append(pm)
251 matchfns.append(pm)
252
252
253 if not matchfns:
253 if not matchfns:
254 m = util.always
254 m = util.always
255 self._always = True
255 self._always = True
256 elif len(matchfns) == 1:
256 elif len(matchfns) == 1:
257 m = matchfns[0]
257 m = matchfns[0]
258 else:
258 else:
259 def m(f):
259 def m(f):
260 for matchfn in matchfns:
260 for matchfn in matchfns:
261 if not matchfn(f):
261 if not matchfn(f):
262 return False
262 return False
263 return True
263 return True
264
264
265 self.matchfn = m
265 self.matchfn = m
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
272
273 # Callbacks related to how the matcher is used by dirstate.walk.
273 # Callbacks related to how the matcher is used by dirstate.walk.
274 # Subscribers to these events must monkeypatch the matcher object.
274 # Subscribers to these events must monkeypatch the matcher object.
275 def bad(self, f, msg):
275 def bad(self, f, msg):
276 '''Callback from dirstate.walk for each explicit file that can't be
276 '''Callback from dirstate.walk for each explicit file that can't be
277 found/accessed, with an error message.'''
277 found/accessed, with an error message.'''
278 pass
278 pass
279
279
280 # If an explicitdir is set, it will be called when an explicitly listed
280 # If an explicitdir is set, it will be called when an explicitly listed
281 # directory is visited.
281 # directory is visited.
282 explicitdir = None
282 explicitdir = None
283
283
284 # If an traversedir is set, it will be called when a directory discovered
284 # If an traversedir is set, it will be called when a directory discovered
285 # by recursive traversal is visited.
285 # by recursive traversal is visited.
286 traversedir = None
286 traversedir = None
287
287
288 def abs(self, f):
288 def abs(self, f):
289 '''Convert a repo path back to path that is relative to the root of the
289 '''Convert a repo path back to path that is relative to the root of the
290 matcher.'''
290 matcher.'''
291 return f
291 return f
292
292
293 def rel(self, f):
293 def rel(self, f):
294 '''Convert repo path back to path that is relative to cwd of matcher.'''
294 '''Convert repo path back to path that is relative to cwd of matcher.'''
295 return util.pathto(self._root, self._cwd, f)
295 return util.pathto(self._root, self._cwd, f)
296
296
297 def uipath(self, f):
297 def uipath(self, f):
298 '''Convert repo path to a display path. If patterns or -I/-X were used
298 '''Convert repo path to a display path. If patterns or -I/-X were used
299 to create this matcher, the display path will be relative to cwd.
299 to create this matcher, the display path will be relative to cwd.
300 Otherwise it is relative to the root of the repo.'''
300 Otherwise it is relative to the root of the repo.'''
301 return (self._pathrestricted and self.rel(f)) or self.abs(f)
301 return (self._pathrestricted and self.rel(f)) or self.abs(f)
302
302
303 def files(self):
303 def files(self):
304 '''Explicitly listed files or patterns or roots:
304 '''Explicitly listed files or patterns or roots:
305 if no patterns or .always(): empty list,
305 if no patterns or .always(): empty list,
306 if exact: list exact files,
306 if exact: list exact files,
307 if not .anypats(): list all files and dirs,
307 if not .anypats(): list all files and dirs,
308 else: optimal roots'''
308 else: optimal roots'''
309 return self._files
309 return self._files
310
310
311 @propertycache
311 @propertycache
312 def _fileset(self):
312 def _fileset(self):
313 return set(self._files)
313 return set(self._files)
314
314
315 @propertycache
315 @propertycache
316 def _dirs(self):
316 def _dirs(self):
317 return set(util.dirs(self._fileset)) | {'.'}
317 return set(util.dirs(self._fileset)) | {'.'}
318
318
319 def visitdir(self, dir):
319 def visitdir(self, dir):
320 '''Decides whether a directory should be visited based on whether it
320 '''Decides whether a directory should be visited based on whether it
321 has potential matches in it or one of its subdirectories. This is
321 has potential matches in it or one of its subdirectories. This is
322 based on the match's primary, included, and excluded patterns.
322 based on the match's primary, included, and excluded patterns.
323
323
324 Returns the string 'all' if the given directory and all subdirectories
324 Returns the string 'all' if the given directory and all subdirectories
325 should be visited. Otherwise returns True or False indicating whether
325 should be visited. Otherwise returns True or False indicating whether
326 the given directory should be visited.
326 the given directory should be visited.
327
327
328 This function's behavior is undefined if it has returned False for
328 This function's behavior is undefined if it has returned False for
329 one of the dir's parent directories.
329 one of the dir's parent directories.
330 '''
330 '''
331 if self.prefix() and dir in self._fileset:
331 if self.prefix() and dir in self._fileset:
332 return 'all'
332 return 'all'
333 if dir in self._excluderoots:
333 if dir in self._excluderoots:
334 return False
334 return False
335 if ((self._includeroots or self._includedirs) and
335 if ((self._includeroots or self._includedirs) and
336 '.' not in self._includeroots and
336 '.' not in self._includeroots and
337 dir not in self._includeroots and
337 dir not in self._includeroots and
338 dir not in self._includedirs and
338 dir not in self._includedirs and
339 not any(parent in self._includeroots
339 not any(parent in self._includeroots
340 for parent in util.finddirs(dir))):
340 for parent in util.finddirs(dir))):
341 return False
341 return False
342 return (not self._fileset or
342 return (not self._fileset or
343 '.' in self._fileset or
343 '.' in self._fileset or
344 dir in self._fileset or
344 dir in self._fileset or
345 dir in self._dirs or
345 dir in self._dirs or
346 any(parentdir in self._fileset
346 any(parentdir in self._fileset
347 for parentdir in util.finddirs(dir)))
347 for parentdir in util.finddirs(dir)))
348
348
349 def exact(self, f):
349 def exact(self, f):
350 '''Returns True if f is in .files().'''
350 '''Returns True if f is in .files().'''
351 return f in self._fileset
351 return f in self._fileset
352
352
353 def anypats(self):
353 def anypats(self):
354 '''Matcher uses patterns or include/exclude.'''
354 '''Matcher uses patterns or include/exclude.'''
355 return self._anypats
355 return self._anypats
356
356
357 def always(self):
357 def always(self):
358 '''Matcher will match everything and .files() will be empty
358 '''Matcher will match everything and .files() will be empty
359 - optimization might be possible and necessary.'''
359 - optimization might be possible and necessary.'''
360 return self._always
360 return self._always
361
361
362 def isexact(self):
362 def isexact(self):
363 return self.matchfn == self.exact
363 return self.matchfn == self.exact
364
364
365 def prefix(self):
365 def prefix(self):
366 return not self.always() and not self.isexact() and not self.anypats()
366 return not self.always() and not self.isexact() and not self.anypats()
367
367
368 def _normalize(self, patterns, default, root, cwd, auditor, warn):
368 def _normalize(self, patterns, default, root, cwd, auditor, warn):
369 return _donormalize(patterns, default, root, cwd, auditor, warn)
369 return _donormalize(patterns, default, root, cwd, auditor, warn)
370
370
371 class subdirmatcher(matcher):
371 class subdirmatcher(matcher):
372 """Adapt a matcher to work on a subdirectory only.
372 """Adapt a matcher to work on a subdirectory only.
373
373
374 The paths are remapped to remove/insert the path as needed:
374 The paths are remapped to remove/insert the path as needed:
375
375
376 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
376 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
377 >>> m2 = subdirmatcher('sub', m1)
377 >>> m2 = subdirmatcher('sub', m1)
378 >>> bool(m2('a.txt'))
378 >>> bool(m2('a.txt'))
379 False
379 False
380 >>> bool(m2('b.txt'))
380 >>> bool(m2('b.txt'))
381 True
381 True
382 >>> bool(m2.matchfn('a.txt'))
382 >>> bool(m2.matchfn('a.txt'))
383 False
383 False
384 >>> bool(m2.matchfn('b.txt'))
384 >>> bool(m2.matchfn('b.txt'))
385 True
385 True
386 >>> m2.files()
386 >>> m2.files()
387 ['b.txt']
387 ['b.txt']
388 >>> m2.exact('b.txt')
388 >>> m2.exact('b.txt')
389 True
389 True
390 >>> util.pconvert(m2.rel('b.txt'))
390 >>> util.pconvert(m2.rel('b.txt'))
391 'sub/b.txt'
391 'sub/b.txt'
392 >>> def bad(f, msg):
392 >>> def bad(f, msg):
393 ... print "%s: %s" % (f, msg)
393 ... print "%s: %s" % (f, msg)
394 >>> m1.bad = bad
394 >>> m1.bad = bad
395 >>> m2.bad('x.txt', 'No such file')
395 >>> m2.bad('x.txt', 'No such file')
396 sub/x.txt: No such file
396 sub/x.txt: No such file
397 >>> m2.abs('c.txt')
397 >>> m2.abs('c.txt')
398 'sub/c.txt'
398 'sub/c.txt'
399 """
399 """
400
400
401 def __init__(self, path, matcher):
401 def __init__(self, path, matcher):
402 self._root = matcher._root
402 self._root = matcher._root
403 self._cwd = matcher._cwd
403 self._cwd = matcher._cwd
404 self._path = path
404 self._path = path
405 self._matcher = matcher
405 self._matcher = matcher
406 self._always = matcher._always
406 self._always = matcher._always
407
407
408 self._files = [f[len(path) + 1:] for f in matcher._files
408 self._files = [f[len(path) + 1:] for f in matcher._files
409 if f.startswith(path + "/")]
409 if f.startswith(path + "/")]
410
410
411 # If the parent repo had a path to this subrepo and the matcher is
411 # If the parent repo had a path to this subrepo and the matcher is
412 # a prefix matcher, this submatcher always matches.
412 # a prefix matcher, this submatcher always matches.
413 if matcher.prefix():
413 if matcher.prefix():
414 self._always = any(f == path for f in matcher._files)
414 self._always = any(f == path for f in matcher._files)
415
415
416 self._anypats = matcher._anypats
416 self._anypats = matcher._anypats
417 # Some information is lost in the superclass's constructor, so we
417 # Some information is lost in the superclass's constructor, so we
418 # can not accurately create the matching function for the subdirectory
418 # can not accurately create the matching function for the subdirectory
419 # from the inputs. Instead, we override matchfn() and visitdir() to
419 # from the inputs. Instead, we override matchfn() and visitdir() to
420 # call the original matcher with the subdirectory path prepended.
420 # call the original matcher with the subdirectory path prepended.
421 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
421 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
422
422
423 def bad(self, f, msg):
423 def bad(self, f, msg):
424 self._matcher.bad(self._path + "/" + f, msg)
424 self._matcher.bad(self._path + "/" + f, msg)
425
425
426 def abs(self, f):
426 def abs(self, f):
427 return self._matcher.abs(self._path + "/" + f)
427 return self._matcher.abs(self._path + "/" + f)
428
428
429 def rel(self, f):
429 def rel(self, f):
430 return self._matcher.rel(self._path + "/" + f)
430 return self._matcher.rel(self._path + "/" + f)
431
431
432 def uipath(self, f):
432 def uipath(self, f):
433 return self._matcher.uipath(self._path + "/" + f)
433 return self._matcher.uipath(self._path + "/" + f)
434
434
435 def visitdir(self, dir):
435 def visitdir(self, dir):
436 if dir == '.':
436 if dir == '.':
437 dir = self._path
437 dir = self._path
438 else:
438 else:
439 dir = self._path + "/" + dir
439 dir = self._path + "/" + dir
440 return self._matcher.visitdir(dir)
440 return self._matcher.visitdir(dir)
441
441
442 class icasefsmatcher(matcher):
442 class icasefsmatcher(matcher):
443
443
444 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
444 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
445 ctx, listsubrepos=False, badfn=None):
445 ctx, listsubrepos=False, badfn=None):
446 init = super(icasefsmatcher, self).__init__
446 init = super(icasefsmatcher, self).__init__
447 self._dirstate = ctx.repo().dirstate
447 self._dirstate = ctx.repo().dirstate
448 self._dsnormalize = self._dirstate.normalize
448 self._dsnormalize = self._dirstate.normalize
449
449
450 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
450 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
451 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
451 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
452
452
453 # m.exact(file) must be based off of the actual user input, otherwise
454 # inexact case matches are treated as exact, and not noted without -v.
455 if self._files:
456 roots, dirs = _rootsanddirs(self._kp)
457 self._fileset = set(roots)
458 self._fileset.update(dirs)
459
460 def _normalize(self, patterns, default, root, cwd, auditor, warn):
453 def _normalize(self, patterns, default, root, cwd, auditor, warn):
461 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
454 kp = super(icasefsmatcher, self)._normalize(patterns, default, root,
462 root, cwd, auditor,
455 cwd, auditor, warn)
463 warn)
464 kindpats = []
456 kindpats = []
465 for kind, pats, source in self._kp:
457 for kind, pats, source in kp:
466 if kind not in ('re', 'relre'): # regex can't be normalized
458 if kind not in ('re', 'relre'): # regex can't be normalized
467 p = pats
459 p = pats
468 pats = self._dsnormalize(pats)
460 pats = self._dsnormalize(pats)
469
461
470 # Preserve the original to handle a case only rename.
462 # Preserve the original to handle a case only rename.
471 if p != pats and p in self._dirstate:
463 if p != pats and p in self._dirstate:
472 kindpats.append((kind, p, source))
464 kindpats.append((kind, p, source))
473
465
474 kindpats.append((kind, pats, source))
466 kindpats.append((kind, pats, source))
475 return kindpats
467 return kindpats
476
468
477 def patkind(pattern, default=None):
469 def patkind(pattern, default=None):
478 '''If pattern is 'kind:pat' with a known kind, return kind.'''
470 '''If pattern is 'kind:pat' with a known kind, return kind.'''
479 return _patsplit(pattern, default)[0]
471 return _patsplit(pattern, default)[0]
480
472
481 def _patsplit(pattern, default):
473 def _patsplit(pattern, default):
482 """Split a string into the optional pattern kind prefix and the actual
474 """Split a string into the optional pattern kind prefix and the actual
483 pattern."""
475 pattern."""
484 if ':' in pattern:
476 if ':' in pattern:
485 kind, pat = pattern.split(':', 1)
477 kind, pat = pattern.split(':', 1)
486 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
478 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
487 'listfile', 'listfile0', 'set', 'include', 'subinclude',
479 'listfile', 'listfile0', 'set', 'include', 'subinclude',
488 'rootfilesin'):
480 'rootfilesin'):
489 return kind, pat
481 return kind, pat
490 return default, pattern
482 return default, pattern
491
483
492 def _globre(pat):
484 def _globre(pat):
493 r'''Convert an extended glob string to a regexp string.
485 r'''Convert an extended glob string to a regexp string.
494
486
495 >>> print _globre(r'?')
487 >>> print _globre(r'?')
496 .
488 .
497 >>> print _globre(r'*')
489 >>> print _globre(r'*')
498 [^/]*
490 [^/]*
499 >>> print _globre(r'**')
491 >>> print _globre(r'**')
500 .*
492 .*
501 >>> print _globre(r'**/a')
493 >>> print _globre(r'**/a')
502 (?:.*/)?a
494 (?:.*/)?a
503 >>> print _globre(r'a/**/b')
495 >>> print _globre(r'a/**/b')
504 a\/(?:.*/)?b
496 a\/(?:.*/)?b
505 >>> print _globre(r'[a*?!^][^b][!c]')
497 >>> print _globre(r'[a*?!^][^b][!c]')
506 [a*?!^][\^b][^c]
498 [a*?!^][\^b][^c]
507 >>> print _globre(r'{a,b}')
499 >>> print _globre(r'{a,b}')
508 (?:a|b)
500 (?:a|b)
509 >>> print _globre(r'.\*\?')
501 >>> print _globre(r'.\*\?')
510 \.\*\?
502 \.\*\?
511 '''
503 '''
512 i, n = 0, len(pat)
504 i, n = 0, len(pat)
513 res = ''
505 res = ''
514 group = 0
506 group = 0
515 escape = util.re.escape
507 escape = util.re.escape
516 def peek():
508 def peek():
517 return i < n and pat[i:i + 1]
509 return i < n and pat[i:i + 1]
518 while i < n:
510 while i < n:
519 c = pat[i:i + 1]
511 c = pat[i:i + 1]
520 i += 1
512 i += 1
521 if c not in '*?[{},\\':
513 if c not in '*?[{},\\':
522 res += escape(c)
514 res += escape(c)
523 elif c == '*':
515 elif c == '*':
524 if peek() == '*':
516 if peek() == '*':
525 i += 1
517 i += 1
526 if peek() == '/':
518 if peek() == '/':
527 i += 1
519 i += 1
528 res += '(?:.*/)?'
520 res += '(?:.*/)?'
529 else:
521 else:
530 res += '.*'
522 res += '.*'
531 else:
523 else:
532 res += '[^/]*'
524 res += '[^/]*'
533 elif c == '?':
525 elif c == '?':
534 res += '.'
526 res += '.'
535 elif c == '[':
527 elif c == '[':
536 j = i
528 j = i
537 if j < n and pat[j:j + 1] in '!]':
529 if j < n and pat[j:j + 1] in '!]':
538 j += 1
530 j += 1
539 while j < n and pat[j:j + 1] != ']':
531 while j < n and pat[j:j + 1] != ']':
540 j += 1
532 j += 1
541 if j >= n:
533 if j >= n:
542 res += '\\['
534 res += '\\['
543 else:
535 else:
544 stuff = pat[i:j].replace('\\','\\\\')
536 stuff = pat[i:j].replace('\\','\\\\')
545 i = j + 1
537 i = j + 1
546 if stuff[0:1] == '!':
538 if stuff[0:1] == '!':
547 stuff = '^' + stuff[1:]
539 stuff = '^' + stuff[1:]
548 elif stuff[0:1] == '^':
540 elif stuff[0:1] == '^':
549 stuff = '\\' + stuff
541 stuff = '\\' + stuff
550 res = '%s[%s]' % (res, stuff)
542 res = '%s[%s]' % (res, stuff)
551 elif c == '{':
543 elif c == '{':
552 group += 1
544 group += 1
553 res += '(?:'
545 res += '(?:'
554 elif c == '}' and group:
546 elif c == '}' and group:
555 res += ')'
547 res += ')'
556 group -= 1
548 group -= 1
557 elif c == ',' and group:
549 elif c == ',' and group:
558 res += '|'
550 res += '|'
559 elif c == '\\':
551 elif c == '\\':
560 p = peek()
552 p = peek()
561 if p:
553 if p:
562 i += 1
554 i += 1
563 res += escape(p)
555 res += escape(p)
564 else:
556 else:
565 res += escape(c)
557 res += escape(c)
566 else:
558 else:
567 res += escape(c)
559 res += escape(c)
568 return res
560 return res
569
561
570 def _regex(kind, pat, globsuffix):
562 def _regex(kind, pat, globsuffix):
571 '''Convert a (normalized) pattern of any kind into a regular expression.
563 '''Convert a (normalized) pattern of any kind into a regular expression.
572 globsuffix is appended to the regexp of globs.'''
564 globsuffix is appended to the regexp of globs.'''
573 if not pat:
565 if not pat:
574 return ''
566 return ''
575 if kind == 're':
567 if kind == 're':
576 return pat
568 return pat
577 if kind == 'path':
569 if kind == 'path':
578 if pat == '.':
570 if pat == '.':
579 return ''
571 return ''
580 return '^' + util.re.escape(pat) + '(?:/|$)'
572 return '^' + util.re.escape(pat) + '(?:/|$)'
581 if kind == 'rootfilesin':
573 if kind == 'rootfilesin':
582 if pat == '.':
574 if pat == '.':
583 escaped = ''
575 escaped = ''
584 else:
576 else:
585 # Pattern is a directory name.
577 # Pattern is a directory name.
586 escaped = util.re.escape(pat) + '/'
578 escaped = util.re.escape(pat) + '/'
587 # Anything after the pattern must be a non-directory.
579 # Anything after the pattern must be a non-directory.
588 return '^' + escaped + '[^/]+$'
580 return '^' + escaped + '[^/]+$'
589 if kind == 'relglob':
581 if kind == 'relglob':
590 return '(?:|.*/)' + _globre(pat) + globsuffix
582 return '(?:|.*/)' + _globre(pat) + globsuffix
591 if kind == 'relpath':
583 if kind == 'relpath':
592 return util.re.escape(pat) + '(?:/|$)'
584 return util.re.escape(pat) + '(?:/|$)'
593 if kind == 'relre':
585 if kind == 'relre':
594 if pat.startswith('^'):
586 if pat.startswith('^'):
595 return pat
587 return pat
596 return '.*' + pat
588 return '.*' + pat
597 return _globre(pat) + globsuffix
589 return _globre(pat) + globsuffix
598
590
599 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
591 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
600 '''Return regexp string and a matcher function for kindpats.
592 '''Return regexp string and a matcher function for kindpats.
601 globsuffix is appended to the regexp of globs.'''
593 globsuffix is appended to the regexp of globs.'''
602 matchfuncs = []
594 matchfuncs = []
603
595
604 subincludes, kindpats = _expandsubinclude(kindpats, root)
596 subincludes, kindpats = _expandsubinclude(kindpats, root)
605 if subincludes:
597 if subincludes:
606 submatchers = {}
598 submatchers = {}
607 def matchsubinclude(f):
599 def matchsubinclude(f):
608 for prefix, matcherargs in subincludes:
600 for prefix, matcherargs in subincludes:
609 if f.startswith(prefix):
601 if f.startswith(prefix):
610 mf = submatchers.get(prefix)
602 mf = submatchers.get(prefix)
611 if mf is None:
603 if mf is None:
612 mf = match(*matcherargs)
604 mf = match(*matcherargs)
613 submatchers[prefix] = mf
605 submatchers[prefix] = mf
614
606
615 if mf(f[len(prefix):]):
607 if mf(f[len(prefix):]):
616 return True
608 return True
617 return False
609 return False
618 matchfuncs.append(matchsubinclude)
610 matchfuncs.append(matchsubinclude)
619
611
620 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
612 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
621 if fset:
613 if fset:
622 matchfuncs.append(fset.__contains__)
614 matchfuncs.append(fset.__contains__)
623
615
624 regex = ''
616 regex = ''
625 if kindpats:
617 if kindpats:
626 regex, mf = _buildregexmatch(kindpats, globsuffix)
618 regex, mf = _buildregexmatch(kindpats, globsuffix)
627 matchfuncs.append(mf)
619 matchfuncs.append(mf)
628
620
629 if len(matchfuncs) == 1:
621 if len(matchfuncs) == 1:
630 return regex, matchfuncs[0]
622 return regex, matchfuncs[0]
631 else:
623 else:
632 return regex, lambda f: any(mf(f) for mf in matchfuncs)
624 return regex, lambda f: any(mf(f) for mf in matchfuncs)
633
625
634 def _buildregexmatch(kindpats, globsuffix):
626 def _buildregexmatch(kindpats, globsuffix):
635 """Build a match function from a list of kinds and kindpats,
627 """Build a match function from a list of kinds and kindpats,
636 return regexp string and a matcher function."""
628 return regexp string and a matcher function."""
637 try:
629 try:
638 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
630 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
639 for (k, p, s) in kindpats])
631 for (k, p, s) in kindpats])
640 if len(regex) > 20000:
632 if len(regex) > 20000:
641 raise OverflowError
633 raise OverflowError
642 return regex, _rematcher(regex)
634 return regex, _rematcher(regex)
643 except OverflowError:
635 except OverflowError:
644 # We're using a Python with a tiny regex engine and we
636 # We're using a Python with a tiny regex engine and we
645 # made it explode, so we'll divide the pattern list in two
637 # made it explode, so we'll divide the pattern list in two
646 # until it works
638 # until it works
647 l = len(kindpats)
639 l = len(kindpats)
648 if l < 2:
640 if l < 2:
649 raise
641 raise
650 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
642 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
651 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
643 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
652 return regex, lambda s: a(s) or b(s)
644 return regex, lambda s: a(s) or b(s)
653 except re.error:
645 except re.error:
654 for k, p, s in kindpats:
646 for k, p, s in kindpats:
655 try:
647 try:
656 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
648 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
657 except re.error:
649 except re.error:
658 if s:
650 if s:
659 raise error.Abort(_("%s: invalid pattern (%s): %s") %
651 raise error.Abort(_("%s: invalid pattern (%s): %s") %
660 (s, k, p))
652 (s, k, p))
661 else:
653 else:
662 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
654 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
663 raise error.Abort(_("invalid pattern"))
655 raise error.Abort(_("invalid pattern"))
664
656
665 def _patternrootsanddirs(kindpats):
657 def _patternrootsanddirs(kindpats):
666 '''Returns roots and directories corresponding to each pattern.
658 '''Returns roots and directories corresponding to each pattern.
667
659
668 This calculates the roots and directories exactly matching the patterns and
660 This calculates the roots and directories exactly matching the patterns and
669 returns a tuple of (roots, dirs) for each. It does not return other
661 returns a tuple of (roots, dirs) for each. It does not return other
670 directories which may also need to be considered, like the parent
662 directories which may also need to be considered, like the parent
671 directories.
663 directories.
672 '''
664 '''
673 r = []
665 r = []
674 d = []
666 d = []
675 for kind, pat, source in kindpats:
667 for kind, pat, source in kindpats:
676 if kind == 'glob': # find the non-glob prefix
668 if kind == 'glob': # find the non-glob prefix
677 root = []
669 root = []
678 for p in pat.split('/'):
670 for p in pat.split('/'):
679 if '[' in p or '{' in p or '*' in p or '?' in p:
671 if '[' in p or '{' in p or '*' in p or '?' in p:
680 break
672 break
681 root.append(p)
673 root.append(p)
682 r.append('/'.join(root) or '.')
674 r.append('/'.join(root) or '.')
683 elif kind in ('relpath', 'path'):
675 elif kind in ('relpath', 'path'):
684 r.append(pat or '.')
676 r.append(pat or '.')
685 elif kind in ('rootfilesin',):
677 elif kind in ('rootfilesin',):
686 d.append(pat or '.')
678 d.append(pat or '.')
687 else: # relglob, re, relre
679 else: # relglob, re, relre
688 r.append('.')
680 r.append('.')
689 return r, d
681 return r, d
690
682
691 def _roots(kindpats):
683 def _roots(kindpats):
692 '''Returns root directories to match recursively from the given patterns.'''
684 '''Returns root directories to match recursively from the given patterns.'''
693 roots, dirs = _patternrootsanddirs(kindpats)
685 roots, dirs = _patternrootsanddirs(kindpats)
694 return roots
686 return roots
695
687
696 def _rootsanddirs(kindpats):
688 def _rootsanddirs(kindpats):
697 '''Returns roots and exact directories from patterns.
689 '''Returns roots and exact directories from patterns.
698
690
699 roots are directories to match recursively, whereas exact directories should
691 roots are directories to match recursively, whereas exact directories should
700 be matched non-recursively. The returned (roots, dirs) tuple will also
692 be matched non-recursively. The returned (roots, dirs) tuple will also
701 include directories that need to be implicitly considered as either, such as
693 include directories that need to be implicitly considered as either, such as
702 parent directories.
694 parent directories.
703
695
704 >>> _rootsanddirs(\
696 >>> _rootsanddirs(\
705 [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
697 [('glob', 'g/h/*', ''), ('glob', 'g/h', ''), ('glob', 'g*', '')])
706 (['g/h', 'g/h', '.'], ['g', '.'])
698 (['g/h', 'g/h', '.'], ['g', '.'])
707 >>> _rootsanddirs(\
699 >>> _rootsanddirs(\
708 [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
700 [('rootfilesin', 'g/h', ''), ('rootfilesin', '', '')])
709 ([], ['g/h', '.', 'g', '.'])
701 ([], ['g/h', '.', 'g', '.'])
710 >>> _rootsanddirs(\
702 >>> _rootsanddirs(\
711 [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
703 [('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
712 (['r', 'p/p', '.'], ['p', '.'])
704 (['r', 'p/p', '.'], ['p', '.'])
713 >>> _rootsanddirs(\
705 >>> _rootsanddirs(\
714 [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
706 [('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
715 (['.', '.', '.'], ['.'])
707 (['.', '.', '.'], ['.'])
716 '''
708 '''
717 r, d = _patternrootsanddirs(kindpats)
709 r, d = _patternrootsanddirs(kindpats)
718
710
719 # Append the parents as non-recursive/exact directories, since they must be
711 # Append the parents as non-recursive/exact directories, since they must be
720 # scanned to get to either the roots or the other exact directories.
712 # scanned to get to either the roots or the other exact directories.
721 d.extend(util.dirs(d))
713 d.extend(util.dirs(d))
722 d.extend(util.dirs(r))
714 d.extend(util.dirs(r))
723 # util.dirs() does not include the root directory, so add it manually
715 # util.dirs() does not include the root directory, so add it manually
724 d.append('.')
716 d.append('.')
725
717
726 return r, d
718 return r, d
727
719
728 def _explicitfiles(kindpats):
720 def _explicitfiles(kindpats):
729 '''Returns the potential explicit filenames from the patterns.
721 '''Returns the potential explicit filenames from the patterns.
730
722
731 >>> _explicitfiles([('path', 'foo/bar', '')])
723 >>> _explicitfiles([('path', 'foo/bar', '')])
732 ['foo/bar']
724 ['foo/bar']
733 >>> _explicitfiles([('rootfilesin', 'foo/bar', '')])
725 >>> _explicitfiles([('rootfilesin', 'foo/bar', '')])
734 []
726 []
735 '''
727 '''
736 # Keep only the pattern kinds where one can specify filenames (vs only
728 # Keep only the pattern kinds where one can specify filenames (vs only
737 # directory names).
729 # directory names).
738 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
730 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
739 return _roots(filable)
731 return _roots(filable)
740
732
741 def _anypats(kindpats):
733 def _anypats(kindpats):
742 for kind, pat, source in kindpats:
734 for kind, pat, source in kindpats:
743 if kind in ('glob', 're', 'relglob', 'relre', 'set', 'rootfilesin'):
735 if kind in ('glob', 're', 'relglob', 'relre', 'set', 'rootfilesin'):
744 return True
736 return True
745
737
746 _commentre = None
738 _commentre = None
747
739
748 def readpatternfile(filepath, warn, sourceinfo=False):
740 def readpatternfile(filepath, warn, sourceinfo=False):
749 '''parse a pattern file, returning a list of
741 '''parse a pattern file, returning a list of
750 patterns. These patterns should be given to compile()
742 patterns. These patterns should be given to compile()
751 to be validated and converted into a match function.
743 to be validated and converted into a match function.
752
744
753 trailing white space is dropped.
745 trailing white space is dropped.
754 the escape character is backslash.
746 the escape character is backslash.
755 comments start with #.
747 comments start with #.
756 empty lines are skipped.
748 empty lines are skipped.
757
749
758 lines can be of the following formats:
750 lines can be of the following formats:
759
751
760 syntax: regexp # defaults following lines to non-rooted regexps
752 syntax: regexp # defaults following lines to non-rooted regexps
761 syntax: glob # defaults following lines to non-rooted globs
753 syntax: glob # defaults following lines to non-rooted globs
762 re:pattern # non-rooted regular expression
754 re:pattern # non-rooted regular expression
763 glob:pattern # non-rooted glob
755 glob:pattern # non-rooted glob
764 pattern # pattern of the current default type
756 pattern # pattern of the current default type
765
757
766 if sourceinfo is set, returns a list of tuples:
758 if sourceinfo is set, returns a list of tuples:
767 (pattern, lineno, originalline). This is useful to debug ignore patterns.
759 (pattern, lineno, originalline). This is useful to debug ignore patterns.
768 '''
760 '''
769
761
770 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
762 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
771 'include': 'include', 'subinclude': 'subinclude'}
763 'include': 'include', 'subinclude': 'subinclude'}
772 syntax = 'relre:'
764 syntax = 'relre:'
773 patterns = []
765 patterns = []
774
766
775 fp = open(filepath, 'rb')
767 fp = open(filepath, 'rb')
776 for lineno, line in enumerate(util.iterfile(fp), start=1):
768 for lineno, line in enumerate(util.iterfile(fp), start=1):
777 if "#" in line:
769 if "#" in line:
778 global _commentre
770 global _commentre
779 if not _commentre:
771 if not _commentre:
780 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
772 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
781 # remove comments prefixed by an even number of escapes
773 # remove comments prefixed by an even number of escapes
782 m = _commentre.search(line)
774 m = _commentre.search(line)
783 if m:
775 if m:
784 line = line[:m.end(1)]
776 line = line[:m.end(1)]
785 # fixup properly escaped comments that survived the above
777 # fixup properly escaped comments that survived the above
786 line = line.replace("\\#", "#")
778 line = line.replace("\\#", "#")
787 line = line.rstrip()
779 line = line.rstrip()
788 if not line:
780 if not line:
789 continue
781 continue
790
782
791 if line.startswith('syntax:'):
783 if line.startswith('syntax:'):
792 s = line[7:].strip()
784 s = line[7:].strip()
793 try:
785 try:
794 syntax = syntaxes[s]
786 syntax = syntaxes[s]
795 except KeyError:
787 except KeyError:
796 if warn:
788 if warn:
797 warn(_("%s: ignoring invalid syntax '%s'\n") %
789 warn(_("%s: ignoring invalid syntax '%s'\n") %
798 (filepath, s))
790 (filepath, s))
799 continue
791 continue
800
792
801 linesyntax = syntax
793 linesyntax = syntax
802 for s, rels in syntaxes.iteritems():
794 for s, rels in syntaxes.iteritems():
803 if line.startswith(rels):
795 if line.startswith(rels):
804 linesyntax = rels
796 linesyntax = rels
805 line = line[len(rels):]
797 line = line[len(rels):]
806 break
798 break
807 elif line.startswith(s+':'):
799 elif line.startswith(s+':'):
808 linesyntax = rels
800 linesyntax = rels
809 line = line[len(s) + 1:]
801 line = line[len(s) + 1:]
810 break
802 break
811 if sourceinfo:
803 if sourceinfo:
812 patterns.append((linesyntax + line, lineno, line))
804 patterns.append((linesyntax + line, lineno, line))
813 else:
805 else:
814 patterns.append(linesyntax + line)
806 patterns.append(linesyntax + line)
815 fp.close()
807 fp.close()
816 return patterns
808 return patterns
@@ -1,248 +1,246 b''
1 $ hg init a
1 $ hg init a
2 $ cd a
2 $ cd a
3 $ echo a > a
3 $ echo a > a
4 $ hg add -n
4 $ hg add -n
5 adding a
5 adding a
6 $ hg st
6 $ hg st
7 ? a
7 ? a
8 $ hg add
8 $ hg add
9 adding a
9 adding a
10 $ hg st
10 $ hg st
11 A a
11 A a
12 $ hg forget a
12 $ hg forget a
13 $ hg add
13 $ hg add
14 adding a
14 adding a
15 $ hg st
15 $ hg st
16 A a
16 A a
17
17
18 $ echo b > b
18 $ echo b > b
19 $ hg add -n b
19 $ hg add -n b
20 $ hg st
20 $ hg st
21 A a
21 A a
22 ? b
22 ? b
23 $ hg add b
23 $ hg add b
24 $ hg st
24 $ hg st
25 A a
25 A a
26 A b
26 A b
27
27
28 should fail
28 should fail
29
29
30 $ hg add b
30 $ hg add b
31 b already tracked!
31 b already tracked!
32 $ hg st
32 $ hg st
33 A a
33 A a
34 A b
34 A b
35
35
36 #if no-windows
36 #if no-windows
37 $ echo foo > con.xml
37 $ echo foo > con.xml
38 $ hg --config ui.portablefilenames=jump add con.xml
38 $ hg --config ui.portablefilenames=jump add con.xml
39 abort: ui.portablefilenames value is invalid ('jump')
39 abort: ui.portablefilenames value is invalid ('jump')
40 [255]
40 [255]
41 $ hg --config ui.portablefilenames=abort add con.xml
41 $ hg --config ui.portablefilenames=abort add con.xml
42 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
42 abort: filename contains 'con', which is reserved on Windows: 'con.xml'
43 [255]
43 [255]
44 $ hg st
44 $ hg st
45 A a
45 A a
46 A b
46 A b
47 ? con.xml
47 ? con.xml
48 $ hg add con.xml
48 $ hg add con.xml
49 warning: filename contains 'con', which is reserved on Windows: 'con.xml'
49 warning: filename contains 'con', which is reserved on Windows: 'con.xml'
50 $ hg st
50 $ hg st
51 A a
51 A a
52 A b
52 A b
53 A con.xml
53 A con.xml
54 $ hg forget con.xml
54 $ hg forget con.xml
55 $ rm con.xml
55 $ rm con.xml
56 #endif
56 #endif
57
57
58 #if eol-in-paths
58 #if eol-in-paths
59 $ echo bla > 'hello:world'
59 $ echo bla > 'hello:world'
60 $ hg --config ui.portablefilenames=abort add
60 $ hg --config ui.portablefilenames=abort add
61 adding hello:world
61 adding hello:world
62 abort: filename contains ':', which is reserved on Windows: 'hello:world'
62 abort: filename contains ':', which is reserved on Windows: 'hello:world'
63 [255]
63 [255]
64 $ hg st
64 $ hg st
65 A a
65 A a
66 A b
66 A b
67 ? hello:world
67 ? hello:world
68 $ hg --config ui.portablefilenames=ignore add
68 $ hg --config ui.portablefilenames=ignore add
69 adding hello:world
69 adding hello:world
70 $ hg st
70 $ hg st
71 A a
71 A a
72 A b
72 A b
73 A hello:world
73 A hello:world
74 #endif
74 #endif
75
75
76 $ hg ci -m 0 --traceback
76 $ hg ci -m 0 --traceback
77
77
78 $ hg log -r "heads(. or wdir() & file('**'))"
78 $ hg log -r "heads(. or wdir() & file('**'))"
79 changeset: 0:* (glob)
79 changeset: 0:* (glob)
80 tag: tip
80 tag: tip
81 user: test
81 user: test
82 date: Thu Jan 01 00:00:00 1970 +0000
82 date: Thu Jan 01 00:00:00 1970 +0000
83 summary: 0
83 summary: 0
84
84
85 should fail
85 should fail
86
86
87 $ hg add a
87 $ hg add a
88 a already tracked!
88 a already tracked!
89
89
90 $ echo aa > a
90 $ echo aa > a
91 $ hg ci -m 1
91 $ hg ci -m 1
92 $ hg up 0
92 $ hg up 0
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 $ echo aaa > a
94 $ echo aaa > a
95 $ hg ci -m 2
95 $ hg ci -m 2
96 created new head
96 created new head
97
97
98 $ hg merge
98 $ hg merge
99 merging a
99 merging a
100 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
100 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
101 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
101 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
102 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
102 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
103 [1]
103 [1]
104 $ hg st
104 $ hg st
105 M a
105 M a
106 ? a.orig
106 ? a.orig
107
107
108 wdir doesn't cause a crash, and can be dynamically selected if dirty
108 wdir doesn't cause a crash, and can be dynamically selected if dirty
109
109
110 $ hg log -r "heads(. or wdir() & file('**'))"
110 $ hg log -r "heads(. or wdir() & file('**'))"
111 changeset: 2147483647:ffffffffffff
111 changeset: 2147483647:ffffffffffff
112 parent: 2:* (glob)
112 parent: 2:* (glob)
113 parent: 1:* (glob)
113 parent: 1:* (glob)
114 user: test
114 user: test
115 date: * (glob)
115 date: * (glob)
116
116
117 should fail
117 should fail
118
118
119 $ hg add a
119 $ hg add a
120 a already tracked!
120 a already tracked!
121 $ hg st
121 $ hg st
122 M a
122 M a
123 ? a.orig
123 ? a.orig
124 $ hg resolve -m a
124 $ hg resolve -m a
125 (no more unresolved files)
125 (no more unresolved files)
126 $ hg ci -m merge
126 $ hg ci -m merge
127
127
128 Issue683: peculiarity with hg revert of an removed then added file
128 Issue683: peculiarity with hg revert of an removed then added file
129
129
130 $ hg forget a
130 $ hg forget a
131 $ hg add a
131 $ hg add a
132 $ hg st
132 $ hg st
133 ? a.orig
133 ? a.orig
134 $ hg rm a
134 $ hg rm a
135 $ hg st
135 $ hg st
136 R a
136 R a
137 ? a.orig
137 ? a.orig
138 $ echo a > a
138 $ echo a > a
139 $ hg add a
139 $ hg add a
140 $ hg st
140 $ hg st
141 M a
141 M a
142 ? a.orig
142 ? a.orig
143
143
144 Forgotten file can be added back (as either clean or modified)
144 Forgotten file can be added back (as either clean or modified)
145
145
146 $ hg forget b
146 $ hg forget b
147 $ hg add b
147 $ hg add b
148 $ hg st -A b
148 $ hg st -A b
149 C b
149 C b
150 $ hg forget b
150 $ hg forget b
151 $ echo modified > b
151 $ echo modified > b
152 $ hg add b
152 $ hg add b
153 $ hg st -A b
153 $ hg st -A b
154 M b
154 M b
155 $ hg revert -qC b
155 $ hg revert -qC b
156
156
157 $ hg add c && echo "unexpected addition of missing file"
157 $ hg add c && echo "unexpected addition of missing file"
158 c: * (glob)
158 c: * (glob)
159 [1]
159 [1]
160 $ echo c > c
160 $ echo c > c
161 $ hg add d c && echo "unexpected addition of missing file"
161 $ hg add d c && echo "unexpected addition of missing file"
162 d: * (glob)
162 d: * (glob)
163 [1]
163 [1]
164 $ hg st
164 $ hg st
165 M a
165 M a
166 A c
166 A c
167 ? a.orig
167 ? a.orig
168 $ hg up -C
168 $ hg up -C
169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
170
170
171 forget and get should have the right order: added but missing dir should be
171 forget and get should have the right order: added but missing dir should be
172 forgotten before file with same name is added
172 forgotten before file with same name is added
173
173
174 $ echo file d > d
174 $ echo file d > d
175 $ hg add d
175 $ hg add d
176 $ hg ci -md
176 $ hg ci -md
177 $ hg rm d
177 $ hg rm d
178 $ mkdir d
178 $ mkdir d
179 $ echo a > d/a
179 $ echo a > d/a
180 $ hg add d/a
180 $ hg add d/a
181 $ rm -r d
181 $ rm -r d
182 $ hg up -C
182 $ hg up -C
183 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
183 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 $ cat d
184 $ cat d
185 file d
185 file d
186
186
187 Test that adding a directory doesn't require case matching (issue4578)
187 Test that adding a directory doesn't require case matching (issue4578)
188 #if icasefs
188 #if icasefs
189 $ mkdir -p CapsDir1/CapsDir
189 $ mkdir -p CapsDir1/CapsDir
190 $ echo abc > CapsDir1/CapsDir/AbC.txt
190 $ echo abc > CapsDir1/CapsDir/AbC.txt
191 $ mkdir CapsDir1/CapsDir/SubDir
191 $ mkdir CapsDir1/CapsDir/SubDir
192 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
192 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
193
193
194 $ hg add capsdir1/capsdir
194 $ hg add capsdir1/capsdir
195 adding CapsDir1/CapsDir/AbC.txt (glob)
195 adding CapsDir1/CapsDir/AbC.txt (glob)
196 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
196 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
197
197
198 $ hg forget capsdir1/capsdir/abc.txt
198 $ hg forget capsdir1/capsdir/abc.txt
199 removing CapsDir1/CapsDir/AbC.txt (glob)
200
199
201 $ hg forget capsdir1/capsdir
200 $ hg forget capsdir1/capsdir
202 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
201 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
203
202
204 $ hg add capsdir1
203 $ hg add capsdir1
205 adding CapsDir1/CapsDir/AbC.txt (glob)
204 adding CapsDir1/CapsDir/AbC.txt (glob)
206 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
205 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
207
206
208 $ hg ci -m "AbCDef" capsdir1/capsdir
207 $ hg ci -m "AbCDef" capsdir1/capsdir
209
208
210 $ hg status -A capsdir1/capsdir
209 $ hg status -A capsdir1/capsdir
211 C CapsDir1/CapsDir/AbC.txt
210 C CapsDir1/CapsDir/AbC.txt
212 C CapsDir1/CapsDir/SubDir/Def.txt
211 C CapsDir1/CapsDir/SubDir/Def.txt
213
212
214 $ hg files capsdir1/capsdir
213 $ hg files capsdir1/capsdir
215 CapsDir1/CapsDir/AbC.txt (glob)
214 CapsDir1/CapsDir/AbC.txt (glob)
216 CapsDir1/CapsDir/SubDir/Def.txt (glob)
215 CapsDir1/CapsDir/SubDir/Def.txt (glob)
217
216
218 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
217 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
219 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
218 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
220
219
221 $ hg revert -r '.^' capsdir1/capsdir
220 $ hg revert -r '.^' capsdir1/capsdir
222 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
221 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
223
222
224 The conditional tests above mean the hash on the diff line differs on Windows
223 The conditional tests above mean the hash on the diff line differs on Windows
225 and OS X
224 and OS X
226 $ hg diff capsdir1/capsdir
225 $ hg diff capsdir1/capsdir
227 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
226 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
228 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
227 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
229 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
228 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * (glob)
230 @@ -1,1 +1,1 @@
229 @@ -1,1 +1,1 @@
231 -xyz
230 -xyz
232 +def
231 +def
233
232
234 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
233 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
235 moving CapsDir1/CapsDir/AbC.txt to CapsDir1/CapsDir/ABC.txt (glob)
236 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
234 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
237
235
238 $ hg status -A capsdir1/capsdir
236 $ hg status -A capsdir1/capsdir
239 M CapsDir1/CapsDir/SubDir/Def.txt
237 M CapsDir1/CapsDir/SubDir/Def.txt
240 C CapsDir1/CapsDir/ABC.txt
238 C CapsDir1/CapsDir/ABC.txt
241
239
242 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
240 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
243 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
241 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
244 removing CapsDir1/CapsDir/ABC.txt (glob)
242 removing CapsDir1/CapsDir/ABC.txt (glob)
245 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
243 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
246 #endif
244 #endif
247
245
248 $ cd ..
246 $ cd ..
@@ -1,259 +1,256 b''
1 #require icasefs
1 #require icasefs
2
2
3 $ hg debugfs | grep 'case-sensitive:'
3 $ hg debugfs | grep 'case-sensitive:'
4 case-sensitive: no
4 case-sensitive: no
5
5
6 test file addition with bad case
6 test file addition with bad case
7
7
8 $ hg init repo1
8 $ hg init repo1
9 $ cd repo1
9 $ cd repo1
10 $ echo a > a
10 $ echo a > a
11 $ hg add A
11 $ hg add A
12 adding a
13 $ hg st
12 $ hg st
14 A a
13 A a
15 $ hg ci -m adda
14 $ hg ci -m adda
16 $ hg manifest
15 $ hg manifest
17 a
16 a
18 $ cd ..
17 $ cd ..
19
18
20 test case collision on rename (issue750)
19 test case collision on rename (issue750)
21
20
22 $ hg init repo2
21 $ hg init repo2
23 $ cd repo2
22 $ cd repo2
24 $ echo a > a
23 $ echo a > a
25 $ hg --debug ci -Am adda
24 $ hg --debug ci -Am adda
26 adding a
25 adding a
27 committing files:
26 committing files:
28 a
27 a
29 committing manifest
28 committing manifest
30 committing changelog
29 committing changelog
31 updating the branch cache
30 updating the branch cache
32 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
31 committed changeset 0:07f4944404050f47db2e5c5071e0e84e7a27bba9
33
32
34 Case-changing renames should work:
33 Case-changing renames should work:
35
34
36 $ hg mv a A
35 $ hg mv a A
37 $ hg mv A a
36 $ hg mv A a
38 $ hg st
37 $ hg st
39
38
40 addremove after case-changing rename has no effect (issue4590)
39 addremove after case-changing rename has no effect (issue4590)
41
40
42 $ hg mv a A
41 $ hg mv a A
43 $ hg addremove
42 $ hg addremove
44 recording removal of a as rename to A (100% similar)
43 recording removal of a as rename to A (100% similar)
45 $ hg revert --all
44 $ hg revert --all
46 forgetting A
45 forgetting A
47 undeleting a
46 undeleting a
48
47
49 test changing case of path components
48 test changing case of path components
50
49
51 $ mkdir D
50 $ mkdir D
52 $ echo b > D/b
51 $ echo b > D/b
53 $ hg ci -Am addb D/b
52 $ hg ci -Am addb D/b
54 $ hg mv D/b d/b
53 $ hg mv D/b d/b
55 D/b: not overwriting - file already committed
54 D/b: not overwriting - file already committed
56 (hg rename --force to replace the file by recording a rename)
55 (hg rename --force to replace the file by recording a rename)
57 $ hg mv D/b d/c
56 $ hg mv D/b d/c
58 $ hg st
57 $ hg st
59 A D/c
58 A D/c
60 R D/b
59 R D/b
61 $ mv D temp
60 $ mv D temp
62 $ mv temp d
61 $ mv temp d
63 $ hg st
62 $ hg st
64 A D/c
63 A D/c
65 R D/b
64 R D/b
66 $ hg revert -aq
65 $ hg revert -aq
67 $ rm d/c
66 $ rm d/c
68 $ echo c > D/c
67 $ echo c > D/c
69 $ hg add D/c
68 $ hg add D/c
70 $ hg st
69 $ hg st
71 A D/c
70 A D/c
72 $ hg ci -m addc D/c
71 $ hg ci -m addc D/c
73 $ hg mv d/b d/e
72 $ hg mv d/b d/e
74 moving D/b to D/e (glob)
75 $ hg st
73 $ hg st
76 A D/e
74 A D/e
77 R D/b
75 R D/b
78 $ hg revert -aq
76 $ hg revert -aq
79 $ rm d/e
77 $ rm d/e
80 $ hg mv d/b D/B
78 $ hg mv d/b D/B
81 moving D/b to D/B (glob)
82 $ hg st
79 $ hg st
83 A D/B
80 A D/B
84 R D/b
81 R D/b
85 $ cd ..
82 $ cd ..
86
83
87 test case collision between revisions (issue912)
84 test case collision between revisions (issue912)
88
85
89 $ hg init repo3
86 $ hg init repo3
90 $ cd repo3
87 $ cd repo3
91 $ echo a > a
88 $ echo a > a
92 $ hg ci -Am adda
89 $ hg ci -Am adda
93 adding a
90 adding a
94 $ hg rm a
91 $ hg rm a
95 $ hg ci -Am removea
92 $ hg ci -Am removea
96 $ echo A > A
93 $ echo A > A
97
94
98 on linux hfs keeps the old case stored, force it
95 on linux hfs keeps the old case stored, force it
99
96
100 $ mv a aa
97 $ mv a aa
101 $ mv aa A
98 $ mv aa A
102 $ hg ci -Am addA
99 $ hg ci -Am addA
103 adding A
100 adding A
104
101
105 used to fail under case insensitive fs
102 used to fail under case insensitive fs
106
103
107 $ hg up -C 0
104 $ hg up -C 0
108 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
105 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
109 $ hg up -C
106 $ hg up -C
110 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
107 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
111
108
112 no clobbering of untracked files with wrong casing
109 no clobbering of untracked files with wrong casing
113
110
114 $ hg up -r null
111 $ hg up -r null
115 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
112 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
116 $ echo gold > a
113 $ echo gold > a
117 $ hg up
114 $ hg up
118 A: untracked file differs
115 A: untracked file differs
119 abort: untracked files in working directory differ from files in requested revision
116 abort: untracked files in working directory differ from files in requested revision
120 [255]
117 [255]
121 $ cat a
118 $ cat a
122 gold
119 gold
123 $ rm a
120 $ rm a
124
121
125 test that normal file in different case on target context is not
122 test that normal file in different case on target context is not
126 unlinked by largefiles extension.
123 unlinked by largefiles extension.
127
124
128 $ cat >> .hg/hgrc <<EOF
125 $ cat >> .hg/hgrc <<EOF
129 > [extensions]
126 > [extensions]
130 > largefiles=
127 > largefiles=
131 > EOF
128 > EOF
132 $ hg update -q -C 1
129 $ hg update -q -C 1
133 $ hg status -A
130 $ hg status -A
134 $ echo 'A as largefiles' > A
131 $ echo 'A as largefiles' > A
135 $ hg add --large A
132 $ hg add --large A
136 $ hg commit -m '#3'
133 $ hg commit -m '#3'
137 created new head
134 created new head
138 $ hg manifest -r 3
135 $ hg manifest -r 3
139 .hglf/A
136 .hglf/A
140 $ hg manifest -r 0
137 $ hg manifest -r 0
141 a
138 a
142 $ hg update -q -C 0
139 $ hg update -q -C 0
143 $ hg status -A
140 $ hg status -A
144 C a
141 C a
145 $ hg update -q -C 3
142 $ hg update -q -C 3
146 $ hg update -q 0
143 $ hg update -q 0
147
144
148 $ hg up -C -r 2
145 $ hg up -C -r 2
149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 $ hg mv A a
147 $ hg mv A a
151 $ hg diff -g > rename.diff
148 $ hg diff -g > rename.diff
152 $ hg ci -m 'A -> a'
149 $ hg ci -m 'A -> a'
153 $ hg up -q '.^'
150 $ hg up -q '.^'
154 $ hg import rename.diff -m "import rename A -> a"
151 $ hg import rename.diff -m "import rename A -> a"
155 applying rename.diff
152 applying rename.diff
156 $ hg st
153 $ hg st
157 ? rename.diff
154 ? rename.diff
158 $ hg files
155 $ hg files
159 a
156 a
160 $ find * | sort
157 $ find * | sort
161 a
158 a
162 rename.diff
159 rename.diff
163
160
164 $ rm rename.diff
161 $ rm rename.diff
165
162
166 $ cd ..
163 $ cd ..
167
164
168 issue 3342: file in nested directory causes unexpected abort
165 issue 3342: file in nested directory causes unexpected abort
169
166
170 $ hg init issue3342
167 $ hg init issue3342
171 $ cd issue3342
168 $ cd issue3342
172
169
173 $ mkdir -p a/B/c/D
170 $ mkdir -p a/B/c/D
174 $ echo e > a/B/c/D/e
171 $ echo e > a/B/c/D/e
175 $ hg add a/B/c/D/e
172 $ hg add a/B/c/D/e
176 $ hg ci -m 'add e'
173 $ hg ci -m 'add e'
177
174
178 issue 4481: revert across case only renames
175 issue 4481: revert across case only renames
179 $ hg mv a/B/c/D/e a/B/c/d/E
176 $ hg mv a/B/c/D/e a/B/c/d/E
180 $ hg ci -m "uppercase E"
177 $ hg ci -m "uppercase E"
181 $ echo 'foo' > a/B/c/D/E
178 $ echo 'foo' > a/B/c/D/E
182 $ hg ci -m 'e content change'
179 $ hg ci -m 'e content change'
183 $ hg revert --all -r 0
180 $ hg revert --all -r 0
184 removing a/B/c/D/E (glob)
181 removing a/B/c/D/E (glob)
185 adding a/B/c/D/e (glob)
182 adding a/B/c/D/e (glob)
186 $ find * | sort
183 $ find * | sort
187 a
184 a
188 a/B
185 a/B
189 a/B/c
186 a/B/c
190 a/B/c/D
187 a/B/c/D
191 a/B/c/D/e
188 a/B/c/D/e
192 a/B/c/D/e.orig
189 a/B/c/D/e.orig
193
190
194 $ cd ..
191 $ cd ..
195
192
196 issue 3340: mq does not handle case changes correctly
193 issue 3340: mq does not handle case changes correctly
197
194
198 in addition to reported case, 'hg qrefresh' is also tested against
195 in addition to reported case, 'hg qrefresh' is also tested against
199 case changes.
196 case changes.
200
197
201 $ echo "[extensions]" >> $HGRCPATH
198 $ echo "[extensions]" >> $HGRCPATH
202 $ echo "mq=" >> $HGRCPATH
199 $ echo "mq=" >> $HGRCPATH
203
200
204 $ hg init issue3340
201 $ hg init issue3340
205 $ cd issue3340
202 $ cd issue3340
206
203
207 $ echo a > mIxEdCaSe
204 $ echo a > mIxEdCaSe
208 $ hg add mIxEdCaSe
205 $ hg add mIxEdCaSe
209 $ hg commit -m '#0'
206 $ hg commit -m '#0'
210 $ hg rename mIxEdCaSe tmp
207 $ hg rename mIxEdCaSe tmp
211 $ hg rename tmp MiXeDcAsE
208 $ hg rename tmp MiXeDcAsE
212 $ hg status -A
209 $ hg status -A
213 A MiXeDcAsE
210 A MiXeDcAsE
214 mIxEdCaSe
211 mIxEdCaSe
215 R mIxEdCaSe
212 R mIxEdCaSe
216 $ hg qnew changecase
213 $ hg qnew changecase
217 $ hg status -A
214 $ hg status -A
218 C MiXeDcAsE
215 C MiXeDcAsE
219
216
220 $ hg qpop -a
217 $ hg qpop -a
221 popping changecase
218 popping changecase
222 patch queue now empty
219 patch queue now empty
223 $ hg qnew refresh-casechange
220 $ hg qnew refresh-casechange
224 $ hg status -A
221 $ hg status -A
225 C mIxEdCaSe
222 C mIxEdCaSe
226 $ hg rename mIxEdCaSe tmp
223 $ hg rename mIxEdCaSe tmp
227 $ hg rename tmp MiXeDcAsE
224 $ hg rename tmp MiXeDcAsE
228 $ hg status -A
225 $ hg status -A
229 A MiXeDcAsE
226 A MiXeDcAsE
230 mIxEdCaSe
227 mIxEdCaSe
231 R mIxEdCaSe
228 R mIxEdCaSe
232 $ hg qrefresh
229 $ hg qrefresh
233 $ hg status -A
230 $ hg status -A
234 C MiXeDcAsE
231 C MiXeDcAsE
235
232
236 $ hg qpop -a
233 $ hg qpop -a
237 popping refresh-casechange
234 popping refresh-casechange
238 patch queue now empty
235 patch queue now empty
239 $ hg qnew refresh-pattern
236 $ hg qnew refresh-pattern
240 $ hg status
237 $ hg status
241 $ echo A > A
238 $ echo A > A
242 $ hg add
239 $ hg add
243 adding A
240 adding A
244 $ hg qrefresh a # issue 3271, qrefresh with file handled case wrong
241 $ hg qrefresh a # issue 3271, qrefresh with file handled case wrong
245 $ hg status # empty status means the qrefresh worked
242 $ hg status # empty status means the qrefresh worked
246
243
247 #if osx
244 #if osx
248
245
249 We assume anyone running the tests on a case-insensitive volume on OS
246 We assume anyone running the tests on a case-insensitive volume on OS
250 X will be using HFS+. If that's not true, this test will fail.
247 X will be using HFS+. If that's not true, this test will fail.
251
248
252 $ rm A
249 $ rm A
253 >>> open(u'a\u200c'.encode('utf-8'), 'w').write('unicode is fun')
250 >>> open(u'a\u200c'.encode('utf-8'), 'w').write('unicode is fun')
254 $ hg status
251 $ hg status
255 M A
252 M A
256
253
257 #endif
254 #endif
258
255
259 $ cd ..
256 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now