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