##// END OF EJS Templates
match: explicitly naming a subrepo implies always() for the submatcher...
Matt Harbison -
r25194:ef4538ba stable
parent child Browse files
Show More
@@ -1,482 +1,488
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 re
8 import 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):
24 def _expandsets(kindpats, ctx):
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 in kindpats:
29 for kind, pat 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 continue
35 continue
36 other.append((kind, pat))
36 other.append((kind, pat))
37 return fset, other
37 return fset, other
38
38
39 def _kindpatsalwaysmatch(kindpats):
39 def _kindpatsalwaysmatch(kindpats):
40 """"Checks whether the kindspats match everything, as e.g.
40 """"Checks whether the kindspats match everything, as e.g.
41 'relpath:.' does.
41 'relpath:.' does.
42 """
42 """
43 for kind, pat in kindpats:
43 for kind, pat in kindpats:
44 if pat != '' or kind not in ['relpath', 'glob']:
44 if pat != '' or kind not in ['relpath', 'glob']:
45 return False
45 return False
46 return True
46 return True
47
47
48 class match(object):
48 class match(object):
49 def __init__(self, root, cwd, patterns, include=[], exclude=[],
49 def __init__(self, root, cwd, patterns, include=[], exclude=[],
50 default='glob', exact=False, auditor=None, ctx=None):
50 default='glob', exact=False, auditor=None, ctx=None):
51 """build an object to match a set of file patterns
51 """build an object to match a set of file patterns
52
52
53 arguments:
53 arguments:
54 root - the canonical root of the tree you're matching against
54 root - the canonical root of the tree you're matching against
55 cwd - the current working directory, if relevant
55 cwd - the current working directory, if relevant
56 patterns - patterns to find
56 patterns - patterns to find
57 include - patterns to include (unless they are excluded)
57 include - patterns to include (unless they are excluded)
58 exclude - patterns to exclude (even if they are included)
58 exclude - patterns to exclude (even if they are included)
59 default - if a pattern in patterns has no explicit type, assume this one
59 default - if a pattern in patterns has no explicit type, assume this one
60 exact - patterns are actually filenames (include/exclude still apply)
60 exact - patterns are actually filenames (include/exclude still apply)
61
61
62 a pattern is one of:
62 a pattern is one of:
63 'glob:<glob>' - a glob relative to cwd
63 'glob:<glob>' - a glob relative to cwd
64 're:<regexp>' - a regular expression
64 're:<regexp>' - a regular expression
65 'path:<path>' - a path relative to repository root
65 'path:<path>' - a path relative to repository root
66 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
66 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
67 'relpath:<path>' - a path relative to cwd
67 'relpath:<path>' - a path relative to cwd
68 'relre:<regexp>' - a regexp that needn't match the start of a name
68 'relre:<regexp>' - a regexp that needn't match the start of a name
69 'set:<fileset>' - a fileset expression
69 'set:<fileset>' - a fileset expression
70 '<something>' - a pattern of the specified default type
70 '<something>' - a pattern of the specified default type
71 """
71 """
72
72
73 self._root = root
73 self._root = root
74 self._cwd = cwd
74 self._cwd = cwd
75 self._files = [] # exact files and roots of patterns
75 self._files = [] # exact files and roots of patterns
76 self._anypats = bool(include or exclude)
76 self._anypats = bool(include or exclude)
77 self._always = False
77 self._always = False
78 self._pathrestricted = bool(include or exclude or patterns)
78 self._pathrestricted = bool(include or exclude or patterns)
79
79
80 matchfns = []
80 matchfns = []
81 if include:
81 if include:
82 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
82 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
83 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
83 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)')
84 matchfns.append(im)
84 matchfns.append(im)
85 if exclude:
85 if exclude:
86 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
86 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
87 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
87 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)')
88 matchfns.append(lambda f: not em(f))
88 matchfns.append(lambda f: not em(f))
89 if exact:
89 if exact:
90 if isinstance(patterns, list):
90 if isinstance(patterns, list):
91 self._files = patterns
91 self._files = patterns
92 else:
92 else:
93 self._files = list(patterns)
93 self._files = list(patterns)
94 matchfns.append(self.exact)
94 matchfns.append(self.exact)
95 elif patterns:
95 elif patterns:
96 kindpats = self._normalize(patterns, default, root, cwd, auditor)
96 kindpats = self._normalize(patterns, default, root, cwd, auditor)
97 if not _kindpatsalwaysmatch(kindpats):
97 if not _kindpatsalwaysmatch(kindpats):
98 self._files = _roots(kindpats)
98 self._files = _roots(kindpats)
99 self._anypats = self._anypats or _anypats(kindpats)
99 self._anypats = self._anypats or _anypats(kindpats)
100 self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
100 self.patternspat, pm = _buildmatch(ctx, kindpats, '$')
101 matchfns.append(pm)
101 matchfns.append(pm)
102
102
103 if not matchfns:
103 if not matchfns:
104 m = util.always
104 m = util.always
105 self._always = True
105 self._always = True
106 elif len(matchfns) == 1:
106 elif len(matchfns) == 1:
107 m = matchfns[0]
107 m = matchfns[0]
108 else:
108 else:
109 def m(f):
109 def m(f):
110 for matchfn in matchfns:
110 for matchfn in matchfns:
111 if not matchfn(f):
111 if not matchfn(f):
112 return False
112 return False
113 return True
113 return True
114
114
115 self.matchfn = m
115 self.matchfn = m
116 self._fmap = set(self._files)
116 self._fmap = set(self._files)
117
117
118 def __call__(self, fn):
118 def __call__(self, fn):
119 return self.matchfn(fn)
119 return self.matchfn(fn)
120 def __iter__(self):
120 def __iter__(self):
121 for f in self._files:
121 for f in self._files:
122 yield f
122 yield f
123
123
124 # Callbacks related to how the matcher is used by dirstate.walk.
124 # Callbacks related to how the matcher is used by dirstate.walk.
125 # Subscribers to these events must monkeypatch the matcher object.
125 # Subscribers to these events must monkeypatch the matcher object.
126 def bad(self, f, msg):
126 def bad(self, f, msg):
127 '''Callback from dirstate.walk for each explicit file that can't be
127 '''Callback from dirstate.walk for each explicit file that can't be
128 found/accessed, with an error message.'''
128 found/accessed, with an error message.'''
129 pass
129 pass
130
130
131 # If an explicitdir is set, it will be called when an explicitly listed
131 # If an explicitdir is set, it will be called when an explicitly listed
132 # directory is visited.
132 # directory is visited.
133 explicitdir = None
133 explicitdir = None
134
134
135 # If an traversedir is set, it will be called when a directory discovered
135 # If an traversedir is set, it will be called when a directory discovered
136 # by recursive traversal is visited.
136 # by recursive traversal is visited.
137 traversedir = None
137 traversedir = None
138
138
139 def abs(self, f):
139 def abs(self, f):
140 '''Convert a repo path back to path that is relative to the root of the
140 '''Convert a repo path back to path that is relative to the root of the
141 matcher.'''
141 matcher.'''
142 return f
142 return f
143
143
144 def rel(self, f):
144 def rel(self, f):
145 '''Convert repo path back to path that is relative to cwd of matcher.'''
145 '''Convert repo path back to path that is relative to cwd of matcher.'''
146 return util.pathto(self._root, self._cwd, f)
146 return util.pathto(self._root, self._cwd, f)
147
147
148 def uipath(self, f):
148 def uipath(self, f):
149 '''Convert repo path to a display path. If patterns or -I/-X were used
149 '''Convert repo path to a display path. If patterns or -I/-X were used
150 to create this matcher, the display path will be relative to cwd.
150 to create this matcher, the display path will be relative to cwd.
151 Otherwise it is relative to the root of the repo.'''
151 Otherwise it is relative to the root of the repo.'''
152 return (self._pathrestricted and self.rel(f)) or self.abs(f)
152 return (self._pathrestricted and self.rel(f)) or self.abs(f)
153
153
154 def files(self):
154 def files(self):
155 '''Explicitly listed files or patterns or roots:
155 '''Explicitly listed files or patterns or roots:
156 if no patterns or .always(): empty list,
156 if no patterns or .always(): empty list,
157 if exact: list exact files,
157 if exact: list exact files,
158 if not .anypats(): list all files and dirs,
158 if not .anypats(): list all files and dirs,
159 else: optimal roots'''
159 else: optimal roots'''
160 return self._files
160 return self._files
161
161
162 @propertycache
162 @propertycache
163 def _dirs(self):
163 def _dirs(self):
164 return set(util.dirs(self._fmap)) | set(['.'])
164 return set(util.dirs(self._fmap)) | set(['.'])
165
165
166 def visitdir(self, dir):
166 def visitdir(self, dir):
167 '''Helps while traversing a directory tree. Returns the string 'all' if
167 '''Helps while traversing a directory tree. Returns the string 'all' if
168 the given directory and all subdirectories should be visited. Otherwise
168 the given directory and all subdirectories should be visited. Otherwise
169 returns True or False indicating whether the given directory should be
169 returns True or False indicating whether the given directory should be
170 visited. If 'all' is returned, calling this method on a subdirectory
170 visited. If 'all' is returned, calling this method on a subdirectory
171 gives an undefined result.'''
171 gives an undefined result.'''
172 if not self._fmap or self.exact(dir):
172 if not self._fmap or self.exact(dir):
173 return 'all'
173 return 'all'
174 return dir in self._dirs
174 return dir in self._dirs
175
175
176 def exact(self, f):
176 def exact(self, f):
177 '''Returns True if f is in .files().'''
177 '''Returns True if f is in .files().'''
178 return f in self._fmap
178 return f in self._fmap
179
179
180 def anypats(self):
180 def anypats(self):
181 '''Matcher uses patterns or include/exclude.'''
181 '''Matcher uses patterns or include/exclude.'''
182 return self._anypats
182 return self._anypats
183
183
184 def always(self):
184 def always(self):
185 '''Matcher will match everything and .files() will be empty
185 '''Matcher will match everything and .files() will be empty
186 - optimization might be possible and necessary.'''
186 - optimization might be possible and necessary.'''
187 return self._always
187 return self._always
188
188
189 def isexact(self):
189 def isexact(self):
190 return self.matchfn == self.exact
190 return self.matchfn == self.exact
191
191
192 def _normalize(self, patterns, default, root, cwd, auditor):
192 def _normalize(self, patterns, default, root, cwd, auditor):
193 '''Convert 'kind:pat' from the patterns list to tuples with kind and
193 '''Convert 'kind:pat' from the patterns list to tuples with kind and
194 normalized and rooted patterns and with listfiles expanded.'''
194 normalized and rooted patterns and with listfiles expanded.'''
195 kindpats = []
195 kindpats = []
196 for kind, pat in [_patsplit(p, default) for p in patterns]:
196 for kind, pat in [_patsplit(p, default) for p in patterns]:
197 if kind in ('glob', 'relpath'):
197 if kind in ('glob', 'relpath'):
198 pat = pathutil.canonpath(root, cwd, pat, auditor)
198 pat = pathutil.canonpath(root, cwd, pat, auditor)
199 elif kind in ('relglob', 'path'):
199 elif kind in ('relglob', 'path'):
200 pat = util.normpath(pat)
200 pat = util.normpath(pat)
201 elif kind in ('listfile', 'listfile0'):
201 elif kind in ('listfile', 'listfile0'):
202 try:
202 try:
203 files = util.readfile(pat)
203 files = util.readfile(pat)
204 if kind == 'listfile0':
204 if kind == 'listfile0':
205 files = files.split('\0')
205 files = files.split('\0')
206 else:
206 else:
207 files = files.splitlines()
207 files = files.splitlines()
208 files = [f for f in files if f]
208 files = [f for f in files if f]
209 except EnvironmentError:
209 except EnvironmentError:
210 raise util.Abort(_("unable to read file list (%s)") % pat)
210 raise util.Abort(_("unable to read file list (%s)") % pat)
211 kindpats += self._normalize(files, default, root, cwd, auditor)
211 kindpats += self._normalize(files, default, root, cwd, auditor)
212 continue
212 continue
213 # else: re or relre - which cannot be normalized
213 # else: re or relre - which cannot be normalized
214 kindpats.append((kind, pat))
214 kindpats.append((kind, pat))
215 return kindpats
215 return kindpats
216
216
217 def exact(root, cwd, files):
217 def exact(root, cwd, files):
218 return match(root, cwd, files, exact=True)
218 return match(root, cwd, files, exact=True)
219
219
220 def always(root, cwd):
220 def always(root, cwd):
221 return match(root, cwd, [])
221 return match(root, cwd, [])
222
222
223 class narrowmatcher(match):
223 class narrowmatcher(match):
224 """Adapt a matcher to work on a subdirectory only.
224 """Adapt a matcher to work on a subdirectory only.
225
225
226 The paths are remapped to remove/insert the path as needed:
226 The paths are remapped to remove/insert the path as needed:
227
227
228 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
228 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
229 >>> m2 = narrowmatcher('sub', m1)
229 >>> m2 = narrowmatcher('sub', m1)
230 >>> bool(m2('a.txt'))
230 >>> bool(m2('a.txt'))
231 False
231 False
232 >>> bool(m2('b.txt'))
232 >>> bool(m2('b.txt'))
233 True
233 True
234 >>> bool(m2.matchfn('a.txt'))
234 >>> bool(m2.matchfn('a.txt'))
235 False
235 False
236 >>> bool(m2.matchfn('b.txt'))
236 >>> bool(m2.matchfn('b.txt'))
237 True
237 True
238 >>> m2.files()
238 >>> m2.files()
239 ['b.txt']
239 ['b.txt']
240 >>> m2.exact('b.txt')
240 >>> m2.exact('b.txt')
241 True
241 True
242 >>> util.pconvert(m2.rel('b.txt'))
242 >>> util.pconvert(m2.rel('b.txt'))
243 'sub/b.txt'
243 'sub/b.txt'
244 >>> def bad(f, msg):
244 >>> def bad(f, msg):
245 ... print "%s: %s" % (f, msg)
245 ... print "%s: %s" % (f, msg)
246 >>> m1.bad = bad
246 >>> m1.bad = bad
247 >>> m2.bad('x.txt', 'No such file')
247 >>> m2.bad('x.txt', 'No such file')
248 sub/x.txt: No such file
248 sub/x.txt: No such file
249 >>> m2.abs('c.txt')
249 >>> m2.abs('c.txt')
250 'sub/c.txt'
250 'sub/c.txt'
251 """
251 """
252
252
253 def __init__(self, path, matcher):
253 def __init__(self, path, matcher):
254 self._root = matcher._root
254 self._root = matcher._root
255 self._cwd = matcher._cwd
255 self._cwd = matcher._cwd
256 self._path = path
256 self._path = path
257 self._matcher = matcher
257 self._matcher = matcher
258 self._always = matcher._always
258 self._always = matcher._always
259 self._pathrestricted = matcher._pathrestricted
259 self._pathrestricted = matcher._pathrestricted
260
260
261 self._files = [f[len(path) + 1:] for f in matcher._files
261 self._files = [f[len(path) + 1:] for f in matcher._files
262 if f.startswith(path + "/")]
262 if f.startswith(path + "/")]
263
264 # If the parent repo had a path to this subrepo and no patterns are
265 # specified, this submatcher always matches.
266 if not self._always and not matcher._anypats:
267 self._always = util.any(f == path for f in matcher._files)
268
263 self._anypats = matcher._anypats
269 self._anypats = matcher._anypats
264 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
270 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
265 self._fmap = set(self._files)
271 self._fmap = set(self._files)
266
272
267 def abs(self, f):
273 def abs(self, f):
268 return self._matcher.abs(self._path + "/" + f)
274 return self._matcher.abs(self._path + "/" + f)
269
275
270 def bad(self, f, msg):
276 def bad(self, f, msg):
271 self._matcher.bad(self._path + "/" + f, msg)
277 self._matcher.bad(self._path + "/" + f, msg)
272
278
273 def rel(self, f):
279 def rel(self, f):
274 return self._matcher.rel(self._path + "/" + f)
280 return self._matcher.rel(self._path + "/" + f)
275
281
276 class icasefsmatcher(match):
282 class icasefsmatcher(match):
277 """A matcher for wdir on case insensitive filesystems, which normalizes the
283 """A matcher for wdir on case insensitive filesystems, which normalizes the
278 given patterns to the case in the filesystem.
284 given patterns to the case in the filesystem.
279 """
285 """
280
286
281 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
287 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
282 ctx):
288 ctx):
283 init = super(icasefsmatcher, self).__init__
289 init = super(icasefsmatcher, self).__init__
284 self._dsnormalize = ctx.repo().dirstate.normalize
290 self._dsnormalize = ctx.repo().dirstate.normalize
285
291
286 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
292 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
287 ctx=ctx)
293 ctx=ctx)
288
294
289 # m.exact(file) must be based off of the actual user input, otherwise
295 # m.exact(file) must be based off of the actual user input, otherwise
290 # inexact case matches are treated as exact, and not noted without -v.
296 # inexact case matches are treated as exact, and not noted without -v.
291 if self._files:
297 if self._files:
292 self._fmap = set(_roots(self._kp))
298 self._fmap = set(_roots(self._kp))
293
299
294 def _normalize(self, patterns, default, root, cwd, auditor):
300 def _normalize(self, patterns, default, root, cwd, auditor):
295 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
301 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
296 root, cwd, auditor)
302 root, cwd, auditor)
297 kindpats = []
303 kindpats = []
298 for kind, pats in self._kp:
304 for kind, pats in self._kp:
299 if kind not in ('re', 'relre'): # regex can't be normalized
305 if kind not in ('re', 'relre'): # regex can't be normalized
300 pats = self._dsnormalize(pats)
306 pats = self._dsnormalize(pats)
301 kindpats.append((kind, pats))
307 kindpats.append((kind, pats))
302 return kindpats
308 return kindpats
303
309
304 def patkind(pattern, default=None):
310 def patkind(pattern, default=None):
305 '''If pattern is 'kind:pat' with a known kind, return kind.'''
311 '''If pattern is 'kind:pat' with a known kind, return kind.'''
306 return _patsplit(pattern, default)[0]
312 return _patsplit(pattern, default)[0]
307
313
308 def _patsplit(pattern, default):
314 def _patsplit(pattern, default):
309 """Split a string into the optional pattern kind prefix and the actual
315 """Split a string into the optional pattern kind prefix and the actual
310 pattern."""
316 pattern."""
311 if ':' in pattern:
317 if ':' in pattern:
312 kind, pat = pattern.split(':', 1)
318 kind, pat = pattern.split(':', 1)
313 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
319 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
314 'listfile', 'listfile0', 'set'):
320 'listfile', 'listfile0', 'set'):
315 return kind, pat
321 return kind, pat
316 return default, pattern
322 return default, pattern
317
323
318 def _globre(pat):
324 def _globre(pat):
319 r'''Convert an extended glob string to a regexp string.
325 r'''Convert an extended glob string to a regexp string.
320
326
321 >>> print _globre(r'?')
327 >>> print _globre(r'?')
322 .
328 .
323 >>> print _globre(r'*')
329 >>> print _globre(r'*')
324 [^/]*
330 [^/]*
325 >>> print _globre(r'**')
331 >>> print _globre(r'**')
326 .*
332 .*
327 >>> print _globre(r'**/a')
333 >>> print _globre(r'**/a')
328 (?:.*/)?a
334 (?:.*/)?a
329 >>> print _globre(r'a/**/b')
335 >>> print _globre(r'a/**/b')
330 a\/(?:.*/)?b
336 a\/(?:.*/)?b
331 >>> print _globre(r'[a*?!^][^b][!c]')
337 >>> print _globre(r'[a*?!^][^b][!c]')
332 [a*?!^][\^b][^c]
338 [a*?!^][\^b][^c]
333 >>> print _globre(r'{a,b}')
339 >>> print _globre(r'{a,b}')
334 (?:a|b)
340 (?:a|b)
335 >>> print _globre(r'.\*\?')
341 >>> print _globre(r'.\*\?')
336 \.\*\?
342 \.\*\?
337 '''
343 '''
338 i, n = 0, len(pat)
344 i, n = 0, len(pat)
339 res = ''
345 res = ''
340 group = 0
346 group = 0
341 escape = util.re.escape
347 escape = util.re.escape
342 def peek():
348 def peek():
343 return i < n and pat[i]
349 return i < n and pat[i]
344 while i < n:
350 while i < n:
345 c = pat[i]
351 c = pat[i]
346 i += 1
352 i += 1
347 if c not in '*?[{},\\':
353 if c not in '*?[{},\\':
348 res += escape(c)
354 res += escape(c)
349 elif c == '*':
355 elif c == '*':
350 if peek() == '*':
356 if peek() == '*':
351 i += 1
357 i += 1
352 if peek() == '/':
358 if peek() == '/':
353 i += 1
359 i += 1
354 res += '(?:.*/)?'
360 res += '(?:.*/)?'
355 else:
361 else:
356 res += '.*'
362 res += '.*'
357 else:
363 else:
358 res += '[^/]*'
364 res += '[^/]*'
359 elif c == '?':
365 elif c == '?':
360 res += '.'
366 res += '.'
361 elif c == '[':
367 elif c == '[':
362 j = i
368 j = i
363 if j < n and pat[j] in '!]':
369 if j < n and pat[j] in '!]':
364 j += 1
370 j += 1
365 while j < n and pat[j] != ']':
371 while j < n and pat[j] != ']':
366 j += 1
372 j += 1
367 if j >= n:
373 if j >= n:
368 res += '\\['
374 res += '\\['
369 else:
375 else:
370 stuff = pat[i:j].replace('\\','\\\\')
376 stuff = pat[i:j].replace('\\','\\\\')
371 i = j + 1
377 i = j + 1
372 if stuff[0] == '!':
378 if stuff[0] == '!':
373 stuff = '^' + stuff[1:]
379 stuff = '^' + stuff[1:]
374 elif stuff[0] == '^':
380 elif stuff[0] == '^':
375 stuff = '\\' + stuff
381 stuff = '\\' + stuff
376 res = '%s[%s]' % (res, stuff)
382 res = '%s[%s]' % (res, stuff)
377 elif c == '{':
383 elif c == '{':
378 group += 1
384 group += 1
379 res += '(?:'
385 res += '(?:'
380 elif c == '}' and group:
386 elif c == '}' and group:
381 res += ')'
387 res += ')'
382 group -= 1
388 group -= 1
383 elif c == ',' and group:
389 elif c == ',' and group:
384 res += '|'
390 res += '|'
385 elif c == '\\':
391 elif c == '\\':
386 p = peek()
392 p = peek()
387 if p:
393 if p:
388 i += 1
394 i += 1
389 res += escape(p)
395 res += escape(p)
390 else:
396 else:
391 res += escape(c)
397 res += escape(c)
392 else:
398 else:
393 res += escape(c)
399 res += escape(c)
394 return res
400 return res
395
401
396 def _regex(kind, pat, globsuffix):
402 def _regex(kind, pat, globsuffix):
397 '''Convert a (normalized) pattern of any kind into a regular expression.
403 '''Convert a (normalized) pattern of any kind into a regular expression.
398 globsuffix is appended to the regexp of globs.'''
404 globsuffix is appended to the regexp of globs.'''
399 if not pat:
405 if not pat:
400 return ''
406 return ''
401 if kind == 're':
407 if kind == 're':
402 return pat
408 return pat
403 if kind == 'path':
409 if kind == 'path':
404 return '^' + util.re.escape(pat) + '(?:/|$)'
410 return '^' + util.re.escape(pat) + '(?:/|$)'
405 if kind == 'relglob':
411 if kind == 'relglob':
406 return '(?:|.*/)' + _globre(pat) + globsuffix
412 return '(?:|.*/)' + _globre(pat) + globsuffix
407 if kind == 'relpath':
413 if kind == 'relpath':
408 return util.re.escape(pat) + '(?:/|$)'
414 return util.re.escape(pat) + '(?:/|$)'
409 if kind == 'relre':
415 if kind == 'relre':
410 if pat.startswith('^'):
416 if pat.startswith('^'):
411 return pat
417 return pat
412 return '.*' + pat
418 return '.*' + pat
413 return _globre(pat) + globsuffix
419 return _globre(pat) + globsuffix
414
420
415 def _buildmatch(ctx, kindpats, globsuffix):
421 def _buildmatch(ctx, kindpats, globsuffix):
416 '''Return regexp string and a matcher function for kindpats.
422 '''Return regexp string and a matcher function for kindpats.
417 globsuffix is appended to the regexp of globs.'''
423 globsuffix is appended to the regexp of globs.'''
418 fset, kindpats = _expandsets(kindpats, ctx)
424 fset, kindpats = _expandsets(kindpats, ctx)
419 if not kindpats:
425 if not kindpats:
420 return "", fset.__contains__
426 return "", fset.__contains__
421
427
422 regex, mf = _buildregexmatch(kindpats, globsuffix)
428 regex, mf = _buildregexmatch(kindpats, globsuffix)
423 if fset:
429 if fset:
424 return regex, lambda f: f in fset or mf(f)
430 return regex, lambda f: f in fset or mf(f)
425 return regex, mf
431 return regex, mf
426
432
427 def _buildregexmatch(kindpats, globsuffix):
433 def _buildregexmatch(kindpats, globsuffix):
428 """Build a match function from a list of kinds and kindpats,
434 """Build a match function from a list of kinds and kindpats,
429 return regexp string and a matcher function."""
435 return regexp string and a matcher function."""
430 try:
436 try:
431 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
437 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
432 for (k, p) in kindpats])
438 for (k, p) in kindpats])
433 if len(regex) > 20000:
439 if len(regex) > 20000:
434 raise OverflowError
440 raise OverflowError
435 return regex, _rematcher(regex)
441 return regex, _rematcher(regex)
436 except OverflowError:
442 except OverflowError:
437 # We're using a Python with a tiny regex engine and we
443 # We're using a Python with a tiny regex engine and we
438 # made it explode, so we'll divide the pattern list in two
444 # made it explode, so we'll divide the pattern list in two
439 # until it works
445 # until it works
440 l = len(kindpats)
446 l = len(kindpats)
441 if l < 2:
447 if l < 2:
442 raise
448 raise
443 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
449 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
444 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
450 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
445 return regex, lambda s: a(s) or b(s)
451 return regex, lambda s: a(s) or b(s)
446 except re.error:
452 except re.error:
447 for k, p in kindpats:
453 for k, p in kindpats:
448 try:
454 try:
449 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
455 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
450 except re.error:
456 except re.error:
451 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
457 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
452 raise util.Abort(_("invalid pattern"))
458 raise util.Abort(_("invalid pattern"))
453
459
454 def _roots(kindpats):
460 def _roots(kindpats):
455 '''return roots and exact explicitly listed files from patterns
461 '''return roots and exact explicitly listed files from patterns
456
462
457 >>> _roots([('glob', 'g/*'), ('glob', 'g'), ('glob', 'g*')])
463 >>> _roots([('glob', 'g/*'), ('glob', 'g'), ('glob', 'g*')])
458 ['g', 'g', '.']
464 ['g', 'g', '.']
459 >>> _roots([('relpath', 'r'), ('path', 'p/p'), ('path', '')])
465 >>> _roots([('relpath', 'r'), ('path', 'p/p'), ('path', '')])
460 ['r', 'p/p', '.']
466 ['r', 'p/p', '.']
461 >>> _roots([('relglob', 'rg*'), ('re', 're/'), ('relre', 'rr')])
467 >>> _roots([('relglob', 'rg*'), ('re', 're/'), ('relre', 'rr')])
462 ['.', '.', '.']
468 ['.', '.', '.']
463 '''
469 '''
464 r = []
470 r = []
465 for kind, pat in kindpats:
471 for kind, pat in kindpats:
466 if kind == 'glob': # find the non-glob prefix
472 if kind == 'glob': # find the non-glob prefix
467 root = []
473 root = []
468 for p in pat.split('/'):
474 for p in pat.split('/'):
469 if '[' in p or '{' in p or '*' in p or '?' in p:
475 if '[' in p or '{' in p or '*' in p or '?' in p:
470 break
476 break
471 root.append(p)
477 root.append(p)
472 r.append('/'.join(root) or '.')
478 r.append('/'.join(root) or '.')
473 elif kind in ('relpath', 'path'):
479 elif kind in ('relpath', 'path'):
474 r.append(pat or '.')
480 r.append(pat or '.')
475 else: # relglob, re, relre
481 else: # relglob, re, relre
476 r.append('.')
482 r.append('.')
477 return r
483 return r
478
484
479 def _anypats(kindpats):
485 def _anypats(kindpats):
480 for kind, pat in kindpats:
486 for kind, pat in kindpats:
481 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
487 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
482 return True
488 return True
@@ -1,550 +1,563
1 Preparing the subrepository 'sub2'
1 Preparing the subrepository 'sub2'
2
2
3 $ hg init sub2
3 $ hg init sub2
4 $ echo sub2 > sub2/sub2
4 $ echo sub2 > sub2/sub2
5 $ hg add -R sub2
5 $ hg add -R sub2
6 adding sub2/sub2 (glob)
6 adding sub2/sub2 (glob)
7 $ hg commit -R sub2 -m "sub2 import"
7 $ hg commit -R sub2 -m "sub2 import"
8
8
9 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
9 Preparing the 'sub1' repo which depends on the subrepo 'sub2'
10
10
11 $ hg init sub1
11 $ hg init sub1
12 $ echo sub1 > sub1/sub1
12 $ echo sub1 > sub1/sub1
13 $ echo "sub2 = ../sub2" > sub1/.hgsub
13 $ echo "sub2 = ../sub2" > sub1/.hgsub
14 $ hg clone sub2 sub1/sub2
14 $ hg clone sub2 sub1/sub2
15 updating to branch default
15 updating to branch default
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
17 $ hg add -R sub1
17 $ hg add -R sub1
18 adding sub1/.hgsub (glob)
18 adding sub1/.hgsub (glob)
19 adding sub1/sub1 (glob)
19 adding sub1/sub1 (glob)
20 $ hg commit -R sub1 -m "sub1 import"
20 $ hg commit -R sub1 -m "sub1 import"
21
21
22 Preparing the 'main' repo which depends on the subrepo 'sub1'
22 Preparing the 'main' repo which depends on the subrepo 'sub1'
23
23
24 $ hg init main
24 $ hg init main
25 $ echo main > main/main
25 $ echo main > main/main
26 $ echo "sub1 = ../sub1" > main/.hgsub
26 $ echo "sub1 = ../sub1" > main/.hgsub
27 $ hg clone sub1 main/sub1
27 $ hg clone sub1 main/sub1
28 updating to branch default
28 updating to branch default
29 cloning subrepo sub2 from $TESTTMP/sub2
29 cloning subrepo sub2 from $TESTTMP/sub2
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 $ hg add -R main
31 $ hg add -R main
32 adding main/.hgsub (glob)
32 adding main/.hgsub (glob)
33 adding main/main (glob)
33 adding main/main (glob)
34 $ hg commit -R main -m "main import"
34 $ hg commit -R main -m "main import"
35
35
36 Cleaning both repositories, just as a clone -U
36 Cleaning both repositories, just as a clone -U
37
37
38 $ hg up -C -R sub2 null
38 $ hg up -C -R sub2 null
39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
39 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
40 $ hg up -C -R sub1 null
40 $ hg up -C -R sub1 null
41 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
41 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
42 $ hg up -C -R main null
42 $ hg up -C -R main null
43 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
43 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
44 $ rm -rf main/sub1
44 $ rm -rf main/sub1
45 $ rm -rf sub1/sub2
45 $ rm -rf sub1/sub2
46
46
47 Clone main
47 Clone main
48
48
49 $ hg --config extensions.largefiles= clone main cloned
49 $ hg --config extensions.largefiles= clone main cloned
50 updating to branch default
50 updating to branch default
51 cloning subrepo sub1 from $TESTTMP/sub1
51 cloning subrepo sub1 from $TESTTMP/sub1
52 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
52 cloning subrepo sub1/sub2 from $TESTTMP/sub2 (glob)
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
54
54
55 Largefiles is NOT enabled in the clone if the source repo doesn't require it
55 Largefiles is NOT enabled in the clone if the source repo doesn't require it
56 $ cat cloned/.hg/hgrc
56 $ cat cloned/.hg/hgrc
57 # example repository config (see "hg help config" for more info)
57 # example repository config (see "hg help config" for more info)
58 [paths]
58 [paths]
59 default = $TESTTMP/main (glob)
59 default = $TESTTMP/main (glob)
60
60
61 # path aliases to other clones of this repo in URLs or filesystem paths
61 # path aliases to other clones of this repo in URLs or filesystem paths
62 # (see "hg help config.paths" for more info)
62 # (see "hg help config.paths" for more info)
63 #
63 #
64 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
64 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
65 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
65 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
66 # my-clone = /home/jdoe/jdoes-clone
66 # my-clone = /home/jdoe/jdoes-clone
67
67
68 [ui]
68 [ui]
69 # name and email (local to this repository, optional), e.g.
69 # name and email (local to this repository, optional), e.g.
70 # username = Jane Doe <jdoe@example.com>
70 # username = Jane Doe <jdoe@example.com>
71
71
72 Checking cloned repo ids
72 Checking cloned repo ids
73
73
74 $ printf "cloned " ; hg id -R cloned
74 $ printf "cloned " ; hg id -R cloned
75 cloned 7f491f53a367 tip
75 cloned 7f491f53a367 tip
76 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
76 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
77 cloned/sub1 fc3b4ce2696f tip
77 cloned/sub1 fc3b4ce2696f tip
78 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
78 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
79 cloned/sub1/sub2 c57a0840e3ba tip
79 cloned/sub1/sub2 c57a0840e3ba tip
80
80
81 debugsub output for main and sub1
81 debugsub output for main and sub1
82
82
83 $ hg debugsub -R cloned
83 $ hg debugsub -R cloned
84 path sub1
84 path sub1
85 source ../sub1
85 source ../sub1
86 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
86 revision fc3b4ce2696f7741438c79207583768f2ce6b0dd
87 $ hg debugsub -R cloned/sub1
87 $ hg debugsub -R cloned/sub1
88 path sub2
88 path sub2
89 source ../sub2
89 source ../sub2
90 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
90 revision c57a0840e3badd667ef3c3ef65471609acb2ba3c
91
91
92 Modifying deeply nested 'sub2'
92 Modifying deeply nested 'sub2'
93
93
94 $ echo modified > cloned/sub1/sub2/sub2
94 $ echo modified > cloned/sub1/sub2/sub2
95 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
95 $ hg commit --subrepos -m "deep nested modif should trigger a commit" -R cloned
96 committing subrepository sub1
96 committing subrepository sub1
97 committing subrepository sub1/sub2 (glob)
97 committing subrepository sub1/sub2 (glob)
98
98
99 Checking modified node ids
99 Checking modified node ids
100
100
101 $ printf "cloned " ; hg id -R cloned
101 $ printf "cloned " ; hg id -R cloned
102 cloned ffe6649062fe tip
102 cloned ffe6649062fe tip
103 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
103 $ printf "cloned/sub1 " ; hg id -R cloned/sub1
104 cloned/sub1 2ecb03bf44a9 tip
104 cloned/sub1 2ecb03bf44a9 tip
105 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
105 $ printf "cloned/sub1/sub2 " ; hg id -R cloned/sub1/sub2
106 cloned/sub1/sub2 53dd3430bcaf tip
106 cloned/sub1/sub2 53dd3430bcaf tip
107
107
108 debugsub output for main and sub1
108 debugsub output for main and sub1
109
109
110 $ hg debugsub -R cloned
110 $ hg debugsub -R cloned
111 path sub1
111 path sub1
112 source ../sub1
112 source ../sub1
113 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
113 revision 2ecb03bf44a94e749e8669481dd9069526ce7cb9
114 $ hg debugsub -R cloned/sub1
114 $ hg debugsub -R cloned/sub1
115 path sub2
115 path sub2
116 source ../sub2
116 source ../sub2
117 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
117 revision 53dd3430bcaf5ab4a7c48262bcad6d441f510487
118
118
119 Check that deep archiving works
119 Check that deep archiving works
120
120
121 $ cd cloned
121 $ cd cloned
122 $ echo 'test' > sub1/sub2/test.txt
122 $ echo 'test' > sub1/sub2/test.txt
123 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
123 $ hg --config extensions.largefiles=! add sub1/sub2/test.txt
124 $ mkdir sub1/sub2/folder
124 $ mkdir sub1/sub2/folder
125 $ echo 'subfolder' > sub1/sub2/folder/test.txt
125 $ echo 'subfolder' > sub1/sub2/folder/test.txt
126 $ hg ci -ASm "add test.txt"
126 $ hg ci -ASm "add test.txt"
127 adding sub1/sub2/folder/test.txt
127 adding sub1/sub2/folder/test.txt
128 committing subrepository sub1
128 committing subrepository sub1
129 committing subrepository sub1/sub2 (glob)
129 committing subrepository sub1/sub2 (glob)
130
130
131 .. but first take a detour through some deep removal testing
131 .. but first take a detour through some deep removal testing
132
132
133 $ hg remove -S -I 're:.*.txt' .
133 $ hg remove -S -I 're:.*.txt' .
134 removing sub1/sub2/folder/test.txt (glob)
134 removing sub1/sub2/folder/test.txt (glob)
135 removing sub1/sub2/test.txt (glob)
135 removing sub1/sub2/test.txt (glob)
136 $ hg status -S
136 $ hg status -S
137 R sub1/sub2/folder/test.txt
137 R sub1/sub2/folder/test.txt
138 R sub1/sub2/test.txt
138 R sub1/sub2/test.txt
139 $ hg update -Cq
139 $ hg update -Cq
140 $ hg remove -I 're:.*.txt' sub1
140 $ hg remove -I 're:.*.txt' sub1
141 $ hg status -S
141 $ hg status -S
142 $ hg remove sub1/sub2/folder/test.txt
142 $ hg remove sub1/sub2/folder/test.txt
143 $ hg remove sub1/.hgsubstate
143 $ hg remove sub1/.hgsubstate
144 $ mv sub1/.hgsub sub1/x.hgsub
144 $ mv sub1/.hgsub sub1/x.hgsub
145 $ hg status -S
145 $ hg status -S
146 warning: subrepo spec file 'sub1/.hgsub' not found (glob)
146 warning: subrepo spec file 'sub1/.hgsub' not found (glob)
147 R sub1/.hgsubstate
147 R sub1/.hgsubstate
148 R sub1/sub2/folder/test.txt
148 R sub1/sub2/folder/test.txt
149 ! sub1/.hgsub
149 ! sub1/.hgsub
150 ? sub1/x.hgsub
150 ? sub1/x.hgsub
151 $ mv sub1/x.hgsub sub1/.hgsub
151 $ mv sub1/x.hgsub sub1/.hgsub
152 $ hg update -Cq
152 $ hg update -Cq
153 $ touch sub1/foo
153 $ touch sub1/foo
154 $ hg forget sub1/sub2/folder/test.txt
154 $ hg forget sub1/sub2/folder/test.txt
155 $ rm sub1/sub2/test.txt
155 $ rm sub1/sub2/test.txt
156
156
157 Test relative path printing + subrepos
157 Test relative path printing + subrepos
158 $ mkdir -p foo/bar
158 $ mkdir -p foo/bar
159 $ cd foo
159 $ cd foo
160 $ touch bar/abc
160 $ touch bar/abc
161 $ hg addremove -S ..
161 $ hg addremove -S ..
162 adding ../sub1/sub2/folder/test.txt (glob)
162 adding ../sub1/sub2/folder/test.txt (glob)
163 removing ../sub1/sub2/test.txt (glob)
163 removing ../sub1/sub2/test.txt (glob)
164 adding ../sub1/foo (glob)
164 adding ../sub1/foo (glob)
165 adding bar/abc (glob)
165 adding bar/abc (glob)
166 $ cd ..
166 $ cd ..
167 $ hg status -S
167 $ hg status -S
168 A foo/bar/abc
168 A foo/bar/abc
169 A sub1/foo
169 A sub1/foo
170 R sub1/sub2/test.txt
170 R sub1/sub2/test.txt
171 $ hg update -Cq
171 $ hg update -Cq
172 $ touch sub1/sub2/folder/bar
172 $ touch sub1/sub2/folder/bar
173 $ hg addremove sub1/sub2
173 $ hg addremove sub1/sub2
174 adding sub1/sub2/folder/bar (glob)
174 adding sub1/sub2/folder/bar (glob)
175 $ hg status -S
175 $ hg status -S
176 A sub1/sub2/folder/bar
176 A sub1/sub2/folder/bar
177 ? foo/bar/abc
177 ? foo/bar/abc
178 ? sub1/foo
178 ? sub1/foo
179 $ hg update -Cq
179 $ hg update -Cq
180 $ hg addremove sub1
180 $ hg addremove sub1
181 adding sub1/sub2/folder/bar (glob)
181 adding sub1/sub2/folder/bar (glob)
182 adding sub1/foo (glob)
182 adding sub1/foo (glob)
183 $ hg update -Cq
183 $ hg update -Cq
184 $ rm sub1/sub2/folder/test.txt
184 $ rm sub1/sub2/folder/test.txt
185 $ rm sub1/sub2/test.txt
185 $ rm sub1/sub2/test.txt
186 $ hg ci -ASm "remove test.txt"
186 $ hg ci -ASm "remove test.txt"
187 adding sub1/sub2/folder/bar
187 adding sub1/sub2/folder/bar
188 removing sub1/sub2/folder/test.txt
188 removing sub1/sub2/folder/test.txt
189 removing sub1/sub2/test.txt
189 removing sub1/sub2/test.txt
190 adding sub1/foo
190 adding sub1/foo
191 adding foo/bar/abc
191 adding foo/bar/abc
192 committing subrepository sub1
192 committing subrepository sub1
193 committing subrepository sub1/sub2 (glob)
193 committing subrepository sub1/sub2 (glob)
194
194
195 $ hg forget sub1/sub2/sub2
195 $ hg forget sub1/sub2/sub2
196 $ echo x > sub1/sub2/x.txt
196 $ echo x > sub1/sub2/x.txt
197 $ hg add sub1/sub2/x.txt
197 $ hg add sub1/sub2/x.txt
198
198
199 Files sees uncommitted adds and removes in subrepos
199 Files sees uncommitted adds and removes in subrepos
200 $ hg files -S
200 $ hg files -S
201 .hgsub
201 .hgsub
202 .hgsubstate
202 .hgsubstate
203 foo/bar/abc (glob)
203 foo/bar/abc (glob)
204 main
204 main
205 sub1/.hgsub (glob)
205 sub1/.hgsub (glob)
206 sub1/.hgsubstate (glob)
206 sub1/.hgsubstate (glob)
207 sub1/foo (glob)
207 sub1/foo (glob)
208 sub1/sub1 (glob)
208 sub1/sub1 (glob)
209 sub1/sub2/folder/bar (glob)
209 sub1/sub2/folder/bar (glob)
210 sub1/sub2/x.txt (glob)
210 sub1/sub2/x.txt (glob)
211
211
212 $ hg files -S -r '.^' sub1/sub2/folder
212 $ hg files -S -r '.^' sub1/sub2/folder
213 sub1/sub2/folder/test.txt (glob)
213 sub1/sub2/folder/test.txt (glob)
214
214
215 $ hg files -S -r '.^' sub1/sub2/missing
215 $ hg files -S -r '.^' sub1/sub2/missing
216 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
216 sub1/sub2/missing: no such file in rev 78026e779ea6 (glob)
217 [1]
217 [1]
218
218
219 $ hg files -S -r '.^' sub1/
220 sub1/.hgsub (glob)
221 sub1/.hgsubstate (glob)
222 sub1/sub1 (glob)
223 sub1/sub2/folder/test.txt (glob)
224 sub1/sub2/sub2 (glob)
225 sub1/sub2/test.txt (glob)
226
227 $ hg files -S -r '.^' sub1/sub2
228 sub1/sub2/folder/test.txt (glob)
229 sub1/sub2/sub2 (glob)
230 sub1/sub2/test.txt (glob)
231
219 $ hg rollback -q
232 $ hg rollback -q
220 $ hg up -Cq
233 $ hg up -Cq
221
234
222 $ hg --config extensions.largefiles=! archive -S ../archive_all
235 $ hg --config extensions.largefiles=! archive -S ../archive_all
223 $ find ../archive_all | sort
236 $ find ../archive_all | sort
224 ../archive_all
237 ../archive_all
225 ../archive_all/.hg_archival.txt
238 ../archive_all/.hg_archival.txt
226 ../archive_all/.hgsub
239 ../archive_all/.hgsub
227 ../archive_all/.hgsubstate
240 ../archive_all/.hgsubstate
228 ../archive_all/main
241 ../archive_all/main
229 ../archive_all/sub1
242 ../archive_all/sub1
230 ../archive_all/sub1/.hgsub
243 ../archive_all/sub1/.hgsub
231 ../archive_all/sub1/.hgsubstate
244 ../archive_all/sub1/.hgsubstate
232 ../archive_all/sub1/sub1
245 ../archive_all/sub1/sub1
233 ../archive_all/sub1/sub2
246 ../archive_all/sub1/sub2
234 ../archive_all/sub1/sub2/folder
247 ../archive_all/sub1/sub2/folder
235 ../archive_all/sub1/sub2/folder/test.txt
248 ../archive_all/sub1/sub2/folder/test.txt
236 ../archive_all/sub1/sub2/sub2
249 ../archive_all/sub1/sub2/sub2
237 ../archive_all/sub1/sub2/test.txt
250 ../archive_all/sub1/sub2/test.txt
238
251
239 Check that archive -X works in deep subrepos
252 Check that archive -X works in deep subrepos
240
253
241 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
254 $ hg --config extensions.largefiles=! archive -S -X '**test*' ../archive_exclude
242 $ find ../archive_exclude | sort
255 $ find ../archive_exclude | sort
243 ../archive_exclude
256 ../archive_exclude
244 ../archive_exclude/.hg_archival.txt
257 ../archive_exclude/.hg_archival.txt
245 ../archive_exclude/.hgsub
258 ../archive_exclude/.hgsub
246 ../archive_exclude/.hgsubstate
259 ../archive_exclude/.hgsubstate
247 ../archive_exclude/main
260 ../archive_exclude/main
248 ../archive_exclude/sub1
261 ../archive_exclude/sub1
249 ../archive_exclude/sub1/.hgsub
262 ../archive_exclude/sub1/.hgsub
250 ../archive_exclude/sub1/.hgsubstate
263 ../archive_exclude/sub1/.hgsubstate
251 ../archive_exclude/sub1/sub1
264 ../archive_exclude/sub1/sub1
252 ../archive_exclude/sub1/sub2
265 ../archive_exclude/sub1/sub2
253 ../archive_exclude/sub1/sub2/sub2
266 ../archive_exclude/sub1/sub2/sub2
254
267
255 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
268 $ hg --config extensions.largefiles=! archive -S -I '**test*' ../archive_include
256 $ find ../archive_include | sort
269 $ find ../archive_include | sort
257 ../archive_include
270 ../archive_include
258 ../archive_include/sub1
271 ../archive_include/sub1
259 ../archive_include/sub1/sub2
272 ../archive_include/sub1/sub2
260 ../archive_include/sub1/sub2/folder
273 ../archive_include/sub1/sub2/folder
261 ../archive_include/sub1/sub2/folder/test.txt
274 ../archive_include/sub1/sub2/folder/test.txt
262 ../archive_include/sub1/sub2/test.txt
275 ../archive_include/sub1/sub2/test.txt
263
276
264 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
277 Check that deep archive works with largefiles (which overrides hgsubrepo impl)
265 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
278 This also tests the repo.ui regression in 43fb170a23bd, and that lf subrepo
266 subrepos are archived properly.
279 subrepos are archived properly.
267 Note that add --large through a subrepo currently adds the file as a normal file
280 Note that add --large through a subrepo currently adds the file as a normal file
268
281
269 $ echo "large" > sub1/sub2/large.bin
282 $ echo "large" > sub1/sub2/large.bin
270 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
283 $ hg --config extensions.largefiles= add --large -R sub1/sub2 sub1/sub2/large.bin
271 $ echo "large" > large.bin
284 $ echo "large" > large.bin
272 $ hg --config extensions.largefiles= add --large large.bin
285 $ hg --config extensions.largefiles= add --large large.bin
273 $ hg --config extensions.largefiles= ci -S -m "add large files"
286 $ hg --config extensions.largefiles= ci -S -m "add large files"
274 committing subrepository sub1
287 committing subrepository sub1
275 committing subrepository sub1/sub2 (glob)
288 committing subrepository sub1/sub2 (glob)
276
289
277 $ hg --config extensions.largefiles= archive -S ../archive_lf
290 $ hg --config extensions.largefiles= archive -S ../archive_lf
278 $ find ../archive_lf | sort
291 $ find ../archive_lf | sort
279 ../archive_lf
292 ../archive_lf
280 ../archive_lf/.hg_archival.txt
293 ../archive_lf/.hg_archival.txt
281 ../archive_lf/.hgsub
294 ../archive_lf/.hgsub
282 ../archive_lf/.hgsubstate
295 ../archive_lf/.hgsubstate
283 ../archive_lf/large.bin
296 ../archive_lf/large.bin
284 ../archive_lf/main
297 ../archive_lf/main
285 ../archive_lf/sub1
298 ../archive_lf/sub1
286 ../archive_lf/sub1/.hgsub
299 ../archive_lf/sub1/.hgsub
287 ../archive_lf/sub1/.hgsubstate
300 ../archive_lf/sub1/.hgsubstate
288 ../archive_lf/sub1/sub1
301 ../archive_lf/sub1/sub1
289 ../archive_lf/sub1/sub2
302 ../archive_lf/sub1/sub2
290 ../archive_lf/sub1/sub2/folder
303 ../archive_lf/sub1/sub2/folder
291 ../archive_lf/sub1/sub2/folder/test.txt
304 ../archive_lf/sub1/sub2/folder/test.txt
292 ../archive_lf/sub1/sub2/large.bin
305 ../archive_lf/sub1/sub2/large.bin
293 ../archive_lf/sub1/sub2/sub2
306 ../archive_lf/sub1/sub2/sub2
294 ../archive_lf/sub1/sub2/test.txt
307 ../archive_lf/sub1/sub2/test.txt
295 $ rm -rf ../archive_lf
308 $ rm -rf ../archive_lf
296
309
297 Exclude large files from main and sub-sub repo
310 Exclude large files from main and sub-sub repo
298
311
299 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
312 $ hg --config extensions.largefiles= archive -S -X '**.bin' ../archive_lf
300 $ find ../archive_lf | sort
313 $ find ../archive_lf | sort
301 ../archive_lf
314 ../archive_lf
302 ../archive_lf/.hg_archival.txt
315 ../archive_lf/.hg_archival.txt
303 ../archive_lf/.hgsub
316 ../archive_lf/.hgsub
304 ../archive_lf/.hgsubstate
317 ../archive_lf/.hgsubstate
305 ../archive_lf/main
318 ../archive_lf/main
306 ../archive_lf/sub1
319 ../archive_lf/sub1
307 ../archive_lf/sub1/.hgsub
320 ../archive_lf/sub1/.hgsub
308 ../archive_lf/sub1/.hgsubstate
321 ../archive_lf/sub1/.hgsubstate
309 ../archive_lf/sub1/sub1
322 ../archive_lf/sub1/sub1
310 ../archive_lf/sub1/sub2
323 ../archive_lf/sub1/sub2
311 ../archive_lf/sub1/sub2/folder
324 ../archive_lf/sub1/sub2/folder
312 ../archive_lf/sub1/sub2/folder/test.txt
325 ../archive_lf/sub1/sub2/folder/test.txt
313 ../archive_lf/sub1/sub2/sub2
326 ../archive_lf/sub1/sub2/sub2
314 ../archive_lf/sub1/sub2/test.txt
327 ../archive_lf/sub1/sub2/test.txt
315 $ rm -rf ../archive_lf
328 $ rm -rf ../archive_lf
316
329
317 Exclude normal files from main and sub-sub repo
330 Exclude normal files from main and sub-sub repo
318
331
319 $ hg --config extensions.largefiles= archive -S -X '**.txt' ../archive_lf.tgz
332 $ hg --config extensions.largefiles= archive -S -X '**.txt' ../archive_lf.tgz
320 $ tar -tzf ../archive_lf.tgz | sort
333 $ tar -tzf ../archive_lf.tgz | sort
321 archive_lf/.hgsub
334 archive_lf/.hgsub
322 archive_lf/.hgsubstate
335 archive_lf/.hgsubstate
323 archive_lf/large.bin
336 archive_lf/large.bin
324 archive_lf/main
337 archive_lf/main
325 archive_lf/sub1/.hgsub
338 archive_lf/sub1/.hgsub
326 archive_lf/sub1/.hgsubstate
339 archive_lf/sub1/.hgsubstate
327 archive_lf/sub1/sub1
340 archive_lf/sub1/sub1
328 archive_lf/sub1/sub2/large.bin
341 archive_lf/sub1/sub2/large.bin
329 archive_lf/sub1/sub2/sub2
342 archive_lf/sub1/sub2/sub2
330
343
331 Include normal files from within a largefiles subrepo
344 Include normal files from within a largefiles subrepo
332
345
333 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
346 $ hg --config extensions.largefiles= archive -S -I '**.txt' ../archive_lf
334 $ find ../archive_lf | sort
347 $ find ../archive_lf | sort
335 ../archive_lf
348 ../archive_lf
336 ../archive_lf/.hg_archival.txt
349 ../archive_lf/.hg_archival.txt
337 ../archive_lf/sub1
350 ../archive_lf/sub1
338 ../archive_lf/sub1/sub2
351 ../archive_lf/sub1/sub2
339 ../archive_lf/sub1/sub2/folder
352 ../archive_lf/sub1/sub2/folder
340 ../archive_lf/sub1/sub2/folder/test.txt
353 ../archive_lf/sub1/sub2/folder/test.txt
341 ../archive_lf/sub1/sub2/test.txt
354 ../archive_lf/sub1/sub2/test.txt
342 $ rm -rf ../archive_lf
355 $ rm -rf ../archive_lf
343
356
344 Include large files from within a largefiles subrepo
357 Include large files from within a largefiles subrepo
345
358
346 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
359 $ hg --config extensions.largefiles= archive -S -I '**.bin' ../archive_lf
347 $ find ../archive_lf | sort
360 $ find ../archive_lf | sort
348 ../archive_lf
361 ../archive_lf
349 ../archive_lf/large.bin
362 ../archive_lf/large.bin
350 ../archive_lf/sub1
363 ../archive_lf/sub1
351 ../archive_lf/sub1/sub2
364 ../archive_lf/sub1/sub2
352 ../archive_lf/sub1/sub2/large.bin
365 ../archive_lf/sub1/sub2/large.bin
353 $ rm -rf ../archive_lf
366 $ rm -rf ../archive_lf
354
367
355 Find an exact largefile match in a largefiles subrepo
368 Find an exact largefile match in a largefiles subrepo
356
369
357 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
370 $ hg --config extensions.largefiles= archive -S -I 'sub1/sub2/large.bin' ../archive_lf
358 $ find ../archive_lf | sort
371 $ find ../archive_lf | sort
359 ../archive_lf
372 ../archive_lf
360 ../archive_lf/sub1
373 ../archive_lf/sub1
361 ../archive_lf/sub1/sub2
374 ../archive_lf/sub1/sub2
362 ../archive_lf/sub1/sub2/large.bin
375 ../archive_lf/sub1/sub2/large.bin
363 $ rm -rf ../archive_lf
376 $ rm -rf ../archive_lf
364
377
365 The local repo enables largefiles if a largefiles repo is cloned
378 The local repo enables largefiles if a largefiles repo is cloned
366 $ hg showconfig extensions
379 $ hg showconfig extensions
367 abort: repository requires features unknown to this Mercurial: largefiles!
380 abort: repository requires features unknown to this Mercurial: largefiles!
368 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
381 (see http://mercurial.selenic.com/wiki/MissingRequirement for more information)
369 [255]
382 [255]
370 $ hg --config extensions.largefiles= clone -qU . ../lfclone
383 $ hg --config extensions.largefiles= clone -qU . ../lfclone
371 $ cat ../lfclone/.hg/hgrc
384 $ cat ../lfclone/.hg/hgrc
372 # example repository config (see "hg help config" for more info)
385 # example repository config (see "hg help config" for more info)
373 [paths]
386 [paths]
374 default = $TESTTMP/cloned (glob)
387 default = $TESTTMP/cloned (glob)
375
388
376 # path aliases to other clones of this repo in URLs or filesystem paths
389 # path aliases to other clones of this repo in URLs or filesystem paths
377 # (see "hg help config.paths" for more info)
390 # (see "hg help config.paths" for more info)
378 #
391 #
379 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
392 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
380 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
393 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
381 # my-clone = /home/jdoe/jdoes-clone
394 # my-clone = /home/jdoe/jdoes-clone
382
395
383 [ui]
396 [ui]
384 # name and email (local to this repository, optional), e.g.
397 # name and email (local to this repository, optional), e.g.
385 # username = Jane Doe <jdoe@example.com>
398 # username = Jane Doe <jdoe@example.com>
386
399
387 [extensions]
400 [extensions]
388 largefiles=
401 largefiles=
389
402
390 Find an exact match to a standin (should archive nothing)
403 Find an exact match to a standin (should archive nothing)
391 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
404 $ hg --config extensions.largefiles= archive -S -I 'sub/sub2/.hglf/large.bin' ../archive_lf
392 $ find ../archive_lf 2> /dev/null | sort
405 $ find ../archive_lf 2> /dev/null | sort
393
406
394 $ cat >> $HGRCPATH <<EOF
407 $ cat >> $HGRCPATH <<EOF
395 > [extensions]
408 > [extensions]
396 > largefiles=
409 > largefiles=
397 > [largefiles]
410 > [largefiles]
398 > patterns=glob:**.dat
411 > patterns=glob:**.dat
399 > EOF
412 > EOF
400
413
401 Test forget through a deep subrepo with the largefiles extension, both a
414 Test forget through a deep subrepo with the largefiles extension, both a
402 largefile and a normal file. Then a largefile that hasn't been committed yet.
415 largefile and a normal file. Then a largefile that hasn't been committed yet.
403 $ touch sub1/sub2/untracked.txt
416 $ touch sub1/sub2/untracked.txt
404 $ touch sub1/sub2/large.dat
417 $ touch sub1/sub2/large.dat
405 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
418 $ hg forget sub1/sub2/large.bin sub1/sub2/test.txt sub1/sub2/untracked.txt
406 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
419 not removing sub1/sub2/untracked.txt: file is already untracked (glob)
407 [1]
420 [1]
408 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
421 $ hg add --large --dry-run -v sub1/sub2/untracked.txt
409 adding sub1/sub2/untracked.txt as a largefile (glob)
422 adding sub1/sub2/untracked.txt as a largefile (glob)
410 $ hg add --large -v sub1/sub2/untracked.txt
423 $ hg add --large -v sub1/sub2/untracked.txt
411 adding sub1/sub2/untracked.txt as a largefile (glob)
424 adding sub1/sub2/untracked.txt as a largefile (glob)
412 $ hg add --normal -v sub1/sub2/large.dat
425 $ hg add --normal -v sub1/sub2/large.dat
413 adding sub1/sub2/large.dat (glob)
426 adding sub1/sub2/large.dat (glob)
414 $ hg forget -v sub1/sub2/untracked.txt
427 $ hg forget -v sub1/sub2/untracked.txt
415 removing sub1/sub2/untracked.txt (glob)
428 removing sub1/sub2/untracked.txt (glob)
416 $ hg status -S
429 $ hg status -S
417 A sub1/sub2/large.dat
430 A sub1/sub2/large.dat
418 R sub1/sub2/large.bin
431 R sub1/sub2/large.bin
419 R sub1/sub2/test.txt
432 R sub1/sub2/test.txt
420 ? foo/bar/abc
433 ? foo/bar/abc
421 ? sub1/sub2/untracked.txt
434 ? sub1/sub2/untracked.txt
422 ? sub1/sub2/x.txt
435 ? sub1/sub2/x.txt
423 $ hg add sub1/sub2
436 $ hg add sub1/sub2
424 $ hg ci -Sqm 'forget testing'
437 $ hg ci -Sqm 'forget testing'
425
438
426 Test issue4330: commit a directory where only normal files have changed
439 Test issue4330: commit a directory where only normal files have changed
427 $ touch foo/bar/large.dat
440 $ touch foo/bar/large.dat
428 $ hg add --large foo/bar/large.dat
441 $ hg add --large foo/bar/large.dat
429 $ hg ci -m 'add foo/bar/large.dat'
442 $ hg ci -m 'add foo/bar/large.dat'
430 $ touch a.txt
443 $ touch a.txt
431 $ touch a.dat
444 $ touch a.dat
432 $ hg add -v foo/bar/abc a.txt a.dat
445 $ hg add -v foo/bar/abc a.txt a.dat
433 adding a.dat as a largefile
446 adding a.dat as a largefile
434 adding a.txt
447 adding a.txt
435 adding foo/bar/abc (glob)
448 adding foo/bar/abc (glob)
436 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
449 $ hg ci -m 'dir commit with only normal file deltas' foo/bar
437 $ hg status
450 $ hg status
438 A a.dat
451 A a.dat
439 A a.txt
452 A a.txt
440
453
441 Test a directory commit with a changed largefile and a changed normal file
454 Test a directory commit with a changed largefile and a changed normal file
442 $ echo changed > foo/bar/large.dat
455 $ echo changed > foo/bar/large.dat
443 $ echo changed > foo/bar/abc
456 $ echo changed > foo/bar/abc
444 $ hg ci -m 'dir commit with normal and lf file deltas' foo
457 $ hg ci -m 'dir commit with normal and lf file deltas' foo
445 $ hg status
458 $ hg status
446 A a.dat
459 A a.dat
447 A a.txt
460 A a.txt
448
461
449 $ hg ci -m "add a.*"
462 $ hg ci -m "add a.*"
450 $ hg mv a.dat b.dat
463 $ hg mv a.dat b.dat
451 $ hg mv foo/bar/abc foo/bar/def
464 $ hg mv foo/bar/abc foo/bar/def
452 $ hg status -C
465 $ hg status -C
453 A b.dat
466 A b.dat
454 a.dat
467 a.dat
455 A foo/bar/def
468 A foo/bar/def
456 foo/bar/abc
469 foo/bar/abc
457 R a.dat
470 R a.dat
458 R foo/bar/abc
471 R foo/bar/abc
459
472
460 $ hg ci -m "move large and normal"
473 $ hg ci -m "move large and normal"
461 $ hg status -C --rev '.^' --rev .
474 $ hg status -C --rev '.^' --rev .
462 A b.dat
475 A b.dat
463 a.dat
476 a.dat
464 A foo/bar/def
477 A foo/bar/def
465 foo/bar/abc
478 foo/bar/abc
466 R a.dat
479 R a.dat
467 R foo/bar/abc
480 R foo/bar/abc
468
481
469
482
470 $ echo foo > main
483 $ echo foo > main
471 $ hg ci -m "mod parent only"
484 $ hg ci -m "mod parent only"
472 $ hg init sub3
485 $ hg init sub3
473 $ echo "sub3 = sub3" >> .hgsub
486 $ echo "sub3 = sub3" >> .hgsub
474 $ echo xyz > sub3/a.txt
487 $ echo xyz > sub3/a.txt
475 $ hg add sub3/a.txt
488 $ hg add sub3/a.txt
476 $ hg ci -Sm "add sub3"
489 $ hg ci -Sm "add sub3"
477 committing subrepository sub3
490 committing subrepository sub3
478 $ cat .hgsub | grep -v sub3 > .hgsub1
491 $ cat .hgsub | grep -v sub3 > .hgsub1
479 $ mv .hgsub1 .hgsub
492 $ mv .hgsub1 .hgsub
480 $ hg ci -m "remove sub3"
493 $ hg ci -m "remove sub3"
481
494
482 $ hg log -r "subrepo()" --style compact
495 $ hg log -r "subrepo()" --style compact
483 0 7f491f53a367 1970-01-01 00:00 +0000 test
496 0 7f491f53a367 1970-01-01 00:00 +0000 test
484 main import
497 main import
485
498
486 1 ffe6649062fe 1970-01-01 00:00 +0000 test
499 1 ffe6649062fe 1970-01-01 00:00 +0000 test
487 deep nested modif should trigger a commit
500 deep nested modif should trigger a commit
488
501
489 2 9bb10eebee29 1970-01-01 00:00 +0000 test
502 2 9bb10eebee29 1970-01-01 00:00 +0000 test
490 add test.txt
503 add test.txt
491
504
492 3 7c64f035294f 1970-01-01 00:00 +0000 test
505 3 7c64f035294f 1970-01-01 00:00 +0000 test
493 add large files
506 add large files
494
507
495 4 f734a59e2e35 1970-01-01 00:00 +0000 test
508 4 f734a59e2e35 1970-01-01 00:00 +0000 test
496 forget testing
509 forget testing
497
510
498 11 9685a22af5db 1970-01-01 00:00 +0000 test
511 11 9685a22af5db 1970-01-01 00:00 +0000 test
499 add sub3
512 add sub3
500
513
501 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
514 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
502 remove sub3
515 remove sub3
503
516
504 $ hg log -r "subrepo('sub3')" --style compact
517 $ hg log -r "subrepo('sub3')" --style compact
505 11 9685a22af5db 1970-01-01 00:00 +0000 test
518 11 9685a22af5db 1970-01-01 00:00 +0000 test
506 add sub3
519 add sub3
507
520
508 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
521 12[tip] 2e0485b475b9 1970-01-01 00:00 +0000 test
509 remove sub3
522 remove sub3
510
523
511 $ hg log -r "subrepo('bogus')" --style compact
524 $ hg log -r "subrepo('bogus')" --style compact
512
525
513
526
514 Test .hgsubstate in the R state
527 Test .hgsubstate in the R state
515
528
516 $ hg rm .hgsub .hgsubstate
529 $ hg rm .hgsub .hgsubstate
517 $ hg ci -m 'trash subrepo tracking'
530 $ hg ci -m 'trash subrepo tracking'
518
531
519 $ hg log -r "subrepo('re:sub\d+')" --style compact
532 $ hg log -r "subrepo('re:sub\d+')" --style compact
520 0 7f491f53a367 1970-01-01 00:00 +0000 test
533 0 7f491f53a367 1970-01-01 00:00 +0000 test
521 main import
534 main import
522
535
523 1 ffe6649062fe 1970-01-01 00:00 +0000 test
536 1 ffe6649062fe 1970-01-01 00:00 +0000 test
524 deep nested modif should trigger a commit
537 deep nested modif should trigger a commit
525
538
526 2 9bb10eebee29 1970-01-01 00:00 +0000 test
539 2 9bb10eebee29 1970-01-01 00:00 +0000 test
527 add test.txt
540 add test.txt
528
541
529 3 7c64f035294f 1970-01-01 00:00 +0000 test
542 3 7c64f035294f 1970-01-01 00:00 +0000 test
530 add large files
543 add large files
531
544
532 4 f734a59e2e35 1970-01-01 00:00 +0000 test
545 4 f734a59e2e35 1970-01-01 00:00 +0000 test
533 forget testing
546 forget testing
534
547
535 11 9685a22af5db 1970-01-01 00:00 +0000 test
548 11 9685a22af5db 1970-01-01 00:00 +0000 test
536 add sub3
549 add sub3
537
550
538 12 2e0485b475b9 1970-01-01 00:00 +0000 test
551 12 2e0485b475b9 1970-01-01 00:00 +0000 test
539 remove sub3
552 remove sub3
540
553
541 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
554 13[tip] a68b2c361653 1970-01-01 00:00 +0000 test
542 trash subrepo tracking
555 trash subrepo tracking
543
556
544
557
545 Restore the trashed subrepo tracking
558 Restore the trashed subrepo tracking
546
559
547 $ hg rollback -q
560 $ hg rollback -q
548 $ hg update -Cq .
561 $ hg update -Cq .
549
562
550 $ cd ..
563 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now