##// END OF EJS Templates
match: fix a caseonly rename + explicit path commit on icasefs (issue4768)...
Matt Harbison -
r26000:9ac4e81b stable
parent child Browse files
Show More
@@ -1,672 +1,679 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 import copy, os, re
8 import copy, os, re
9 import util, pathutil
9 import util, pathutil
10 from i18n import _
10 from i18n import _
11
11
12 propertycache = util.propertycache
12 propertycache = util.propertycache
13
13
14 def _rematcher(regex):
14 def _rematcher(regex):
15 '''compile the regexp with the best available regexp engine and return a
15 '''compile the regexp with the best available regexp engine and return a
16 matcher function'''
16 matcher function'''
17 m = util.re.compile(regex)
17 m = util.re.compile(regex)
18 try:
18 try:
19 # slightly faster, provided by facebook's re2 bindings
19 # slightly faster, provided by facebook's re2 bindings
20 return m.test_match
20 return m.test_match
21 except AttributeError:
21 except AttributeError:
22 return m.match
22 return m.match
23
23
24 def _expandsets(kindpats, ctx, listsubrepos):
24 def _expandsets(kindpats, ctx, listsubrepos):
25 '''Returns the kindpats list with the 'set' patterns expanded.'''
25 '''Returns the kindpats list with the 'set' patterns expanded.'''
26 fset = set()
26 fset = set()
27 other = []
27 other = []
28
28
29 for kind, pat, source in kindpats:
29 for kind, pat, source in kindpats:
30 if kind == 'set':
30 if kind == 'set':
31 if not ctx:
31 if not ctx:
32 raise util.Abort("fileset expression with no context")
32 raise util.Abort("fileset expression with no context")
33 s = ctx.getfileset(pat)
33 s = ctx.getfileset(pat)
34 fset.update(s)
34 fset.update(s)
35
35
36 if listsubrepos:
36 if listsubrepos:
37 for subpath in ctx.substate:
37 for subpath in ctx.substate:
38 s = ctx.sub(subpath).getfileset(pat)
38 s = ctx.sub(subpath).getfileset(pat)
39 fset.update(subpath + '/' + f for f in s)
39 fset.update(subpath + '/' + f for f in s)
40
40
41 continue
41 continue
42 other.append((kind, pat, source))
42 other.append((kind, pat, source))
43 return fset, other
43 return fset, other
44
44
45 def _expandsubinclude(kindpats, root):
45 def _expandsubinclude(kindpats, root):
46 '''Returns the list of subinclude matchers and the kindpats without the
46 '''Returns the list of subinclude matchers and the kindpats without the
47 subincludes in it.'''
47 subincludes in it.'''
48 relmatchers = []
48 relmatchers = []
49 other = []
49 other = []
50
50
51 for kind, pat, source in kindpats:
51 for kind, pat, source in kindpats:
52 if kind == 'subinclude':
52 if kind == 'subinclude':
53 sourceroot = pathutil.dirname(util.normpath(source))
53 sourceroot = pathutil.dirname(util.normpath(source))
54 pat = util.pconvert(pat)
54 pat = util.pconvert(pat)
55 path = pathutil.join(sourceroot, pat)
55 path = pathutil.join(sourceroot, pat)
56
56
57 newroot = pathutil.dirname(path)
57 newroot = pathutil.dirname(path)
58 relmatcher = match(newroot, '', [], ['include:%s' % path])
58 relmatcher = match(newroot, '', [], ['include:%s' % path])
59
59
60 prefix = pathutil.canonpath(root, root, newroot)
60 prefix = pathutil.canonpath(root, root, newroot)
61 if prefix:
61 if prefix:
62 prefix += '/'
62 prefix += '/'
63 relmatchers.append((prefix, relmatcher))
63 relmatchers.append((prefix, relmatcher))
64 else:
64 else:
65 other.append((kind, pat, source))
65 other.append((kind, pat, source))
66
66
67 return relmatchers, other
67 return relmatchers, other
68
68
69 def _kindpatsalwaysmatch(kindpats):
69 def _kindpatsalwaysmatch(kindpats):
70 """"Checks whether the kindspats match everything, as e.g.
70 """"Checks whether the kindspats match everything, as e.g.
71 'relpath:.' does.
71 'relpath:.' does.
72 """
72 """
73 for kind, pat, source in kindpats:
73 for kind, pat, source in kindpats:
74 if pat != '' or kind not in ['relpath', 'glob']:
74 if pat != '' or kind not in ['relpath', 'glob']:
75 return False
75 return False
76 return True
76 return True
77
77
78 class match(object):
78 class match(object):
79 def __init__(self, root, cwd, patterns, include=[], exclude=[],
79 def __init__(self, root, cwd, patterns, include=[], exclude=[],
80 default='glob', exact=False, auditor=None, ctx=None,
80 default='glob', exact=False, auditor=None, ctx=None,
81 listsubrepos=False, warn=None, badfn=None):
81 listsubrepos=False, warn=None, badfn=None):
82 """build an object to match a set of file patterns
82 """build an object to match a set of file patterns
83
83
84 arguments:
84 arguments:
85 root - the canonical root of the tree you're matching against
85 root - the canonical root of the tree you're matching against
86 cwd - the current working directory, if relevant
86 cwd - the current working directory, if relevant
87 patterns - patterns to find
87 patterns - patterns to find
88 include - patterns to include (unless they are excluded)
88 include - patterns to include (unless they are excluded)
89 exclude - patterns to exclude (even if they are included)
89 exclude - patterns to exclude (even if they are included)
90 default - if a pattern in patterns has no explicit type, assume this one
90 default - if a pattern in patterns has no explicit type, assume this one
91 exact - patterns are actually filenames (include/exclude still apply)
91 exact - patterns are actually filenames (include/exclude still apply)
92 warn - optional function used for printing warnings
92 warn - optional function used for printing warnings
93 badfn - optional bad() callback for this matcher instead of the default
93 badfn - optional bad() callback for this matcher instead of the default
94
94
95 a pattern is one of:
95 a pattern is one of:
96 'glob:<glob>' - a glob relative to cwd
96 'glob:<glob>' - a glob relative to cwd
97 're:<regexp>' - a regular expression
97 're:<regexp>' - a regular expression
98 'path:<path>' - a path relative to repository root
98 'path:<path>' - a path relative to repository root
99 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
99 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
100 'relpath:<path>' - a path relative to cwd
100 'relpath:<path>' - a path relative to cwd
101 'relre:<regexp>' - a regexp that needn't match the start of a name
101 'relre:<regexp>' - a regexp that needn't match the start of a name
102 'set:<fileset>' - a fileset expression
102 'set:<fileset>' - a fileset expression
103 'include:<path>' - a file of patterns to read and include
103 'include:<path>' - a file of patterns to read and include
104 'subinclude:<path>' - a file of patterns to match against files under
104 'subinclude:<path>' - a file of patterns to match against files under
105 the same directory
105 the same directory
106 '<something>' - a pattern of the specified default type
106 '<something>' - a pattern of the specified default type
107 """
107 """
108
108
109 self._root = root
109 self._root = root
110 self._cwd = cwd
110 self._cwd = cwd
111 self._files = [] # exact files and roots of patterns
111 self._files = [] # exact files and roots of patterns
112 self._anypats = bool(include or exclude)
112 self._anypats = bool(include or exclude)
113 self._always = False
113 self._always = False
114 self._pathrestricted = bool(include or exclude or patterns)
114 self._pathrestricted = bool(include or exclude or patterns)
115 self._warn = warn
115 self._warn = warn
116 self._includeroots = set()
116 self._includeroots = set()
117 self._includedirs = set(['.'])
117 self._includedirs = set(['.'])
118 self._excluderoots = set()
118 self._excluderoots = set()
119
119
120 if badfn is not None:
120 if badfn is not None:
121 self.bad = badfn
121 self.bad = badfn
122
122
123 matchfns = []
123 matchfns = []
124 if include:
124 if include:
125 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
125 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
126 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
126 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
127 listsubrepos, root)
127 listsubrepos, root)
128 self._includeroots.update(_roots(kindpats))
128 self._includeroots.update(_roots(kindpats))
129 self._includedirs.update(util.dirs(self._includeroots))
129 self._includedirs.update(util.dirs(self._includeroots))
130 matchfns.append(im)
130 matchfns.append(im)
131 if exclude:
131 if exclude:
132 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
132 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
133 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
133 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
134 listsubrepos, root)
134 listsubrepos, root)
135 if not _anypats(kindpats):
135 if not _anypats(kindpats):
136 self._excluderoots.update(_roots(kindpats))
136 self._excluderoots.update(_roots(kindpats))
137 matchfns.append(lambda f: not em(f))
137 matchfns.append(lambda f: not em(f))
138 if exact:
138 if exact:
139 if isinstance(patterns, list):
139 if isinstance(patterns, list):
140 self._files = patterns
140 self._files = patterns
141 else:
141 else:
142 self._files = list(patterns)
142 self._files = list(patterns)
143 matchfns.append(self.exact)
143 matchfns.append(self.exact)
144 elif patterns:
144 elif patterns:
145 kindpats = self._normalize(patterns, default, root, cwd, auditor)
145 kindpats = self._normalize(patterns, default, root, cwd, auditor)
146 if not _kindpatsalwaysmatch(kindpats):
146 if not _kindpatsalwaysmatch(kindpats):
147 self._files = _roots(kindpats)
147 self._files = _roots(kindpats)
148 self._anypats = self._anypats or _anypats(kindpats)
148 self._anypats = self._anypats or _anypats(kindpats)
149 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
149 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
150 listsubrepos, root)
150 listsubrepos, root)
151 matchfns.append(pm)
151 matchfns.append(pm)
152
152
153 if not matchfns:
153 if not matchfns:
154 m = util.always
154 m = util.always
155 self._always = True
155 self._always = True
156 elif len(matchfns) == 1:
156 elif len(matchfns) == 1:
157 m = matchfns[0]
157 m = matchfns[0]
158 else:
158 else:
159 def m(f):
159 def m(f):
160 for matchfn in matchfns:
160 for matchfn in matchfns:
161 if not matchfn(f):
161 if not matchfn(f):
162 return False
162 return False
163 return True
163 return True
164
164
165 self.matchfn = m
165 self.matchfn = m
166 self._fileroots = set(self._files)
166 self._fileroots = set(self._files)
167
167
168 def __call__(self, fn):
168 def __call__(self, fn):
169 return self.matchfn(fn)
169 return self.matchfn(fn)
170 def __iter__(self):
170 def __iter__(self):
171 for f in self._files:
171 for f in self._files:
172 yield f
172 yield f
173
173
174 # Callbacks related to how the matcher is used by dirstate.walk.
174 # Callbacks related to how the matcher is used by dirstate.walk.
175 # Subscribers to these events must monkeypatch the matcher object.
175 # Subscribers to these events must monkeypatch the matcher object.
176 def bad(self, f, msg):
176 def bad(self, f, msg):
177 '''Callback from dirstate.walk for each explicit file that can't be
177 '''Callback from dirstate.walk for each explicit file that can't be
178 found/accessed, with an error message.'''
178 found/accessed, with an error message.'''
179 pass
179 pass
180
180
181 # If an explicitdir is set, it will be called when an explicitly listed
181 # If an explicitdir is set, it will be called when an explicitly listed
182 # directory is visited.
182 # directory is visited.
183 explicitdir = None
183 explicitdir = None
184
184
185 # If an traversedir is set, it will be called when a directory discovered
185 # If an traversedir is set, it will be called when a directory discovered
186 # by recursive traversal is visited.
186 # by recursive traversal is visited.
187 traversedir = None
187 traversedir = None
188
188
189 def abs(self, f):
189 def abs(self, f):
190 '''Convert a repo path back to path that is relative to the root of the
190 '''Convert a repo path back to path that is relative to the root of the
191 matcher.'''
191 matcher.'''
192 return f
192 return f
193
193
194 def rel(self, f):
194 def rel(self, f):
195 '''Convert repo path back to path that is relative to cwd of matcher.'''
195 '''Convert repo path back to path that is relative to cwd of matcher.'''
196 return util.pathto(self._root, self._cwd, f)
196 return util.pathto(self._root, self._cwd, f)
197
197
198 def uipath(self, f):
198 def uipath(self, f):
199 '''Convert repo path to a display path. If patterns or -I/-X were used
199 '''Convert repo path to a display path. If patterns or -I/-X were used
200 to create this matcher, the display path will be relative to cwd.
200 to create this matcher, the display path will be relative to cwd.
201 Otherwise it is relative to the root of the repo.'''
201 Otherwise it is relative to the root of the repo.'''
202 return (self._pathrestricted and self.rel(f)) or self.abs(f)
202 return (self._pathrestricted and self.rel(f)) or self.abs(f)
203
203
204 def files(self):
204 def files(self):
205 '''Explicitly listed files or patterns or roots:
205 '''Explicitly listed files or patterns or roots:
206 if no patterns or .always(): empty list,
206 if no patterns or .always(): empty list,
207 if exact: list exact files,
207 if exact: list exact files,
208 if not .anypats(): list all files and dirs,
208 if not .anypats(): list all files and dirs,
209 else: optimal roots'''
209 else: optimal roots'''
210 return self._files
210 return self._files
211
211
212 @propertycache
212 @propertycache
213 def _dirs(self):
213 def _dirs(self):
214 return set(util.dirs(self._fileroots)) | set(['.'])
214 return set(util.dirs(self._fileroots)) | set(['.'])
215
215
216 def visitdir(self, dir):
216 def visitdir(self, dir):
217 '''Decides whether a directory should be visited based on whether it
217 '''Decides whether a directory should be visited based on whether it
218 has potential matches in it or one of its subdirectories. This is
218 has potential matches in it or one of its subdirectories. This is
219 based on the match's primary, included, and excluded patterns.
219 based on the match's primary, included, and excluded patterns.
220
220
221 This function's behavior is undefined if it has returned False for
221 This function's behavior is undefined if it has returned False for
222 one of the dir's parent directories.
222 one of the dir's parent directories.
223 '''
223 '''
224 if dir in self._excluderoots:
224 if dir in self._excluderoots:
225 return False
225 return False
226 if (self._includeroots and
226 if (self._includeroots and
227 '.' not in self._includeroots and
227 '.' not in self._includeroots and
228 dir not in self._includeroots and
228 dir not in self._includeroots and
229 dir not in self._includedirs and
229 dir not in self._includedirs and
230 not any(parent in self._includeroots
230 not any(parent in self._includeroots
231 for parent in util.finddirs(dir))):
231 for parent in util.finddirs(dir))):
232 return False
232 return False
233 return (not self._fileroots or
233 return (not self._fileroots or
234 '.' in self._fileroots or
234 '.' in self._fileroots or
235 dir in self._fileroots or
235 dir in self._fileroots or
236 dir in self._dirs or
236 dir in self._dirs or
237 any(parentdir in self._fileroots
237 any(parentdir in self._fileroots
238 for parentdir in util.finddirs(dir)))
238 for parentdir in util.finddirs(dir)))
239
239
240 def exact(self, f):
240 def exact(self, f):
241 '''Returns True if f is in .files().'''
241 '''Returns True if f is in .files().'''
242 return f in self._fileroots
242 return f in self._fileroots
243
243
244 def anypats(self):
244 def anypats(self):
245 '''Matcher uses patterns or include/exclude.'''
245 '''Matcher uses patterns or include/exclude.'''
246 return self._anypats
246 return self._anypats
247
247
248 def always(self):
248 def always(self):
249 '''Matcher will match everything and .files() will be empty
249 '''Matcher will match everything and .files() will be empty
250 - optimization might be possible and necessary.'''
250 - optimization might be possible and necessary.'''
251 return self._always
251 return self._always
252
252
253 def ispartial(self):
253 def ispartial(self):
254 '''True if the matcher won't always match.
254 '''True if the matcher won't always match.
255
255
256 Although it's just the inverse of _always in this implementation,
256 Although it's just the inverse of _always in this implementation,
257 an extenion such as narrowhg might make it return something
257 an extenion such as narrowhg might make it return something
258 slightly different.'''
258 slightly different.'''
259 return not self._always
259 return not self._always
260
260
261 def isexact(self):
261 def isexact(self):
262 return self.matchfn == self.exact
262 return self.matchfn == self.exact
263
263
264 def prefix(self):
264 def prefix(self):
265 return not self.always() and not self.isexact() and not self.anypats()
265 return not self.always() and not self.isexact() and not self.anypats()
266
266
267 def _normalize(self, patterns, default, root, cwd, auditor):
267 def _normalize(self, patterns, default, root, cwd, auditor):
268 '''Convert 'kind:pat' from the patterns list to tuples with kind and
268 '''Convert 'kind:pat' from the patterns list to tuples with kind and
269 normalized and rooted patterns and with listfiles expanded.'''
269 normalized and rooted patterns and with listfiles expanded.'''
270 kindpats = []
270 kindpats = []
271 for kind, pat in [_patsplit(p, default) for p in patterns]:
271 for kind, pat in [_patsplit(p, default) for p in patterns]:
272 if kind in ('glob', 'relpath'):
272 if kind in ('glob', 'relpath'):
273 pat = pathutil.canonpath(root, cwd, pat, auditor)
273 pat = pathutil.canonpath(root, cwd, pat, auditor)
274 elif kind in ('relglob', 'path'):
274 elif kind in ('relglob', 'path'):
275 pat = util.normpath(pat)
275 pat = util.normpath(pat)
276 elif kind in ('listfile', 'listfile0'):
276 elif kind in ('listfile', 'listfile0'):
277 try:
277 try:
278 files = util.readfile(pat)
278 files = util.readfile(pat)
279 if kind == 'listfile0':
279 if kind == 'listfile0':
280 files = files.split('\0')
280 files = files.split('\0')
281 else:
281 else:
282 files = files.splitlines()
282 files = files.splitlines()
283 files = [f for f in files if f]
283 files = [f for f in files if f]
284 except EnvironmentError:
284 except EnvironmentError:
285 raise util.Abort(_("unable to read file list (%s)") % pat)
285 raise util.Abort(_("unable to read file list (%s)") % pat)
286 for k, p, source in self._normalize(files, default, root, cwd,
286 for k, p, source in self._normalize(files, default, root, cwd,
287 auditor):
287 auditor):
288 kindpats.append((k, p, pat))
288 kindpats.append((k, p, pat))
289 continue
289 continue
290 elif kind == 'include':
290 elif kind == 'include':
291 try:
291 try:
292 fullpath = os.path.join(root, util.localpath(pat))
292 fullpath = os.path.join(root, util.localpath(pat))
293 includepats = readpatternfile(fullpath, self._warn)
293 includepats = readpatternfile(fullpath, self._warn)
294 for k, p, source in self._normalize(includepats, default,
294 for k, p, source in self._normalize(includepats, default,
295 root, cwd, auditor):
295 root, cwd, auditor):
296 kindpats.append((k, p, source or pat))
296 kindpats.append((k, p, source or pat))
297 except util.Abort as inst:
297 except util.Abort as inst:
298 raise util.Abort('%s: %s' % (pat, inst[0]))
298 raise util.Abort('%s: %s' % (pat, inst[0]))
299 except IOError as inst:
299 except IOError as inst:
300 if self._warn:
300 if self._warn:
301 self._warn(_("skipping unreadable pattern file "
301 self._warn(_("skipping unreadable pattern file "
302 "'%s': %s\n") % (pat, inst.strerror))
302 "'%s': %s\n") % (pat, inst.strerror))
303 continue
303 continue
304 # else: re or relre - which cannot be normalized
304 # else: re or relre - which cannot be normalized
305 kindpats.append((kind, pat, ''))
305 kindpats.append((kind, pat, ''))
306 return kindpats
306 return kindpats
307
307
308 def exact(root, cwd, files, badfn=None):
308 def exact(root, cwd, files, badfn=None):
309 return match(root, cwd, files, exact=True, badfn=badfn)
309 return match(root, cwd, files, exact=True, badfn=badfn)
310
310
311 def always(root, cwd):
311 def always(root, cwd):
312 return match(root, cwd, [])
312 return match(root, cwd, [])
313
313
314 def badmatch(match, badfn):
314 def badmatch(match, badfn):
315 """Make a copy of the given matcher, replacing its bad method with the given
315 """Make a copy of the given matcher, replacing its bad method with the given
316 one.
316 one.
317 """
317 """
318 m = copy.copy(match)
318 m = copy.copy(match)
319 m.bad = badfn
319 m.bad = badfn
320 return m
320 return m
321
321
322 class narrowmatcher(match):
322 class narrowmatcher(match):
323 """Adapt a matcher to work on a subdirectory only.
323 """Adapt a matcher to work on a subdirectory only.
324
324
325 The paths are remapped to remove/insert the path as needed:
325 The paths are remapped to remove/insert the path as needed:
326
326
327 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
327 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
328 >>> m2 = narrowmatcher('sub', m1)
328 >>> m2 = narrowmatcher('sub', m1)
329 >>> bool(m2('a.txt'))
329 >>> bool(m2('a.txt'))
330 False
330 False
331 >>> bool(m2('b.txt'))
331 >>> bool(m2('b.txt'))
332 True
332 True
333 >>> bool(m2.matchfn('a.txt'))
333 >>> bool(m2.matchfn('a.txt'))
334 False
334 False
335 >>> bool(m2.matchfn('b.txt'))
335 >>> bool(m2.matchfn('b.txt'))
336 True
336 True
337 >>> m2.files()
337 >>> m2.files()
338 ['b.txt']
338 ['b.txt']
339 >>> m2.exact('b.txt')
339 >>> m2.exact('b.txt')
340 True
340 True
341 >>> util.pconvert(m2.rel('b.txt'))
341 >>> util.pconvert(m2.rel('b.txt'))
342 'sub/b.txt'
342 'sub/b.txt'
343 >>> def bad(f, msg):
343 >>> def bad(f, msg):
344 ... print "%s: %s" % (f, msg)
344 ... print "%s: %s" % (f, msg)
345 >>> m1.bad = bad
345 >>> m1.bad = bad
346 >>> m2.bad('x.txt', 'No such file')
346 >>> m2.bad('x.txt', 'No such file')
347 sub/x.txt: No such file
347 sub/x.txt: No such file
348 >>> m2.abs('c.txt')
348 >>> m2.abs('c.txt')
349 'sub/c.txt'
349 'sub/c.txt'
350 """
350 """
351
351
352 def __init__(self, path, matcher):
352 def __init__(self, path, matcher):
353 self._root = matcher._root
353 self._root = matcher._root
354 self._cwd = matcher._cwd
354 self._cwd = matcher._cwd
355 self._path = path
355 self._path = path
356 self._matcher = matcher
356 self._matcher = matcher
357 self._always = matcher._always
357 self._always = matcher._always
358 self._pathrestricted = matcher._pathrestricted
358 self._pathrestricted = matcher._pathrestricted
359
359
360 self._files = [f[len(path) + 1:] for f in matcher._files
360 self._files = [f[len(path) + 1:] for f in matcher._files
361 if f.startswith(path + "/")]
361 if f.startswith(path + "/")]
362
362
363 # If the parent repo had a path to this subrepo and no patterns are
363 # If the parent repo had a path to this subrepo and no patterns are
364 # specified, this submatcher always matches.
364 # specified, this submatcher always matches.
365 if not self._always and not matcher._anypats:
365 if not self._always and not matcher._anypats:
366 self._always = any(f == path for f in matcher._files)
366 self._always = any(f == path for f in matcher._files)
367
367
368 self._anypats = matcher._anypats
368 self._anypats = matcher._anypats
369 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
369 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
370 self._fileroots = set(self._files)
370 self._fileroots = set(self._files)
371
371
372 def abs(self, f):
372 def abs(self, f):
373 return self._matcher.abs(self._path + "/" + f)
373 return self._matcher.abs(self._path + "/" + f)
374
374
375 def bad(self, f, msg):
375 def bad(self, f, msg):
376 self._matcher.bad(self._path + "/" + f, msg)
376 self._matcher.bad(self._path + "/" + f, msg)
377
377
378 def rel(self, f):
378 def rel(self, f):
379 return self._matcher.rel(self._path + "/" + f)
379 return self._matcher.rel(self._path + "/" + f)
380
380
381 class icasefsmatcher(match):
381 class icasefsmatcher(match):
382 """A matcher for wdir on case insensitive filesystems, which normalizes the
382 """A matcher for wdir on case insensitive filesystems, which normalizes the
383 given patterns to the case in the filesystem.
383 given patterns to the case in the filesystem.
384 """
384 """
385
385
386 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
386 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
387 ctx, listsubrepos=False, badfn=None):
387 ctx, listsubrepos=False, badfn=None):
388 init = super(icasefsmatcher, self).__init__
388 init = super(icasefsmatcher, self).__init__
389 self._dsnormalize = ctx.repo().dirstate.normalize
389 self._dirstate = ctx.repo().dirstate
390 self._dsnormalize = self._dirstate.normalize
390
391
391 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
392 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
392 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
393 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
393
394
394 # m.exact(file) must be based off of the actual user input, otherwise
395 # m.exact(file) must be based off of the actual user input, otherwise
395 # inexact case matches are treated as exact, and not noted without -v.
396 # inexact case matches are treated as exact, and not noted without -v.
396 if self._files:
397 if self._files:
397 self._fileroots = set(_roots(self._kp))
398 self._fileroots = set(_roots(self._kp))
398
399
399 def _normalize(self, patterns, default, root, cwd, auditor):
400 def _normalize(self, patterns, default, root, cwd, auditor):
400 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
401 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
401 root, cwd, auditor)
402 root, cwd, auditor)
402 kindpats = []
403 kindpats = []
403 for kind, pats, source in self._kp:
404 for kind, pats, source in self._kp:
404 if kind not in ('re', 'relre'): # regex can't be normalized
405 if kind not in ('re', 'relre'): # regex can't be normalized
406 p = pats
405 pats = self._dsnormalize(pats)
407 pats = self._dsnormalize(pats)
408
409 # Preserve the original to handle a case only rename.
410 if p != pats and p in self._dirstate:
411 kindpats.append((kind, p, source))
412
406 kindpats.append((kind, pats, source))
413 kindpats.append((kind, pats, source))
407 return kindpats
414 return kindpats
408
415
409 def patkind(pattern, default=None):
416 def patkind(pattern, default=None):
410 '''If pattern is 'kind:pat' with a known kind, return kind.'''
417 '''If pattern is 'kind:pat' with a known kind, return kind.'''
411 return _patsplit(pattern, default)[0]
418 return _patsplit(pattern, default)[0]
412
419
413 def _patsplit(pattern, default):
420 def _patsplit(pattern, default):
414 """Split a string into the optional pattern kind prefix and the actual
421 """Split a string into the optional pattern kind prefix and the actual
415 pattern."""
422 pattern."""
416 if ':' in pattern:
423 if ':' in pattern:
417 kind, pat = pattern.split(':', 1)
424 kind, pat = pattern.split(':', 1)
418 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
425 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
419 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
426 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
420 return kind, pat
427 return kind, pat
421 return default, pattern
428 return default, pattern
422
429
423 def _globre(pat):
430 def _globre(pat):
424 r'''Convert an extended glob string to a regexp string.
431 r'''Convert an extended glob string to a regexp string.
425
432
426 >>> print _globre(r'?')
433 >>> print _globre(r'?')
427 .
434 .
428 >>> print _globre(r'*')
435 >>> print _globre(r'*')
429 [^/]*
436 [^/]*
430 >>> print _globre(r'**')
437 >>> print _globre(r'**')
431 .*
438 .*
432 >>> print _globre(r'**/a')
439 >>> print _globre(r'**/a')
433 (?:.*/)?a
440 (?:.*/)?a
434 >>> print _globre(r'a/**/b')
441 >>> print _globre(r'a/**/b')
435 a\/(?:.*/)?b
442 a\/(?:.*/)?b
436 >>> print _globre(r'[a*?!^][^b][!c]')
443 >>> print _globre(r'[a*?!^][^b][!c]')
437 [a*?!^][\^b][^c]
444 [a*?!^][\^b][^c]
438 >>> print _globre(r'{a,b}')
445 >>> print _globre(r'{a,b}')
439 (?:a|b)
446 (?:a|b)
440 >>> print _globre(r'.\*\?')
447 >>> print _globre(r'.\*\?')
441 \.\*\?
448 \.\*\?
442 '''
449 '''
443 i, n = 0, len(pat)
450 i, n = 0, len(pat)
444 res = ''
451 res = ''
445 group = 0
452 group = 0
446 escape = util.re.escape
453 escape = util.re.escape
447 def peek():
454 def peek():
448 return i < n and pat[i]
455 return i < n and pat[i]
449 while i < n:
456 while i < n:
450 c = pat[i]
457 c = pat[i]
451 i += 1
458 i += 1
452 if c not in '*?[{},\\':
459 if c not in '*?[{},\\':
453 res += escape(c)
460 res += escape(c)
454 elif c == '*':
461 elif c == '*':
455 if peek() == '*':
462 if peek() == '*':
456 i += 1
463 i += 1
457 if peek() == '/':
464 if peek() == '/':
458 i += 1
465 i += 1
459 res += '(?:.*/)?'
466 res += '(?:.*/)?'
460 else:
467 else:
461 res += '.*'
468 res += '.*'
462 else:
469 else:
463 res += '[^/]*'
470 res += '[^/]*'
464 elif c == '?':
471 elif c == '?':
465 res += '.'
472 res += '.'
466 elif c == '[':
473 elif c == '[':
467 j = i
474 j = i
468 if j < n and pat[j] in '!]':
475 if j < n and pat[j] in '!]':
469 j += 1
476 j += 1
470 while j < n and pat[j] != ']':
477 while j < n and pat[j] != ']':
471 j += 1
478 j += 1
472 if j >= n:
479 if j >= n:
473 res += '\\['
480 res += '\\['
474 else:
481 else:
475 stuff = pat[i:j].replace('\\','\\\\')
482 stuff = pat[i:j].replace('\\','\\\\')
476 i = j + 1
483 i = j + 1
477 if stuff[0] == '!':
484 if stuff[0] == '!':
478 stuff = '^' + stuff[1:]
485 stuff = '^' + stuff[1:]
479 elif stuff[0] == '^':
486 elif stuff[0] == '^':
480 stuff = '\\' + stuff
487 stuff = '\\' + stuff
481 res = '%s[%s]' % (res, stuff)
488 res = '%s[%s]' % (res, stuff)
482 elif c == '{':
489 elif c == '{':
483 group += 1
490 group += 1
484 res += '(?:'
491 res += '(?:'
485 elif c == '}' and group:
492 elif c == '}' and group:
486 res += ')'
493 res += ')'
487 group -= 1
494 group -= 1
488 elif c == ',' and group:
495 elif c == ',' and group:
489 res += '|'
496 res += '|'
490 elif c == '\\':
497 elif c == '\\':
491 p = peek()
498 p = peek()
492 if p:
499 if p:
493 i += 1
500 i += 1
494 res += escape(p)
501 res += escape(p)
495 else:
502 else:
496 res += escape(c)
503 res += escape(c)
497 else:
504 else:
498 res += escape(c)
505 res += escape(c)
499 return res
506 return res
500
507
501 def _regex(kind, pat, globsuffix):
508 def _regex(kind, pat, globsuffix):
502 '''Convert a (normalized) pattern of any kind into a regular expression.
509 '''Convert a (normalized) pattern of any kind into a regular expression.
503 globsuffix is appended to the regexp of globs.'''
510 globsuffix is appended to the regexp of globs.'''
504 if not pat:
511 if not pat:
505 return ''
512 return ''
506 if kind == 're':
513 if kind == 're':
507 return pat
514 return pat
508 if kind == 'path':
515 if kind == 'path':
509 if pat == '.':
516 if pat == '.':
510 return ''
517 return ''
511 return '^' + util.re.escape(pat) + '(?:/|$)'
518 return '^' + util.re.escape(pat) + '(?:/|$)'
512 if kind == 'relglob':
519 if kind == 'relglob':
513 return '(?:|.*/)' + _globre(pat) + globsuffix
520 return '(?:|.*/)' + _globre(pat) + globsuffix
514 if kind == 'relpath':
521 if kind == 'relpath':
515 return util.re.escape(pat) + '(?:/|$)'
522 return util.re.escape(pat) + '(?:/|$)'
516 if kind == 'relre':
523 if kind == 'relre':
517 if pat.startswith('^'):
524 if pat.startswith('^'):
518 return pat
525 return pat
519 return '.*' + pat
526 return '.*' + pat
520 return _globre(pat) + globsuffix
527 return _globre(pat) + globsuffix
521
528
522 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
529 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
523 '''Return regexp string and a matcher function for kindpats.
530 '''Return regexp string and a matcher function for kindpats.
524 globsuffix is appended to the regexp of globs.'''
531 globsuffix is appended to the regexp of globs.'''
525 matchfuncs = []
532 matchfuncs = []
526
533
527 subincludes, kindpats = _expandsubinclude(kindpats, root)
534 subincludes, kindpats = _expandsubinclude(kindpats, root)
528 if subincludes:
535 if subincludes:
529 def matchsubinclude(f):
536 def matchsubinclude(f):
530 for prefix, mf in subincludes:
537 for prefix, mf in subincludes:
531 if f.startswith(prefix) and mf(f[len(prefix):]):
538 if f.startswith(prefix) and mf(f[len(prefix):]):
532 return True
539 return True
533 return False
540 return False
534 matchfuncs.append(matchsubinclude)
541 matchfuncs.append(matchsubinclude)
535
542
536 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
543 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
537 if fset:
544 if fset:
538 matchfuncs.append(fset.__contains__)
545 matchfuncs.append(fset.__contains__)
539
546
540 regex = ''
547 regex = ''
541 if kindpats:
548 if kindpats:
542 regex, mf = _buildregexmatch(kindpats, globsuffix)
549 regex, mf = _buildregexmatch(kindpats, globsuffix)
543 matchfuncs.append(mf)
550 matchfuncs.append(mf)
544
551
545 if len(matchfuncs) == 1:
552 if len(matchfuncs) == 1:
546 return regex, matchfuncs[0]
553 return regex, matchfuncs[0]
547 else:
554 else:
548 return regex, lambda f: any(mf(f) for mf in matchfuncs)
555 return regex, lambda f: any(mf(f) for mf in matchfuncs)
549
556
550 def _buildregexmatch(kindpats, globsuffix):
557 def _buildregexmatch(kindpats, globsuffix):
551 """Build a match function from a list of kinds and kindpats,
558 """Build a match function from a list of kinds and kindpats,
552 return regexp string and a matcher function."""
559 return regexp string and a matcher function."""
553 try:
560 try:
554 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
561 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
555 for (k, p, s) in kindpats])
562 for (k, p, s) in kindpats])
556 if len(regex) > 20000:
563 if len(regex) > 20000:
557 raise OverflowError
564 raise OverflowError
558 return regex, _rematcher(regex)
565 return regex, _rematcher(regex)
559 except OverflowError:
566 except OverflowError:
560 # We're using a Python with a tiny regex engine and we
567 # We're using a Python with a tiny regex engine and we
561 # made it explode, so we'll divide the pattern list in two
568 # made it explode, so we'll divide the pattern list in two
562 # until it works
569 # until it works
563 l = len(kindpats)
570 l = len(kindpats)
564 if l < 2:
571 if l < 2:
565 raise
572 raise
566 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
573 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
567 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
574 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
568 return regex, lambda s: a(s) or b(s)
575 return regex, lambda s: a(s) or b(s)
569 except re.error:
576 except re.error:
570 for k, p, s in kindpats:
577 for k, p, s in kindpats:
571 try:
578 try:
572 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
579 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
573 except re.error:
580 except re.error:
574 if s:
581 if s:
575 raise util.Abort(_("%s: invalid pattern (%s): %s") %
582 raise util.Abort(_("%s: invalid pattern (%s): %s") %
576 (s, k, p))
583 (s, k, p))
577 else:
584 else:
578 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
585 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
579 raise util.Abort(_("invalid pattern"))
586 raise util.Abort(_("invalid pattern"))
580
587
581 def _roots(kindpats):
588 def _roots(kindpats):
582 '''return roots and exact explicitly listed files from patterns
589 '''return roots and exact explicitly listed files from patterns
583
590
584 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
591 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
585 ['g', 'g', '.']
592 ['g', 'g', '.']
586 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
593 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
587 ['r', 'p/p', '.']
594 ['r', 'p/p', '.']
588 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
595 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
589 ['.', '.', '.']
596 ['.', '.', '.']
590 '''
597 '''
591 r = []
598 r = []
592 for kind, pat, source in kindpats:
599 for kind, pat, source in kindpats:
593 if kind == 'glob': # find the non-glob prefix
600 if kind == 'glob': # find the non-glob prefix
594 root = []
601 root = []
595 for p in pat.split('/'):
602 for p in pat.split('/'):
596 if '[' in p or '{' in p or '*' in p or '?' in p:
603 if '[' in p or '{' in p or '*' in p or '?' in p:
597 break
604 break
598 root.append(p)
605 root.append(p)
599 r.append('/'.join(root) or '.')
606 r.append('/'.join(root) or '.')
600 elif kind in ('relpath', 'path'):
607 elif kind in ('relpath', 'path'):
601 r.append(pat or '.')
608 r.append(pat or '.')
602 else: # relglob, re, relre
609 else: # relglob, re, relre
603 r.append('.')
610 r.append('.')
604 return r
611 return r
605
612
606 def _anypats(kindpats):
613 def _anypats(kindpats):
607 for kind, pat, source in kindpats:
614 for kind, pat, source in kindpats:
608 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
615 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
609 return True
616 return True
610
617
611 _commentre = None
618 _commentre = None
612
619
613 def readpatternfile(filepath, warn):
620 def readpatternfile(filepath, warn):
614 '''parse a pattern file, returning a list of
621 '''parse a pattern file, returning a list of
615 patterns. These patterns should be given to compile()
622 patterns. These patterns should be given to compile()
616 to be validated and converted into a match function.
623 to be validated and converted into a match function.
617
624
618 trailing white space is dropped.
625 trailing white space is dropped.
619 the escape character is backslash.
626 the escape character is backslash.
620 comments start with #.
627 comments start with #.
621 empty lines are skipped.
628 empty lines are skipped.
622
629
623 lines can be of the following formats:
630 lines can be of the following formats:
624
631
625 syntax: regexp # defaults following lines to non-rooted regexps
632 syntax: regexp # defaults following lines to non-rooted regexps
626 syntax: glob # defaults following lines to non-rooted globs
633 syntax: glob # defaults following lines to non-rooted globs
627 re:pattern # non-rooted regular expression
634 re:pattern # non-rooted regular expression
628 glob:pattern # non-rooted glob
635 glob:pattern # non-rooted glob
629 pattern # pattern of the current default type'''
636 pattern # pattern of the current default type'''
630
637
631 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
638 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
632 'include': 'include', 'subinclude': 'subinclude'}
639 'include': 'include', 'subinclude': 'subinclude'}
633 syntax = 'relre:'
640 syntax = 'relre:'
634 patterns = []
641 patterns = []
635
642
636 fp = open(filepath)
643 fp = open(filepath)
637 for line in fp:
644 for line in fp:
638 if "#" in line:
645 if "#" in line:
639 global _commentre
646 global _commentre
640 if not _commentre:
647 if not _commentre:
641 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
648 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
642 # remove comments prefixed by an even number of escapes
649 # remove comments prefixed by an even number of escapes
643 line = _commentre.sub(r'\1', line)
650 line = _commentre.sub(r'\1', line)
644 # fixup properly escaped comments that survived the above
651 # fixup properly escaped comments that survived the above
645 line = line.replace("\\#", "#")
652 line = line.replace("\\#", "#")
646 line = line.rstrip()
653 line = line.rstrip()
647 if not line:
654 if not line:
648 continue
655 continue
649
656
650 if line.startswith('syntax:'):
657 if line.startswith('syntax:'):
651 s = line[7:].strip()
658 s = line[7:].strip()
652 try:
659 try:
653 syntax = syntaxes[s]
660 syntax = syntaxes[s]
654 except KeyError:
661 except KeyError:
655 if warn:
662 if warn:
656 warn(_("%s: ignoring invalid syntax '%s'\n") %
663 warn(_("%s: ignoring invalid syntax '%s'\n") %
657 (filepath, s))
664 (filepath, s))
658 continue
665 continue
659
666
660 linesyntax = syntax
667 linesyntax = syntax
661 for s, rels in syntaxes.iteritems():
668 for s, rels in syntaxes.iteritems():
662 if line.startswith(rels):
669 if line.startswith(rels):
663 linesyntax = rels
670 linesyntax = rels
664 line = line[len(rels):]
671 line = line[len(rels):]
665 break
672 break
666 elif line.startswith(s+':'):
673 elif line.startswith(s+':'):
667 linesyntax = rels
674 linesyntax = rels
668 line = line[len(s) + 1:]
675 line = line[len(s) + 1:]
669 break
676 break
670 patterns.append(linesyntax + line)
677 patterns.append(linesyntax + line)
671 fp.close()
678 fp.close()
672 return patterns
679 return patterns
@@ -1,241 +1,249 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 during merge.
100 warning: conflicts during merge.
101 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
101 merging a incomplete! (edit conflicts, then use 'hg resolve --mark')
102 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
102 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
103 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
103 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
104 [1]
104 [1]
105 $ hg st
105 $ hg st
106 M a
106 M a
107 ? a.orig
107 ? a.orig
108
108
109 wdir doesn't cause a crash, and can be dynamically selected if dirty
109 wdir doesn't cause a crash, and can be dynamically selected if dirty
110
110
111 $ hg log -r "heads(. or wdir() & file('**'))"
111 $ hg log -r "heads(. or wdir() & file('**'))"
112 changeset: 2147483647:ffffffffffff
112 changeset: 2147483647:ffffffffffff
113 parent: 2:* (glob)
113 parent: 2:* (glob)
114 parent: 1:* (glob)
114 parent: 1:* (glob)
115 user: test
115 user: test
116 date: * (glob)
116 date: * (glob)
117
117
118 should fail
118 should fail
119
119
120 $ hg add a
120 $ hg add a
121 a already tracked!
121 a already tracked!
122 $ hg st
122 $ hg st
123 M a
123 M a
124 ? a.orig
124 ? a.orig
125 $ hg resolve -m a
125 $ hg resolve -m a
126 (no more unresolved files)
126 (no more unresolved files)
127 $ hg ci -m merge
127 $ hg ci -m merge
128
128
129 Issue683: peculiarity with hg revert of an removed then added file
129 Issue683: peculiarity with hg revert of an removed then added file
130
130
131 $ hg forget a
131 $ hg forget a
132 $ hg add a
132 $ hg add a
133 $ hg st
133 $ hg st
134 ? a.orig
134 ? a.orig
135 $ hg rm a
135 $ hg rm a
136 $ hg st
136 $ hg st
137 R a
137 R a
138 ? a.orig
138 ? a.orig
139 $ echo a > a
139 $ echo a > a
140 $ hg add a
140 $ hg add a
141 $ hg st
141 $ hg st
142 M a
142 M a
143 ? a.orig
143 ? a.orig
144
144
145 Forgotten file can be added back (as either clean or modified)
145 Forgotten file can be added back (as either clean or modified)
146
146
147 $ hg forget b
147 $ hg forget b
148 $ hg add b
148 $ hg add b
149 $ hg st -A b
149 $ hg st -A b
150 C b
150 C b
151 $ hg forget b
151 $ hg forget b
152 $ echo modified > b
152 $ echo modified > b
153 $ hg add b
153 $ hg add b
154 $ hg st -A b
154 $ hg st -A b
155 M b
155 M b
156 $ hg revert -qC b
156 $ hg revert -qC b
157
157
158 $ hg add c && echo "unexpected addition of missing file"
158 $ hg add c && echo "unexpected addition of missing file"
159 c: * (glob)
159 c: * (glob)
160 [1]
160 [1]
161 $ echo c > c
161 $ echo c > c
162 $ hg add d c && echo "unexpected addition of missing file"
162 $ hg add d c && echo "unexpected addition of missing file"
163 d: * (glob)
163 d: * (glob)
164 [1]
164 [1]
165 $ hg st
165 $ hg st
166 M a
166 M a
167 A c
167 A c
168 ? a.orig
168 ? a.orig
169 $ hg up -C
169 $ hg up -C
170 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
171
171
172 forget and get should have the right order: added but missing dir should be
172 forget and get should have the right order: added but missing dir should be
173 forgotten before file with same name is added
173 forgotten before file with same name is added
174
174
175 $ echo file d > d
175 $ echo file d > d
176 $ hg add d
176 $ hg add d
177 $ hg ci -md
177 $ hg ci -md
178 $ hg rm d
178 $ hg rm d
179 $ mkdir d
179 $ mkdir d
180 $ echo a > d/a
180 $ echo a > d/a
181 $ hg add d/a
181 $ hg add d/a
182 $ rm -r d
182 $ rm -r d
183 $ hg up -C
183 $ hg up -C
184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
185 $ cat d
185 $ cat d
186 file d
186 file d
187
187
188 Test that adding a directory doesn't require case matching (issue4578)
188 Test that adding a directory doesn't require case matching (issue4578)
189 #if icasefs
189 #if icasefs
190 $ mkdir -p CapsDir1/CapsDir
190 $ mkdir -p CapsDir1/CapsDir
191 $ echo abc > CapsDir1/CapsDir/AbC.txt
191 $ echo abc > CapsDir1/CapsDir/AbC.txt
192 $ mkdir CapsDir1/CapsDir/SubDir
192 $ mkdir CapsDir1/CapsDir/SubDir
193 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
193 $ echo def > CapsDir1/CapsDir/SubDir/Def.txt
194
194
195 $ hg add capsdir1/capsdir
195 $ hg add capsdir1/capsdir
196 adding CapsDir1/CapsDir/AbC.txt (glob)
196 adding CapsDir1/CapsDir/AbC.txt (glob)
197 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
197 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
198
198
199 $ hg forget capsdir1/capsdir/abc.txt
199 $ hg forget capsdir1/capsdir/abc.txt
200 removing CapsDir1/CapsDir/AbC.txt (glob)
200 removing CapsDir1/CapsDir/AbC.txt (glob)
201
201
202 $ hg forget capsdir1/capsdir
202 $ hg forget capsdir1/capsdir
203 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
203 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
204
204
205 $ hg add capsdir1
205 $ hg add capsdir1
206 adding CapsDir1/CapsDir/AbC.txt (glob)
206 adding CapsDir1/CapsDir/AbC.txt (glob)
207 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
207 adding CapsDir1/CapsDir/SubDir/Def.txt (glob)
208
208
209 $ hg ci -m "AbCDef" capsdir1/capsdir
209 $ hg ci -m "AbCDef" capsdir1/capsdir
210
210
211 $ hg status -A capsdir1/capsdir
211 $ hg status -A capsdir1/capsdir
212 C CapsDir1/CapsDir/AbC.txt
212 C CapsDir1/CapsDir/AbC.txt
213 C CapsDir1/CapsDir/SubDir/Def.txt
213 C CapsDir1/CapsDir/SubDir/Def.txt
214
214
215 $ hg files capsdir1/capsdir
215 $ hg files capsdir1/capsdir
216 CapsDir1/CapsDir/AbC.txt (glob)
216 CapsDir1/CapsDir/AbC.txt (glob)
217 CapsDir1/CapsDir/SubDir/Def.txt (glob)
217 CapsDir1/CapsDir/SubDir/Def.txt (glob)
218
218
219 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
219 $ echo xyz > CapsDir1/CapsDir/SubDir/Def.txt
220 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
220 $ hg ci -m xyz capsdir1/capsdir/subdir/def.txt
221
221
222 $ hg revert -r '.^' capsdir1/capsdir
222 $ hg revert -r '.^' capsdir1/capsdir
223 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
223 reverting CapsDir1/CapsDir/SubDir/Def.txt (glob)
224
224
225 The conditional tests above mean the hash on the diff line differs on Windows
225 The conditional tests above mean the hash on the diff line differs on Windows
226 and OS X
226 and OS X
227 $ hg diff capsdir1/capsdir
227 $ hg diff capsdir1/capsdir
228 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
228 diff -r * CapsDir1/CapsDir/SubDir/Def.txt (glob)
229 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
229 --- a/CapsDir1/CapsDir/SubDir/Def.txt Thu Jan 01 00:00:00 1970 +0000
230 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * +0000 (glob)
230 +++ b/CapsDir1/CapsDir/SubDir/Def.txt * +0000 (glob)
231 @@ -1,1 +1,1 @@
231 @@ -1,1 +1,1 @@
232 -xyz
232 -xyz
233 +def
233 +def
234
234
235 $ hg mv CapsDir1/CapsDir/abc.txt CapsDir1/CapsDir/ABC.txt
236 moving CapsDir1/CapsDir/AbC.txt to CapsDir1/CapsDir/ABC.txt (glob)
237 $ hg ci -m "case changing rename" CapsDir1/CapsDir/AbC.txt CapsDir1/CapsDir/ABC.txt
238
239 $ hg status -A capsdir1/capsdir
240 M CapsDir1/CapsDir/SubDir/Def.txt
241 C CapsDir1/CapsDir/ABC.txt
242
235 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
243 $ hg remove -f 'glob:**.txt' -X capsdir1/capsdir
236 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
244 $ hg remove -f 'glob:**.txt' -I capsdir1/capsdir
237 removing CapsDir1/CapsDir/AbC.txt (glob)
245 removing CapsDir1/CapsDir/ABC.txt (glob)
238 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
246 removing CapsDir1/CapsDir/SubDir/Def.txt (glob)
239 #endif
247 #endif
240
248
241 $ cd ..
249 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now