##// END OF EJS Templates
merge with stable
Matt Mackall -
r26014:a5f62af2 merge default
parent child Browse files
Show More
@@ -1,680 +1,687
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import copy
10 import copy
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 pathutil,
16 pathutil,
17 util,
17 util,
18 )
18 )
19
19
20 propertycache = util.propertycache
20 propertycache = util.propertycache
21
21
22 def _rematcher(regex):
22 def _rematcher(regex):
23 '''compile the regexp with the best available regexp engine and return a
23 '''compile the regexp with the best available regexp engine and return a
24 matcher function'''
24 matcher function'''
25 m = util.re.compile(regex)
25 m = util.re.compile(regex)
26 try:
26 try:
27 # slightly faster, provided by facebook's re2 bindings
27 # slightly faster, provided by facebook's re2 bindings
28 return m.test_match
28 return m.test_match
29 except AttributeError:
29 except AttributeError:
30 return m.match
30 return m.match
31
31
32 def _expandsets(kindpats, ctx, listsubrepos):
32 def _expandsets(kindpats, ctx, listsubrepos):
33 '''Returns the kindpats list with the 'set' patterns expanded.'''
33 '''Returns the kindpats list with the 'set' patterns expanded.'''
34 fset = set()
34 fset = set()
35 other = []
35 other = []
36
36
37 for kind, pat, source in kindpats:
37 for kind, pat, source in kindpats:
38 if kind == 'set':
38 if kind == 'set':
39 if not ctx:
39 if not ctx:
40 raise util.Abort("fileset expression with no context")
40 raise util.Abort("fileset expression with no context")
41 s = ctx.getfileset(pat)
41 s = ctx.getfileset(pat)
42 fset.update(s)
42 fset.update(s)
43
43
44 if listsubrepos:
44 if listsubrepos:
45 for subpath in ctx.substate:
45 for subpath in ctx.substate:
46 s = ctx.sub(subpath).getfileset(pat)
46 s = ctx.sub(subpath).getfileset(pat)
47 fset.update(subpath + '/' + f for f in s)
47 fset.update(subpath + '/' + f for f in s)
48
48
49 continue
49 continue
50 other.append((kind, pat, source))
50 other.append((kind, pat, source))
51 return fset, other
51 return fset, other
52
52
53 def _expandsubinclude(kindpats, root):
53 def _expandsubinclude(kindpats, root):
54 '''Returns the list of subinclude matchers and the kindpats without the
54 '''Returns the list of subinclude matchers and the kindpats without the
55 subincludes in it.'''
55 subincludes in it.'''
56 relmatchers = []
56 relmatchers = []
57 other = []
57 other = []
58
58
59 for kind, pat, source in kindpats:
59 for kind, pat, source in kindpats:
60 if kind == 'subinclude':
60 if kind == 'subinclude':
61 sourceroot = pathutil.dirname(util.normpath(source))
61 sourceroot = pathutil.dirname(util.normpath(source))
62 pat = util.pconvert(pat)
62 pat = util.pconvert(pat)
63 path = pathutil.join(sourceroot, pat)
63 path = pathutil.join(sourceroot, pat)
64
64
65 newroot = pathutil.dirname(path)
65 newroot = pathutil.dirname(path)
66 relmatcher = match(newroot, '', [], ['include:%s' % path])
66 relmatcher = match(newroot, '', [], ['include:%s' % path])
67
67
68 prefix = pathutil.canonpath(root, root, newroot)
68 prefix = pathutil.canonpath(root, root, newroot)
69 if prefix:
69 if prefix:
70 prefix += '/'
70 prefix += '/'
71 relmatchers.append((prefix, relmatcher))
71 relmatchers.append((prefix, relmatcher))
72 else:
72 else:
73 other.append((kind, pat, source))
73 other.append((kind, pat, source))
74
74
75 return relmatchers, other
75 return relmatchers, other
76
76
77 def _kindpatsalwaysmatch(kindpats):
77 def _kindpatsalwaysmatch(kindpats):
78 """"Checks whether the kindspats match everything, as e.g.
78 """"Checks whether the kindspats match everything, as e.g.
79 'relpath:.' does.
79 'relpath:.' does.
80 """
80 """
81 for kind, pat, source in kindpats:
81 for kind, pat, source in kindpats:
82 if pat != '' or kind not in ['relpath', 'glob']:
82 if pat != '' or kind not in ['relpath', 'glob']:
83 return False
83 return False
84 return True
84 return True
85
85
86 class match(object):
86 class match(object):
87 def __init__(self, root, cwd, patterns, include=[], exclude=[],
87 def __init__(self, root, cwd, patterns, include=[], exclude=[],
88 default='glob', exact=False, auditor=None, ctx=None,
88 default='glob', exact=False, auditor=None, ctx=None,
89 listsubrepos=False, warn=None, badfn=None):
89 listsubrepos=False, warn=None, badfn=None):
90 """build an object to match a set of file patterns
90 """build an object to match a set of file patterns
91
91
92 arguments:
92 arguments:
93 root - the canonical root of the tree you're matching against
93 root - the canonical root of the tree you're matching against
94 cwd - the current working directory, if relevant
94 cwd - the current working directory, if relevant
95 patterns - patterns to find
95 patterns - patterns to find
96 include - patterns to include (unless they are excluded)
96 include - patterns to include (unless they are excluded)
97 exclude - patterns to exclude (even if they are included)
97 exclude - patterns to exclude (even if they are included)
98 default - if a pattern in patterns has no explicit type, assume this one
98 default - if a pattern in patterns has no explicit type, assume this one
99 exact - patterns are actually filenames (include/exclude still apply)
99 exact - patterns are actually filenames (include/exclude still apply)
100 warn - optional function used for printing warnings
100 warn - optional function used for printing warnings
101 badfn - optional bad() callback for this matcher instead of the default
101 badfn - optional bad() callback for this matcher instead of the default
102
102
103 a pattern is one of:
103 a pattern is one of:
104 'glob:<glob>' - a glob relative to cwd
104 'glob:<glob>' - a glob relative to cwd
105 're:<regexp>' - a regular expression
105 're:<regexp>' - a regular expression
106 'path:<path>' - a path relative to repository root
106 'path:<path>' - a path relative to repository root
107 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
107 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
108 'relpath:<path>' - a path relative to cwd
108 'relpath:<path>' - a path relative to cwd
109 'relre:<regexp>' - a regexp that needn't match the start of a name
109 'relre:<regexp>' - a regexp that needn't match the start of a name
110 'set:<fileset>' - a fileset expression
110 'set:<fileset>' - a fileset expression
111 'include:<path>' - a file of patterns to read and include
111 'include:<path>' - a file of patterns to read and include
112 'subinclude:<path>' - a file of patterns to match against files under
112 'subinclude:<path>' - a file of patterns to match against files under
113 the same directory
113 the same directory
114 '<something>' - a pattern of the specified default type
114 '<something>' - a pattern of the specified default type
115 """
115 """
116
116
117 self._root = root
117 self._root = root
118 self._cwd = cwd
118 self._cwd = cwd
119 self._files = [] # exact files and roots of patterns
119 self._files = [] # exact files and roots of patterns
120 self._anypats = bool(include or exclude)
120 self._anypats = bool(include or exclude)
121 self._always = False
121 self._always = False
122 self._pathrestricted = bool(include or exclude or patterns)
122 self._pathrestricted = bool(include or exclude or patterns)
123 self._warn = warn
123 self._warn = warn
124 self._includeroots = set()
124 self._includeroots = set()
125 self._includedirs = set(['.'])
125 self._includedirs = set(['.'])
126 self._excluderoots = set()
126 self._excluderoots = set()
127
127
128 if badfn is not None:
128 if badfn is not None:
129 self.bad = badfn
129 self.bad = badfn
130
130
131 matchfns = []
131 matchfns = []
132 if include:
132 if include:
133 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
133 kindpats = self._normalize(include, 'glob', root, cwd, auditor)
134 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
134 self.includepat, im = _buildmatch(ctx, kindpats, '(?:/|$)',
135 listsubrepos, root)
135 listsubrepos, root)
136 self._includeroots.update(_roots(kindpats))
136 self._includeroots.update(_roots(kindpats))
137 self._includedirs.update(util.dirs(self._includeroots))
137 self._includedirs.update(util.dirs(self._includeroots))
138 matchfns.append(im)
138 matchfns.append(im)
139 if exclude:
139 if exclude:
140 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
140 kindpats = self._normalize(exclude, 'glob', root, cwd, auditor)
141 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
141 self.excludepat, em = _buildmatch(ctx, kindpats, '(?:/|$)',
142 listsubrepos, root)
142 listsubrepos, root)
143 if not _anypats(kindpats):
143 if not _anypats(kindpats):
144 self._excluderoots.update(_roots(kindpats))
144 self._excluderoots.update(_roots(kindpats))
145 matchfns.append(lambda f: not em(f))
145 matchfns.append(lambda f: not em(f))
146 if exact:
146 if exact:
147 if isinstance(patterns, list):
147 if isinstance(patterns, list):
148 self._files = patterns
148 self._files = patterns
149 else:
149 else:
150 self._files = list(patterns)
150 self._files = list(patterns)
151 matchfns.append(self.exact)
151 matchfns.append(self.exact)
152 elif patterns:
152 elif patterns:
153 kindpats = self._normalize(patterns, default, root, cwd, auditor)
153 kindpats = self._normalize(patterns, default, root, cwd, auditor)
154 if not _kindpatsalwaysmatch(kindpats):
154 if not _kindpatsalwaysmatch(kindpats):
155 self._files = _roots(kindpats)
155 self._files = _roots(kindpats)
156 self._anypats = self._anypats or _anypats(kindpats)
156 self._anypats = self._anypats or _anypats(kindpats)
157 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
157 self.patternspat, pm = _buildmatch(ctx, kindpats, '$',
158 listsubrepos, root)
158 listsubrepos, root)
159 matchfns.append(pm)
159 matchfns.append(pm)
160
160
161 if not matchfns:
161 if not matchfns:
162 m = util.always
162 m = util.always
163 self._always = True
163 self._always = True
164 elif len(matchfns) == 1:
164 elif len(matchfns) == 1:
165 m = matchfns[0]
165 m = matchfns[0]
166 else:
166 else:
167 def m(f):
167 def m(f):
168 for matchfn in matchfns:
168 for matchfn in matchfns:
169 if not matchfn(f):
169 if not matchfn(f):
170 return False
170 return False
171 return True
171 return True
172
172
173 self.matchfn = m
173 self.matchfn = m
174 self._fileroots = set(self._files)
174 self._fileroots = set(self._files)
175
175
176 def __call__(self, fn):
176 def __call__(self, fn):
177 return self.matchfn(fn)
177 return self.matchfn(fn)
178 def __iter__(self):
178 def __iter__(self):
179 for f in self._files:
179 for f in self._files:
180 yield f
180 yield f
181
181
182 # Callbacks related to how the matcher is used by dirstate.walk.
182 # Callbacks related to how the matcher is used by dirstate.walk.
183 # Subscribers to these events must monkeypatch the matcher object.
183 # Subscribers to these events must monkeypatch the matcher object.
184 def bad(self, f, msg):
184 def bad(self, f, msg):
185 '''Callback from dirstate.walk for each explicit file that can't be
185 '''Callback from dirstate.walk for each explicit file that can't be
186 found/accessed, with an error message.'''
186 found/accessed, with an error message.'''
187 pass
187 pass
188
188
189 # If an explicitdir is set, it will be called when an explicitly listed
189 # If an explicitdir is set, it will be called when an explicitly listed
190 # directory is visited.
190 # directory is visited.
191 explicitdir = None
191 explicitdir = None
192
192
193 # If an traversedir is set, it will be called when a directory discovered
193 # If an traversedir is set, it will be called when a directory discovered
194 # by recursive traversal is visited.
194 # by recursive traversal is visited.
195 traversedir = None
195 traversedir = None
196
196
197 def abs(self, f):
197 def abs(self, f):
198 '''Convert a repo path back to path that is relative to the root of the
198 '''Convert a repo path back to path that is relative to the root of the
199 matcher.'''
199 matcher.'''
200 return f
200 return f
201
201
202 def rel(self, f):
202 def rel(self, f):
203 '''Convert repo path back to path that is relative to cwd of matcher.'''
203 '''Convert repo path back to path that is relative to cwd of matcher.'''
204 return util.pathto(self._root, self._cwd, f)
204 return util.pathto(self._root, self._cwd, f)
205
205
206 def uipath(self, f):
206 def uipath(self, f):
207 '''Convert repo path to a display path. If patterns or -I/-X were used
207 '''Convert repo path to a display path. If patterns or -I/-X were used
208 to create this matcher, the display path will be relative to cwd.
208 to create this matcher, the display path will be relative to cwd.
209 Otherwise it is relative to the root of the repo.'''
209 Otherwise it is relative to the root of the repo.'''
210 return (self._pathrestricted and self.rel(f)) or self.abs(f)
210 return (self._pathrestricted and self.rel(f)) or self.abs(f)
211
211
212 def files(self):
212 def files(self):
213 '''Explicitly listed files or patterns or roots:
213 '''Explicitly listed files or patterns or roots:
214 if no patterns or .always(): empty list,
214 if no patterns or .always(): empty list,
215 if exact: list exact files,
215 if exact: list exact files,
216 if not .anypats(): list all files and dirs,
216 if not .anypats(): list all files and dirs,
217 else: optimal roots'''
217 else: optimal roots'''
218 return self._files
218 return self._files
219
219
220 @propertycache
220 @propertycache
221 def _dirs(self):
221 def _dirs(self):
222 return set(util.dirs(self._fileroots)) | set(['.'])
222 return set(util.dirs(self._fileroots)) | set(['.'])
223
223
224 def visitdir(self, dir):
224 def visitdir(self, dir):
225 '''Decides whether a directory should be visited based on whether it
225 '''Decides whether a directory should be visited based on whether it
226 has potential matches in it or one of its subdirectories. This is
226 has potential matches in it or one of its subdirectories. This is
227 based on the match's primary, included, and excluded patterns.
227 based on the match's primary, included, and excluded patterns.
228
228
229 This function's behavior is undefined if it has returned False for
229 This function's behavior is undefined if it has returned False for
230 one of the dir's parent directories.
230 one of the dir's parent directories.
231 '''
231 '''
232 if dir in self._excluderoots:
232 if dir in self._excluderoots:
233 return False
233 return False
234 if (self._includeroots and
234 if (self._includeroots and
235 '.' not in self._includeroots and
235 '.' not in self._includeroots and
236 dir not in self._includeroots and
236 dir not in self._includeroots and
237 dir not in self._includedirs and
237 dir not in self._includedirs and
238 not any(parent in self._includeroots
238 not any(parent in self._includeroots
239 for parent in util.finddirs(dir))):
239 for parent in util.finddirs(dir))):
240 return False
240 return False
241 return (not self._fileroots or
241 return (not self._fileroots or
242 '.' in self._fileroots or
242 '.' in self._fileroots or
243 dir in self._fileroots or
243 dir in self._fileroots or
244 dir in self._dirs or
244 dir in self._dirs or
245 any(parentdir in self._fileroots
245 any(parentdir in self._fileroots
246 for parentdir in util.finddirs(dir)))
246 for parentdir in util.finddirs(dir)))
247
247
248 def exact(self, f):
248 def exact(self, f):
249 '''Returns True if f is in .files().'''
249 '''Returns True if f is in .files().'''
250 return f in self._fileroots
250 return f in self._fileroots
251
251
252 def anypats(self):
252 def anypats(self):
253 '''Matcher uses patterns or include/exclude.'''
253 '''Matcher uses patterns or include/exclude.'''
254 return self._anypats
254 return self._anypats
255
255
256 def always(self):
256 def always(self):
257 '''Matcher will match everything and .files() will be empty
257 '''Matcher will match everything and .files() will be empty
258 - optimization might be possible and necessary.'''
258 - optimization might be possible and necessary.'''
259 return self._always
259 return self._always
260
260
261 def ispartial(self):
261 def ispartial(self):
262 '''True if the matcher won't always match.
262 '''True if the matcher won't always match.
263
263
264 Although it's just the inverse of _always in this implementation,
264 Although it's just the inverse of _always in this implementation,
265 an extenion such as narrowhg might make it return something
265 an extenion such as narrowhg might make it return something
266 slightly different.'''
266 slightly different.'''
267 return not self._always
267 return not self._always
268
268
269 def isexact(self):
269 def isexact(self):
270 return self.matchfn == self.exact
270 return self.matchfn == self.exact
271
271
272 def prefix(self):
272 def prefix(self):
273 return not self.always() and not self.isexact() and not self.anypats()
273 return not self.always() and not self.isexact() and not self.anypats()
274
274
275 def _normalize(self, patterns, default, root, cwd, auditor):
275 def _normalize(self, patterns, default, root, cwd, auditor):
276 '''Convert 'kind:pat' from the patterns list to tuples with kind and
276 '''Convert 'kind:pat' from the patterns list to tuples with kind and
277 normalized and rooted patterns and with listfiles expanded.'''
277 normalized and rooted patterns and with listfiles expanded.'''
278 kindpats = []
278 kindpats = []
279 for kind, pat in [_patsplit(p, default) for p in patterns]:
279 for kind, pat in [_patsplit(p, default) for p in patterns]:
280 if kind in ('glob', 'relpath'):
280 if kind in ('glob', 'relpath'):
281 pat = pathutil.canonpath(root, cwd, pat, auditor)
281 pat = pathutil.canonpath(root, cwd, pat, auditor)
282 elif kind in ('relglob', 'path'):
282 elif kind in ('relglob', 'path'):
283 pat = util.normpath(pat)
283 pat = util.normpath(pat)
284 elif kind in ('listfile', 'listfile0'):
284 elif kind in ('listfile', 'listfile0'):
285 try:
285 try:
286 files = util.readfile(pat)
286 files = util.readfile(pat)
287 if kind == 'listfile0':
287 if kind == 'listfile0':
288 files = files.split('\0')
288 files = files.split('\0')
289 else:
289 else:
290 files = files.splitlines()
290 files = files.splitlines()
291 files = [f for f in files if f]
291 files = [f for f in files if f]
292 except EnvironmentError:
292 except EnvironmentError:
293 raise util.Abort(_("unable to read file list (%s)") % pat)
293 raise util.Abort(_("unable to read file list (%s)") % pat)
294 for k, p, source in self._normalize(files, default, root, cwd,
294 for k, p, source in self._normalize(files, default, root, cwd,
295 auditor):
295 auditor):
296 kindpats.append((k, p, pat))
296 kindpats.append((k, p, pat))
297 continue
297 continue
298 elif kind == 'include':
298 elif kind == 'include':
299 try:
299 try:
300 fullpath = os.path.join(root, util.localpath(pat))
300 fullpath = os.path.join(root, util.localpath(pat))
301 includepats = readpatternfile(fullpath, self._warn)
301 includepats = readpatternfile(fullpath, self._warn)
302 for k, p, source in self._normalize(includepats, default,
302 for k, p, source in self._normalize(includepats, default,
303 root, cwd, auditor):
303 root, cwd, auditor):
304 kindpats.append((k, p, source or pat))
304 kindpats.append((k, p, source or pat))
305 except util.Abort as inst:
305 except util.Abort as inst:
306 raise util.Abort('%s: %s' % (pat, inst[0]))
306 raise util.Abort('%s: %s' % (pat, inst[0]))
307 except IOError as inst:
307 except IOError as inst:
308 if self._warn:
308 if self._warn:
309 self._warn(_("skipping unreadable pattern file "
309 self._warn(_("skipping unreadable pattern file "
310 "'%s': %s\n") % (pat, inst.strerror))
310 "'%s': %s\n") % (pat, inst.strerror))
311 continue
311 continue
312 # else: re or relre - which cannot be normalized
312 # else: re or relre - which cannot be normalized
313 kindpats.append((kind, pat, ''))
313 kindpats.append((kind, pat, ''))
314 return kindpats
314 return kindpats
315
315
316 def exact(root, cwd, files, badfn=None):
316 def exact(root, cwd, files, badfn=None):
317 return match(root, cwd, files, exact=True, badfn=badfn)
317 return match(root, cwd, files, exact=True, badfn=badfn)
318
318
319 def always(root, cwd):
319 def always(root, cwd):
320 return match(root, cwd, [])
320 return match(root, cwd, [])
321
321
322 def badmatch(match, badfn):
322 def badmatch(match, badfn):
323 """Make a copy of the given matcher, replacing its bad method with the given
323 """Make a copy of the given matcher, replacing its bad method with the given
324 one.
324 one.
325 """
325 """
326 m = copy.copy(match)
326 m = copy.copy(match)
327 m.bad = badfn
327 m.bad = badfn
328 return m
328 return m
329
329
330 class narrowmatcher(match):
330 class narrowmatcher(match):
331 """Adapt a matcher to work on a subdirectory only.
331 """Adapt a matcher to work on a subdirectory only.
332
332
333 The paths are remapped to remove/insert the path as needed:
333 The paths are remapped to remove/insert the path as needed:
334
334
335 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
335 >>> m1 = match('root', '', ['a.txt', 'sub/b.txt'])
336 >>> m2 = narrowmatcher('sub', m1)
336 >>> m2 = narrowmatcher('sub', m1)
337 >>> bool(m2('a.txt'))
337 >>> bool(m2('a.txt'))
338 False
338 False
339 >>> bool(m2('b.txt'))
339 >>> bool(m2('b.txt'))
340 True
340 True
341 >>> bool(m2.matchfn('a.txt'))
341 >>> bool(m2.matchfn('a.txt'))
342 False
342 False
343 >>> bool(m2.matchfn('b.txt'))
343 >>> bool(m2.matchfn('b.txt'))
344 True
344 True
345 >>> m2.files()
345 >>> m2.files()
346 ['b.txt']
346 ['b.txt']
347 >>> m2.exact('b.txt')
347 >>> m2.exact('b.txt')
348 True
348 True
349 >>> util.pconvert(m2.rel('b.txt'))
349 >>> util.pconvert(m2.rel('b.txt'))
350 'sub/b.txt'
350 'sub/b.txt'
351 >>> def bad(f, msg):
351 >>> def bad(f, msg):
352 ... print "%s: %s" % (f, msg)
352 ... print "%s: %s" % (f, msg)
353 >>> m1.bad = bad
353 >>> m1.bad = bad
354 >>> m2.bad('x.txt', 'No such file')
354 >>> m2.bad('x.txt', 'No such file')
355 sub/x.txt: No such file
355 sub/x.txt: No such file
356 >>> m2.abs('c.txt')
356 >>> m2.abs('c.txt')
357 'sub/c.txt'
357 'sub/c.txt'
358 """
358 """
359
359
360 def __init__(self, path, matcher):
360 def __init__(self, path, matcher):
361 self._root = matcher._root
361 self._root = matcher._root
362 self._cwd = matcher._cwd
362 self._cwd = matcher._cwd
363 self._path = path
363 self._path = path
364 self._matcher = matcher
364 self._matcher = matcher
365 self._always = matcher._always
365 self._always = matcher._always
366 self._pathrestricted = matcher._pathrestricted
366 self._pathrestricted = matcher._pathrestricted
367
367
368 self._files = [f[len(path) + 1:] for f in matcher._files
368 self._files = [f[len(path) + 1:] for f in matcher._files
369 if f.startswith(path + "/")]
369 if f.startswith(path + "/")]
370
370
371 # If the parent repo had a path to this subrepo and no patterns are
371 # If the parent repo had a path to this subrepo and no patterns are
372 # specified, this submatcher always matches.
372 # specified, this submatcher always matches.
373 if not self._always and not matcher._anypats:
373 if not self._always and not matcher._anypats:
374 self._always = any(f == path for f in matcher._files)
374 self._always = any(f == path for f in matcher._files)
375
375
376 self._anypats = matcher._anypats
376 self._anypats = matcher._anypats
377 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
377 self.matchfn = lambda fn: matcher.matchfn(self._path + "/" + fn)
378 self._fileroots = set(self._files)
378 self._fileroots = set(self._files)
379
379
380 def abs(self, f):
380 def abs(self, f):
381 return self._matcher.abs(self._path + "/" + f)
381 return self._matcher.abs(self._path + "/" + f)
382
382
383 def bad(self, f, msg):
383 def bad(self, f, msg):
384 self._matcher.bad(self._path + "/" + f, msg)
384 self._matcher.bad(self._path + "/" + f, msg)
385
385
386 def rel(self, f):
386 def rel(self, f):
387 return self._matcher.rel(self._path + "/" + f)
387 return self._matcher.rel(self._path + "/" + f)
388
388
389 class icasefsmatcher(match):
389 class icasefsmatcher(match):
390 """A matcher for wdir on case insensitive filesystems, which normalizes the
390 """A matcher for wdir on case insensitive filesystems, which normalizes the
391 given patterns to the case in the filesystem.
391 given patterns to the case in the filesystem.
392 """
392 """
393
393
394 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
394 def __init__(self, root, cwd, patterns, include, exclude, default, auditor,
395 ctx, listsubrepos=False, badfn=None):
395 ctx, listsubrepos=False, badfn=None):
396 init = super(icasefsmatcher, self).__init__
396 init = super(icasefsmatcher, self).__init__
397 self._dsnormalize = ctx.repo().dirstate.normalize
397 self._dirstate = ctx.repo().dirstate
398 self._dsnormalize = self._dirstate.normalize
398
399
399 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
400 init(root, cwd, patterns, include, exclude, default, auditor=auditor,
400 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
401 ctx=ctx, listsubrepos=listsubrepos, badfn=badfn)
401
402
402 # m.exact(file) must be based off of the actual user input, otherwise
403 # m.exact(file) must be based off of the actual user input, otherwise
403 # inexact case matches are treated as exact, and not noted without -v.
404 # inexact case matches are treated as exact, and not noted without -v.
404 if self._files:
405 if self._files:
405 self._fileroots = set(_roots(self._kp))
406 self._fileroots = set(_roots(self._kp))
406
407
407 def _normalize(self, patterns, default, root, cwd, auditor):
408 def _normalize(self, patterns, default, root, cwd, auditor):
408 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
409 self._kp = super(icasefsmatcher, self)._normalize(patterns, default,
409 root, cwd, auditor)
410 root, cwd, auditor)
410 kindpats = []
411 kindpats = []
411 for kind, pats, source in self._kp:
412 for kind, pats, source in self._kp:
412 if kind not in ('re', 'relre'): # regex can't be normalized
413 if kind not in ('re', 'relre'): # regex can't be normalized
414 p = pats
413 pats = self._dsnormalize(pats)
415 pats = self._dsnormalize(pats)
416
417 # Preserve the original to handle a case only rename.
418 if p != pats and p in self._dirstate:
419 kindpats.append((kind, p, source))
420
414 kindpats.append((kind, pats, source))
421 kindpats.append((kind, pats, source))
415 return kindpats
422 return kindpats
416
423
417 def patkind(pattern, default=None):
424 def patkind(pattern, default=None):
418 '''If pattern is 'kind:pat' with a known kind, return kind.'''
425 '''If pattern is 'kind:pat' with a known kind, return kind.'''
419 return _patsplit(pattern, default)[0]
426 return _patsplit(pattern, default)[0]
420
427
421 def _patsplit(pattern, default):
428 def _patsplit(pattern, default):
422 """Split a string into the optional pattern kind prefix and the actual
429 """Split a string into the optional pattern kind prefix and the actual
423 pattern."""
430 pattern."""
424 if ':' in pattern:
431 if ':' in pattern:
425 kind, pat = pattern.split(':', 1)
432 kind, pat = pattern.split(':', 1)
426 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
433 if kind in ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
427 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
434 'listfile', 'listfile0', 'set', 'include', 'subinclude'):
428 return kind, pat
435 return kind, pat
429 return default, pattern
436 return default, pattern
430
437
431 def _globre(pat):
438 def _globre(pat):
432 r'''Convert an extended glob string to a regexp string.
439 r'''Convert an extended glob string to a regexp string.
433
440
434 >>> print _globre(r'?')
441 >>> print _globre(r'?')
435 .
442 .
436 >>> print _globre(r'*')
443 >>> print _globre(r'*')
437 [^/]*
444 [^/]*
438 >>> print _globre(r'**')
445 >>> print _globre(r'**')
439 .*
446 .*
440 >>> print _globre(r'**/a')
447 >>> print _globre(r'**/a')
441 (?:.*/)?a
448 (?:.*/)?a
442 >>> print _globre(r'a/**/b')
449 >>> print _globre(r'a/**/b')
443 a\/(?:.*/)?b
450 a\/(?:.*/)?b
444 >>> print _globre(r'[a*?!^][^b][!c]')
451 >>> print _globre(r'[a*?!^][^b][!c]')
445 [a*?!^][\^b][^c]
452 [a*?!^][\^b][^c]
446 >>> print _globre(r'{a,b}')
453 >>> print _globre(r'{a,b}')
447 (?:a|b)
454 (?:a|b)
448 >>> print _globre(r'.\*\?')
455 >>> print _globre(r'.\*\?')
449 \.\*\?
456 \.\*\?
450 '''
457 '''
451 i, n = 0, len(pat)
458 i, n = 0, len(pat)
452 res = ''
459 res = ''
453 group = 0
460 group = 0
454 escape = util.re.escape
461 escape = util.re.escape
455 def peek():
462 def peek():
456 return i < n and pat[i]
463 return i < n and pat[i]
457 while i < n:
464 while i < n:
458 c = pat[i]
465 c = pat[i]
459 i += 1
466 i += 1
460 if c not in '*?[{},\\':
467 if c not in '*?[{},\\':
461 res += escape(c)
468 res += escape(c)
462 elif c == '*':
469 elif c == '*':
463 if peek() == '*':
470 if peek() == '*':
464 i += 1
471 i += 1
465 if peek() == '/':
472 if peek() == '/':
466 i += 1
473 i += 1
467 res += '(?:.*/)?'
474 res += '(?:.*/)?'
468 else:
475 else:
469 res += '.*'
476 res += '.*'
470 else:
477 else:
471 res += '[^/]*'
478 res += '[^/]*'
472 elif c == '?':
479 elif c == '?':
473 res += '.'
480 res += '.'
474 elif c == '[':
481 elif c == '[':
475 j = i
482 j = i
476 if j < n and pat[j] in '!]':
483 if j < n and pat[j] in '!]':
477 j += 1
484 j += 1
478 while j < n and pat[j] != ']':
485 while j < n and pat[j] != ']':
479 j += 1
486 j += 1
480 if j >= n:
487 if j >= n:
481 res += '\\['
488 res += '\\['
482 else:
489 else:
483 stuff = pat[i:j].replace('\\','\\\\')
490 stuff = pat[i:j].replace('\\','\\\\')
484 i = j + 1
491 i = j + 1
485 if stuff[0] == '!':
492 if stuff[0] == '!':
486 stuff = '^' + stuff[1:]
493 stuff = '^' + stuff[1:]
487 elif stuff[0] == '^':
494 elif stuff[0] == '^':
488 stuff = '\\' + stuff
495 stuff = '\\' + stuff
489 res = '%s[%s]' % (res, stuff)
496 res = '%s[%s]' % (res, stuff)
490 elif c == '{':
497 elif c == '{':
491 group += 1
498 group += 1
492 res += '(?:'
499 res += '(?:'
493 elif c == '}' and group:
500 elif c == '}' and group:
494 res += ')'
501 res += ')'
495 group -= 1
502 group -= 1
496 elif c == ',' and group:
503 elif c == ',' and group:
497 res += '|'
504 res += '|'
498 elif c == '\\':
505 elif c == '\\':
499 p = peek()
506 p = peek()
500 if p:
507 if p:
501 i += 1
508 i += 1
502 res += escape(p)
509 res += escape(p)
503 else:
510 else:
504 res += escape(c)
511 res += escape(c)
505 else:
512 else:
506 res += escape(c)
513 res += escape(c)
507 return res
514 return res
508
515
509 def _regex(kind, pat, globsuffix):
516 def _regex(kind, pat, globsuffix):
510 '''Convert a (normalized) pattern of any kind into a regular expression.
517 '''Convert a (normalized) pattern of any kind into a regular expression.
511 globsuffix is appended to the regexp of globs.'''
518 globsuffix is appended to the regexp of globs.'''
512 if not pat:
519 if not pat:
513 return ''
520 return ''
514 if kind == 're':
521 if kind == 're':
515 return pat
522 return pat
516 if kind == 'path':
523 if kind == 'path':
517 if pat == '.':
524 if pat == '.':
518 return ''
525 return ''
519 return '^' + util.re.escape(pat) + '(?:/|$)'
526 return '^' + util.re.escape(pat) + '(?:/|$)'
520 if kind == 'relglob':
527 if kind == 'relglob':
521 return '(?:|.*/)' + _globre(pat) + globsuffix
528 return '(?:|.*/)' + _globre(pat) + globsuffix
522 if kind == 'relpath':
529 if kind == 'relpath':
523 return util.re.escape(pat) + '(?:/|$)'
530 return util.re.escape(pat) + '(?:/|$)'
524 if kind == 'relre':
531 if kind == 'relre':
525 if pat.startswith('^'):
532 if pat.startswith('^'):
526 return pat
533 return pat
527 return '.*' + pat
534 return '.*' + pat
528 return _globre(pat) + globsuffix
535 return _globre(pat) + globsuffix
529
536
530 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
537 def _buildmatch(ctx, kindpats, globsuffix, listsubrepos, root):
531 '''Return regexp string and a matcher function for kindpats.
538 '''Return regexp string and a matcher function for kindpats.
532 globsuffix is appended to the regexp of globs.'''
539 globsuffix is appended to the regexp of globs.'''
533 matchfuncs = []
540 matchfuncs = []
534
541
535 subincludes, kindpats = _expandsubinclude(kindpats, root)
542 subincludes, kindpats = _expandsubinclude(kindpats, root)
536 if subincludes:
543 if subincludes:
537 def matchsubinclude(f):
544 def matchsubinclude(f):
538 for prefix, mf in subincludes:
545 for prefix, mf in subincludes:
539 if f.startswith(prefix) and mf(f[len(prefix):]):
546 if f.startswith(prefix) and mf(f[len(prefix):]):
540 return True
547 return True
541 return False
548 return False
542 matchfuncs.append(matchsubinclude)
549 matchfuncs.append(matchsubinclude)
543
550
544 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
551 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
545 if fset:
552 if fset:
546 matchfuncs.append(fset.__contains__)
553 matchfuncs.append(fset.__contains__)
547
554
548 regex = ''
555 regex = ''
549 if kindpats:
556 if kindpats:
550 regex, mf = _buildregexmatch(kindpats, globsuffix)
557 regex, mf = _buildregexmatch(kindpats, globsuffix)
551 matchfuncs.append(mf)
558 matchfuncs.append(mf)
552
559
553 if len(matchfuncs) == 1:
560 if len(matchfuncs) == 1:
554 return regex, matchfuncs[0]
561 return regex, matchfuncs[0]
555 else:
562 else:
556 return regex, lambda f: any(mf(f) for mf in matchfuncs)
563 return regex, lambda f: any(mf(f) for mf in matchfuncs)
557
564
558 def _buildregexmatch(kindpats, globsuffix):
565 def _buildregexmatch(kindpats, globsuffix):
559 """Build a match function from a list of kinds and kindpats,
566 """Build a match function from a list of kinds and kindpats,
560 return regexp string and a matcher function."""
567 return regexp string and a matcher function."""
561 try:
568 try:
562 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
569 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
563 for (k, p, s) in kindpats])
570 for (k, p, s) in kindpats])
564 if len(regex) > 20000:
571 if len(regex) > 20000:
565 raise OverflowError
572 raise OverflowError
566 return regex, _rematcher(regex)
573 return regex, _rematcher(regex)
567 except OverflowError:
574 except OverflowError:
568 # We're using a Python with a tiny regex engine and we
575 # We're using a Python with a tiny regex engine and we
569 # made it explode, so we'll divide the pattern list in two
576 # made it explode, so we'll divide the pattern list in two
570 # until it works
577 # until it works
571 l = len(kindpats)
578 l = len(kindpats)
572 if l < 2:
579 if l < 2:
573 raise
580 raise
574 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
581 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
575 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
582 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
576 return regex, lambda s: a(s) or b(s)
583 return regex, lambda s: a(s) or b(s)
577 except re.error:
584 except re.error:
578 for k, p, s in kindpats:
585 for k, p, s in kindpats:
579 try:
586 try:
580 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
587 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
581 except re.error:
588 except re.error:
582 if s:
589 if s:
583 raise util.Abort(_("%s: invalid pattern (%s): %s") %
590 raise util.Abort(_("%s: invalid pattern (%s): %s") %
584 (s, k, p))
591 (s, k, p))
585 else:
592 else:
586 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
593 raise util.Abort(_("invalid pattern (%s): %s") % (k, p))
587 raise util.Abort(_("invalid pattern"))
594 raise util.Abort(_("invalid pattern"))
588
595
589 def _roots(kindpats):
596 def _roots(kindpats):
590 '''return roots and exact explicitly listed files from patterns
597 '''return roots and exact explicitly listed files from patterns
591
598
592 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
599 >>> _roots([('glob', 'g/*', ''), ('glob', 'g', ''), ('glob', 'g*', '')])
593 ['g', 'g', '.']
600 ['g', 'g', '.']
594 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
601 >>> _roots([('relpath', 'r', ''), ('path', 'p/p', ''), ('path', '', '')])
595 ['r', 'p/p', '.']
602 ['r', 'p/p', '.']
596 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
603 >>> _roots([('relglob', 'rg*', ''), ('re', 're/', ''), ('relre', 'rr', '')])
597 ['.', '.', '.']
604 ['.', '.', '.']
598 '''
605 '''
599 r = []
606 r = []
600 for kind, pat, source in kindpats:
607 for kind, pat, source in kindpats:
601 if kind == 'glob': # find the non-glob prefix
608 if kind == 'glob': # find the non-glob prefix
602 root = []
609 root = []
603 for p in pat.split('/'):
610 for p in pat.split('/'):
604 if '[' in p or '{' in p or '*' in p or '?' in p:
611 if '[' in p or '{' in p or '*' in p or '?' in p:
605 break
612 break
606 root.append(p)
613 root.append(p)
607 r.append('/'.join(root) or '.')
614 r.append('/'.join(root) or '.')
608 elif kind in ('relpath', 'path'):
615 elif kind in ('relpath', 'path'):
609 r.append(pat or '.')
616 r.append(pat or '.')
610 else: # relglob, re, relre
617 else: # relglob, re, relre
611 r.append('.')
618 r.append('.')
612 return r
619 return r
613
620
614 def _anypats(kindpats):
621 def _anypats(kindpats):
615 for kind, pat, source in kindpats:
622 for kind, pat, source in kindpats:
616 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
623 if kind in ('glob', 're', 'relglob', 'relre', 'set'):
617 return True
624 return True
618
625
619 _commentre = None
626 _commentre = None
620
627
621 def readpatternfile(filepath, warn):
628 def readpatternfile(filepath, warn):
622 '''parse a pattern file, returning a list of
629 '''parse a pattern file, returning a list of
623 patterns. These patterns should be given to compile()
630 patterns. These patterns should be given to compile()
624 to be validated and converted into a match function.
631 to be validated and converted into a match function.
625
632
626 trailing white space is dropped.
633 trailing white space is dropped.
627 the escape character is backslash.
634 the escape character is backslash.
628 comments start with #.
635 comments start with #.
629 empty lines are skipped.
636 empty lines are skipped.
630
637
631 lines can be of the following formats:
638 lines can be of the following formats:
632
639
633 syntax: regexp # defaults following lines to non-rooted regexps
640 syntax: regexp # defaults following lines to non-rooted regexps
634 syntax: glob # defaults following lines to non-rooted globs
641 syntax: glob # defaults following lines to non-rooted globs
635 re:pattern # non-rooted regular expression
642 re:pattern # non-rooted regular expression
636 glob:pattern # non-rooted glob
643 glob:pattern # non-rooted glob
637 pattern # pattern of the current default type'''
644 pattern # pattern of the current default type'''
638
645
639 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
646 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
640 'include': 'include', 'subinclude': 'subinclude'}
647 'include': 'include', 'subinclude': 'subinclude'}
641 syntax = 'relre:'
648 syntax = 'relre:'
642 patterns = []
649 patterns = []
643
650
644 fp = open(filepath)
651 fp = open(filepath)
645 for line in fp:
652 for line in fp:
646 if "#" in line:
653 if "#" in line:
647 global _commentre
654 global _commentre
648 if not _commentre:
655 if not _commentre:
649 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
656 _commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
650 # remove comments prefixed by an even number of escapes
657 # remove comments prefixed by an even number of escapes
651 line = _commentre.sub(r'\1', line)
658 line = _commentre.sub(r'\1', line)
652 # fixup properly escaped comments that survived the above
659 # fixup properly escaped comments that survived the above
653 line = line.replace("\\#", "#")
660 line = line.replace("\\#", "#")
654 line = line.rstrip()
661 line = line.rstrip()
655 if not line:
662 if not line:
656 continue
663 continue
657
664
658 if line.startswith('syntax:'):
665 if line.startswith('syntax:'):
659 s = line[7:].strip()
666 s = line[7:].strip()
660 try:
667 try:
661 syntax = syntaxes[s]
668 syntax = syntaxes[s]
662 except KeyError:
669 except KeyError:
663 if warn:
670 if warn:
664 warn(_("%s: ignoring invalid syntax '%s'\n") %
671 warn(_("%s: ignoring invalid syntax '%s'\n") %
665 (filepath, s))
672 (filepath, s))
666 continue
673 continue
667
674
668 linesyntax = syntax
675 linesyntax = syntax
669 for s, rels in syntaxes.iteritems():
676 for s, rels in syntaxes.iteritems():
670 if line.startswith(rels):
677 if line.startswith(rels):
671 linesyntax = rels
678 linesyntax = rels
672 line = line[len(rels):]
679 line = line[len(rels):]
673 break
680 break
674 elif line.startswith(s+':'):
681 elif line.startswith(s+':'):
675 linesyntax = rels
682 linesyntax = rels
676 line = line[len(s) + 1:]
683 line = line[len(s) + 1:]
677 break
684 break
678 patterns.append(linesyntax + line)
685 patterns.append(linesyntax + line)
679 fp.close()
686 fp.close()
680 return patterns
687 return patterns
@@ -1,306 +1,305
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import errno
11 import errno
12
12
13 from .i18n import _
13 from .i18n import _
14 from .node import short
14 from .node import short
15 from . import (
15 from . import (
16 bundle2,
16 bundle2,
17 changegroup,
17 changegroup,
18 exchange,
18 exchange,
19 util,
19 util,
20 )
20 )
21
21
22 def _bundle(repo, bases, heads, node, suffix, compress=True):
22 def _bundle(repo, bases, heads, node, suffix, compress=True):
23 """create a bundle with the specified revisions as a backup"""
23 """create a bundle with the specified revisions as a backup"""
24 usebundle2 = (repo.ui.configbool('experimental', 'bundle2-exp', True) and
24 usebundle2 = (repo.ui.configbool('experimental', 'bundle2-exp', True) and
25 repo.ui.config('experimental', 'strip-bundle2-version'))
25 repo.ui.config('experimental', 'strip-bundle2-version'))
26 if usebundle2:
26 if usebundle2:
27 cgversion = repo.ui.config('experimental', 'strip-bundle2-version')
27 cgversion = repo.ui.config('experimental', 'strip-bundle2-version')
28 if cgversion not in changegroup.packermap:
28 if cgversion not in changegroup.packermap:
29 repo.ui.warn(_('unknown strip-bundle2-version value %r; '
29 repo.ui.warn(_('unknown strip-bundle2-version value %r; '
30 'should be one of %r\n') %
30 'should be one of %r\n') %
31 (cgversion, sorted(changegroup.packermap.keys()),))
31 (cgversion, sorted(changegroup.packermap.keys()),))
32 cgversion = '01'
32 cgversion = '01'
33 usebundle2 = False
33 usebundle2 = False
34 else:
34 else:
35 cgversion = '01'
35 cgversion = '01'
36
36
37 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip',
37 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip',
38 version=cgversion)
38 version=cgversion)
39 backupdir = "strip-backup"
39 backupdir = "strip-backup"
40 vfs = repo.vfs
40 vfs = repo.vfs
41 if not vfs.isdir(backupdir):
41 if not vfs.isdir(backupdir):
42 vfs.mkdir(backupdir)
42 vfs.mkdir(backupdir)
43
43
44 # Include a hash of all the nodes in the filename for uniqueness
44 # Include a hash of all the nodes in the filename for uniqueness
45 allcommits = repo.set('%ln::%ln', bases, heads)
45 allcommits = repo.set('%ln::%ln', bases, heads)
46 allhashes = sorted(c.hex() for c in allcommits)
46 allhashes = sorted(c.hex() for c in allcommits)
47 totalhash = util.sha1(''.join(allhashes)).hexdigest()
47 totalhash = util.sha1(''.join(allhashes)).hexdigest()
48 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
48 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
49
49
50 if usebundle2:
50 if usebundle2:
51 bundletype = "HG20"
51 bundletype = "HG20"
52 elif compress:
52 elif compress:
53 bundletype = "HG10BZ"
53 bundletype = "HG10BZ"
54 else:
54 else:
55 bundletype = "HG10UN"
55 bundletype = "HG10UN"
56 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs)
56 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs)
57
57
58 def _collectfiles(repo, striprev):
58 def _collectfiles(repo, striprev):
59 """find out the filelogs affected by the strip"""
59 """find out the filelogs affected by the strip"""
60 files = set()
60 files = set()
61
61
62 for x in xrange(striprev, len(repo)):
62 for x in xrange(striprev, len(repo)):
63 files.update(repo[x].files())
63 files.update(repo[x].files())
64
64
65 return sorted(files)
65 return sorted(files)
66
66
67 def _collectbrokencsets(repo, files, striprev):
67 def _collectbrokencsets(repo, files, striprev):
68 """return the changesets which will be broken by the truncation"""
68 """return the changesets which will be broken by the truncation"""
69 s = set()
69 s = set()
70 def collectone(revlog):
70 def collectone(revlog):
71 _, brokenset = revlog.getstrippoint(striprev)
71 _, brokenset = revlog.getstrippoint(striprev)
72 s.update([revlog.linkrev(r) for r in brokenset])
72 s.update([revlog.linkrev(r) for r in brokenset])
73
73
74 collectone(repo.manifest)
74 collectone(repo.manifest)
75 for fname in files:
75 for fname in files:
76 collectone(repo.file(fname))
76 collectone(repo.file(fname))
77
77
78 return s
78 return s
79
79
80 def strip(ui, repo, nodelist, backup=True, topic='backup'):
80 def strip(ui, repo, nodelist, backup=True, topic='backup'):
81
81
82 # Simple way to maintain backwards compatibility for this
82 # Simple way to maintain backwards compatibility for this
83 # argument.
83 # argument.
84 if backup in ['none', 'strip']:
84 if backup in ['none', 'strip']:
85 backup = False
85 backup = False
86
86
87 repo = repo.unfiltered()
87 repo = repo.unfiltered()
88 repo.destroying()
88 repo.destroying()
89
89
90 cl = repo.changelog
90 cl = repo.changelog
91 # TODO handle undo of merge sets
91 # TODO handle undo of merge sets
92 if isinstance(nodelist, str):
92 if isinstance(nodelist, str):
93 nodelist = [nodelist]
93 nodelist = [nodelist]
94 striplist = [cl.rev(node) for node in nodelist]
94 striplist = [cl.rev(node) for node in nodelist]
95 striprev = min(striplist)
95 striprev = min(striplist)
96
96
97 # Some revisions with rev > striprev may not be descendants of striprev.
97 # Some revisions with rev > striprev may not be descendants of striprev.
98 # We have to find these revisions and put them in a bundle, so that
98 # We have to find these revisions and put them in a bundle, so that
99 # we can restore them after the truncations.
99 # we can restore them after the truncations.
100 # To create the bundle we use repo.changegroupsubset which requires
100 # To create the bundle we use repo.changegroupsubset which requires
101 # the list of heads and bases of the set of interesting revisions.
101 # the list of heads and bases of the set of interesting revisions.
102 # (head = revision in the set that has no descendant in the set;
102 # (head = revision in the set that has no descendant in the set;
103 # base = revision in the set that has no ancestor in the set)
103 # base = revision in the set that has no ancestor in the set)
104 tostrip = set(striplist)
104 tostrip = set(striplist)
105 for rev in striplist:
105 for rev in striplist:
106 for desc in cl.descendants([rev]):
106 for desc in cl.descendants([rev]):
107 tostrip.add(desc)
107 tostrip.add(desc)
108
108
109 files = _collectfiles(repo, striprev)
109 files = _collectfiles(repo, striprev)
110 saverevs = _collectbrokencsets(repo, files, striprev)
110 saverevs = _collectbrokencsets(repo, files, striprev)
111
111
112 # compute heads
112 # compute heads
113 saveheads = set(saverevs)
113 saveheads = set(saverevs)
114 for r in xrange(striprev + 1, len(cl)):
114 for r in xrange(striprev + 1, len(cl)):
115 if r not in tostrip:
115 if r not in tostrip:
116 saverevs.add(r)
116 saverevs.add(r)
117 saveheads.difference_update(cl.parentrevs(r))
117 saveheads.difference_update(cl.parentrevs(r))
118 saveheads.add(r)
118 saveheads.add(r)
119 saveheads = [cl.node(r) for r in saveheads]
119 saveheads = [cl.node(r) for r in saveheads]
120
120
121 # compute base nodes
121 # compute base nodes
122 if saverevs:
122 if saverevs:
123 descendants = set(cl.descendants(saverevs))
123 descendants = set(cl.descendants(saverevs))
124 saverevs.difference_update(descendants)
124 saverevs.difference_update(descendants)
125 savebases = [cl.node(r) for r in saverevs]
125 savebases = [cl.node(r) for r in saverevs]
126 stripbases = [cl.node(r) for r in tostrip]
126 stripbases = [cl.node(r) for r in tostrip]
127
127
128 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
128 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
129 # is much faster
129 # is much faster
130 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
130 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
131 if newbmtarget:
131 if newbmtarget:
132 newbmtarget = repo[newbmtarget.first()].node()
132 newbmtarget = repo[newbmtarget.first()].node()
133 else:
133 else:
134 newbmtarget = '.'
134 newbmtarget = '.'
135
135
136 bm = repo._bookmarks
136 bm = repo._bookmarks
137 updatebm = []
137 updatebm = []
138 for m in bm:
138 for m in bm:
139 rev = repo[bm[m]].rev()
139 rev = repo[bm[m]].rev()
140 if rev in tostrip:
140 if rev in tostrip:
141 updatebm.append(m)
141 updatebm.append(m)
142
142
143 # create a changegroup for all the branches we need to keep
143 # create a changegroup for all the branches we need to keep
144 backupfile = None
144 backupfile = None
145 vfs = repo.vfs
145 vfs = repo.vfs
146 node = nodelist[-1]
146 node = nodelist[-1]
147 if backup:
147 if backup:
148 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
148 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
149 repo.ui.status(_("saved backup bundle to %s\n") %
149 repo.ui.status(_("saved backup bundle to %s\n") %
150 vfs.join(backupfile))
150 vfs.join(backupfile))
151 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
151 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
152 vfs.join(backupfile))
152 vfs.join(backupfile))
153 if saveheads or savebases:
153 if saveheads or savebases:
154 # do not compress partial bundle if we remove it from disk later
154 # do not compress partial bundle if we remove it from disk later
155 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
155 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
156 compress=False)
156 compress=False)
157
157
158 mfst = repo.manifest
158 mfst = repo.manifest
159
159
160 curtr = repo.currenttransaction()
160 curtr = repo.currenttransaction()
161 if curtr is not None:
161 if curtr is not None:
162 del curtr # avoid carrying reference to transaction for nothing
162 del curtr # avoid carrying reference to transaction for nothing
163 msg = _('programming error: cannot strip from inside a transaction')
163 msg = _('programming error: cannot strip from inside a transaction')
164 raise util.Abort(msg, hint=_('contact your extension maintainer'))
164 raise util.Abort(msg, hint=_('contact your extension maintainer'))
165
165
166 tr = repo.transaction("strip")
166 tr = repo.transaction("strip")
167 offset = len(tr.entries)
167 offset = len(tr.entries)
168
168
169 try:
169 try:
170 tr.startgroup()
170 tr.startgroup()
171 cl.strip(striprev, tr)
171 cl.strip(striprev, tr)
172 mfst.strip(striprev, tr)
172 mfst.strip(striprev, tr)
173 for fn in files:
173 for fn in files:
174 repo.file(fn).strip(striprev, tr)
174 repo.file(fn).strip(striprev, tr)
175 tr.endgroup()
175 tr.endgroup()
176
176
177 try:
177 try:
178 for i in xrange(offset, len(tr.entries)):
178 for i in xrange(offset, len(tr.entries)):
179 file, troffset, ignore = tr.entries[i]
179 file, troffset, ignore = tr.entries[i]
180 repo.svfs(file, 'a').truncate(troffset)
180 repo.svfs(file, 'a').truncate(troffset)
181 if troffset == 0:
181 if troffset == 0:
182 repo.store.markremoved(file)
182 repo.store.markremoved(file)
183 tr.close()
183 tr.close()
184 except: # re-raises
184 finally:
185 tr.abort()
185 tr.release()
186 raise
187
186
188 if saveheads or savebases:
187 if saveheads or savebases:
189 ui.note(_("adding branch\n"))
188 ui.note(_("adding branch\n"))
190 f = vfs.open(chgrpfile, "rb")
189 f = vfs.open(chgrpfile, "rb")
191 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
190 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
192 if not repo.ui.verbose:
191 if not repo.ui.verbose:
193 # silence internal shuffling chatter
192 # silence internal shuffling chatter
194 repo.ui.pushbuffer()
193 repo.ui.pushbuffer()
195 if isinstance(gen, bundle2.unbundle20):
194 if isinstance(gen, bundle2.unbundle20):
196 tr = repo.transaction('strip')
195 tr = repo.transaction('strip')
197 tr.hookargs = {'source': 'strip',
196 tr.hookargs = {'source': 'strip',
198 'url': 'bundle:' + vfs.join(chgrpfile)}
197 'url': 'bundle:' + vfs.join(chgrpfile)}
199 try:
198 try:
200 bundle2.processbundle(repo, gen, lambda: tr)
199 bundle2.processbundle(repo, gen, lambda: tr)
201 tr.close()
200 tr.close()
202 finally:
201 finally:
203 tr.release()
202 tr.release()
204 else:
203 else:
205 changegroup.addchangegroup(repo, gen, 'strip',
204 changegroup.addchangegroup(repo, gen, 'strip',
206 'bundle:' + vfs.join(chgrpfile),
205 'bundle:' + vfs.join(chgrpfile),
207 True)
206 True)
208 if not repo.ui.verbose:
207 if not repo.ui.verbose:
209 repo.ui.popbuffer()
208 repo.ui.popbuffer()
210 f.close()
209 f.close()
211
210
212 # remove undo files
211 # remove undo files
213 for undovfs, undofile in repo.undofiles():
212 for undovfs, undofile in repo.undofiles():
214 try:
213 try:
215 undovfs.unlink(undofile)
214 undovfs.unlink(undofile)
216 except OSError as e:
215 except OSError as e:
217 if e.errno != errno.ENOENT:
216 if e.errno != errno.ENOENT:
218 ui.warn(_('error removing %s: %s\n') %
217 ui.warn(_('error removing %s: %s\n') %
219 (undovfs.join(undofile), str(e)))
218 (undovfs.join(undofile), str(e)))
220
219
221 for m in updatebm:
220 for m in updatebm:
222 bm[m] = repo[newbmtarget].node()
221 bm[m] = repo[newbmtarget].node()
223 bm.write()
222 bm.write()
224 except: # re-raises
223 except: # re-raises
225 if backupfile:
224 if backupfile:
226 ui.warn(_("strip failed, full bundle stored in '%s'\n")
225 ui.warn(_("strip failed, full bundle stored in '%s'\n")
227 % vfs.join(backupfile))
226 % vfs.join(backupfile))
228 elif saveheads:
227 elif saveheads:
229 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
228 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
230 % vfs.join(chgrpfile))
229 % vfs.join(chgrpfile))
231 raise
230 raise
232 else:
231 else:
233 if saveheads or savebases:
232 if saveheads or savebases:
234 # Remove partial backup only if there were no exceptions
233 # Remove partial backup only if there were no exceptions
235 vfs.unlink(chgrpfile)
234 vfs.unlink(chgrpfile)
236
235
237 repo.destroyed()
236 repo.destroyed()
238
237
239 def rebuildfncache(ui, repo):
238 def rebuildfncache(ui, repo):
240 """Rebuilds the fncache file from repo history.
239 """Rebuilds the fncache file from repo history.
241
240
242 Missing entries will be added. Extra entries will be removed.
241 Missing entries will be added. Extra entries will be removed.
243 """
242 """
244 repo = repo.unfiltered()
243 repo = repo.unfiltered()
245
244
246 if 'fncache' not in repo.requirements:
245 if 'fncache' not in repo.requirements:
247 ui.warn(_('(not rebuilding fncache because repository does not '
246 ui.warn(_('(not rebuilding fncache because repository does not '
248 'support fncache)\n'))
247 'support fncache)\n'))
249 return
248 return
250
249
251 lock = repo.lock()
250 lock = repo.lock()
252 try:
251 try:
253 fnc = repo.store.fncache
252 fnc = repo.store.fncache
254 # Trigger load of fncache.
253 # Trigger load of fncache.
255 if 'irrelevant' in fnc:
254 if 'irrelevant' in fnc:
256 pass
255 pass
257
256
258 oldentries = set(fnc.entries)
257 oldentries = set(fnc.entries)
259 newentries = set()
258 newentries = set()
260 seenfiles = set()
259 seenfiles = set()
261
260
262 repolen = len(repo)
261 repolen = len(repo)
263 for rev in repo:
262 for rev in repo:
264 ui.progress(_('changeset'), rev, total=repolen)
263 ui.progress(_('changeset'), rev, total=repolen)
265
264
266 ctx = repo[rev]
265 ctx = repo[rev]
267 for f in ctx.files():
266 for f in ctx.files():
268 # This is to minimize I/O.
267 # This is to minimize I/O.
269 if f in seenfiles:
268 if f in seenfiles:
270 continue
269 continue
271 seenfiles.add(f)
270 seenfiles.add(f)
272
271
273 i = 'data/%s.i' % f
272 i = 'data/%s.i' % f
274 d = 'data/%s.d' % f
273 d = 'data/%s.d' % f
275
274
276 if repo.store._exists(i):
275 if repo.store._exists(i):
277 newentries.add(i)
276 newentries.add(i)
278 if repo.store._exists(d):
277 if repo.store._exists(d):
279 newentries.add(d)
278 newentries.add(d)
280
279
281 ui.progress(_('changeset'), None)
280 ui.progress(_('changeset'), None)
282
281
283 addcount = len(newentries - oldentries)
282 addcount = len(newentries - oldentries)
284 removecount = len(oldentries - newentries)
283 removecount = len(oldentries - newentries)
285 for p in sorted(oldentries - newentries):
284 for p in sorted(oldentries - newentries):
286 ui.write(_('removing %s\n') % p)
285 ui.write(_('removing %s\n') % p)
287 for p in sorted(newentries - oldentries):
286 for p in sorted(newentries - oldentries):
288 ui.write(_('adding %s\n') % p)
287 ui.write(_('adding %s\n') % p)
289
288
290 if addcount or removecount:
289 if addcount or removecount:
291 ui.write(_('%d items added, %d removed from fncache\n') %
290 ui.write(_('%d items added, %d removed from fncache\n') %
292 (addcount, removecount))
291 (addcount, removecount))
293 fnc.entries = newentries
292 fnc.entries = newentries
294 fnc._dirty = True
293 fnc._dirty = True
295
294
296 tr = repo.transaction('fncache')
295 tr = repo.transaction('fncache')
297 try:
296 try:
298 fnc.write(tr)
297 fnc.write(tr)
299 tr.close()
298 tr.close()
300 finally:
299 finally:
301 tr.release()
300 tr.release()
302 else:
301 else:
303 ui.write(_('fncache already up to date\n'))
302 ui.write(_('fncache already up to date\n'))
304 finally:
303 finally:
305 lock.release()
304 lock.release()
306
305
@@ -1,241 +1,249
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 ..
@@ -1,828 +1,851
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "strip=" >> $HGRCPATH
2 $ echo "strip=" >> $HGRCPATH
3
3
4 $ restore() {
4 $ restore() {
5 > hg unbundle -q .hg/strip-backup/*
5 > hg unbundle -q .hg/strip-backup/*
6 > rm .hg/strip-backup/*
6 > rm .hg/strip-backup/*
7 > }
7 > }
8 $ teststrip() {
8 $ teststrip() {
9 > hg up -C $1
9 > hg up -C $1
10 > echo % before update $1, strip $2
10 > echo % before update $1, strip $2
11 > hg parents
11 > hg parents
12 > hg --traceback strip $2
12 > hg --traceback strip $2
13 > echo % after update $1, strip $2
13 > echo % after update $1, strip $2
14 > hg parents
14 > hg parents
15 > restore
15 > restore
16 > }
16 > }
17
17
18 $ hg init test
18 $ hg init test
19 $ cd test
19 $ cd test
20
20
21 $ echo foo > bar
21 $ echo foo > bar
22 $ hg ci -Ama
22 $ hg ci -Ama
23 adding bar
23 adding bar
24
24
25 $ echo more >> bar
25 $ echo more >> bar
26 $ hg ci -Amb
26 $ hg ci -Amb
27
27
28 $ echo blah >> bar
28 $ echo blah >> bar
29 $ hg ci -Amc
29 $ hg ci -Amc
30
30
31 $ hg up 1
31 $ hg up 1
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 $ echo blah >> bar
33 $ echo blah >> bar
34 $ hg ci -Amd
34 $ hg ci -Amd
35 created new head
35 created new head
36
36
37 $ echo final >> bar
37 $ echo final >> bar
38 $ hg ci -Ame
38 $ hg ci -Ame
39
39
40 $ hg log
40 $ hg log
41 changeset: 4:443431ffac4f
41 changeset: 4:443431ffac4f
42 tag: tip
42 tag: tip
43 user: test
43 user: test
44 date: Thu Jan 01 00:00:00 1970 +0000
44 date: Thu Jan 01 00:00:00 1970 +0000
45 summary: e
45 summary: e
46
46
47 changeset: 3:65bd5f99a4a3
47 changeset: 3:65bd5f99a4a3
48 parent: 1:ef3a871183d7
48 parent: 1:ef3a871183d7
49 user: test
49 user: test
50 date: Thu Jan 01 00:00:00 1970 +0000
50 date: Thu Jan 01 00:00:00 1970 +0000
51 summary: d
51 summary: d
52
52
53 changeset: 2:264128213d29
53 changeset: 2:264128213d29
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:00 1970 +0000
55 date: Thu Jan 01 00:00:00 1970 +0000
56 summary: c
56 summary: c
57
57
58 changeset: 1:ef3a871183d7
58 changeset: 1:ef3a871183d7
59 user: test
59 user: test
60 date: Thu Jan 01 00:00:00 1970 +0000
60 date: Thu Jan 01 00:00:00 1970 +0000
61 summary: b
61 summary: b
62
62
63 changeset: 0:9ab35a2d17cb
63 changeset: 0:9ab35a2d17cb
64 user: test
64 user: test
65 date: Thu Jan 01 00:00:00 1970 +0000
65 date: Thu Jan 01 00:00:00 1970 +0000
66 summary: a
66 summary: a
67
67
68
68
69 $ teststrip 4 4
69 $ teststrip 4 4
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 % before update 4, strip 4
71 % before update 4, strip 4
72 changeset: 4:443431ffac4f
72 changeset: 4:443431ffac4f
73 tag: tip
73 tag: tip
74 user: test
74 user: test
75 date: Thu Jan 01 00:00:00 1970 +0000
75 date: Thu Jan 01 00:00:00 1970 +0000
76 summary: e
76 summary: e
77
77
78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
79 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
80 % after update 4, strip 4
80 % after update 4, strip 4
81 changeset: 3:65bd5f99a4a3
81 changeset: 3:65bd5f99a4a3
82 tag: tip
82 tag: tip
83 parent: 1:ef3a871183d7
83 parent: 1:ef3a871183d7
84 user: test
84 user: test
85 date: Thu Jan 01 00:00:00 1970 +0000
85 date: Thu Jan 01 00:00:00 1970 +0000
86 summary: d
86 summary: d
87
87
88 $ teststrip 4 3
88 $ teststrip 4 3
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 % before update 4, strip 3
90 % before update 4, strip 3
91 changeset: 4:443431ffac4f
91 changeset: 4:443431ffac4f
92 tag: tip
92 tag: tip
93 user: test
93 user: test
94 date: Thu Jan 01 00:00:00 1970 +0000
94 date: Thu Jan 01 00:00:00 1970 +0000
95 summary: e
95 summary: e
96
96
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
98 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
99 % after update 4, strip 3
99 % after update 4, strip 3
100 changeset: 1:ef3a871183d7
100 changeset: 1:ef3a871183d7
101 user: test
101 user: test
102 date: Thu Jan 01 00:00:00 1970 +0000
102 date: Thu Jan 01 00:00:00 1970 +0000
103 summary: b
103 summary: b
104
104
105 $ teststrip 1 4
105 $ teststrip 1 4
106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 % before update 1, strip 4
107 % before update 1, strip 4
108 changeset: 1:ef3a871183d7
108 changeset: 1:ef3a871183d7
109 user: test
109 user: test
110 date: Thu Jan 01 00:00:00 1970 +0000
110 date: Thu Jan 01 00:00:00 1970 +0000
111 summary: b
111 summary: b
112
112
113 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
113 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
114 % after update 1, strip 4
114 % after update 1, strip 4
115 changeset: 1:ef3a871183d7
115 changeset: 1:ef3a871183d7
116 user: test
116 user: test
117 date: Thu Jan 01 00:00:00 1970 +0000
117 date: Thu Jan 01 00:00:00 1970 +0000
118 summary: b
118 summary: b
119
119
120 $ teststrip 4 2
120 $ teststrip 4 2
121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 % before update 4, strip 2
122 % before update 4, strip 2
123 changeset: 4:443431ffac4f
123 changeset: 4:443431ffac4f
124 tag: tip
124 tag: tip
125 user: test
125 user: test
126 date: Thu Jan 01 00:00:00 1970 +0000
126 date: Thu Jan 01 00:00:00 1970 +0000
127 summary: e
127 summary: e
128
128
129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
129 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
130 % after update 4, strip 2
130 % after update 4, strip 2
131 changeset: 3:443431ffac4f
131 changeset: 3:443431ffac4f
132 tag: tip
132 tag: tip
133 user: test
133 user: test
134 date: Thu Jan 01 00:00:00 1970 +0000
134 date: Thu Jan 01 00:00:00 1970 +0000
135 summary: e
135 summary: e
136
136
137 $ teststrip 4 1
137 $ teststrip 4 1
138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 % before update 4, strip 1
139 % before update 4, strip 1
140 changeset: 4:264128213d29
140 changeset: 4:264128213d29
141 tag: tip
141 tag: tip
142 parent: 1:ef3a871183d7
142 parent: 1:ef3a871183d7
143 user: test
143 user: test
144 date: Thu Jan 01 00:00:00 1970 +0000
144 date: Thu Jan 01 00:00:00 1970 +0000
145 summary: c
145 summary: c
146
146
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
148 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
149 % after update 4, strip 1
149 % after update 4, strip 1
150 changeset: 0:9ab35a2d17cb
150 changeset: 0:9ab35a2d17cb
151 tag: tip
151 tag: tip
152 user: test
152 user: test
153 date: Thu Jan 01 00:00:00 1970 +0000
153 date: Thu Jan 01 00:00:00 1970 +0000
154 summary: a
154 summary: a
155
155
156 $ teststrip null 4
156 $ teststrip null 4
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
157 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
158 % before update null, strip 4
158 % before update null, strip 4
159 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
159 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
160 % after update null, strip 4
160 % after update null, strip 4
161
161
162 $ hg log
162 $ hg log
163 changeset: 4:264128213d29
163 changeset: 4:264128213d29
164 tag: tip
164 tag: tip
165 parent: 1:ef3a871183d7
165 parent: 1:ef3a871183d7
166 user: test
166 user: test
167 date: Thu Jan 01 00:00:00 1970 +0000
167 date: Thu Jan 01 00:00:00 1970 +0000
168 summary: c
168 summary: c
169
169
170 changeset: 3:443431ffac4f
170 changeset: 3:443431ffac4f
171 user: test
171 user: test
172 date: Thu Jan 01 00:00:00 1970 +0000
172 date: Thu Jan 01 00:00:00 1970 +0000
173 summary: e
173 summary: e
174
174
175 changeset: 2:65bd5f99a4a3
175 changeset: 2:65bd5f99a4a3
176 user: test
176 user: test
177 date: Thu Jan 01 00:00:00 1970 +0000
177 date: Thu Jan 01 00:00:00 1970 +0000
178 summary: d
178 summary: d
179
179
180 changeset: 1:ef3a871183d7
180 changeset: 1:ef3a871183d7
181 user: test
181 user: test
182 date: Thu Jan 01 00:00:00 1970 +0000
182 date: Thu Jan 01 00:00:00 1970 +0000
183 summary: b
183 summary: b
184
184
185 changeset: 0:9ab35a2d17cb
185 changeset: 0:9ab35a2d17cb
186 user: test
186 user: test
187 date: Thu Jan 01 00:00:00 1970 +0000
187 date: Thu Jan 01 00:00:00 1970 +0000
188 summary: a
188 summary: a
189
189
190 $ hg up -C 4
190 $ hg up -C 4
191 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 $ hg parents
192 $ hg parents
193 changeset: 4:264128213d29
193 changeset: 4:264128213d29
194 tag: tip
194 tag: tip
195 parent: 1:ef3a871183d7
195 parent: 1:ef3a871183d7
196 user: test
196 user: test
197 date: Thu Jan 01 00:00:00 1970 +0000
197 date: Thu Jan 01 00:00:00 1970 +0000
198 summary: c
198 summary: c
199
199
200 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=INVALID strip 4
200 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=INVALID strip 4
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 unknown strip-bundle2-version value 'INVALID'; should be one of ['01', '02']
202 unknown strip-bundle2-version value 'INVALID'; should be one of ['01', '02']
203 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
203 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
204 $ hg debugbundle .hg/strip-backup/*
204 $ hg debugbundle .hg/strip-backup/*
205 264128213d290d868c54642d13aeaa3675551a78
205 264128213d290d868c54642d13aeaa3675551a78
206 $ restore
206 $ restore
207
207
208 $ hg up -C 4
208 $ hg up -C 4
209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
210 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
212 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
213 $ hg parents
213 $ hg parents
214 changeset: 1:ef3a871183d7
214 changeset: 1:ef3a871183d7
215 user: test
215 user: test
216 date: Thu Jan 01 00:00:00 1970 +0000
216 date: Thu Jan 01 00:00:00 1970 +0000
217 summary: b
217 summary: b
218
218
219 $ hg debugbundle .hg/strip-backup/*
219 $ hg debugbundle .hg/strip-backup/*
220 Stream params: {}
220 Stream params: {}
221 changegroup -- "{'version': '02'}"
221 changegroup -- "{'version': '02'}"
222 264128213d290d868c54642d13aeaa3675551a78
222 264128213d290d868c54642d13aeaa3675551a78
223 $ hg incoming .hg/strip-backup/*
223 $ hg incoming .hg/strip-backup/*
224 comparing with .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
224 comparing with .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
225 searching for changes
225 searching for changes
226 changeset: 4:264128213d29
226 changeset: 4:264128213d29
227 tag: tip
227 tag: tip
228 parent: 1:ef3a871183d7
228 parent: 1:ef3a871183d7
229 user: test
229 user: test
230 date: Thu Jan 01 00:00:00 1970 +0000
230 date: Thu Jan 01 00:00:00 1970 +0000
231 summary: c
231 summary: c
232
232
233 $ restore
233 $ restore
234 $ hg up -C 4
234 $ hg up -C 4
235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
236 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
236 $ hg --config experimental.bundle2-exp=True --config experimental.strip-bundle2-version=02 --traceback strip 4
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
237 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
238 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
238 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
239 $ hg parents
239 $ hg parents
240 changeset: 1:ef3a871183d7
240 changeset: 1:ef3a871183d7
241 user: test
241 user: test
242 date: Thu Jan 01 00:00:00 1970 +0000
242 date: Thu Jan 01 00:00:00 1970 +0000
243 summary: b
243 summary: b
244
244
245 $ hg debugbundle .hg/strip-backup/*
245 $ hg debugbundle .hg/strip-backup/*
246 Stream params: {}
246 Stream params: {}
247 changegroup -- "{'version': '02'}"
247 changegroup -- "{'version': '02'}"
248 264128213d290d868c54642d13aeaa3675551a78
248 264128213d290d868c54642d13aeaa3675551a78
249 $ hg pull .hg/strip-backup/*
249 $ hg pull .hg/strip-backup/*
250 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
250 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
251 searching for changes
251 searching for changes
252 adding changesets
252 adding changesets
253 adding manifests
253 adding manifests
254 adding file changes
254 adding file changes
255 added 1 changesets with 0 changes to 0 files (+1 heads)
255 added 1 changesets with 0 changes to 0 files (+1 heads)
256 (run 'hg heads' to see heads, 'hg merge' to merge)
256 (run 'hg heads' to see heads, 'hg merge' to merge)
257 $ rm .hg/strip-backup/*
257 $ rm .hg/strip-backup/*
258 $ hg log --graph
258 $ hg log --graph
259 o changeset: 4:264128213d29
259 o changeset: 4:264128213d29
260 | tag: tip
260 | tag: tip
261 | parent: 1:ef3a871183d7
261 | parent: 1:ef3a871183d7
262 | user: test
262 | user: test
263 | date: Thu Jan 01 00:00:00 1970 +0000
263 | date: Thu Jan 01 00:00:00 1970 +0000
264 | summary: c
264 | summary: c
265 |
265 |
266 | o changeset: 3:443431ffac4f
266 | o changeset: 3:443431ffac4f
267 | | user: test
267 | | user: test
268 | | date: Thu Jan 01 00:00:00 1970 +0000
268 | | date: Thu Jan 01 00:00:00 1970 +0000
269 | | summary: e
269 | | summary: e
270 | |
270 | |
271 | o changeset: 2:65bd5f99a4a3
271 | o changeset: 2:65bd5f99a4a3
272 |/ user: test
272 |/ user: test
273 | date: Thu Jan 01 00:00:00 1970 +0000
273 | date: Thu Jan 01 00:00:00 1970 +0000
274 | summary: d
274 | summary: d
275 |
275 |
276 @ changeset: 1:ef3a871183d7
276 @ changeset: 1:ef3a871183d7
277 | user: test
277 | user: test
278 | date: Thu Jan 01 00:00:00 1970 +0000
278 | date: Thu Jan 01 00:00:00 1970 +0000
279 | summary: b
279 | summary: b
280 |
280 |
281 o changeset: 0:9ab35a2d17cb
281 o changeset: 0:9ab35a2d17cb
282 user: test
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
283 date: Thu Jan 01 00:00:00 1970 +0000
284 summary: a
284 summary: a
285
285
286 $ hg up -C 2
286 $ hg up -C 2
287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ hg merge 4
288 $ hg merge 4
289 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
289 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 (branch merge, don't forget to commit)
290 (branch merge, don't forget to commit)
291
291
292 before strip of merge parent
292 before strip of merge parent
293
293
294 $ hg parents
294 $ hg parents
295 changeset: 2:65bd5f99a4a3
295 changeset: 2:65bd5f99a4a3
296 user: test
296 user: test
297 date: Thu Jan 01 00:00:00 1970 +0000
297 date: Thu Jan 01 00:00:00 1970 +0000
298 summary: d
298 summary: d
299
299
300 changeset: 4:264128213d29
300 changeset: 4:264128213d29
301 tag: tip
301 tag: tip
302 parent: 1:ef3a871183d7
302 parent: 1:ef3a871183d7
303 user: test
303 user: test
304 date: Thu Jan 01 00:00:00 1970 +0000
304 date: Thu Jan 01 00:00:00 1970 +0000
305 summary: c
305 summary: c
306
306
307 $ hg strip 4
307 $ hg strip 4
308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
309 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
309 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
310
310
311 after strip of merge parent
311 after strip of merge parent
312
312
313 $ hg parents
313 $ hg parents
314 changeset: 1:ef3a871183d7
314 changeset: 1:ef3a871183d7
315 user: test
315 user: test
316 date: Thu Jan 01 00:00:00 1970 +0000
316 date: Thu Jan 01 00:00:00 1970 +0000
317 summary: b
317 summary: b
318
318
319 $ restore
319 $ restore
320
320
321 $ hg up
321 $ hg up
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 $ hg log -G
323 $ hg log -G
324 @ changeset: 4:264128213d29
324 @ changeset: 4:264128213d29
325 | tag: tip
325 | tag: tip
326 | parent: 1:ef3a871183d7
326 | parent: 1:ef3a871183d7
327 | user: test
327 | user: test
328 | date: Thu Jan 01 00:00:00 1970 +0000
328 | date: Thu Jan 01 00:00:00 1970 +0000
329 | summary: c
329 | summary: c
330 |
330 |
331 | o changeset: 3:443431ffac4f
331 | o changeset: 3:443431ffac4f
332 | | user: test
332 | | user: test
333 | | date: Thu Jan 01 00:00:00 1970 +0000
333 | | date: Thu Jan 01 00:00:00 1970 +0000
334 | | summary: e
334 | | summary: e
335 | |
335 | |
336 | o changeset: 2:65bd5f99a4a3
336 | o changeset: 2:65bd5f99a4a3
337 |/ user: test
337 |/ user: test
338 | date: Thu Jan 01 00:00:00 1970 +0000
338 | date: Thu Jan 01 00:00:00 1970 +0000
339 | summary: d
339 | summary: d
340 |
340 |
341 o changeset: 1:ef3a871183d7
341 o changeset: 1:ef3a871183d7
342 | user: test
342 | user: test
343 | date: Thu Jan 01 00:00:00 1970 +0000
343 | date: Thu Jan 01 00:00:00 1970 +0000
344 | summary: b
344 | summary: b
345 |
345 |
346 o changeset: 0:9ab35a2d17cb
346 o changeset: 0:9ab35a2d17cb
347 user: test
347 user: test
348 date: Thu Jan 01 00:00:00 1970 +0000
348 date: Thu Jan 01 00:00:00 1970 +0000
349 summary: a
349 summary: a
350
350
351
351
352 2 is parent of 3, only one strip should happen
352 2 is parent of 3, only one strip should happen
353
353
354 $ hg strip "roots(2)" 3
354 $ hg strip "roots(2)" 3
355 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
355 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
356 $ hg log -G
356 $ hg log -G
357 @ changeset: 2:264128213d29
357 @ changeset: 2:264128213d29
358 | tag: tip
358 | tag: tip
359 | user: test
359 | user: test
360 | date: Thu Jan 01 00:00:00 1970 +0000
360 | date: Thu Jan 01 00:00:00 1970 +0000
361 | summary: c
361 | summary: c
362 |
362 |
363 o changeset: 1:ef3a871183d7
363 o changeset: 1:ef3a871183d7
364 | user: test
364 | user: test
365 | date: Thu Jan 01 00:00:00 1970 +0000
365 | date: Thu Jan 01 00:00:00 1970 +0000
366 | summary: b
366 | summary: b
367 |
367 |
368 o changeset: 0:9ab35a2d17cb
368 o changeset: 0:9ab35a2d17cb
369 user: test
369 user: test
370 date: Thu Jan 01 00:00:00 1970 +0000
370 date: Thu Jan 01 00:00:00 1970 +0000
371 summary: a
371 summary: a
372
372
373 $ restore
373 $ restore
374 $ hg log -G
374 $ hg log -G
375 o changeset: 4:443431ffac4f
375 o changeset: 4:443431ffac4f
376 | tag: tip
376 | tag: tip
377 | user: test
377 | user: test
378 | date: Thu Jan 01 00:00:00 1970 +0000
378 | date: Thu Jan 01 00:00:00 1970 +0000
379 | summary: e
379 | summary: e
380 |
380 |
381 o changeset: 3:65bd5f99a4a3
381 o changeset: 3:65bd5f99a4a3
382 | parent: 1:ef3a871183d7
382 | parent: 1:ef3a871183d7
383 | user: test
383 | user: test
384 | date: Thu Jan 01 00:00:00 1970 +0000
384 | date: Thu Jan 01 00:00:00 1970 +0000
385 | summary: d
385 | summary: d
386 |
386 |
387 | @ changeset: 2:264128213d29
387 | @ changeset: 2:264128213d29
388 |/ user: test
388 |/ user: test
389 | date: Thu Jan 01 00:00:00 1970 +0000
389 | date: Thu Jan 01 00:00:00 1970 +0000
390 | summary: c
390 | summary: c
391 |
391 |
392 o changeset: 1:ef3a871183d7
392 o changeset: 1:ef3a871183d7
393 | user: test
393 | user: test
394 | date: Thu Jan 01 00:00:00 1970 +0000
394 | date: Thu Jan 01 00:00:00 1970 +0000
395 | summary: b
395 | summary: b
396 |
396 |
397 o changeset: 0:9ab35a2d17cb
397 o changeset: 0:9ab35a2d17cb
398 user: test
398 user: test
399 date: Thu Jan 01 00:00:00 1970 +0000
399 date: Thu Jan 01 00:00:00 1970 +0000
400 summary: a
400 summary: a
401
401
402
402
403 2 different branches: 2 strips
403 2 different branches: 2 strips
404
404
405 $ hg strip 2 4
405 $ hg strip 2 4
406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
407 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
408 $ hg log -G
408 $ hg log -G
409 o changeset: 2:65bd5f99a4a3
409 o changeset: 2:65bd5f99a4a3
410 | tag: tip
410 | tag: tip
411 | user: test
411 | user: test
412 | date: Thu Jan 01 00:00:00 1970 +0000
412 | date: Thu Jan 01 00:00:00 1970 +0000
413 | summary: d
413 | summary: d
414 |
414 |
415 @ changeset: 1:ef3a871183d7
415 @ changeset: 1:ef3a871183d7
416 | user: test
416 | user: test
417 | date: Thu Jan 01 00:00:00 1970 +0000
417 | date: Thu Jan 01 00:00:00 1970 +0000
418 | summary: b
418 | summary: b
419 |
419 |
420 o changeset: 0:9ab35a2d17cb
420 o changeset: 0:9ab35a2d17cb
421 user: test
421 user: test
422 date: Thu Jan 01 00:00:00 1970 +0000
422 date: Thu Jan 01 00:00:00 1970 +0000
423 summary: a
423 summary: a
424
424
425 $ restore
425 $ restore
426
426
427 2 different branches and a common ancestor: 1 strip
427 2 different branches and a common ancestor: 1 strip
428
428
429 $ hg strip 1 "2|4"
429 $ hg strip 1 "2|4"
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
431 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
432 $ restore
432 $ restore
433
433
434 verify fncache is kept up-to-date
434 verify fncache is kept up-to-date
435
435
436 $ touch a
436 $ touch a
437 $ hg ci -qAm a
437 $ hg ci -qAm a
438 $ cat .hg/store/fncache | sort
438 $ cat .hg/store/fncache | sort
439 data/a.i
439 data/a.i
440 data/bar.i
440 data/bar.i
441 $ hg strip tip
441 $ hg strip tip
442 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
442 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
443 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
443 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
444 $ cat .hg/store/fncache
444 $ cat .hg/store/fncache
445 data/bar.i
445 data/bar.i
446
446
447 stripping an empty revset
447 stripping an empty revset
448
448
449 $ hg strip "1 and not 1"
449 $ hg strip "1 and not 1"
450 abort: empty revision set
450 abort: empty revision set
451 [255]
451 [255]
452
452
453 remove branchy history for qimport tests
453 remove branchy history for qimport tests
454
454
455 $ hg strip 3
455 $ hg strip 3
456 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
456 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
457
457
458
458
459 strip of applied mq should cleanup status file
459 strip of applied mq should cleanup status file
460
460
461 $ echo "mq=" >> $HGRCPATH
461 $ echo "mq=" >> $HGRCPATH
462 $ hg up -C 3
462 $ hg up -C 3
463 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
463 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 $ echo fooagain >> bar
464 $ echo fooagain >> bar
465 $ hg ci -mf
465 $ hg ci -mf
466 $ hg qimport -r tip:2
466 $ hg qimport -r tip:2
467
467
468 applied patches before strip
468 applied patches before strip
469
469
470 $ hg qapplied
470 $ hg qapplied
471 2.diff
471 2.diff
472 3.diff
472 3.diff
473 4.diff
473 4.diff
474
474
475 stripping revision in queue
475 stripping revision in queue
476
476
477 $ hg strip 3
477 $ hg strip 3
478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
478 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
479 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
479 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
480
480
481 applied patches after stripping rev in queue
481 applied patches after stripping rev in queue
482
482
483 $ hg qapplied
483 $ hg qapplied
484 2.diff
484 2.diff
485
485
486 stripping ancestor of queue
486 stripping ancestor of queue
487
487
488 $ hg strip 1
488 $ hg strip 1
489 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
490 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
490 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
491
491
492 applied patches after stripping ancestor of queue
492 applied patches after stripping ancestor of queue
493
493
494 $ hg qapplied
494 $ hg qapplied
495
495
496 Verify strip protects against stripping wc parent when there are uncommitted mods
496 Verify strip protects against stripping wc parent when there are uncommitted mods
497
497
498 $ echo b > b
498 $ echo b > b
499 $ echo bb > bar
499 $ echo bb > bar
500 $ hg add b
500 $ hg add b
501 $ hg ci -m 'b'
501 $ hg ci -m 'b'
502 $ hg log --graph
502 $ hg log --graph
503 @ changeset: 1:76dcf9fab855
503 @ changeset: 1:76dcf9fab855
504 | tag: tip
504 | tag: tip
505 | user: test
505 | user: test
506 | date: Thu Jan 01 00:00:00 1970 +0000
506 | date: Thu Jan 01 00:00:00 1970 +0000
507 | summary: b
507 | summary: b
508 |
508 |
509 o changeset: 0:9ab35a2d17cb
509 o changeset: 0:9ab35a2d17cb
510 user: test
510 user: test
511 date: Thu Jan 01 00:00:00 1970 +0000
511 date: Thu Jan 01 00:00:00 1970 +0000
512 summary: a
512 summary: a
513
513
514 $ hg up 0
514 $ hg up 0
515 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
515 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
516 $ echo c > bar
516 $ echo c > bar
517 $ hg up -t false
517 $ hg up -t false
518 merging bar
518 merging bar
519 merging bar failed!
519 merging bar failed!
520 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
520 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
521 use 'hg resolve' to retry unresolved file merges
521 use 'hg resolve' to retry unresolved file merges
522 [1]
522 [1]
523 $ hg sum
523 $ hg sum
524 parent: 1:76dcf9fab855 tip
524 parent: 1:76dcf9fab855 tip
525 b
525 b
526 branch: default
526 branch: default
527 commit: 1 modified, 1 unknown, 1 unresolved
527 commit: 1 modified, 1 unknown, 1 unresolved
528 update: (current)
528 update: (current)
529 phases: 2 draft
529 phases: 2 draft
530 mq: 3 unapplied
530 mq: 3 unapplied
531
531
532 $ echo c > b
532 $ echo c > b
533 $ hg strip tip
533 $ hg strip tip
534 abort: local changes found
534 abort: local changes found
535 [255]
535 [255]
536 $ hg strip tip --keep
536 $ hg strip tip --keep
537 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
537 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
538 $ hg log --graph
538 $ hg log --graph
539 @ changeset: 0:9ab35a2d17cb
539 @ changeset: 0:9ab35a2d17cb
540 tag: tip
540 tag: tip
541 user: test
541 user: test
542 date: Thu Jan 01 00:00:00 1970 +0000
542 date: Thu Jan 01 00:00:00 1970 +0000
543 summary: a
543 summary: a
544
544
545 $ hg status
545 $ hg status
546 M bar
546 M bar
547 ? b
547 ? b
548 ? bar.orig
548 ? bar.orig
549
549
550 $ rm bar.orig
550 $ rm bar.orig
551 $ hg sum
551 $ hg sum
552 parent: 0:9ab35a2d17cb tip
552 parent: 0:9ab35a2d17cb tip
553 a
553 a
554 branch: default
554 branch: default
555 commit: 1 modified, 1 unknown
555 commit: 1 modified, 1 unknown
556 update: (current)
556 update: (current)
557 phases: 1 draft
557 phases: 1 draft
558 mq: 3 unapplied
558 mq: 3 unapplied
559
559
560 Strip adds, removes, modifies with --keep
560 Strip adds, removes, modifies with --keep
561
561
562 $ touch b
562 $ touch b
563 $ hg add b
563 $ hg add b
564 $ hg commit -mb
564 $ hg commit -mb
565 $ touch c
565 $ touch c
566
566
567 ... with a clean working dir
567 ... with a clean working dir
568
568
569 $ hg add c
569 $ hg add c
570 $ hg rm bar
570 $ hg rm bar
571 $ hg commit -mc
571 $ hg commit -mc
572 $ hg status
572 $ hg status
573 $ hg strip --keep tip
573 $ hg strip --keep tip
574 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
574 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
575 $ hg status
575 $ hg status
576 ! bar
576 ! bar
577 ? c
577 ? c
578
578
579 ... with a dirty working dir
579 ... with a dirty working dir
580
580
581 $ hg add c
581 $ hg add c
582 $ hg rm bar
582 $ hg rm bar
583 $ hg commit -mc
583 $ hg commit -mc
584 $ hg status
584 $ hg status
585 $ echo b > b
585 $ echo b > b
586 $ echo d > d
586 $ echo d > d
587 $ hg strip --keep tip
587 $ hg strip --keep tip
588 saved backup bundle to $TESTTMP/test/.hg/strip-backup/57e364c8a475-4cfed93c-backup.hg (glob)
588 saved backup bundle to $TESTTMP/test/.hg/strip-backup/57e364c8a475-4cfed93c-backup.hg (glob)
589 $ hg status
589 $ hg status
590 M b
590 M b
591 ! bar
591 ! bar
592 ? c
592 ? c
593 ? d
593 ? d
594 $ cd ..
594 $ cd ..
595
595
596 stripping many nodes on a complex graph (issue3299)
596 stripping many nodes on a complex graph (issue3299)
597
597
598 $ hg init issue3299
598 $ hg init issue3299
599 $ cd issue3299
599 $ cd issue3299
600 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
600 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
601 $ hg strip 'not ancestors(x)'
601 $ hg strip 'not ancestors(x)'
602 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
602 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
603
603
604 test hg strip -B bookmark
604 test hg strip -B bookmark
605
605
606 $ cd ..
606 $ cd ..
607 $ hg init bookmarks
607 $ hg init bookmarks
608 $ cd bookmarks
608 $ cd bookmarks
609 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
609 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b'
610 $ hg bookmark -r 'a' 'todelete'
610 $ hg bookmark -r 'a' 'todelete'
611 $ hg bookmark -r 'b' 'B'
611 $ hg bookmark -r 'b' 'B'
612 $ hg bookmark -r 'b' 'nostrip'
612 $ hg bookmark -r 'b' 'nostrip'
613 $ hg bookmark -r 'c' 'delete'
613 $ hg bookmark -r 'c' 'delete'
614 $ hg up -C todelete
614 $ hg up -C todelete
615 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
615 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
616 (activating bookmark todelete)
616 (activating bookmark todelete)
617 $ hg strip -B nostrip
617 $ hg strip -B nostrip
618 bookmark 'nostrip' deleted
618 bookmark 'nostrip' deleted
619 abort: empty revision set
619 abort: empty revision set
620 [255]
620 [255]
621 $ hg strip -B todelete
621 $ hg strip -B todelete
622 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
622 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
623 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
623 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
624 bookmark 'todelete' deleted
624 bookmark 'todelete' deleted
625 $ hg id -ir dcbb326fdec2
625 $ hg id -ir dcbb326fdec2
626 abort: unknown revision 'dcbb326fdec2'!
626 abort: unknown revision 'dcbb326fdec2'!
627 [255]
627 [255]
628 $ hg id -ir d62d843c9a01
628 $ hg id -ir d62d843c9a01
629 d62d843c9a01
629 d62d843c9a01
630 $ hg bookmarks
630 $ hg bookmarks
631 B 9:ff43616e5d0f
631 B 9:ff43616e5d0f
632 delete 6:2702dd0c91e7
632 delete 6:2702dd0c91e7
633 $ hg strip -B delete
633 $ hg strip -B delete
634 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
634 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
635 bookmark 'delete' deleted
635 bookmark 'delete' deleted
636 $ hg id -ir 6:2702dd0c91e7
636 $ hg id -ir 6:2702dd0c91e7
637 abort: unknown revision '2702dd0c91e7'!
637 abort: unknown revision '2702dd0c91e7'!
638 [255]
638 [255]
639 $ hg update B
639 $ hg update B
640 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
640 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
641 (activating bookmark B)
641 (activating bookmark B)
642 $ echo a > a
642 $ echo a > a
643 $ hg add a
643 $ hg add a
644 $ hg strip -B B
644 $ hg strip -B B
645 abort: local changes found
645 abort: local changes found
646 [255]
646 [255]
647 $ hg bookmarks
647 $ hg bookmarks
648 * B 6:ff43616e5d0f
648 * B 6:ff43616e5d0f
649
649
650 Make sure no one adds back a -b option:
650 Make sure no one adds back a -b option:
651
651
652 $ hg strip -b tip
652 $ hg strip -b tip
653 hg strip: option -b not recognized
653 hg strip: option -b not recognized
654 hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...
654 hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...
655
655
656 strip changesets and all their descendants from the repository
656 strip changesets and all their descendants from the repository
657
657
658 (use "hg help -e strip" to show help for the strip extension)
658 (use "hg help -e strip" to show help for the strip extension)
659
659
660 options ([+] can be repeated):
660 options ([+] can be repeated):
661
661
662 -r --rev REV [+] strip specified revision (optional, can specify revisions
662 -r --rev REV [+] strip specified revision (optional, can specify revisions
663 without this option)
663 without this option)
664 -f --force force removal of changesets, discard uncommitted changes
664 -f --force force removal of changesets, discard uncommitted changes
665 (no backup)
665 (no backup)
666 --no-backup no backups
666 --no-backup no backups
667 -k --keep do not modify working directory during strip
667 -k --keep do not modify working directory during strip
668 -B --bookmark VALUE remove revs only reachable from given bookmark
668 -B --bookmark VALUE remove revs only reachable from given bookmark
669 --mq operate on patch repository
669 --mq operate on patch repository
670
670
671 (use "hg strip -h" to show more help)
671 (use "hg strip -h" to show more help)
672 [255]
672 [255]
673
673
674 $ cd ..
674 $ cd ..
675
675
676 Verify bundles don't get overwritten:
676 Verify bundles don't get overwritten:
677
677
678 $ hg init doublebundle
678 $ hg init doublebundle
679 $ cd doublebundle
679 $ cd doublebundle
680 $ touch a
680 $ touch a
681 $ hg commit -Aqm a
681 $ hg commit -Aqm a
682 $ touch b
682 $ touch b
683 $ hg commit -Aqm b
683 $ hg commit -Aqm b
684 $ hg strip -r 0
684 $ hg strip -r 0
685 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
685 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
686 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
686 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
687 $ ls .hg/strip-backup
687 $ ls .hg/strip-backup
688 3903775176ed-e68910bd-backup.hg
688 3903775176ed-e68910bd-backup.hg
689 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
689 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
690 $ hg strip -r 0
690 $ hg strip -r 0
691 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
691 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
692 $ ls .hg/strip-backup
692 $ ls .hg/strip-backup
693 3903775176ed-54390173-backup.hg
693 3903775176ed-54390173-backup.hg
694 3903775176ed-e68910bd-backup.hg
694 3903775176ed-e68910bd-backup.hg
695 $ cd ..
695 $ cd ..
696
696
697 Test that we only bundle the stripped changesets (issue4736)
697 Test that we only bundle the stripped changesets (issue4736)
698 ------------------------------------------------------------
698 ------------------------------------------------------------
699
699
700 initialisation (previous repo is empty anyway)
700 initialisation (previous repo is empty anyway)
701
701
702 $ hg init issue4736
702 $ hg init issue4736
703 $ cd issue4736
703 $ cd issue4736
704 $ echo a > a
704 $ echo a > a
705 $ hg add a
705 $ hg add a
706 $ hg commit -m commitA
706 $ hg commit -m commitA
707 $ echo b > b
707 $ echo b > b
708 $ hg add b
708 $ hg add b
709 $ hg commit -m commitB
709 $ hg commit -m commitB
710 $ echo c > c
710 $ echo c > c
711 $ hg add c
711 $ hg add c
712 $ hg commit -m commitC
712 $ hg commit -m commitC
713 $ hg up 'desc(commitB)'
713 $ hg up 'desc(commitB)'
714 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
714 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
715 $ echo d > d
715 $ echo d > d
716 $ hg add d
716 $ hg add d
717 $ hg commit -m commitD
717 $ hg commit -m commitD
718 created new head
718 created new head
719 $ hg up 'desc(commitC)'
719 $ hg up 'desc(commitC)'
720 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
720 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
721 $ hg merge 'desc(commitD)'
721 $ hg merge 'desc(commitD)'
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
722 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
723 (branch merge, don't forget to commit)
723 (branch merge, don't forget to commit)
724 $ hg ci -m 'mergeCD'
724 $ hg ci -m 'mergeCD'
725 $ hg log -G
725 $ hg log -G
726 @ changeset: 4:d8db9d137221
726 @ changeset: 4:d8db9d137221
727 |\ tag: tip
727 |\ tag: tip
728 | | parent: 2:5c51d8d6557d
728 | | parent: 2:5c51d8d6557d
729 | | parent: 3:6625a5168474
729 | | parent: 3:6625a5168474
730 | | user: test
730 | | user: test
731 | | date: Thu Jan 01 00:00:00 1970 +0000
731 | | date: Thu Jan 01 00:00:00 1970 +0000
732 | | summary: mergeCD
732 | | summary: mergeCD
733 | |
733 | |
734 | o changeset: 3:6625a5168474
734 | o changeset: 3:6625a5168474
735 | | parent: 1:eca11cf91c71
735 | | parent: 1:eca11cf91c71
736 | | user: test
736 | | user: test
737 | | date: Thu Jan 01 00:00:00 1970 +0000
737 | | date: Thu Jan 01 00:00:00 1970 +0000
738 | | summary: commitD
738 | | summary: commitD
739 | |
739 | |
740 o | changeset: 2:5c51d8d6557d
740 o | changeset: 2:5c51d8d6557d
741 |/ user: test
741 |/ user: test
742 | date: Thu Jan 01 00:00:00 1970 +0000
742 | date: Thu Jan 01 00:00:00 1970 +0000
743 | summary: commitC
743 | summary: commitC
744 |
744 |
745 o changeset: 1:eca11cf91c71
745 o changeset: 1:eca11cf91c71
746 | user: test
746 | user: test
747 | date: Thu Jan 01 00:00:00 1970 +0000
747 | date: Thu Jan 01 00:00:00 1970 +0000
748 | summary: commitB
748 | summary: commitB
749 |
749 |
750 o changeset: 0:105141ef12d0
750 o changeset: 0:105141ef12d0
751 user: test
751 user: test
752 date: Thu Jan 01 00:00:00 1970 +0000
752 date: Thu Jan 01 00:00:00 1970 +0000
753 summary: commitA
753 summary: commitA
754
754
755
755
756 Check bundle behavior:
756 Check bundle behavior:
757
757
758 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
758 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
759 2 changesets found
759 2 changesets found
760 $ hg log -r 'bundle()' -R ../issue4736.hg
760 $ hg log -r 'bundle()' -R ../issue4736.hg
761 changeset: 3:6625a5168474
761 changeset: 3:6625a5168474
762 parent: 1:eca11cf91c71
762 parent: 1:eca11cf91c71
763 user: test
763 user: test
764 date: Thu Jan 01 00:00:00 1970 +0000
764 date: Thu Jan 01 00:00:00 1970 +0000
765 summary: commitD
765 summary: commitD
766
766
767 changeset: 4:d8db9d137221
767 changeset: 4:d8db9d137221
768 tag: tip
768 tag: tip
769 parent: 2:5c51d8d6557d
769 parent: 2:5c51d8d6557d
770 parent: 3:6625a5168474
770 parent: 3:6625a5168474
771 user: test
771 user: test
772 date: Thu Jan 01 00:00:00 1970 +0000
772 date: Thu Jan 01 00:00:00 1970 +0000
773 summary: mergeCD
773 summary: mergeCD
774
774
775
775
776 check strip behavior
776 check strip behavior
777
777
778 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
778 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
779 resolving manifests
779 resolving manifests
780 branchmerge: False, force: True, partial: False
780 branchmerge: False, force: True, partial: False
781 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
781 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
782 c: other deleted -> r
782 c: other deleted -> r
783 removing c
783 removing c
784 d: other deleted -> r
784 d: other deleted -> r
785 removing d
785 removing d
786 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
786 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
787 2 changesets found
787 2 changesets found
788 list of changesets:
788 list of changesets:
789 6625a516847449b6f0fa3737b9ba56e9f0f3032c
789 6625a516847449b6f0fa3737b9ba56e9f0f3032c
790 d8db9d1372214336d2b5570f20ee468d2c72fa8b
790 d8db9d1372214336d2b5570f20ee468d2c72fa8b
791 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
791 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
792 invalid branchheads cache (served): tip differs
792 invalid branchheads cache (served): tip differs
793 truncating cache/rbc-revs-v1 to 24
793 truncating cache/rbc-revs-v1 to 24
794 $ hg log -G
794 $ hg log -G
795 o changeset: 2:5c51d8d6557d
795 o changeset: 2:5c51d8d6557d
796 | tag: tip
796 | tag: tip
797 | user: test
797 | user: test
798 | date: Thu Jan 01 00:00:00 1970 +0000
798 | date: Thu Jan 01 00:00:00 1970 +0000
799 | summary: commitC
799 | summary: commitC
800 |
800 |
801 @ changeset: 1:eca11cf91c71
801 @ changeset: 1:eca11cf91c71
802 | user: test
802 | user: test
803 | date: Thu Jan 01 00:00:00 1970 +0000
803 | date: Thu Jan 01 00:00:00 1970 +0000
804 | summary: commitB
804 | summary: commitB
805 |
805 |
806 o changeset: 0:105141ef12d0
806 o changeset: 0:105141ef12d0
807 user: test
807 user: test
808 date: Thu Jan 01 00:00:00 1970 +0000
808 date: Thu Jan 01 00:00:00 1970 +0000
809 summary: commitA
809 summary: commitA
810
810
811
811
812 strip backup content
812 strip backup content
813
813
814 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
814 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
815 changeset: 3:6625a5168474
815 changeset: 3:6625a5168474
816 parent: 1:eca11cf91c71
816 parent: 1:eca11cf91c71
817 user: test
817 user: test
818 date: Thu Jan 01 00:00:00 1970 +0000
818 date: Thu Jan 01 00:00:00 1970 +0000
819 summary: commitD
819 summary: commitD
820
820
821 changeset: 4:d8db9d137221
821 changeset: 4:d8db9d137221
822 tag: tip
822 tag: tip
823 parent: 2:5c51d8d6557d
823 parent: 2:5c51d8d6557d
824 parent: 3:6625a5168474
824 parent: 3:6625a5168474
825 user: test
825 user: test
826 date: Thu Jan 01 00:00:00 1970 +0000
826 date: Thu Jan 01 00:00:00 1970 +0000
827 summary: mergeCD
827 summary: mergeCD
828
828
829
830 Error during post-close callback of the strip transaction
831 (They should be gracefully handled and reported)
832
833 $ cat > ../crashstrip.py << EOF
834 > from mercurial import error
835 > def reposetup(ui, repo):
836 > class crashstriprepo(repo.__class__):
837 > def transaction(self, desc, *args, **kwargs):
838 > tr = super(crashstriprepo, self).transaction(self, desc, *args, **kwargs)
839 > if desc == 'strip':
840 > def crash(tra): raise error.Abort('boom')
841 > tr.addpostclose('crash', crash)
842 > return tr
843 > repo.__class__ = crashstriprepo
844 > EOF
845 $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py
846 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob)
847 strip failed, full bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg'
848 abort: boom
849 [255]
850
851
General Comments 0
You need to be logged in to leave comments. Login now