##// END OF EJS Templates
histedit: limit updated and merging output to important updates...
timeless -
r28004:34165875 default
parent child Browse files
Show More
@@ -1,1524 +1,1535 b''
1 1 # histedit.py - interactive history editing for mercurial
2 2 #
3 3 # Copyright 2009 Augie Fackler <raf@durin42.com>
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 """interactive history editing
8 8
9 9 With this extension installed, Mercurial gains one new command: histedit. Usage
10 10 is as follows, assuming the following history::
11 11
12 12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
13 13 | Add delta
14 14 |
15 15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
16 16 | Add gamma
17 17 |
18 18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
19 19 | Add beta
20 20 |
21 21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
22 22 Add alpha
23 23
24 24 If you were to run ``hg histedit c561b4e977df``, you would see the following
25 25 file open in your editor::
26 26
27 27 pick c561b4e977df Add beta
28 28 pick 030b686bedc4 Add gamma
29 29 pick 7c2fd3b9020c Add delta
30 30
31 31 # Edit history between c561b4e977df and 7c2fd3b9020c
32 32 #
33 33 # Commits are listed from least to most recent
34 34 #
35 35 # Commands:
36 36 # p, pick = use commit
37 37 # e, edit = use commit, but stop for amending
38 38 # f, fold = use commit, but combine it with the one above
39 39 # r, roll = like fold, but discard this commit's description
40 40 # d, drop = remove commit from history
41 41 # m, mess = edit commit message without changing commit content
42 42 #
43 43
44 44 In this file, lines beginning with ``#`` are ignored. You must specify a rule
45 45 for each revision in your history. For example, if you had meant to add gamma
46 46 before beta, and then wanted to add delta in the same revision as beta, you
47 47 would reorganize the file to look like this::
48 48
49 49 pick 030b686bedc4 Add gamma
50 50 pick c561b4e977df Add beta
51 51 fold 7c2fd3b9020c Add delta
52 52
53 53 # Edit history between c561b4e977df and 7c2fd3b9020c
54 54 #
55 55 # Commits are listed from least to most recent
56 56 #
57 57 # Commands:
58 58 # p, pick = use commit
59 59 # e, edit = use commit, but stop for amending
60 60 # f, fold = use commit, but combine it with the one above
61 61 # r, roll = like fold, but discard this commit's description
62 62 # d, drop = remove commit from history
63 63 # m, mess = edit commit message without changing commit content
64 64 #
65 65
66 66 At which point you close the editor and ``histedit`` starts working. When you
67 67 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
68 68 those revisions together, offering you a chance to clean up the commit message::
69 69
70 70 Add beta
71 71 ***
72 72 Add delta
73 73
74 74 Edit the commit message to your liking, then close the editor. For
75 75 this example, let's assume that the commit message was changed to
76 76 ``Add beta and delta.`` After histedit has run and had a chance to
77 77 remove any old or temporary revisions it needed, the history looks
78 78 like this::
79 79
80 80 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
81 81 | Add beta and delta.
82 82 |
83 83 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
84 84 | Add gamma
85 85 |
86 86 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
87 87 Add alpha
88 88
89 89 Note that ``histedit`` does *not* remove any revisions (even its own temporary
90 90 ones) until after it has completed all the editing operations, so it will
91 91 probably perform several strip operations when it's done. For the above example,
92 92 it had to run strip twice. Strip can be slow depending on a variety of factors,
93 93 so you might need to be a little patient. You can choose to keep the original
94 94 revisions by passing the ``--keep`` flag.
95 95
96 96 The ``edit`` operation will drop you back to a command prompt,
97 97 allowing you to edit files freely, or even use ``hg record`` to commit
98 98 some changes as a separate commit. When you're done, any remaining
99 99 uncommitted changes will be committed as well. When done, run ``hg
100 100 histedit --continue`` to finish this step. You'll be prompted for a
101 101 new commit message, but the default commit message will be the
102 102 original message for the ``edit`` ed revision.
103 103
104 104 The ``message`` operation will give you a chance to revise a commit
105 105 message without changing the contents. It's a shortcut for doing
106 106 ``edit`` immediately followed by `hg histedit --continue``.
107 107
108 108 If ``histedit`` encounters a conflict when moving a revision (while
109 109 handling ``pick`` or ``fold``), it'll stop in a similar manner to
110 110 ``edit`` with the difference that it won't prompt you for a commit
111 111 message when done. If you decide at this point that you don't like how
112 112 much work it will be to rearrange history, or that you made a mistake,
113 113 you can use ``hg histedit --abort`` to abandon the new changes you
114 114 have made and return to the state before you attempted to edit your
115 115 history.
116 116
117 117 If we clone the histedit-ed example repository above and add four more
118 118 changes, such that we have the following history::
119 119
120 120 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
121 121 | Add theta
122 122 |
123 123 o 5 140988835471 2009-04-27 18:04 -0500 stefan
124 124 | Add eta
125 125 |
126 126 o 4 122930637314 2009-04-27 18:04 -0500 stefan
127 127 | Add zeta
128 128 |
129 129 o 3 836302820282 2009-04-27 18:04 -0500 stefan
130 130 | Add epsilon
131 131 |
132 132 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
133 133 | Add beta and delta.
134 134 |
135 135 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
136 136 | Add gamma
137 137 |
138 138 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
139 139 Add alpha
140 140
141 141 If you run ``hg histedit --outgoing`` on the clone then it is the same
142 142 as running ``hg histedit 836302820282``. If you need plan to push to a
143 143 repository that Mercurial does not detect to be related to the source
144 144 repo, you can add a ``--force`` option.
145 145
146 146 Config
147 147 ------
148 148
149 149 Histedit rule lines are truncated to 80 characters by default. You
150 150 can customize this behavior by setting a different length in your
151 151 configuration file::
152 152
153 153 [histedit]
154 154 linelen = 120 # truncate rule lines at 120 characters
155 155
156 156 ``hg histedit`` attempts to automatically choose an appropriate base
157 157 revision to use. To change which base revision is used, define a
158 158 revset in your configuration file::
159 159
160 160 [histedit]
161 161 defaultrev = only(.) & draft()
162 162
163 163 By default each edited revision needs to be present in histedit commands.
164 164 To remove revision you need to use ``drop`` operation. You can configure
165 165 the drop to be implicit for missing commits by adding::
166 166
167 167 [histedit]
168 168 dropmissing = True
169 169
170 170 """
171 171
172 172 import pickle
173 173 import errno
174 174 import os
175 175 import sys
176 176
177 177 from mercurial import bundle2
178 178 from mercurial import cmdutil
179 179 from mercurial import discovery
180 180 from mercurial import error
181 181 from mercurial import copies
182 182 from mercurial import context
183 183 from mercurial import destutil
184 184 from mercurial import exchange
185 185 from mercurial import extensions
186 186 from mercurial import hg
187 187 from mercurial import node
188 188 from mercurial import repair
189 189 from mercurial import scmutil
190 190 from mercurial import util
191 191 from mercurial import obsolete
192 192 from mercurial import merge as mergemod
193 193 from mercurial.lock import release
194 194 from mercurial.i18n import _
195 195
196 196 cmdtable = {}
197 197 command = cmdutil.command(cmdtable)
198 198
199 199 class _constraints(object):
200 200 # aborts if there are multiple rules for one node
201 201 noduplicates = 'noduplicates'
202 202 # abort if the node does belong to edited stack
203 203 forceother = 'forceother'
204 204 # abort if the node doesn't belong to edited stack
205 205 noother = 'noother'
206 206
207 207 @classmethod
208 208 def known(cls):
209 209 return set([v for k, v in cls.__dict__.items() if k[0] != '_'])
210 210
211 211 # Note for extension authors: ONLY specify testedwith = 'internal' for
212 212 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
213 213 # be specifying the version(s) of Mercurial they are tested with, or
214 214 # leave the attribute unspecified.
215 215 testedwith = 'internal'
216 216
217 217 actiontable = {}
218 218 primaryactions = set()
219 219 secondaryactions = set()
220 220 tertiaryactions = set()
221 221 internalactions = set()
222 222
223 223 def geteditcomment(first, last):
224 224 """ construct the editor comment
225 225 The comment includes::
226 226 - an intro
227 227 - sorted primary commands
228 228 - sorted short commands
229 229 - sorted long commands
230 230
231 231 Commands are only included once.
232 232 """
233 233 intro = _("""Edit history between %s and %s
234 234
235 235 Commits are listed from least to most recent
236 236
237 237 Commands:
238 238 """)
239 239 actions = []
240 240 def addverb(v):
241 241 a = actiontable[v]
242 242 lines = a.message.split("\n")
243 243 if len(a.verbs):
244 244 v = ', '.join(sorted(a.verbs, key=lambda v: len(v)))
245 245 actions.append(" %s = %s" % (v, lines[0]))
246 246 actions.extend([' %s' for l in lines[1:]])
247 247
248 248 for v in (
249 249 sorted(primaryactions) +
250 250 sorted(secondaryactions) +
251 251 sorted(tertiaryactions)
252 252 ):
253 253 addverb(v)
254 254 actions.append('')
255 255
256 256 return ''.join(['# %s\n' % l if l else '#\n'
257 257 for l in ((intro % (first, last)).split('\n')) + actions])
258 258
259 259 class histeditstate(object):
260 260 def __init__(self, repo, parentctxnode=None, actions=None, keep=None,
261 261 topmost=None, replacements=None, lock=None, wlock=None):
262 262 self.repo = repo
263 263 self.actions = actions
264 264 self.keep = keep
265 265 self.topmost = topmost
266 266 self.parentctxnode = parentctxnode
267 267 self.lock = lock
268 268 self.wlock = wlock
269 269 self.backupfile = None
270 270 if replacements is None:
271 271 self.replacements = []
272 272 else:
273 273 self.replacements = replacements
274 274
275 275 def read(self):
276 276 """Load histedit state from disk and set fields appropriately."""
277 277 try:
278 278 state = self.repo.vfs.read('histedit-state')
279 279 except IOError as err:
280 280 if err.errno != errno.ENOENT:
281 281 raise
282 282 raise error.Abort(_('no histedit in progress'))
283 283
284 284 if state.startswith('v1\n'):
285 285 data = self._load()
286 286 parentctxnode, rules, keep, topmost, replacements, backupfile = data
287 287 else:
288 288 data = pickle.loads(state)
289 289 parentctxnode, rules, keep, topmost, replacements = data
290 290 backupfile = None
291 291
292 292 self.parentctxnode = parentctxnode
293 293 rules = "\n".join(["%s %s" % (verb, rest) for [verb, rest] in rules])
294 294 actions = parserules(rules, self)
295 295 self.actions = actions
296 296 self.keep = keep
297 297 self.topmost = topmost
298 298 self.replacements = replacements
299 299 self.backupfile = backupfile
300 300
301 301 def write(self):
302 302 fp = self.repo.vfs('histedit-state', 'w')
303 303 fp.write('v1\n')
304 304 fp.write('%s\n' % node.hex(self.parentctxnode))
305 305 fp.write('%s\n' % node.hex(self.topmost))
306 306 fp.write('%s\n' % self.keep)
307 307 fp.write('%d\n' % len(self.actions))
308 308 for action in self.actions:
309 309 fp.write('%s\n' % action.tostate())
310 310 fp.write('%d\n' % len(self.replacements))
311 311 for replacement in self.replacements:
312 312 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
313 313 for r in replacement[1])))
314 314 backupfile = self.backupfile
315 315 if not backupfile:
316 316 backupfile = ''
317 317 fp.write('%s\n' % backupfile)
318 318 fp.close()
319 319
320 320 def _load(self):
321 321 fp = self.repo.vfs('histedit-state', 'r')
322 322 lines = [l[:-1] for l in fp.readlines()]
323 323
324 324 index = 0
325 325 lines[index] # version number
326 326 index += 1
327 327
328 328 parentctxnode = node.bin(lines[index])
329 329 index += 1
330 330
331 331 topmost = node.bin(lines[index])
332 332 index += 1
333 333
334 334 keep = lines[index] == 'True'
335 335 index += 1
336 336
337 337 # Rules
338 338 rules = []
339 339 rulelen = int(lines[index])
340 340 index += 1
341 341 for i in xrange(rulelen):
342 342 ruleaction = lines[index]
343 343 index += 1
344 344 rule = lines[index]
345 345 index += 1
346 346 rules.append((ruleaction, rule))
347 347
348 348 # Replacements
349 349 replacements = []
350 350 replacementlen = int(lines[index])
351 351 index += 1
352 352 for i in xrange(replacementlen):
353 353 replacement = lines[index]
354 354 original = node.bin(replacement[:40])
355 355 succ = [node.bin(replacement[i:i + 40]) for i in
356 356 range(40, len(replacement), 40)]
357 357 replacements.append((original, succ))
358 358 index += 1
359 359
360 360 backupfile = lines[index]
361 361 index += 1
362 362
363 363 fp.close()
364 364
365 365 return parentctxnode, rules, keep, topmost, replacements, backupfile
366 366
367 367 def clear(self):
368 368 if self.inprogress():
369 369 self.repo.vfs.unlink('histedit-state')
370 370
371 371 def inprogress(self):
372 372 return self.repo.vfs.exists('histedit-state')
373 373
374 374
375 375 class histeditaction(object):
376 376 def __init__(self, state, node):
377 377 self.state = state
378 378 self.repo = state.repo
379 379 self.node = node
380 380
381 381 @classmethod
382 382 def fromrule(cls, state, rule):
383 383 """Parses the given rule, returning an instance of the histeditaction.
384 384 """
385 385 rulehash = rule.strip().split(' ', 1)[0]
386 386 try:
387 387 rev = node.bin(rulehash)
388 388 except TypeError:
389 389 raise error.ParseError("invalid changeset %s" % rulehash)
390 390 return cls(state, rev)
391 391
392 392 def verify(self, prev):
393 393 """ Verifies semantic correctness of the rule"""
394 394 repo = self.repo
395 395 ha = node.hex(self.node)
396 396 try:
397 397 self.node = repo[ha].node()
398 398 except error.RepoError:
399 399 raise error.ParseError(_('unknown changeset %s listed')
400 400 % ha[:12])
401 401
402 402 def torule(self):
403 403 """build a histedit rule line for an action
404 404
405 405 by default lines are in the form:
406 406 <hash> <rev> <summary>
407 407 """
408 408 ctx = self.repo[self.node]
409 409 summary = ''
410 410 if ctx.description():
411 411 summary = ctx.description().splitlines()[0]
412 412 line = '%s %s %d %s' % (self.verb, ctx, ctx.rev(), summary)
413 413 # trim to 75 columns by default so it's not stupidly wide in my editor
414 414 # (the 5 more are left for verb)
415 415 maxlen = self.repo.ui.configint('histedit', 'linelen', default=80)
416 416 maxlen = max(maxlen, 22) # avoid truncating hash
417 417 return util.ellipsis(line, maxlen)
418 418
419 419 def tostate(self):
420 420 """Print an action in format used by histedit state files
421 421 (the first line is a verb, the remainder is the second)
422 422 """
423 423 return "%s\n%s" % (self.verb, node.hex(self.node))
424 424
425 425 def constraints(self):
426 426 """Return a set of constrains that this action should be verified for
427 427 """
428 428 return set([_constraints.noduplicates, _constraints.noother])
429 429
430 430 def nodetoverify(self):
431 431 """Returns a node associated with the action that will be used for
432 432 verification purposes.
433 433
434 434 If the action doesn't correspond to node it should return None
435 435 """
436 436 return self.node
437 437
438 438 def run(self):
439 439 """Runs the action. The default behavior is simply apply the action's
440 440 rulectx onto the current parentctx."""
441 441 self.applychange()
442 442 self.continuedirty()
443 443 return self.continueclean()
444 444
445 445 def applychange(self):
446 446 """Applies the changes from this action's rulectx onto the current
447 447 parentctx, but does not commit them."""
448 448 repo = self.repo
449 449 rulectx = repo[self.node]
450 repo.ui.pushbuffer(error=True, labeled=True)
450 451 hg.update(repo, self.state.parentctxnode, quietempty=True)
451 452 stats = applychanges(repo.ui, repo, rulectx, {})
452 453 if stats and stats[3] > 0:
454 buf = repo.ui.popbuffer()
455 repo.ui.write(*buf)
453 456 raise error.InterventionRequired(
454 457 _('Fix up the change (%s %s)') %
455 458 (self.verb, node.short(self.node)),
456 459 hint=_('hg histedit --continue to resume'))
460 else:
461 repo.ui.popbuffer()
457 462
458 463 def continuedirty(self):
459 464 """Continues the action when changes have been applied to the working
460 465 copy. The default behavior is to commit the dirty changes."""
461 466 repo = self.repo
462 467 rulectx = repo[self.node]
463 468
464 469 editor = self.commiteditor()
465 470 commit = commitfuncfor(repo, rulectx)
466 471
467 472 commit(text=rulectx.description(), user=rulectx.user(),
468 473 date=rulectx.date(), extra=rulectx.extra(), editor=editor)
469 474
470 475 def commiteditor(self):
471 476 """The editor to be used to edit the commit message."""
472 477 return False
473 478
474 479 def continueclean(self):
475 480 """Continues the action when the working copy is clean. The default
476 481 behavior is to accept the current commit as the new version of the
477 482 rulectx."""
478 483 ctx = self.repo['.']
479 484 if ctx.node() == self.state.parentctxnode:
480 485 self.repo.ui.warn(_('%s: empty changeset\n') %
481 486 node.short(self.node))
482 487 return ctx, [(self.node, tuple())]
483 488 if ctx.node() == self.node:
484 489 # Nothing changed
485 490 return ctx, []
486 491 return ctx, [(self.node, (ctx.node(),))]
487 492
488 493 def commitfuncfor(repo, src):
489 494 """Build a commit function for the replacement of <src>
490 495
491 496 This function ensure we apply the same treatment to all changesets.
492 497
493 498 - Add a 'histedit_source' entry in extra.
494 499
495 500 Note that fold has its own separated logic because its handling is a bit
496 501 different and not easily factored out of the fold method.
497 502 """
498 503 phasemin = src.phase()
499 504 def commitfunc(**kwargs):
500 505 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
501 506 try:
502 507 repo.ui.setconfig('phases', 'new-commit', phasemin,
503 508 'histedit')
504 509 extra = kwargs.get('extra', {}).copy()
505 510 extra['histedit_source'] = src.hex()
506 511 kwargs['extra'] = extra
507 512 return repo.commit(**kwargs)
508 513 finally:
509 514 repo.ui.restoreconfig(phasebackup)
510 515 return commitfunc
511 516
512 517 def applychanges(ui, repo, ctx, opts):
513 518 """Merge changeset from ctx (only) in the current working directory"""
514 519 wcpar = repo.dirstate.parents()[0]
515 520 if ctx.p1().node() == wcpar:
516 521 # edits are "in place" we do not need to make any merge,
517 522 # just applies changes on parent for editing
518 523 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
519 524 stats = None
520 525 else:
521 526 try:
522 527 # ui.forcemerge is an internal variable, do not document
523 528 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
524 529 'histedit')
525 530 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
526 531 finally:
527 532 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
528 533 return stats
529 534
530 535 def collapse(repo, first, last, commitopts, skipprompt=False):
531 536 """collapse the set of revisions from first to last as new one.
532 537
533 538 Expected commit options are:
534 539 - message
535 540 - date
536 541 - username
537 542 Commit message is edited in all cases.
538 543
539 544 This function works in memory."""
540 545 ctxs = list(repo.set('%d::%d', first, last))
541 546 if not ctxs:
542 547 return None
543 548 for c in ctxs:
544 549 if not c.mutable():
545 550 raise error.ParseError(
546 551 _("cannot fold into public change %s") % node.short(c.node()))
547 552 base = first.parents()[0]
548 553
549 554 # commit a new version of the old changeset, including the update
550 555 # collect all files which might be affected
551 556 files = set()
552 557 for ctx in ctxs:
553 558 files.update(ctx.files())
554 559
555 560 # Recompute copies (avoid recording a -> b -> a)
556 561 copied = copies.pathcopies(base, last)
557 562
558 563 # prune files which were reverted by the updates
559 564 def samefile(f):
560 565 if f in last.manifest():
561 566 a = last.filectx(f)
562 567 if f in base.manifest():
563 568 b = base.filectx(f)
564 569 return (a.data() == b.data()
565 570 and a.flags() == b.flags())
566 571 else:
567 572 return False
568 573 else:
569 574 return f not in base.manifest()
570 575 files = [f for f in files if not samefile(f)]
571 576 # commit version of these files as defined by head
572 577 headmf = last.manifest()
573 578 def filectxfn(repo, ctx, path):
574 579 if path in headmf:
575 580 fctx = last[path]
576 581 flags = fctx.flags()
577 582 mctx = context.memfilectx(repo,
578 583 fctx.path(), fctx.data(),
579 584 islink='l' in flags,
580 585 isexec='x' in flags,
581 586 copied=copied.get(path))
582 587 return mctx
583 588 return None
584 589
585 590 if commitopts.get('message'):
586 591 message = commitopts['message']
587 592 else:
588 593 message = first.description()
589 594 user = commitopts.get('user')
590 595 date = commitopts.get('date')
591 596 extra = commitopts.get('extra')
592 597
593 598 parents = (first.p1().node(), first.p2().node())
594 599 editor = None
595 600 if not skipprompt:
596 601 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
597 602 new = context.memctx(repo,
598 603 parents=parents,
599 604 text=message,
600 605 files=files,
601 606 filectxfn=filectxfn,
602 607 user=user,
603 608 date=date,
604 609 extra=extra,
605 610 editor=editor)
606 611 return repo.commitctx(new)
607 612
608 613 def _isdirtywc(repo):
609 614 return repo[None].dirty(missing=True)
610 615
611 616 def abortdirty():
612 617 raise error.Abort(_('working copy has pending changes'),
613 618 hint=_('amend, commit, or revert them and run histedit '
614 619 '--continue, or abort with histedit --abort'))
615 620
616 621 def action(verbs, message, priority=False, internal=False):
617 622 def wrap(cls):
618 623 assert not priority or not internal
619 624 verb = verbs[0]
620 625 if priority:
621 626 primaryactions.add(verb)
622 627 elif internal:
623 628 internalactions.add(verb)
624 629 elif len(verbs) > 1:
625 630 secondaryactions.add(verb)
626 631 else:
627 632 tertiaryactions.add(verb)
628 633
629 634 cls.verb = verb
630 635 cls.verbs = verbs
631 636 cls.message = message
632 637 for verb in verbs:
633 638 actiontable[verb] = cls
634 639 return cls
635 640 return wrap
636 641
637 642 @action(['pick', 'p'],
638 643 _('use commit'),
639 644 priority=True)
640 645 class pick(histeditaction):
641 646 def run(self):
642 647 rulectx = self.repo[self.node]
643 648 if rulectx.parents()[0].node() == self.state.parentctxnode:
644 649 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
645 650 return rulectx, []
646 651
647 652 return super(pick, self).run()
648 653
649 654 @action(['edit', 'e'],
650 655 _('use commit, but stop for amending'),
651 656 priority=True)
652 657 class edit(histeditaction):
653 658 def run(self):
654 659 repo = self.repo
655 660 rulectx = repo[self.node]
656 661 hg.update(repo, self.state.parentctxnode, quietempty=True)
657 662 applychanges(repo.ui, repo, rulectx, {})
658 663 raise error.InterventionRequired(
659 664 _('Editing (%s), you may commit or record as needed now.')
660 665 % node.short(self.node),
661 666 hint=_('hg histedit --continue to resume'))
662 667
663 668 def commiteditor(self):
664 669 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
665 670
666 671 @action(['fold', 'f'],
667 672 _('use commit, but combine it with the one above'))
668 673 class fold(histeditaction):
669 674 def verify(self, prev):
670 675 """ Verifies semantic correctness of the fold rule"""
671 676 super(fold, self).verify(prev)
672 677 repo = self.repo
673 678 if not prev:
674 679 c = repo[self.node].parents()[0]
675 680 elif not prev.verb in ('pick', 'base'):
676 681 return
677 682 else:
678 683 c = repo[prev.node]
679 684 if not c.mutable():
680 685 raise error.ParseError(
681 686 _("cannot fold into public change %s") % node.short(c.node()))
682 687
683 688
684 689 def continuedirty(self):
685 690 repo = self.repo
686 691 rulectx = repo[self.node]
687 692
688 693 commit = commitfuncfor(repo, rulectx)
689 694 commit(text='fold-temp-revision %s' % node.short(self.node),
690 695 user=rulectx.user(), date=rulectx.date(),
691 696 extra=rulectx.extra())
692 697
693 698 def continueclean(self):
694 699 repo = self.repo
695 700 ctx = repo['.']
696 701 rulectx = repo[self.node]
697 702 parentctxnode = self.state.parentctxnode
698 703 if ctx.node() == parentctxnode:
699 704 repo.ui.warn(_('%s: empty changeset\n') %
700 705 node.short(self.node))
701 706 return ctx, [(self.node, (parentctxnode,))]
702 707
703 708 parentctx = repo[parentctxnode]
704 709 newcommits = set(c.node() for c in repo.set('(%d::. - %d)', parentctx,
705 710 parentctx))
706 711 if not newcommits:
707 712 repo.ui.warn(_('%s: cannot fold - working copy is not a '
708 713 'descendant of previous commit %s\n') %
709 714 (node.short(self.node), node.short(parentctxnode)))
710 715 return ctx, [(self.node, (ctx.node(),))]
711 716
712 717 middlecommits = newcommits.copy()
713 718 middlecommits.discard(ctx.node())
714 719
715 720 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
716 721 middlecommits)
717 722
718 723 def skipprompt(self):
719 724 """Returns true if the rule should skip the message editor.
720 725
721 726 For example, 'fold' wants to show an editor, but 'rollup'
722 727 doesn't want to.
723 728 """
724 729 return False
725 730
726 731 def mergedescs(self):
727 732 """Returns true if the rule should merge messages of multiple changes.
728 733
729 734 This exists mainly so that 'rollup' rules can be a subclass of
730 735 'fold'.
731 736 """
732 737 return True
733 738
734 739 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
735 740 parent = ctx.parents()[0].node()
741 repo.ui.pushbuffer()
736 742 hg.update(repo, parent)
743 repo.ui.popbuffer()
737 744 ### prepare new commit data
738 745 commitopts = {}
739 746 commitopts['user'] = ctx.user()
740 747 # commit message
741 748 if not self.mergedescs():
742 749 newmessage = ctx.description()
743 750 else:
744 751 newmessage = '\n***\n'.join(
745 752 [ctx.description()] +
746 753 [repo[r].description() for r in internalchanges] +
747 754 [oldctx.description()]) + '\n'
748 755 commitopts['message'] = newmessage
749 756 # date
750 757 commitopts['date'] = max(ctx.date(), oldctx.date())
751 758 extra = ctx.extra().copy()
752 759 # histedit_source
753 760 # note: ctx is likely a temporary commit but that the best we can do
754 761 # here. This is sufficient to solve issue3681 anyway.
755 762 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
756 763 commitopts['extra'] = extra
757 764 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
758 765 try:
759 766 phasemin = max(ctx.phase(), oldctx.phase())
760 767 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
761 768 n = collapse(repo, ctx, repo[newnode], commitopts,
762 769 skipprompt=self.skipprompt())
763 770 finally:
764 771 repo.ui.restoreconfig(phasebackup)
765 772 if n is None:
766 773 return ctx, []
774 repo.ui.pushbuffer()
767 775 hg.update(repo, n)
776 repo.ui.popbuffer()
768 777 replacements = [(oldctx.node(), (newnode,)),
769 778 (ctx.node(), (n,)),
770 779 (newnode, (n,)),
771 780 ]
772 781 for ich in internalchanges:
773 782 replacements.append((ich, (n,)))
774 783 return repo[n], replacements
775 784
776 785 class base(histeditaction):
777 786 def constraints(self):
778 787 return set([_constraints.forceother])
779 788
780 789 def run(self):
781 790 if self.repo['.'].node() != self.node:
782 791 mergemod.update(self.repo, self.node, False, True)
783 792 # branchmerge, force)
784 793 return self.continueclean()
785 794
786 795 def continuedirty(self):
787 796 abortdirty()
788 797
789 798 def continueclean(self):
790 799 basectx = self.repo['.']
791 800 return basectx, []
792 801
793 802 @action(['_multifold'],
794 803 _(
795 804 """fold subclass used for when multiple folds happen in a row
796 805
797 806 We only want to fire the editor for the folded message once when
798 807 (say) four changes are folded down into a single change. This is
799 808 similar to rollup, but we should preserve both messages so that
800 809 when the last fold operation runs we can show the user all the
801 810 commit messages in their editor.
802 811 """),
803 812 internal=True)
804 813 class _multifold(fold):
805 814 def skipprompt(self):
806 815 return True
807 816
808 817 @action(["roll", "r"],
809 818 _("like fold, but discard this commit's description"))
810 819 class rollup(fold):
811 820 def mergedescs(self):
812 821 return False
813 822
814 823 def skipprompt(self):
815 824 return True
816 825
817 826 @action(["drop", "d"],
818 827 _('remove commit from history'))
819 828 class drop(histeditaction):
820 829 def run(self):
821 830 parentctx = self.repo[self.state.parentctxnode]
822 831 return parentctx, [(self.node, tuple())]
823 832
824 833 @action(["mess", "m"],
825 834 _('edit commit message without changing commit content'),
826 835 priority=True)
827 836 class message(histeditaction):
828 837 def commiteditor(self):
829 838 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
830 839
831 840 def findoutgoing(ui, repo, remote=None, force=False, opts=None):
832 841 """utility function to find the first outgoing changeset
833 842
834 843 Used by initialization code"""
835 844 if opts is None:
836 845 opts = {}
837 846 dest = ui.expandpath(remote or 'default-push', remote or 'default')
838 847 dest, revs = hg.parseurl(dest, None)[:2]
839 848 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
840 849
841 850 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
842 851 other = hg.peer(repo, opts, dest)
843 852
844 853 if revs:
845 854 revs = [repo.lookup(rev) for rev in revs]
846 855
847 856 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
848 857 if not outgoing.missing:
849 858 raise error.Abort(_('no outgoing ancestors'))
850 859 roots = list(repo.revs("roots(%ln)", outgoing.missing))
851 860 if 1 < len(roots):
852 861 msg = _('there are ambiguous outgoing revisions')
853 862 hint = _('see "hg help histedit" for more detail')
854 863 raise error.Abort(msg, hint=hint)
855 864 return repo.lookup(roots[0])
856 865
857 866
858 867 @command('histedit',
859 868 [('', 'commands', '',
860 869 _('read history edits from the specified file'), _('FILE')),
861 870 ('c', 'continue', False, _('continue an edit already in progress')),
862 871 ('', 'edit-plan', False, _('edit remaining actions list')),
863 872 ('k', 'keep', False,
864 873 _("don't strip old nodes after edit is complete")),
865 874 ('', 'abort', False, _('abort an edit in progress')),
866 875 ('o', 'outgoing', False, _('changesets not found in destination')),
867 876 ('f', 'force', False,
868 877 _('force outgoing even for unrelated repositories')),
869 878 ('r', 'rev', [], _('first revision to be edited'), _('REV'))],
870 879 _("[OPTIONS] ([ANCESTOR] | --outgoing [URL])"))
871 880 def histedit(ui, repo, *freeargs, **opts):
872 881 """interactively edit changeset history
873 882
874 883 This command lets you edit a linear series of changesets (up to
875 884 and including the working directory, which should be clean).
876 885 You can:
877 886
878 887 - `pick` to [re]order a changeset
879 888
880 889 - `drop` to omit changeset
881 890
882 891 - `mess` to reword the changeset commit message
883 892
884 893 - `fold` to combine it with the preceding changeset
885 894
886 895 - `roll` like fold, but discarding this commit's description
887 896
888 897 - `edit` to edit this changeset
889 898
890 899 There are a number of ways to select the root changeset:
891 900
892 901 - Specify ANCESTOR directly
893 902
894 903 - Use --outgoing -- it will be the first linear changeset not
895 904 included in destination. (See :hg:`help config.default-push`)
896 905
897 906 - Otherwise, the value from the "histedit.defaultrev" config option
898 907 is used as a revset to select the base revision when ANCESTOR is not
899 908 specified. The first revision returned by the revset is used. By
900 909 default, this selects the editable history that is unique to the
901 910 ancestry of the working directory.
902 911
903 912 .. container:: verbose
904 913
905 914 If you use --outgoing, this command will abort if there are ambiguous
906 915 outgoing revisions. For example, if there are multiple branches
907 916 containing outgoing revisions.
908 917
909 918 Use "min(outgoing() and ::.)" or similar revset specification
910 919 instead of --outgoing to specify edit target revision exactly in
911 920 such ambiguous situation. See :hg:`help revsets` for detail about
912 921 selecting revisions.
913 922
914 923 .. container:: verbose
915 924
916 925 Examples:
917 926
918 927 - A number of changes have been made.
919 928 Revision 3 is no longer needed.
920 929
921 930 Start history editing from revision 3::
922 931
923 932 hg histedit -r 3
924 933
925 934 An editor opens, containing the list of revisions,
926 935 with specific actions specified::
927 936
928 937 pick 5339bf82f0ca 3 Zworgle the foobar
929 938 pick 8ef592ce7cc4 4 Bedazzle the zerlog
930 939 pick 0a9639fcda9d 5 Morgify the cromulancy
931 940
932 941 Additional information about the possible actions
933 942 to take appears below the list of revisions.
934 943
935 944 To remove revision 3 from the history,
936 945 its action (at the beginning of the relevant line)
937 946 is changed to 'drop'::
938 947
939 948 drop 5339bf82f0ca 3 Zworgle the foobar
940 949 pick 8ef592ce7cc4 4 Bedazzle the zerlog
941 950 pick 0a9639fcda9d 5 Morgify the cromulancy
942 951
943 952 - A number of changes have been made.
944 953 Revision 2 and 4 need to be swapped.
945 954
946 955 Start history editing from revision 2::
947 956
948 957 hg histedit -r 2
949 958
950 959 An editor opens, containing the list of revisions,
951 960 with specific actions specified::
952 961
953 962 pick 252a1af424ad 2 Blorb a morgwazzle
954 963 pick 5339bf82f0ca 3 Zworgle the foobar
955 964 pick 8ef592ce7cc4 4 Bedazzle the zerlog
956 965
957 966 To swap revision 2 and 4, its lines are swapped
958 967 in the editor::
959 968
960 969 pick 8ef592ce7cc4 4 Bedazzle the zerlog
961 970 pick 5339bf82f0ca 3 Zworgle the foobar
962 971 pick 252a1af424ad 2 Blorb a morgwazzle
963 972
964 973 Returns 0 on success, 1 if user intervention is required (not only
965 974 for intentional "edit" command, but also for resolving unexpected
966 975 conflicts).
967 976 """
968 977 state = histeditstate(repo)
969 978 try:
970 979 state.wlock = repo.wlock()
971 980 state.lock = repo.lock()
972 981 _histedit(ui, repo, state, *freeargs, **opts)
973 982 finally:
974 983 release(state.lock, state.wlock)
975 984
976 985 def _histedit(ui, repo, state, *freeargs, **opts):
977 986 # TODO only abort if we try to histedit mq patches, not just
978 987 # blanket if mq patches are applied somewhere
979 988 mq = getattr(repo, 'mq', None)
980 989 if mq and mq.applied:
981 990 raise error.Abort(_('source has mq patches applied'))
982 991
983 992 # basic argument incompatibility processing
984 993 outg = opts.get('outgoing')
985 994 cont = opts.get('continue')
986 995 editplan = opts.get('edit_plan')
987 996 abort = opts.get('abort')
988 997 force = opts.get('force')
989 998 rules = opts.get('commands', '')
990 999 revs = opts.get('rev', [])
991 1000 goal = 'new' # This invocation goal, in new, continue, abort
992 1001 if force and not outg:
993 1002 raise error.Abort(_('--force only allowed with --outgoing'))
994 1003 if cont:
995 1004 if any((outg, abort, revs, freeargs, rules, editplan)):
996 1005 raise error.Abort(_('no arguments allowed with --continue'))
997 1006 goal = 'continue'
998 1007 elif abort:
999 1008 if any((outg, revs, freeargs, rules, editplan)):
1000 1009 raise error.Abort(_('no arguments allowed with --abort'))
1001 1010 goal = 'abort'
1002 1011 elif editplan:
1003 1012 if any((outg, revs, freeargs)):
1004 1013 raise error.Abort(_('only --commands argument allowed with '
1005 1014 '--edit-plan'))
1006 1015 goal = 'edit-plan'
1007 1016 else:
1008 1017 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1009 1018 raise error.Abort(_('history edit already in progress, try '
1010 1019 '--continue or --abort'))
1011 1020 if outg:
1012 1021 if revs:
1013 1022 raise error.Abort(_('no revisions allowed with --outgoing'))
1014 1023 if len(freeargs) > 1:
1015 1024 raise error.Abort(
1016 1025 _('only one repo argument allowed with --outgoing'))
1017 1026 else:
1018 1027 revs.extend(freeargs)
1019 1028 if len(revs) == 0:
1020 1029 defaultrev = destutil.desthistedit(ui, repo)
1021 1030 if defaultrev is not None:
1022 1031 revs.append(defaultrev)
1023 1032
1024 1033 if len(revs) != 1:
1025 1034 raise error.Abort(
1026 1035 _('histedit requires exactly one ancestor revision'))
1027 1036
1028 1037
1029 1038 replacements = []
1030 1039 state.keep = opts.get('keep', False)
1031 1040 supportsmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
1032 1041
1033 1042 # rebuild state
1034 1043 if goal == 'continue':
1035 1044 state.read()
1036 1045 state = bootstrapcontinue(ui, state, opts)
1037 1046 elif goal == 'edit-plan':
1038 1047 state.read()
1039 1048 if not rules:
1040 1049 comment = geteditcomment(node.short(state.parentctxnode),
1041 1050 node.short(state.topmost))
1042 1051 rules = ruleeditor(repo, ui, state.actions, comment)
1043 1052 else:
1044 1053 if rules == '-':
1045 1054 f = sys.stdin
1046 1055 else:
1047 1056 f = open(rules)
1048 1057 rules = f.read()
1049 1058 f.close()
1050 1059 actions = parserules(rules, state)
1051 1060 ctxs = [repo[act.nodetoverify()] \
1052 1061 for act in state.actions if act.nodetoverify()]
1053 1062 warnverifyactions(ui, repo, actions, state, ctxs)
1054 1063 state.actions = actions
1055 1064 state.write()
1056 1065 return
1057 1066 elif goal == 'abort':
1058 1067 try:
1059 1068 state.read()
1060 1069 tmpnodes, leafs = newnodestoabort(state)
1061 1070 ui.debug('restore wc to old parent %s\n'
1062 1071 % node.short(state.topmost))
1063 1072
1064 1073 # Recover our old commits if necessary
1065 1074 if not state.topmost in repo and state.backupfile:
1066 1075 backupfile = repo.join(state.backupfile)
1067 1076 f = hg.openpath(ui, backupfile)
1068 1077 gen = exchange.readbundle(ui, f, backupfile)
1069 1078 with repo.transaction('histedit.abort') as tr:
1070 1079 if not isinstance(gen, bundle2.unbundle20):
1071 1080 gen.apply(repo, 'histedit', 'bundle:' + backupfile)
1072 1081 if isinstance(gen, bundle2.unbundle20):
1073 1082 bundle2.applybundle(repo, gen, tr,
1074 1083 source='histedit',
1075 1084 url='bundle:' + backupfile)
1076 1085
1077 1086 os.remove(backupfile)
1078 1087
1079 1088 # check whether we should update away
1080 1089 if repo.unfiltered().revs('parents() and (%n or %ln::)',
1081 1090 state.parentctxnode, leafs | tmpnodes):
1082 1091 hg.clean(repo, state.topmost, show_stats=True, quietempty=True)
1083 1092 cleanupnode(ui, repo, 'created', tmpnodes)
1084 1093 cleanupnode(ui, repo, 'temp', leafs)
1085 1094 except Exception:
1086 1095 if state.inprogress():
1087 1096 ui.warn(_('warning: encountered an exception during histedit '
1088 1097 '--abort; the repository may not have been completely '
1089 1098 'cleaned up\n'))
1090 1099 raise
1091 1100 finally:
1092 1101 state.clear()
1093 1102 return
1094 1103 else:
1095 1104 cmdutil.checkunfinished(repo)
1096 1105 cmdutil.bailifchanged(repo)
1097 1106
1098 1107 topmost, empty = repo.dirstate.parents()
1099 1108 if outg:
1100 1109 if freeargs:
1101 1110 remote = freeargs[0]
1102 1111 else:
1103 1112 remote = None
1104 1113 root = findoutgoing(ui, repo, remote, force, opts)
1105 1114 else:
1106 1115 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
1107 1116 if len(rr) != 1:
1108 1117 raise error.Abort(_('The specified revisions must have '
1109 1118 'exactly one common root'))
1110 1119 root = rr[0].node()
1111 1120
1112 1121 revs = between(repo, root, topmost, state.keep)
1113 1122 if not revs:
1114 1123 raise error.Abort(_('%s is not an ancestor of working directory') %
1115 1124 node.short(root))
1116 1125
1117 1126 ctxs = [repo[r] for r in revs]
1118 1127 if not rules:
1119 1128 comment = geteditcomment(node.short(root), node.short(topmost))
1120 1129 actions = [pick(state, r) for r in revs]
1121 1130 rules = ruleeditor(repo, ui, actions, comment)
1122 1131 else:
1123 1132 if rules == '-':
1124 1133 f = sys.stdin
1125 1134 else:
1126 1135 f = open(rules)
1127 1136 rules = f.read()
1128 1137 f.close()
1129 1138 actions = parserules(rules, state)
1130 1139 warnverifyactions(ui, repo, actions, state, ctxs)
1131 1140
1132 1141 parentctxnode = repo[root].parents()[0].node()
1133 1142
1134 1143 state.parentctxnode = parentctxnode
1135 1144 state.actions = actions
1136 1145 state.topmost = topmost
1137 1146 state.replacements = replacements
1138 1147
1139 1148 # Create a backup so we can always abort completely.
1140 1149 backupfile = None
1141 1150 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
1142 1151 backupfile = repair._bundle(repo, [parentctxnode], [topmost], root,
1143 1152 'histedit')
1144 1153 state.backupfile = backupfile
1145 1154
1146 1155 # preprocess rules so that we can hide inner folds from the user
1147 1156 # and only show one editor
1148 1157 actions = state.actions[:]
1149 1158 for idx, (action, nextact) in enumerate(
1150 1159 zip(actions, actions[1:] + [None])):
1151 1160 if action.verb == 'fold' and nextact and nextact.verb == 'fold':
1152 1161 state.actions[idx].__class__ = _multifold
1153 1162
1154 1163 total = len(state.actions)
1155 1164 pos = 0
1156 1165 while state.actions:
1157 1166 state.write()
1158 1167 actobj = state.actions.pop(0)
1159 1168 pos += 1
1160 1169 ui.progress(_("editing"), pos, actobj.torule(),
1161 1170 _('changes'), total)
1162 1171 ui.debug('histedit: processing %s %s\n' % (actobj.verb,\
1163 1172 actobj.torule()))
1164 1173 parentctx, replacement_ = actobj.run()
1165 1174 state.parentctxnode = parentctx.node()
1166 1175 state.replacements.extend(replacement_)
1167 1176 state.write()
1168 1177 ui.progress(_("editing"), None)
1169 1178
1179 repo.ui.pushbuffer()
1170 1180 hg.update(repo, state.parentctxnode, quietempty=True)
1181 repo.ui.popbuffer()
1171 1182
1172 1183 mapping, tmpnodes, created, ntm = processreplacement(state)
1173 1184 if mapping:
1174 1185 for prec, succs in mapping.iteritems():
1175 1186 if not succs:
1176 1187 ui.debug('histedit: %s is dropped\n' % node.short(prec))
1177 1188 else:
1178 1189 ui.debug('histedit: %s is replaced by %s\n' % (
1179 1190 node.short(prec), node.short(succs[0])))
1180 1191 if len(succs) > 1:
1181 1192 m = 'histedit: %s'
1182 1193 for n in succs[1:]:
1183 1194 ui.debug(m % node.short(n))
1184 1195
1185 1196 if supportsmarkers:
1186 1197 # Only create markers if the temp nodes weren't already removed.
1187 1198 obsolete.createmarkers(repo, ((repo[t],()) for t in sorted(tmpnodes)
1188 1199 if t in repo))
1189 1200 else:
1190 1201 cleanupnode(ui, repo, 'temp', tmpnodes)
1191 1202
1192 1203 if not state.keep:
1193 1204 if mapping:
1194 1205 movebookmarks(ui, repo, mapping, state.topmost, ntm)
1195 1206 # TODO update mq state
1196 1207 if supportsmarkers:
1197 1208 markers = []
1198 1209 # sort by revision number because it sound "right"
1199 1210 for prec in sorted(mapping, key=repo.changelog.rev):
1200 1211 succs = mapping[prec]
1201 1212 markers.append((repo[prec],
1202 1213 tuple(repo[s] for s in succs)))
1203 1214 if markers:
1204 1215 obsolete.createmarkers(repo, markers)
1205 1216 else:
1206 1217 cleanupnode(ui, repo, 'replaced', mapping)
1207 1218
1208 1219 state.clear()
1209 1220 if os.path.exists(repo.sjoin('undo')):
1210 1221 os.unlink(repo.sjoin('undo'))
1211 1222 if repo.vfs.exists('histedit-last-edit.txt'):
1212 1223 repo.vfs.unlink('histedit-last-edit.txt')
1213 1224
1214 1225 def bootstrapcontinue(ui, state, opts):
1215 1226 repo = state.repo
1216 1227 if state.actions:
1217 1228 actobj = state.actions.pop(0)
1218 1229
1219 1230 if _isdirtywc(repo):
1220 1231 actobj.continuedirty()
1221 1232 if _isdirtywc(repo):
1222 1233 abortdirty()
1223 1234
1224 1235 parentctx, replacements = actobj.continueclean()
1225 1236
1226 1237 state.parentctxnode = parentctx.node()
1227 1238 state.replacements.extend(replacements)
1228 1239
1229 1240 return state
1230 1241
1231 1242 def between(repo, old, new, keep):
1232 1243 """select and validate the set of revision to edit
1233 1244
1234 1245 When keep is false, the specified set can't have children."""
1235 1246 ctxs = list(repo.set('%n::%n', old, new))
1236 1247 if ctxs and not keep:
1237 1248 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
1238 1249 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
1239 1250 raise error.Abort(_('cannot edit history that would orphan nodes'))
1240 1251 if repo.revs('(%ld) and merge()', ctxs):
1241 1252 raise error.Abort(_('cannot edit history that contains merges'))
1242 1253 root = ctxs[0] # list is already sorted by repo.set
1243 1254 if not root.mutable():
1244 1255 raise error.Abort(_('cannot edit public changeset: %s') % root,
1245 1256 hint=_('see "hg help phases" for details'))
1246 1257 return [c.node() for c in ctxs]
1247 1258
1248 1259 def ruleeditor(repo, ui, actions, editcomment=""):
1249 1260 """open an editor to edit rules
1250 1261
1251 1262 rules are in the format [ [act, ctx], ...] like in state.rules
1252 1263 """
1253 1264 rules = '\n'.join([act.torule() for act in actions])
1254 1265 rules += '\n\n'
1255 1266 rules += editcomment
1256 1267 rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'})
1257 1268
1258 1269 # Save edit rules in .hg/histedit-last-edit.txt in case
1259 1270 # the user needs to ask for help after something
1260 1271 # surprising happens.
1261 1272 f = open(repo.join('histedit-last-edit.txt'), 'w')
1262 1273 f.write(rules)
1263 1274 f.close()
1264 1275
1265 1276 return rules
1266 1277
1267 1278 def parserules(rules, state):
1268 1279 """Read the histedit rules string and return list of action objects """
1269 1280 rules = [l for l in (r.strip() for r in rules.splitlines())
1270 1281 if l and not l.startswith('#')]
1271 1282 actions = []
1272 1283 for r in rules:
1273 1284 if ' ' not in r:
1274 1285 raise error.ParseError(_('malformed line "%s"') % r)
1275 1286 verb, rest = r.split(' ', 1)
1276 1287
1277 1288 if verb not in actiontable:
1278 1289 raise error.ParseError(_('unknown action "%s"') % verb)
1279 1290
1280 1291 action = actiontable[verb].fromrule(state, rest)
1281 1292 actions.append(action)
1282 1293 return actions
1283 1294
1284 1295 def warnverifyactions(ui, repo, actions, state, ctxs):
1285 1296 try:
1286 1297 verifyactions(actions, state, ctxs)
1287 1298 except error.ParseError:
1288 1299 if repo.vfs.exists('histedit-last-edit.txt'):
1289 1300 ui.warn(_('warning: histedit rules saved '
1290 1301 'to: .hg/histedit-last-edit.txt\n'))
1291 1302 raise
1292 1303
1293 1304 def verifyactions(actions, state, ctxs):
1294 1305 """Verify that there exists exactly one action per given changeset and
1295 1306 other constraints.
1296 1307
1297 1308 Will abort if there are to many or too few rules, a malformed rule,
1298 1309 or a rule on a changeset outside of the user-given range.
1299 1310 """
1300 1311 expected = set(c.hex() for c in ctxs)
1301 1312 seen = set()
1302 1313 prev = None
1303 1314 for action in actions:
1304 1315 action.verify(prev)
1305 1316 prev = action
1306 1317 constraints = action.constraints()
1307 1318 for constraint in constraints:
1308 1319 if constraint not in _constraints.known():
1309 1320 raise error.ParseError(_('unknown constraint "%s"') %
1310 1321 constraint)
1311 1322
1312 1323 nodetoverify = action.nodetoverify()
1313 1324 if nodetoverify is not None:
1314 1325 ha = node.hex(nodetoverify)
1315 1326 if _constraints.noother in constraints and ha not in expected:
1316 1327 raise error.ParseError(
1317 1328 _('%s "%s" changeset was not a candidate')
1318 1329 % (action.verb, ha[:12]),
1319 1330 hint=_('only use listed changesets'))
1320 1331 if _constraints.forceother in constraints and ha in expected:
1321 1332 raise error.ParseError(
1322 1333 _('%s "%s" changeset was not an edited list candidate')
1323 1334 % (action.verb, ha[:12]),
1324 1335 hint=_('only use listed changesets'))
1325 1336 if _constraints.noduplicates in constraints and ha in seen:
1326 1337 raise error.ParseError(_(
1327 1338 'duplicated command for changeset %s') %
1328 1339 ha[:12])
1329 1340 seen.add(ha)
1330 1341 missing = sorted(expected - seen) # sort to stabilize output
1331 1342
1332 1343 if state.repo.ui.configbool('histedit', 'dropmissing'):
1333 1344 drops = [drop(state, node.bin(n)) for n in missing]
1334 1345 # put the in the beginning so they execute immediately and
1335 1346 # don't show in the edit-plan in the future
1336 1347 actions[:0] = drops
1337 1348 elif missing:
1338 1349 raise error.ParseError(_('missing rules for changeset %s') %
1339 1350 missing[0][:12],
1340 1351 hint=_('use "drop %s" to discard, see also: '
1341 1352 '"hg help -e histedit.config"') % missing[0][:12])
1342 1353
1343 1354 def newnodestoabort(state):
1344 1355 """process the list of replacements to return
1345 1356
1346 1357 1) the list of final node
1347 1358 2) the list of temporary node
1348 1359
1349 1360 This is meant to be used on abort as less data are required in this case.
1350 1361 """
1351 1362 replacements = state.replacements
1352 1363 allsuccs = set()
1353 1364 replaced = set()
1354 1365 for rep in replacements:
1355 1366 allsuccs.update(rep[1])
1356 1367 replaced.add(rep[0])
1357 1368 newnodes = allsuccs - replaced
1358 1369 tmpnodes = allsuccs & replaced
1359 1370 return newnodes, tmpnodes
1360 1371
1361 1372
1362 1373 def processreplacement(state):
1363 1374 """process the list of replacements to return
1364 1375
1365 1376 1) the final mapping between original and created nodes
1366 1377 2) the list of temporary node created by histedit
1367 1378 3) the list of new commit created by histedit"""
1368 1379 replacements = state.replacements
1369 1380 allsuccs = set()
1370 1381 replaced = set()
1371 1382 fullmapping = {}
1372 1383 # initialize basic set
1373 1384 # fullmapping records all operations recorded in replacement
1374 1385 for rep in replacements:
1375 1386 allsuccs.update(rep[1])
1376 1387 replaced.add(rep[0])
1377 1388 fullmapping.setdefault(rep[0], set()).update(rep[1])
1378 1389 new = allsuccs - replaced
1379 1390 tmpnodes = allsuccs & replaced
1380 1391 # Reduce content fullmapping into direct relation between original nodes
1381 1392 # and final node created during history edition
1382 1393 # Dropped changeset are replaced by an empty list
1383 1394 toproceed = set(fullmapping)
1384 1395 final = {}
1385 1396 while toproceed:
1386 1397 for x in list(toproceed):
1387 1398 succs = fullmapping[x]
1388 1399 for s in list(succs):
1389 1400 if s in toproceed:
1390 1401 # non final node with unknown closure
1391 1402 # We can't process this now
1392 1403 break
1393 1404 elif s in final:
1394 1405 # non final node, replace with closure
1395 1406 succs.remove(s)
1396 1407 succs.update(final[s])
1397 1408 else:
1398 1409 final[x] = succs
1399 1410 toproceed.remove(x)
1400 1411 # remove tmpnodes from final mapping
1401 1412 for n in tmpnodes:
1402 1413 del final[n]
1403 1414 # we expect all changes involved in final to exist in the repo
1404 1415 # turn `final` into list (topologically sorted)
1405 1416 nm = state.repo.changelog.nodemap
1406 1417 for prec, succs in final.items():
1407 1418 final[prec] = sorted(succs, key=nm.get)
1408 1419
1409 1420 # computed topmost element (necessary for bookmark)
1410 1421 if new:
1411 1422 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
1412 1423 elif not final:
1413 1424 # Nothing rewritten at all. we won't need `newtopmost`
1414 1425 # It is the same as `oldtopmost` and `processreplacement` know it
1415 1426 newtopmost = None
1416 1427 else:
1417 1428 # every body died. The newtopmost is the parent of the root.
1418 1429 r = state.repo.changelog.rev
1419 1430 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
1420 1431
1421 1432 return final, tmpnodes, new, newtopmost
1422 1433
1423 1434 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
1424 1435 """Move bookmark from old to newly created node"""
1425 1436 if not mapping:
1426 1437 # if nothing got rewritten there is not purpose for this function
1427 1438 return
1428 1439 moves = []
1429 1440 for bk, old in sorted(repo._bookmarks.iteritems()):
1430 1441 if old == oldtopmost:
1431 1442 # special case ensure bookmark stay on tip.
1432 1443 #
1433 1444 # This is arguably a feature and we may only want that for the
1434 1445 # active bookmark. But the behavior is kept compatible with the old
1435 1446 # version for now.
1436 1447 moves.append((bk, newtopmost))
1437 1448 continue
1438 1449 base = old
1439 1450 new = mapping.get(base, None)
1440 1451 if new is None:
1441 1452 continue
1442 1453 while not new:
1443 1454 # base is killed, trying with parent
1444 1455 base = repo[base].p1().node()
1445 1456 new = mapping.get(base, (base,))
1446 1457 # nothing to move
1447 1458 moves.append((bk, new[-1]))
1448 1459 if moves:
1449 1460 lock = tr = None
1450 1461 try:
1451 1462 lock = repo.lock()
1452 1463 tr = repo.transaction('histedit')
1453 1464 marks = repo._bookmarks
1454 1465 for mark, new in moves:
1455 1466 old = marks[mark]
1456 1467 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
1457 1468 % (mark, node.short(old), node.short(new)))
1458 1469 marks[mark] = new
1459 1470 marks.recordchange(tr)
1460 1471 tr.close()
1461 1472 finally:
1462 1473 release(tr, lock)
1463 1474
1464 1475 def cleanupnode(ui, repo, name, nodes):
1465 1476 """strip a group of nodes from the repository
1466 1477
1467 1478 The set of node to strip may contains unknown nodes."""
1468 1479 ui.debug('should strip %s nodes %s\n' %
1469 1480 (name, ', '.join([node.short(n) for n in nodes])))
1470 1481 with repo.lock():
1471 1482 # do not let filtering get in the way of the cleanse
1472 1483 # we should probably get rid of obsolescence marker created during the
1473 1484 # histedit, but we currently do not have such information.
1474 1485 repo = repo.unfiltered()
1475 1486 # Find all nodes that need to be stripped
1476 1487 # (we use %lr instead of %ln to silently ignore unknown items)
1477 1488 nm = repo.changelog.nodemap
1478 1489 nodes = sorted(n for n in nodes if n in nm)
1479 1490 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
1480 1491 for c in roots:
1481 1492 # We should process node in reverse order to strip tip most first.
1482 1493 # but this trigger a bug in changegroup hook.
1483 1494 # This would reduce bundle overhead
1484 1495 repair.strip(ui, repo, c)
1485 1496
1486 1497 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
1487 1498 if isinstance(nodelist, str):
1488 1499 nodelist = [nodelist]
1489 1500 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1490 1501 state = histeditstate(repo)
1491 1502 state.read()
1492 1503 histedit_nodes = set([action.nodetoverify() for action
1493 1504 in state.actions if action.nodetoverify()])
1494 1505 strip_nodes = set([repo[n].node() for n in nodelist])
1495 1506 common_nodes = histedit_nodes & strip_nodes
1496 1507 if common_nodes:
1497 1508 raise error.Abort(_("histedit in progress, can't strip %s")
1498 1509 % ', '.join(node.short(x) for x in common_nodes))
1499 1510 return orig(ui, repo, nodelist, *args, **kwargs)
1500 1511
1501 1512 extensions.wrapfunction(repair, 'strip', stripwrapper)
1502 1513
1503 1514 def summaryhook(ui, repo):
1504 1515 if not os.path.exists(repo.join('histedit-state')):
1505 1516 return
1506 1517 state = histeditstate(repo)
1507 1518 state.read()
1508 1519 if state.actions:
1509 1520 # i18n: column positioning for "hg summary"
1510 1521 ui.write(_('hist: %s (histedit --continue)\n') %
1511 1522 (ui.label(_('%d remaining'), 'histedit.remaining') %
1512 1523 len(state.actions)))
1513 1524
1514 1525 def extsetup(ui):
1515 1526 cmdutil.summaryhooks.add('histedit', summaryhook)
1516 1527 cmdutil.unfinishedstates.append(
1517 1528 ['histedit-state', False, True, _('histedit in progress'),
1518 1529 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
1519 1530 cmdutil.afterresolvedstates.append(
1520 1531 ['histedit-state', _('hg histedit --continue')])
1521 1532 if ui.configbool("experimental", "histeditng"):
1522 1533 globals()['base'] = action(['base', 'b'],
1523 1534 _('checkout changeset and apply further changesets from there')
1524 1535 )(base)
@@ -1,451 +1,447 b''
1 1 Test argument handling and various data parsing
2 2 ==================================================
3 3
4 4
5 5 Enable extensions used by this test.
6 6 $ cat >>$HGRCPATH <<EOF
7 7 > [extensions]
8 8 > histedit=
9 9 > EOF
10 10
11 11 Repo setup.
12 12 $ hg init foo
13 13 $ cd foo
14 14 $ echo alpha >> alpha
15 15 $ hg addr
16 16 adding alpha
17 17 $ hg ci -m one
18 18 $ echo alpha >> alpha
19 19 $ hg ci -m two
20 20 $ echo alpha >> alpha
21 21 $ hg ci -m three
22 22 $ echo alpha >> alpha
23 23 $ hg ci -m four
24 24 $ echo alpha >> alpha
25 25 $ hg ci -m five
26 26
27 27 $ hg log --style compact --graph
28 28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
29 29 | five
30 30 |
31 31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
32 32 | four
33 33 |
34 34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
35 35 | three
36 36 |
37 37 o 1 579e40513370 1970-01-01 00:00 +0000 test
38 38 | two
39 39 |
40 40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
41 41 one
42 42
43 43
44 44 histedit --continue/--abort with no existing state
45 45 --------------------------------------------------
46 46
47 47 $ hg histedit --continue
48 48 abort: no histedit in progress
49 49 [255]
50 50 $ hg histedit --abort
51 51 abort: no histedit in progress
52 52 [255]
53 53
54 54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
55 55 --------------------------------------------------------------------
56 56
57 57 $ HGEDITOR=cat hg histedit "tip^^"
58 58 pick eb57da33312f 2 three
59 59 pick c8e68270e35a 3 four
60 60 pick 08d98a8350f3 4 five
61 61
62 62 # Edit history between eb57da33312f and 08d98a8350f3
63 63 #
64 64 # Commits are listed from least to most recent
65 65 #
66 66 # Commands:
67 67 #
68 68 # e, edit = use commit, but stop for amending
69 69 # m, mess = edit commit message without changing commit content
70 70 # p, pick = use commit
71 71 # d, drop = remove commit from history
72 72 # f, fold = use commit, but combine it with the one above
73 73 # r, roll = like fold, but discard this commit's description
74 74 #
75 75
76 76 Run on a revision not ancestors of the current working directory.
77 77 --------------------------------------------------------------------
78 78
79 79 $ hg up 2
80 80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 $ hg histedit -r 4
82 82 abort: 08d98a8350f3 is not an ancestor of working directory
83 83 [255]
84 84 $ hg up --quiet
85 85
86 86
87 87 Test that we pick the minimum of a revrange
88 88 ---------------------------------------
89 89
90 90 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
91 91 > pick eb57da33312f 2 three
92 92 > pick c8e68270e35a 3 four
93 93 > pick 08d98a8350f3 4 five
94 94 > EOF
95 95 $ hg up --quiet
96 96
97 97 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
98 98 > pick eb57da33312f 2 three
99 99 > pick c8e68270e35a 3 four
100 100 > pick 08d98a8350f3 4 five
101 101 > EOF
102 102 $ hg up --quiet
103 103
104 104 Test config specified default
105 105 -----------------------------
106 106
107 107 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
108 108 > pick c8e68270e35a 3 four
109 109 > pick 08d98a8350f3 4 five
110 110 > EOF
111 111
112 112 Run on a revision not descendants of the initial parent
113 113 --------------------------------------------------------------------
114 114
115 115 Test the message shown for inconsistent histedit state, which may be
116 116 created (and forgotten) by Mercurial earlier than 2.7. This emulates
117 117 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
118 118 temporarily.
119 119
120 120 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
121 121 @ 4 08d9 five
122 122 |
123 123 o 3 c8e6 four
124 124 |
125 125 o 2 eb57 three
126 126 |
127 127 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
128 128 > edit 08d98a8350f3 4 five
129 129 > EOF
130 130 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
131 131 reverting alpha
132 132 Editing (08d98a8350f3), you may commit or record as needed now.
133 133 (hg histedit --continue to resume)
134 134 [1]
135 135
136 136 $ mv .hg/histedit-state .hg/histedit-state.back
137 137 $ hg update --quiet --clean 2
138 138 $ echo alpha >> alpha
139 139 $ mv .hg/histedit-state.back .hg/histedit-state
140 140
141 141 $ hg histedit --continue
142 142 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg (glob)
143 143 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
144 144 @ 4 f5ed five
145 145 |
146 146 | o 3 c8e6 four
147 147 |/
148 148 o 2 eb57 three
149 149 |
150 150
151 151 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg
152 152 $ hg strip -q -r f5ed --config extensions.strip=
153 153 $ hg up -q 08d98a8350f3
154 154
155 155 Test that missing revisions are detected
156 156 ---------------------------------------
157 157
158 158 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
159 159 > pick eb57da33312f 2 three
160 160 > pick 08d98a8350f3 4 five
161 161 > EOF
162 162 hg: parse error: missing rules for changeset c8e68270e35a
163 163 (use "drop c8e68270e35a" to discard, see also: "hg help -e histedit.config")
164 164 [255]
165 165
166 166 Test that extra revisions are detected
167 167 ---------------------------------------
168 168
169 169 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
170 170 > pick 6058cbb6cfd7 0 one
171 171 > pick c8e68270e35a 3 four
172 172 > pick 08d98a8350f3 4 five
173 173 > EOF
174 174 hg: parse error: pick "6058cbb6cfd7" changeset was not a candidate
175 175 (only use listed changesets)
176 176 [255]
177 177
178 178 Test malformed line
179 179 ---------------------------------------
180 180
181 181 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
182 182 > pickeb57da33312f2three
183 183 > pick c8e68270e35a 3 four
184 184 > pick 08d98a8350f3 4 five
185 185 > EOF
186 186 hg: parse error: malformed line "pickeb57da33312f2three"
187 187 [255]
188 188
189 189 Test unknown changeset
190 190 ---------------------------------------
191 191
192 192 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
193 193 > pick 0123456789ab 2 three
194 194 > pick c8e68270e35a 3 four
195 195 > pick 08d98a8350f3 4 five
196 196 > EOF
197 197 hg: parse error: unknown changeset 0123456789ab listed
198 198 [255]
199 199
200 200 Test unknown command
201 201 ---------------------------------------
202 202
203 203 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
204 204 > coin eb57da33312f 2 three
205 205 > pick c8e68270e35a 3 four
206 206 > pick 08d98a8350f3 4 five
207 207 > EOF
208 208 hg: parse error: unknown action "coin"
209 209 [255]
210 210
211 211 Test duplicated changeset
212 212 ---------------------------------------
213 213
214 214 So one is missing and one appear twice.
215 215
216 216 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
217 217 > pick eb57da33312f 2 three
218 218 > pick eb57da33312f 2 three
219 219 > pick 08d98a8350f3 4 five
220 220 > EOF
221 221 hg: parse error: duplicated command for changeset eb57da33312f
222 222 [255]
223 223
224 224 Test bogus rev
225 225 ---------------------------------------
226 226
227 227 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
228 228 > pick eb57da33312f 2 three
229 229 > pick 0
230 230 > pick 08d98a8350f3 4 five
231 231 > EOF
232 232 hg: parse error: invalid changeset 0
233 233 [255]
234 234
235 235 Test short version of command
236 236 ---------------------------------------
237 237
238 238 Note: we use varying amounts of white space between command name and changeset
239 239 short hash. This tests issue3893.
240 240
241 241 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
242 242 > pick eb57da33312f 2 three
243 243 > p c8e68270e35a 3 four
244 244 > f 08d98a8350f3 4 five
245 245 > EOF
246 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
247 reverting alpha
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 246 four
250 247 ***
251 248 five
252 249
253 250
254 251
255 252 HG: Enter commit message. Lines beginning with 'HG:' are removed.
256 253 HG: Leave message empty to abort commit.
257 254 HG: --
258 255 HG: user: test
259 256 HG: branch 'default'
260 257 HG: changed alpha
261 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
262 258 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
263 259 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
264 260
265 261 $ hg update -q 2
266 262 $ echo x > x
267 263 $ hg add x
268 264 $ hg commit -m'x' x
269 265 created new head
270 266 $ hg histedit -r 'heads(all())'
271 267 abort: The specified revisions must have exactly one common root
272 268 [255]
273 269
274 270 Test that trimming description using multi-byte characters
275 271 --------------------------------------------------------------------
276 272
277 273 $ python <<EOF
278 274 > fp = open('logfile', 'w')
279 275 > fp.write('12345678901234567890123456789012345678901234567890' +
280 276 > '12345') # there are 5 more columns for 80 columns
281 277 >
282 278 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
283 279 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
284 280 >
285 281 > fp.close()
286 282 > EOF
287 283 $ echo xx >> x
288 284 $ hg --encoding utf-8 commit --logfile logfile
289 285
290 286 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
291 287 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
292 288
293 289 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
294 290 #
295 291 # Commits are listed from least to most recent
296 292 #
297 293 # Commands:
298 294 #
299 295 # e, edit = use commit, but stop for amending
300 296 # m, mess = edit commit message without changing commit content
301 297 # p, pick = use commit
302 298 # d, drop = remove commit from history
303 299 # f, fold = use commit, but combine it with the one above
304 300 # r, roll = like fold, but discard this commit's description
305 301 #
306 302
307 303 Test --continue with --keep
308 304
309 305 $ hg strip -q -r . --config extensions.strip=
310 306 $ hg histedit '.^' -q --keep --commands - << EOF
311 307 > edit eb57da33312f 2 three
312 308 > pick f3cfcca30c44 4 x
313 309 > EOF
314 310 Editing (eb57da33312f), you may commit or record as needed now.
315 311 (hg histedit --continue to resume)
316 312 [1]
317 313 $ echo edit >> alpha
318 314 $ hg histedit -q --continue
319 315 $ hg log -G -T '{rev}:{node|short} {desc}'
320 316 @ 6:8fda0c726bf2 x
321 317 |
322 318 o 5:63379946892c three
323 319 |
324 320 | o 4:f3cfcca30c44 x
325 321 | |
326 322 | | o 3:2a30f3cfee78 four
327 323 | |/ ***
328 324 | | five
329 325 | o 2:eb57da33312f three
330 326 |/
331 327 o 1:579e40513370 two
332 328 |
333 329 o 0:6058cbb6cfd7 one
334 330
335 331
336 332 Test that abort fails gracefully on exception
337 333 ----------------------------------------------
338 334 $ hg histedit . -q --commands - << EOF
339 335 > edit 8fda0c726bf2 6 x
340 336 > EOF
341 337 Editing (8fda0c726bf2), you may commit or record as needed now.
342 338 (hg histedit --continue to resume)
343 339 [1]
344 340 Corrupt histedit state file
345 341 $ sed 's/8fda0c726bf2/123456789012/' .hg/histedit-state > ../corrupt-histedit
346 342 $ mv ../corrupt-histedit .hg/histedit-state
347 343 $ hg histedit --abort
348 344 warning: encountered an exception during histedit --abort; the repository may not have been completely cleaned up
349 345 abort: .*(No such file or directory:|The system cannot find the file specified).* (re)
350 346 [255]
351 347 Histedit state has been exited
352 348 $ hg summary -q
353 349 parent: 5:63379946892c
354 350 commit: 1 added, 1 unknown (new branch head)
355 351 update: 4 new changesets (update)
356 352
357 353 $ cd ..
358 354
359 355 Set up default base revision tests
360 356
361 357 $ hg init defaultbase
362 358 $ cd defaultbase
363 359 $ touch foo
364 360 $ hg -q commit -A -m root
365 361 $ echo 1 > foo
366 362 $ hg commit -m 'public 1'
367 363 $ hg phase --force --public -r .
368 364 $ echo 2 > foo
369 365 $ hg commit -m 'draft after public'
370 366 $ hg -q up -r 1
371 367 $ echo 3 > foo
372 368 $ hg commit -m 'head 1 public'
373 369 created new head
374 370 $ hg phase --force --public -r .
375 371 $ echo 4 > foo
376 372 $ hg commit -m 'head 1 draft 1'
377 373 $ echo 5 > foo
378 374 $ hg commit -m 'head 1 draft 2'
379 375 $ hg -q up -r 2
380 376 $ echo 6 > foo
381 377 $ hg commit -m 'head 2 commit 1'
382 378 $ echo 7 > foo
383 379 $ hg commit -m 'head 2 commit 2'
384 380 $ hg -q up -r 2
385 381 $ echo 8 > foo
386 382 $ hg commit -m 'head 3'
387 383 created new head
388 384 $ hg -q up -r 2
389 385 $ echo 9 > foo
390 386 $ hg commit -m 'head 4'
391 387 created new head
392 388 $ hg merge --tool :local -r 8
393 389 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
394 390 (branch merge, don't forget to commit)
395 391 $ hg commit -m 'merge head 3 into head 4'
396 392 $ echo 11 > foo
397 393 $ hg commit -m 'commit 1 after merge'
398 394 $ echo 12 > foo
399 395 $ hg commit -m 'commit 2 after merge'
400 396
401 397 $ hg log -G -T '{rev}:{node|short} {phase} {desc}\n'
402 398 @ 12:8cde254db839 draft commit 2 after merge
403 399 |
404 400 o 11:6f2f0241f119 draft commit 1 after merge
405 401 |
406 402 o 10:90506cc76b00 draft merge head 3 into head 4
407 403 |\
408 404 | o 9:f8607a373a97 draft head 4
409 405 | |
410 406 o | 8:0da92be05148 draft head 3
411 407 |/
412 408 | o 7:4c35cdf97d5e draft head 2 commit 2
413 409 | |
414 410 | o 6:931820154288 draft head 2 commit 1
415 411 |/
416 412 | o 5:8cdc02b9bc63 draft head 1 draft 2
417 413 | |
418 414 | o 4:463b8c0d2973 draft head 1 draft 1
419 415 | |
420 416 | o 3:23a0c4eefcbf public head 1 public
421 417 | |
422 418 o | 2:4117331c3abb draft draft after public
423 419 |/
424 420 o 1:4426d359ea59 public public 1
425 421 |
426 422 o 0:54136a8ddf32 public root
427 423
428 424
429 425 Default base revision should stop at public changesets
430 426
431 427 $ hg -q up 8cdc02b9bc63
432 428 $ hg histedit --commands - <<EOF
433 429 > pick 463b8c0d2973
434 430 > pick 8cdc02b9bc63
435 431 > EOF
436 432
437 433 Default base revision should stop at branchpoint
438 434
439 435 $ hg -q up 4c35cdf97d5e
440 436 $ hg histedit --commands - <<EOF
441 437 > pick 931820154288
442 438 > pick 4c35cdf97d5e
443 439 > EOF
444 440
445 441 Default base revision should stop at merge commit
446 442
447 443 $ hg -q up 8cde254db839
448 444 $ hg histedit --commands - <<EOF
449 445 > pick 6f2f0241f119
450 446 > pick 8cde254db839
451 447 > EOF
@@ -1,459 +1,450 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > EOF
7 7
8 8 $ initrepo ()
9 9 > {
10 10 > hg init r
11 11 > cd r
12 12 > for x in a b c d e f ; do
13 13 > echo $x > $x
14 14 > hg add $x
15 15 > hg ci -m $x
16 16 > done
17 17 > }
18 18
19 19 $ initrepo
20 20
21 21 log before edit
22 22 $ hg log --graph
23 23 @ changeset: 5:652413bf663e
24 24 | tag: tip
25 25 | user: test
26 26 | date: Thu Jan 01 00:00:00 1970 +0000
27 27 | summary: f
28 28 |
29 29 o changeset: 4:e860deea161a
30 30 | user: test
31 31 | date: Thu Jan 01 00:00:00 1970 +0000
32 32 | summary: e
33 33 |
34 34 o changeset: 3:055a42cdd887
35 35 | user: test
36 36 | date: Thu Jan 01 00:00:00 1970 +0000
37 37 | summary: d
38 38 |
39 39 o changeset: 2:177f92b77385
40 40 | user: test
41 41 | date: Thu Jan 01 00:00:00 1970 +0000
42 42 | summary: c
43 43 |
44 44 o changeset: 1:d2ae7f538514
45 45 | user: test
46 46 | date: Thu Jan 01 00:00:00 1970 +0000
47 47 | summary: b
48 48 |
49 49 o changeset: 0:cb9a9f314b8b
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: a
53 53
54 54
55 55 show the edit commands offered
56 56 $ HGEDITOR=cat hg histedit 177f92b77385
57 57 pick 177f92b77385 2 c
58 58 pick 055a42cdd887 3 d
59 59 pick e860deea161a 4 e
60 60 pick 652413bf663e 5 f
61 61
62 62 # Edit history between 177f92b77385 and 652413bf663e
63 63 #
64 64 # Commits are listed from least to most recent
65 65 #
66 66 # Commands:
67 67 #
68 68 # e, edit = use commit, but stop for amending
69 69 # m, mess = edit commit message without changing commit content
70 70 # p, pick = use commit
71 71 # d, drop = remove commit from history
72 72 # f, fold = use commit, but combine it with the one above
73 73 # r, roll = like fold, but discard this commit's description
74 74 #
75 75
76 76 edit the history
77 77 (use a hacky editor to check histedit-last-edit.txt backup)
78 78
79 79 $ EDITED="$TESTTMP/editedhistory"
80 80 $ cat > $EDITED <<EOF
81 81 > edit 177f92b77385 c
82 82 > pick e860deea161a e
83 83 > pick 652413bf663e f
84 84 > pick 055a42cdd887 d
85 85 > EOF
86 86 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
87 87 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
88 88 Editing (177f92b77385), you may commit or record as needed now.
89 89 (hg histedit --continue to resume)
90 90
91 91 rules should end up in .hg/histedit-last-edit.txt:
92 92 $ cat .hg/histedit-last-edit.txt
93 93 edit 177f92b77385 c
94 94 pick e860deea161a e
95 95 pick 652413bf663e f
96 96 pick 055a42cdd887 d
97 97
98 98 $ hg histedit --abort
99 99 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 100 $ cat > $EDITED <<EOF
101 101 > pick 177f92b77385 c
102 102 > pick e860deea161a e
103 103 > pick 652413bf663e f
104 104 > pick 055a42cdd887 d
105 105 > EOF
106 106 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
107 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
108 107
109 108 log after edit
110 109 $ hg log --graph
111 110 @ changeset: 5:07114f51870f
112 111 | tag: tip
113 112 | user: test
114 113 | date: Thu Jan 01 00:00:00 1970 +0000
115 114 | summary: d
116 115 |
117 116 o changeset: 4:8ade9693061e
118 117 | user: test
119 118 | date: Thu Jan 01 00:00:00 1970 +0000
120 119 | summary: f
121 120 |
122 121 o changeset: 3:d8249471110a
123 122 | user: test
124 123 | date: Thu Jan 01 00:00:00 1970 +0000
125 124 | summary: e
126 125 |
127 126 o changeset: 2:177f92b77385
128 127 | user: test
129 128 | date: Thu Jan 01 00:00:00 1970 +0000
130 129 | summary: c
131 130 |
132 131 o changeset: 1:d2ae7f538514
133 132 | user: test
134 133 | date: Thu Jan 01 00:00:00 1970 +0000
135 134 | summary: b
136 135 |
137 136 o changeset: 0:cb9a9f314b8b
138 137 user: test
139 138 date: Thu Jan 01 00:00:00 1970 +0000
140 139 summary: a
141 140
142 141
143 142 put things back
144 143
145 144 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
146 145 > pick 177f92b77385 c
147 146 > pick 07114f51870f d
148 147 > pick d8249471110a e
149 148 > pick 8ade9693061e f
150 149 > EOF
151 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
152 150
153 151 $ hg log --graph
154 152 @ changeset: 5:7eca9b5b1148
155 153 | tag: tip
156 154 | user: test
157 155 | date: Thu Jan 01 00:00:00 1970 +0000
158 156 | summary: f
159 157 |
160 158 o changeset: 4:915da888f2de
161 159 | user: test
162 160 | date: Thu Jan 01 00:00:00 1970 +0000
163 161 | summary: e
164 162 |
165 163 o changeset: 3:10517e47bbbb
166 164 | user: test
167 165 | date: Thu Jan 01 00:00:00 1970 +0000
168 166 | summary: d
169 167 |
170 168 o changeset: 2:177f92b77385
171 169 | user: test
172 170 | date: Thu Jan 01 00:00:00 1970 +0000
173 171 | summary: c
174 172 |
175 173 o changeset: 1:d2ae7f538514
176 174 | user: test
177 175 | date: Thu Jan 01 00:00:00 1970 +0000
178 176 | summary: b
179 177 |
180 178 o changeset: 0:cb9a9f314b8b
181 179 user: test
182 180 date: Thu Jan 01 00:00:00 1970 +0000
183 181 summary: a
184 182
185 183
186 184 slightly different this time
187 185
188 186 $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle
189 187 > pick 10517e47bbbb d
190 188 > pick 7eca9b5b1148 f
191 189 > pick 915da888f2de e
192 190 > pick 177f92b77385 c
193 191 > EOF
194 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
195 192 $ hg log --graph
196 193 @ changeset: 5:38b92f448761
197 194 | tag: tip
198 195 | user: test
199 196 | date: Thu Jan 01 00:00:00 1970 +0000
200 197 | summary: c
201 198 |
202 199 o changeset: 4:de71b079d9ce
203 200 | user: test
204 201 | date: Thu Jan 01 00:00:00 1970 +0000
205 202 | summary: e
206 203 |
207 204 o changeset: 3:be9ae3a309c6
208 205 | user: test
209 206 | date: Thu Jan 01 00:00:00 1970 +0000
210 207 | summary: f
211 208 |
212 209 o changeset: 2:799205341b6b
213 210 | user: test
214 211 | date: Thu Jan 01 00:00:00 1970 +0000
215 212 | summary: d
216 213 |
217 214 o changeset: 1:d2ae7f538514
218 215 | user: test
219 216 | date: Thu Jan 01 00:00:00 1970 +0000
220 217 | summary: b
221 218 |
222 219 o changeset: 0:cb9a9f314b8b
223 220 user: test
224 221 date: Thu Jan 01 00:00:00 1970 +0000
225 222 summary: a
226 223
227 224
228 225 keep prevents stripping dead revs
229 226 $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle
230 227 > pick 799205341b6b d
231 228 > pick be9ae3a309c6 f
232 229 > pick 38b92f448761 c
233 230 > pick de71b079d9ce e
234 231 > EOF
235 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
236 232 $ hg log --graph
237 233 @ changeset: 7:803ef1c6fcfd
238 234 | tag: tip
239 235 | user: test
240 236 | date: Thu Jan 01 00:00:00 1970 +0000
241 237 | summary: e
242 238 |
243 239 o changeset: 6:ece0b8d93dda
244 240 | parent: 3:be9ae3a309c6
245 241 | user: test
246 242 | date: Thu Jan 01 00:00:00 1970 +0000
247 243 | summary: c
248 244 |
249 245 | o changeset: 5:38b92f448761
250 246 | | user: test
251 247 | | date: Thu Jan 01 00:00:00 1970 +0000
252 248 | | summary: c
253 249 | |
254 250 | o changeset: 4:de71b079d9ce
255 251 |/ user: test
256 252 | date: Thu Jan 01 00:00:00 1970 +0000
257 253 | summary: e
258 254 |
259 255 o changeset: 3:be9ae3a309c6
260 256 | user: test
261 257 | date: Thu Jan 01 00:00:00 1970 +0000
262 258 | summary: f
263 259 |
264 260 o changeset: 2:799205341b6b
265 261 | user: test
266 262 | date: Thu Jan 01 00:00:00 1970 +0000
267 263 | summary: d
268 264 |
269 265 o changeset: 1:d2ae7f538514
270 266 | user: test
271 267 | date: Thu Jan 01 00:00:00 1970 +0000
272 268 | summary: b
273 269 |
274 270 o changeset: 0:cb9a9f314b8b
275 271 user: test
276 272 date: Thu Jan 01 00:00:00 1970 +0000
277 273 summary: a
278 274
279 275
280 276 try with --rev
281 277 $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle
282 278 > pick de71b079d9ce e
283 279 > pick 38b92f448761 c
284 280 > EOF
285 281 hg: parse error: pick "de71b079d9ce" changeset was not a candidate
286 282 (only use listed changesets)
287 283 $ hg log --graph
288 284 @ changeset: 7:803ef1c6fcfd
289 285 | tag: tip
290 286 | user: test
291 287 | date: Thu Jan 01 00:00:00 1970 +0000
292 288 | summary: e
293 289 |
294 290 o changeset: 6:ece0b8d93dda
295 291 | parent: 3:be9ae3a309c6
296 292 | user: test
297 293 | date: Thu Jan 01 00:00:00 1970 +0000
298 294 | summary: c
299 295 |
300 296 | o changeset: 5:38b92f448761
301 297 | | user: test
302 298 | | date: Thu Jan 01 00:00:00 1970 +0000
303 299 | | summary: c
304 300 | |
305 301 | o changeset: 4:de71b079d9ce
306 302 |/ user: test
307 303 | date: Thu Jan 01 00:00:00 1970 +0000
308 304 | summary: e
309 305 |
310 306 o changeset: 3:be9ae3a309c6
311 307 | user: test
312 308 | date: Thu Jan 01 00:00:00 1970 +0000
313 309 | summary: f
314 310 |
315 311 o changeset: 2:799205341b6b
316 312 | user: test
317 313 | date: Thu Jan 01 00:00:00 1970 +0000
318 314 | summary: d
319 315 |
320 316 o changeset: 1:d2ae7f538514
321 317 | user: test
322 318 | date: Thu Jan 01 00:00:00 1970 +0000
323 319 | summary: b
324 320 |
325 321 o changeset: 0:cb9a9f314b8b
326 322 user: test
327 323 date: Thu Jan 01 00:00:00 1970 +0000
328 324 summary: a
329 325
330 326 Verify that revsetalias entries work with histedit:
331 327 $ cat >> $HGRCPATH <<EOF
332 328 > [revsetalias]
333 329 > grandparent(ARG) = p1(p1(ARG))
334 330 > EOF
335 331 $ echo extra commit >> c
336 332 $ hg ci -m 'extra commit to c'
337 333 $ HGEDITOR=cat hg histedit 'grandparent(.)'
338 334 pick ece0b8d93dda 6 c
339 335 pick 803ef1c6fcfd 7 e
340 336 pick 9c863c565126 8 extra commit to c
341 337
342 338 # Edit history between ece0b8d93dda and 9c863c565126
343 339 #
344 340 # Commits are listed from least to most recent
345 341 #
346 342 # Commands:
347 343 #
348 344 # e, edit = use commit, but stop for amending
349 345 # m, mess = edit commit message without changing commit content
350 346 # p, pick = use commit
351 347 # d, drop = remove commit from history
352 348 # f, fold = use commit, but combine it with the one above
353 349 # r, roll = like fold, but discard this commit's description
354 350 #
355 351
356 352 should also work if a commit message is missing
357 353 $ BUNDLE="$TESTDIR/missing-comment.hg"
358 354 $ hg init missing
359 355 $ cd missing
360 356 $ hg unbundle $BUNDLE
361 357 adding changesets
362 358 adding manifests
363 359 adding file changes
364 360 added 3 changesets with 3 changes to 1 files
365 361 (run 'hg update' to get a working copy)
366 362 $ hg co tip
367 363 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
368 364 $ hg log --graph
369 365 @ changeset: 2:bd22688093b3
370 366 | tag: tip
371 367 | user: Robert Altman <robert.altman@telventDTN.com>
372 368 | date: Mon Nov 28 16:40:04 2011 +0000
373 369 | summary: Update file.
374 370 |
375 371 o changeset: 1:3b3e956f9171
376 372 | user: Robert Altman <robert.altman@telventDTN.com>
377 373 | date: Mon Nov 28 16:37:57 2011 +0000
378 374 |
379 375 o changeset: 0:141947992243
380 376 user: Robert Altman <robert.altman@telventDTN.com>
381 377 date: Mon Nov 28 16:35:28 2011 +0000
382 378 summary: Checked in text file
383 379
384 380 $ hg histedit 0
385 381 $ cd ..
386 382
387 383 $ cd ..
388 384
389 385
390 386 Test to make sure folding renames doesn't cause bogus conflicts (issue4251):
391 387 $ hg init issue4251
392 388 $ cd issue4251
393 389
394 390 $ mkdir initial-dir
395 391 $ echo foo > initial-dir/initial-file
396 392 $ hg add initial-dir/initial-file
397 393 $ hg commit -m "initial commit"
398 394
399 395 Move the file to a new directory, and in the same commit, change its content:
400 396 $ mkdir another-dir
401 397 $ hg mv initial-dir/initial-file another-dir/
402 398 $ echo changed > another-dir/initial-file
403 399 $ hg commit -m "moved and changed"
404 400
405 401 Rename the file:
406 402 $ hg mv another-dir/initial-file another-dir/renamed-file
407 403 $ hg commit -m "renamed"
408 404
409 405 Now, let's try to fold the second commit into the first:
410 406 $ cat > editor.sh <<EOF
411 407 > #!/bin/sh
412 408 > cat > \$1 <<ENDOF
413 409 > pick b0f4233702ca 0 initial commit
414 410 > fold 5e8704a8f2d2 1 moved and changed
415 411 > pick 40e7299e8fa7 2 renamed
416 412 > ENDOF
417 413 > EOF
418 414
419 415 $ HGEDITOR="sh ./editor.sh" hg histedit 0
420 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
421 adding another-dir/initial-file (glob)
422 removing initial-dir/initial-file (glob)
423 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
424 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 416 saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/*-backup.hg (glob)
426 417 saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/*-backup.hg (glob)
427 418
428 419 $ hg --config diff.git=yes export 0
429 420 # HG changeset patch
430 421 # User test
431 422 # Date 0 0
432 423 # Thu Jan 01 00:00:00 1970 +0000
433 424 # Node ID fffadc26f8f85623ce60b028a3f1ccc3730f8530
434 425 # Parent 0000000000000000000000000000000000000000
435 426 pick b0f4233702ca 0 initial commit
436 427 fold 5e8704a8f2d2 1 moved and changed
437 428 pick 40e7299e8fa7 2 renamed
438 429
439 430 diff --git a/another-dir/initial-file b/another-dir/initial-file
440 431 new file mode 100644
441 432 --- /dev/null
442 433 +++ b/another-dir/initial-file
443 434 @@ -0,0 +1,1 @@
444 435 +changed
445 436
446 437 $ hg --config diff.git=yes export 1
447 438 # HG changeset patch
448 439 # User test
449 440 # Date 0 0
450 441 # Thu Jan 01 00:00:00 1970 +0000
451 442 # Node ID 9b730d82b00af8a2766facebfa47cc124405a118
452 443 # Parent fffadc26f8f85623ce60b028a3f1ccc3730f8530
453 444 renamed
454 445
455 446 diff --git a/another-dir/initial-file b/another-dir/renamed-file
456 447 rename from another-dir/initial-file
457 448 rename to another-dir/renamed-file
458 449
459 450 $ cd ..
@@ -1,173 +1,170 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > EOF
7 7
8 8 $ initrepo ()
9 9 > {
10 10 > hg init r
11 11 > cd r
12 12 > for x in a b c d e f ; do
13 13 > echo $x > $x
14 14 > hg add $x
15 15 > hg ci -m $x
16 16 > done
17 17 > }
18 18
19 19 $ initrepo
20 20
21 21 log before edit
22 22 $ hg log --graph
23 23 @ changeset: 5:652413bf663e
24 24 | tag: tip
25 25 | user: test
26 26 | date: Thu Jan 01 00:00:00 1970 +0000
27 27 | summary: f
28 28 |
29 29 o changeset: 4:e860deea161a
30 30 | user: test
31 31 | date: Thu Jan 01 00:00:00 1970 +0000
32 32 | summary: e
33 33 |
34 34 o changeset: 3:055a42cdd887
35 35 | user: test
36 36 | date: Thu Jan 01 00:00:00 1970 +0000
37 37 | summary: d
38 38 |
39 39 o changeset: 2:177f92b77385
40 40 | user: test
41 41 | date: Thu Jan 01 00:00:00 1970 +0000
42 42 | summary: c
43 43 |
44 44 o changeset: 1:d2ae7f538514
45 45 | user: test
46 46 | date: Thu Jan 01 00:00:00 1970 +0000
47 47 | summary: b
48 48 |
49 49 o changeset: 0:cb9a9f314b8b
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: a
53 53
54 54
55 55 edit the history
56 56 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
57 57 > drop 177f92b77385 c
58 58 > pick e860deea161a e
59 59 > pick 652413bf663e f
60 60 > pick 055a42cdd887 d
61 61 > EOF
62 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
63 62
64 63 log after edit
65 64 $ hg log --graph
66 65 @ changeset: 4:f518305ce889
67 66 | tag: tip
68 67 | user: test
69 68 | date: Thu Jan 01 00:00:00 1970 +0000
70 69 | summary: d
71 70 |
72 71 o changeset: 3:a4f7421b80f7
73 72 | user: test
74 73 | date: Thu Jan 01 00:00:00 1970 +0000
75 74 | summary: f
76 75 |
77 76 o changeset: 2:ee283cb5f2d5
78 77 | user: test
79 78 | date: Thu Jan 01 00:00:00 1970 +0000
80 79 | summary: e
81 80 |
82 81 o changeset: 1:d2ae7f538514
83 82 | user: test
84 83 | date: Thu Jan 01 00:00:00 1970 +0000
85 84 | summary: b
86 85 |
87 86 o changeset: 0:cb9a9f314b8b
88 87 user: test
89 88 date: Thu Jan 01 00:00:00 1970 +0000
90 89 summary: a
91 90
92 91
93 92 Check histedit_source
94 93
95 94 $ hg log --debug --rev f518305ce889
96 95 changeset: 4:f518305ce889c07cb5bd05522176d75590ef3324
97 96 tag: tip
98 97 phase: draft
99 98 parent: 3:a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3
100 99 parent: -1:0000000000000000000000000000000000000000
101 100 manifest: 4:d3d4f51c157ff242c32ff745d4799aaa26ccda44
102 101 user: test
103 102 date: Thu Jan 01 00:00:00 1970 +0000
104 103 files+: d
105 104 extra: branch=default
106 105 extra: histedit_source=055a42cdd88768532f9cf79daa407fc8d138de9b
107 106 description:
108 107 d
109 108
110 109
111 110
112 111 manifest after edit
113 112 $ hg manifest
114 113 a
115 114 b
116 115 d
117 116 e
118 117 f
119 118
120 119 Drop the last changeset
121 120
122 121 $ hg histedit ee283cb5f2d5 --commands - 2>&1 << EOF | fixbundle
123 122 > pick ee283cb5f2d5 e
124 123 > pick a4f7421b80f7 f
125 124 > drop f518305ce889 d
126 125 > EOF
127 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
128 126 $ hg log --graph
129 127 @ changeset: 3:a4f7421b80f7
130 128 | tag: tip
131 129 | user: test
132 130 | date: Thu Jan 01 00:00:00 1970 +0000
133 131 | summary: f
134 132 |
135 133 o changeset: 2:ee283cb5f2d5
136 134 | user: test
137 135 | date: Thu Jan 01 00:00:00 1970 +0000
138 136 | summary: e
139 137 |
140 138 o changeset: 1:d2ae7f538514
141 139 | user: test
142 140 | date: Thu Jan 01 00:00:00 1970 +0000
143 141 | summary: b
144 142 |
145 143 o changeset: 0:cb9a9f314b8b
146 144 user: test
147 145 date: Thu Jan 01 00:00:00 1970 +0000
148 146 summary: a
149 147
150 148
151 149 $ hg histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
152 150 > pick cb9a9f314b8b a
153 151 > pick ee283cb5f2d5 e
154 152 > EOF
155 153 hg: parse error: missing rules for changeset a4f7421b80f7
156 154 (use "drop a4f7421b80f7" to discard, see also: "hg help -e histedit.config")
157 155 $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle
158 156 > pick cb9a9f314b8b a
159 157 > pick ee283cb5f2d5 e
160 158 > EOF
161 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
162 159 $ hg log --graph
163 160 @ changeset: 1:e99c679bf03e
164 161 | tag: tip
165 162 | user: test
166 163 | date: Thu Jan 01 00:00:00 1970 +0000
167 164 | summary: e
168 165 |
169 166 o changeset: 0:cb9a9f314b8b
170 167 user: test
171 168 date: Thu Jan 01 00:00:00 1970 +0000
172 169 summary: a
173 170
@@ -1,479 +1,474 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > strip=
7 7 > EOF
8 8
9 9 $ initrepo ()
10 10 > {
11 11 > hg init r
12 12 > cd r
13 13 > for x in a b c d e f g; do
14 14 > echo $x > $x
15 15 > hg add $x
16 16 > hg ci -m $x
17 17 > done
18 18 > }
19 19
20 20 $ initrepo
21 21
22 22 log before edit
23 23 $ hg log --graph
24 24 @ changeset: 6:3c6a8ed2ebe8
25 25 | tag: tip
26 26 | user: test
27 27 | date: Thu Jan 01 00:00:00 1970 +0000
28 28 | summary: g
29 29 |
30 30 o changeset: 5:652413bf663e
31 31 | user: test
32 32 | date: Thu Jan 01 00:00:00 1970 +0000
33 33 | summary: f
34 34 |
35 35 o changeset: 4:e860deea161a
36 36 | user: test
37 37 | date: Thu Jan 01 00:00:00 1970 +0000
38 38 | summary: e
39 39 |
40 40 o changeset: 3:055a42cdd887
41 41 | user: test
42 42 | date: Thu Jan 01 00:00:00 1970 +0000
43 43 | summary: d
44 44 |
45 45 o changeset: 2:177f92b77385
46 46 | user: test
47 47 | date: Thu Jan 01 00:00:00 1970 +0000
48 48 | summary: c
49 49 |
50 50 o changeset: 1:d2ae7f538514
51 51 | user: test
52 52 | date: Thu Jan 01 00:00:00 1970 +0000
53 53 | summary: b
54 54 |
55 55 o changeset: 0:cb9a9f314b8b
56 56 user: test
57 57 date: Thu Jan 01 00:00:00 1970 +0000
58 58 summary: a
59 59
60 60 dirty a file
61 61 $ echo a > g
62 62 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF
63 63 > EOF
64 64 abort: uncommitted changes
65 65 [255]
66 66 $ echo g > g
67 67
68 68 edit the history
69 69 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF| fixbundle
70 70 > pick 177f92b77385 c
71 71 > pick 055a42cdd887 d
72 72 > edit e860deea161a e
73 73 > pick 652413bf663e f
74 74 > pick 3c6a8ed2ebe8 g
75 75 > EOF
76 76 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
77 77 Editing (e860deea161a), you may commit or record as needed now.
78 78 (hg histedit --continue to resume)
79 79
80 80 edit the plan via the editor
81 81 $ cat >> $TESTTMP/editplan.sh <<EOF
82 82 > cat > \$1 <<EOF2
83 83 > drop e860deea161a e
84 84 > drop 652413bf663e f
85 85 > drop 3c6a8ed2ebe8 g
86 86 > EOF2
87 87 > EOF
88 88 $ HGEDITOR="sh $TESTTMP/editplan.sh" hg histedit --edit-plan
89 89 $ cat .hg/histedit-state
90 90 v1
91 91 055a42cdd88768532f9cf79daa407fc8d138de9b
92 92 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
93 93 False
94 94 3
95 95 drop
96 96 e860deea161a2f77de56603b340ebbb4536308ae
97 97 drop
98 98 652413bf663ef2a641cab26574e46d5f5a64a55a
99 99 drop
100 100 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
101 101 0
102 102 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
103 103
104 104 edit the plan via --commands
105 105 $ hg histedit --edit-plan --commands - 2>&1 << EOF
106 106 > edit e860deea161a e
107 107 > pick 652413bf663e f
108 108 > drop 3c6a8ed2ebe8 g
109 109 > EOF
110 110 $ cat .hg/histedit-state
111 111 v1
112 112 055a42cdd88768532f9cf79daa407fc8d138de9b
113 113 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
114 114 False
115 115 3
116 116 edit
117 117 e860deea161a2f77de56603b340ebbb4536308ae
118 118 pick
119 119 652413bf663ef2a641cab26574e46d5f5a64a55a
120 120 drop
121 121 3c6a8ed2ebe862cc949d2caa30775dd6f16fb799
122 122 0
123 123 strip-backup/177f92b77385-0ebe6a8f-histedit.hg
124 124
125 125 Go at a random point and try to continue
126 126
127 127 $ hg id -n
128 128 3+
129 129 $ hg up 0
130 130 abort: histedit in progress
131 131 (use 'hg histedit --continue' or 'hg histedit --abort')
132 132 [255]
133 133
134 134 Try to delete necessary commit
135 135 $ hg strip -r 652413b
136 136 abort: histedit in progress, can't strip 652413bf663e
137 137 [255]
138 138
139 139 commit, then edit the revision
140 140 $ hg ci -m 'wat'
141 141 created new head
142 142 $ echo a > e
143 143
144 144 qnew should fail while we're in the middle of the edit step
145 145
146 146 $ hg --config extensions.mq= qnew please-fail
147 147 abort: histedit in progress
148 148 (use 'hg histedit --continue' or 'hg histedit --abort')
149 149 [255]
150 150 $ HGEDITOR='echo foobaz > ' hg histedit --continue 2>&1 | fixbundle
151 151
152 152 $ hg log --graph
153 153 @ changeset: 6:b5f70786f9b0
154 154 | tag: tip
155 155 | user: test
156 156 | date: Thu Jan 01 00:00:00 1970 +0000
157 157 | summary: f
158 158 |
159 159 o changeset: 5:a5e1ba2f7afb
160 160 | user: test
161 161 | date: Thu Jan 01 00:00:00 1970 +0000
162 162 | summary: foobaz
163 163 |
164 164 o changeset: 4:1a60820cd1f6
165 165 | user: test
166 166 | date: Thu Jan 01 00:00:00 1970 +0000
167 167 | summary: wat
168 168 |
169 169 o changeset: 3:055a42cdd887
170 170 | user: test
171 171 | date: Thu Jan 01 00:00:00 1970 +0000
172 172 | summary: d
173 173 |
174 174 o changeset: 2:177f92b77385
175 175 | user: test
176 176 | date: Thu Jan 01 00:00:00 1970 +0000
177 177 | summary: c
178 178 |
179 179 o changeset: 1:d2ae7f538514
180 180 | user: test
181 181 | date: Thu Jan 01 00:00:00 1970 +0000
182 182 | summary: b
183 183 |
184 184 o changeset: 0:cb9a9f314b8b
185 185 user: test
186 186 date: Thu Jan 01 00:00:00 1970 +0000
187 187 summary: a
188 188
189 189
190 190 $ hg cat e
191 191 a
192 192
193 193 Stripping necessary commits should not break --abort
194 194
195 195 $ hg histedit 1a60820cd1f6 --commands - 2>&1 << EOF| fixbundle
196 196 > edit 1a60820cd1f6 wat
197 197 > pick a5e1ba2f7afb foobaz
198 198 > pick b5f70786f9b0 g
199 199 > EOF
200 200 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
201 201 Editing (1a60820cd1f6), you may commit or record as needed now.
202 202 (hg histedit --continue to resume)
203 203
204 204 $ mv .hg/histedit-state .hg/histedit-state.bak
205 205 $ hg strip -q -r b5f70786f9b0
206 206 $ mv .hg/histedit-state.bak .hg/histedit-state
207 207 $ hg histedit --abort
208 208 adding changesets
209 209 adding manifests
210 210 adding file changes
211 211 added 1 changesets with 1 changes to 3 files
212 212 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 213 $ hg log -r .
214 214 changeset: 6:b5f70786f9b0
215 215 tag: tip
216 216 user: test
217 217 date: Thu Jan 01 00:00:00 1970 +0000
218 218 summary: f
219 219
220 220
221 221 check histedit_source
222 222
223 223 $ hg log --debug --rev 5
224 224 changeset: 5:a5e1ba2f7afb899ef1581cea528fd885d2fca70d
225 225 phase: draft
226 226 parent: 4:1a60820cd1f6004a362aa622ebc47d59bc48eb34
227 227 parent: -1:0000000000000000000000000000000000000000
228 228 manifest: 5:5ad3be8791f39117565557781f5464363b918a45
229 229 user: test
230 230 date: Thu Jan 01 00:00:00 1970 +0000
231 231 files: e
232 232 extra: branch=default
233 233 extra: histedit_source=e860deea161a2f77de56603b340ebbb4536308ae
234 234 description:
235 235 foobaz
236 236
237 237
238 238
239 239 $ hg histedit tip --commands - 2>&1 <<EOF| fixbundle
240 240 > edit b5f70786f9b0 f
241 241 > EOF
242 242 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
243 243 Editing (b5f70786f9b0), you may commit or record as needed now.
244 244 (hg histedit --continue to resume)
245 245 $ hg status
246 246 A f
247 247
248 248 $ hg summary
249 249 parent: 5:a5e1ba2f7afb
250 250 foobaz
251 251 branch: default
252 252 commit: 1 added (new branch head)
253 253 update: 1 new changesets (update)
254 254 phases: 7 draft
255 255 hist: 1 remaining (histedit --continue)
256 256
257 257 (test also that editor is invoked if histedit is continued for
258 258 "edit" action)
259 259
260 260 $ HGEDITOR='cat' hg histedit --continue
261 261 f
262 262
263 263
264 264 HG: Enter commit message. Lines beginning with 'HG:' are removed.
265 265 HG: Leave message empty to abort commit.
266 266 HG: --
267 267 HG: user: test
268 268 HG: branch 'default'
269 269 HG: added f
270 270 saved backup bundle to $TESTTMP/r/.hg/strip-backup/b5f70786f9b0-c28d9c86-backup.hg (glob)
271 271
272 272 $ hg status
273 273
274 274 log after edit
275 275 $ hg log --limit 1
276 276 changeset: 6:a107ee126658
277 277 tag: tip
278 278 user: test
279 279 date: Thu Jan 01 00:00:00 1970 +0000
280 280 summary: f
281 281
282 282
283 283 say we'll change the message, but don't.
284 284 $ cat > ../edit.sh <<EOF
285 285 > cat "\$1" | sed s/pick/mess/ > tmp
286 286 > mv tmp "\$1"
287 287 > EOF
288 288 $ HGEDITOR="sh ../edit.sh" hg histedit tip 2>&1 | fixbundle
289 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
290 289 $ hg status
291 290 $ hg log --limit 1
292 291 changeset: 6:1fd3b2fe7754
293 292 tag: tip
294 293 user: test
295 294 date: Thu Jan 01 00:00:00 1970 +0000
296 295 summary: f
297 296
298 297
299 298 modify the message
300 299
301 300 check saving last-message.txt, at first
302 301
303 302 $ cat > $TESTTMP/commitfailure.py <<EOF
304 303 > from mercurial import error
305 304 > def reposetup(ui, repo):
306 305 > class commitfailure(repo.__class__):
307 306 > def commit(self, *args, **kwargs):
308 307 > raise error.Abort('emulating unexpected abort')
309 308 > repo.__class__ = commitfailure
310 309 > EOF
311 310 $ cat >> .hg/hgrc <<EOF
312 311 > [extensions]
313 312 > # this failure occurs before editor invocation
314 313 > commitfailure = $TESTTMP/commitfailure.py
315 314 > EOF
316 315
317 316 $ cat > $TESTTMP/editor.sh <<EOF
318 317 > echo "==== before editing"
319 318 > cat \$1
320 319 > echo "===="
321 320 > echo "check saving last-message.txt" >> \$1
322 321 > EOF
323 322
324 323 (test that editor is not invoked before transaction starting)
325 324
326 325 $ rm -f .hg/last-message.txt
327 326 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF | fixbundle
328 327 > mess 1fd3b2fe7754 f
329 328 > EOF
330 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
331 329 abort: emulating unexpected abort
332 330 $ test -f .hg/last-message.txt
333 331 [1]
334 332
335 333 $ cat >> .hg/hgrc <<EOF
336 334 > [extensions]
337 335 > commitfailure = !
338 336 > EOF
339 337 $ hg histedit --abort -q
340 338
341 339 (test that editor is invoked and commit message is saved into
342 340 "last-message.txt")
343 341
344 342 $ cat >> .hg/hgrc <<EOF
345 343 > [hooks]
346 344 > # this failure occurs after editor invocation
347 345 > pretxncommit.unexpectedabort = false
348 346 > EOF
349 347
350 348 $ hg status --rev '1fd3b2fe7754^1' --rev 1fd3b2fe7754
351 349 A f
352 350
353 351 $ rm -f .hg/last-message.txt
354 352 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit tip --commands - 2>&1 << EOF
355 353 > mess 1fd3b2fe7754 f
356 354 > EOF
357 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
358 adding f
359 355 ==== before editing
360 356 f
361 357
362 358
363 359 HG: Enter commit message. Lines beginning with 'HG:' are removed.
364 360 HG: Leave message empty to abort commit.
365 361 HG: --
366 362 HG: user: test
367 363 HG: branch 'default'
368 364 HG: added f
369 365 ====
370 366 note: commit message saved in .hg/last-message.txt
371 367 transaction abort!
372 368 rollback completed
373 369 abort: pretxncommit.unexpectedabort hook exited with status 1
374 370 [255]
375 371 $ cat .hg/last-message.txt
376 372 f
377 373
378 374
379 375 check saving last-message.txt
380 376
381 377 (test also that editor is invoked if histedit is continued for "message"
382 378 action)
383 379
384 380 $ HGEDITOR=cat hg histedit --continue
385 381 f
386 382
387 383
388 384 HG: Enter commit message. Lines beginning with 'HG:' are removed.
389 385 HG: Leave message empty to abort commit.
390 386 HG: --
391 387 HG: user: test
392 388 HG: branch 'default'
393 389 HG: added f
394 390 note: commit message saved in .hg/last-message.txt
395 391 transaction abort!
396 392 rollback completed
397 393 abort: pretxncommit.unexpectedabort hook exited with status 1
398 394 [255]
399 395
400 396 $ cat >> .hg/hgrc <<EOF
401 397 > [hooks]
402 398 > pretxncommit.unexpectedabort =
403 399 > EOF
404 400 $ hg histedit --abort -q
405 401
406 402 then, check "modify the message" itself
407 403
408 404 $ hg histedit tip --commands - 2>&1 << EOF | fixbundle
409 405 > mess 1fd3b2fe7754 f
410 406 > EOF
411 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
412 407 $ hg status
413 408 $ hg log --limit 1
414 409 changeset: 6:62feedb1200e
415 410 tag: tip
416 411 user: test
417 412 date: Thu Jan 01 00:00:00 1970 +0000
418 413 summary: f
419 414
420 415
421 416 rollback should not work after a histedit
422 417 $ hg rollback
423 418 no rollback information available
424 419 [1]
425 420
426 421 $ cd ..
427 422 $ hg clone -qr0 r r0
428 423 $ cd r0
429 424 $ hg phase -fdr0
430 425 $ hg histedit --commands - 0 2>&1 << EOF
431 426 > edit cb9a9f314b8b a > $EDITED
432 427 > EOF
433 428 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
434 429 adding a
435 430 Editing (cb9a9f314b8b), you may commit or record as needed now.
436 431 (hg histedit --continue to resume)
437 432 [1]
438 433 $ HGEDITOR=true hg histedit --continue
439 434 saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-backup.hg (glob)
440 435
441 436 $ hg log -G
442 437 @ changeset: 0:0efcea34f18a
443 438 tag: tip
444 439 user: test
445 440 date: Thu Jan 01 00:00:00 1970 +0000
446 441 summary: a
447 442
448 443 $ echo foo >> b
449 444 $ hg addr
450 445 adding b
451 446 $ hg ci -m 'add b'
452 447 $ echo foo >> a
453 448 $ hg ci -m 'extend a'
454 449 $ hg phase --public 1
455 450 Attempting to fold a change into a public change should not work:
456 451 $ cat > ../edit.sh <<EOF
457 452 > cat "\$1" | sed s/pick/fold/ > tmp
458 453 > mv tmp "\$1"
459 454 > EOF
460 455 $ HGEDITOR="sh ../edit.sh" hg histedit 2
461 456 warning: histedit rules saved to: .hg/histedit-last-edit.txt
462 457 hg: parse error: cannot fold into public change 18aa70c8ad22
463 458 [255]
464 459 $ cat .hg/histedit-last-edit.txt
465 460 fold 0012be4a27ea 2 extend a
466 461
467 462 # Edit history between 0012be4a27ea and 0012be4a27ea
468 463 #
469 464 # Commits are listed from least to most recent
470 465 #
471 466 # Commands:
472 467 #
473 468 # e, edit = use commit, but stop for amending
474 469 # m, mess = edit commit message without changing commit content
475 470 # p, fold = use commit
476 471 # d, drop = remove commit from history
477 472 # f, fold = use commit, but combine it with the one above
478 473 # r, roll = like fold, but discard this commit's description
479 474 #
@@ -1,347 +1,343 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > EOF
7 7
8 8 $ initrepo ()
9 9 > {
10 10 > hg init $1
11 11 > cd $1
12 12 > for x in a b c d e f ; do
13 13 > echo $x$x$x$x$x > $x
14 14 > hg add $x
15 15 > done
16 16 > hg ci -m 'Initial commit'
17 17 > for x in a b c d e f ; do
18 18 > echo $x > $x
19 19 > hg ci -m $x
20 20 > done
21 21 > echo 'I can haz no commute' > e
22 22 > hg ci -m 'does not commute with e'
23 23 > cd ..
24 24 > }
25 25
26 26 $ initrepo r
27 27 $ cd r
28 28 Initial generation of the command files
29 29
30 30 $ EDITED="$TESTTMP/editedhistory"
31 31 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
32 32 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
33 33 $ hg log --template 'fold {node|short} {rev} {desc}\n' -r 7 >> $EDITED
34 34 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
35 35 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
36 36 $ cat $EDITED
37 37 pick 65a9a84f33fd 3 c
38 38 pick 00f1c5383965 4 d
39 39 fold 39522b764e3d 7 does not commute with e
40 40 pick 7b4e2f4b7bcd 5 e
41 41 pick 500cac37a696 6 f
42 42
43 43 log before edit
44 44 $ hg log --graph
45 45 @ changeset: 7:39522b764e3d
46 46 | tag: tip
47 47 | user: test
48 48 | date: Thu Jan 01 00:00:00 1970 +0000
49 49 | summary: does not commute with e
50 50 |
51 51 o changeset: 6:500cac37a696
52 52 | user: test
53 53 | date: Thu Jan 01 00:00:00 1970 +0000
54 54 | summary: f
55 55 |
56 56 o changeset: 5:7b4e2f4b7bcd
57 57 | user: test
58 58 | date: Thu Jan 01 00:00:00 1970 +0000
59 59 | summary: e
60 60 |
61 61 o changeset: 4:00f1c5383965
62 62 | user: test
63 63 | date: Thu Jan 01 00:00:00 1970 +0000
64 64 | summary: d
65 65 |
66 66 o changeset: 3:65a9a84f33fd
67 67 | user: test
68 68 | date: Thu Jan 01 00:00:00 1970 +0000
69 69 | summary: c
70 70 |
71 71 o changeset: 2:da6535b52e45
72 72 | user: test
73 73 | date: Thu Jan 01 00:00:00 1970 +0000
74 74 | summary: b
75 75 |
76 76 o changeset: 1:c1f09da44841
77 77 | user: test
78 78 | date: Thu Jan 01 00:00:00 1970 +0000
79 79 | summary: a
80 80 |
81 81 o changeset: 0:1715188a53c7
82 82 user: test
83 83 date: Thu Jan 01 00:00:00 1970 +0000
84 84 summary: Initial commit
85 85
86 86
87 87 edit the history
88 88 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
89 89 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 merging e
91 91 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
92 92 Fix up the change (fold 39522b764e3d)
93 93 (hg histedit --continue to resume)
94 94
95 95 fix up
96 96 $ echo 'I can haz no commute' > e
97 97 $ hg resolve --mark e
98 98 (no more unresolved files)
99 99 continue: hg histedit --continue
100 100 $ cat > cat.py <<EOF
101 101 > import sys
102 102 > print open(sys.argv[1]).read()
103 103 > print
104 104 > print
105 105 > EOF
106 106 $ HGEDITOR="python cat.py" hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 107 d
109 108 ***
110 109 does not commute with e
111 110
112 111
113 112
114 113 HG: Enter commit message. Lines beginning with 'HG:' are removed.
115 114 HG: Leave message empty to abort commit.
116 115 HG: --
117 116 HG: user: test
118 117 HG: branch 'default'
119 118 HG: changed d
120 119 HG: changed e
121 120
122 121
123 122
124 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 123 merging e
126 124 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
127 125 Fix up the change (pick 7b4e2f4b7bcd)
128 126 (hg histedit --continue to resume)
129 127
130 128 just continue this time
131 129 $ hg revert -r 'p1()' e
132 130 $ hg resolve --mark e
133 131 (no more unresolved files)
134 132 continue: hg histedit --continue
135 133 $ hg histedit --continue 2>&1 | fixbundle
136 134 7b4e2f4b7bcd: empty changeset
137 135
138 136 log after edit
139 137 $ hg log --graph
140 138 @ changeset: 5:d9cf42e54966
141 139 | tag: tip
142 140 | user: test
143 141 | date: Thu Jan 01 00:00:00 1970 +0000
144 142 | summary: f
145 143 |
146 144 o changeset: 4:10486af2e984
147 145 | user: test
148 146 | date: Thu Jan 01 00:00:00 1970 +0000
149 147 | summary: d
150 148 |
151 149 o changeset: 3:65a9a84f33fd
152 150 | user: test
153 151 | date: Thu Jan 01 00:00:00 1970 +0000
154 152 | summary: c
155 153 |
156 154 o changeset: 2:da6535b52e45
157 155 | user: test
158 156 | date: Thu Jan 01 00:00:00 1970 +0000
159 157 | summary: b
160 158 |
161 159 o changeset: 1:c1f09da44841
162 160 | user: test
163 161 | date: Thu Jan 01 00:00:00 1970 +0000
164 162 | summary: a
165 163 |
166 164 o changeset: 0:1715188a53c7
167 165 user: test
168 166 date: Thu Jan 01 00:00:00 1970 +0000
169 167 summary: Initial commit
170 168
171 169
172 170 contents of e
173 171 $ hg cat e
174 172 I can haz no commute
175 173
176 174 manifest
177 175 $ hg manifest
178 176 a
179 177 b
180 178 c
181 179 d
182 180 e
183 181 f
184 182
185 183 $ cd ..
186 184
187 185 Repeat test using "roll", not "fold". "roll" folds in changes but drops message
188 186
189 187 $ initrepo r2
190 188 $ cd r2
191 189
192 190 Initial generation of the command files
193 191
194 192 $ EDITED="$TESTTMP/editedhistory.2"
195 193 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 3 >> $EDITED
196 194 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 4 >> $EDITED
197 195 $ hg log --template 'roll {node|short} {rev} {desc}\n' -r 7 >> $EDITED
198 196 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 5 >> $EDITED
199 197 $ hg log --template 'pick {node|short} {rev} {desc}\n' -r 6 >> $EDITED
200 198 $ cat $EDITED
201 199 pick 65a9a84f33fd 3 c
202 200 pick 00f1c5383965 4 d
203 201 roll 39522b764e3d 7 does not commute with e
204 202 pick 7b4e2f4b7bcd 5 e
205 203 pick 500cac37a696 6 f
206 204
207 205 log before edit
208 206 $ hg log --graph
209 207 @ changeset: 7:39522b764e3d
210 208 | tag: tip
211 209 | user: test
212 210 | date: Thu Jan 01 00:00:00 1970 +0000
213 211 | summary: does not commute with e
214 212 |
215 213 o changeset: 6:500cac37a696
216 214 | user: test
217 215 | date: Thu Jan 01 00:00:00 1970 +0000
218 216 | summary: f
219 217 |
220 218 o changeset: 5:7b4e2f4b7bcd
221 219 | user: test
222 220 | date: Thu Jan 01 00:00:00 1970 +0000
223 221 | summary: e
224 222 |
225 223 o changeset: 4:00f1c5383965
226 224 | user: test
227 225 | date: Thu Jan 01 00:00:00 1970 +0000
228 226 | summary: d
229 227 |
230 228 o changeset: 3:65a9a84f33fd
231 229 | user: test
232 230 | date: Thu Jan 01 00:00:00 1970 +0000
233 231 | summary: c
234 232 |
235 233 o changeset: 2:da6535b52e45
236 234 | user: test
237 235 | date: Thu Jan 01 00:00:00 1970 +0000
238 236 | summary: b
239 237 |
240 238 o changeset: 1:c1f09da44841
241 239 | user: test
242 240 | date: Thu Jan 01 00:00:00 1970 +0000
243 241 | summary: a
244 242 |
245 243 o changeset: 0:1715188a53c7
246 244 user: test
247 245 date: Thu Jan 01 00:00:00 1970 +0000
248 246 summary: Initial commit
249 247
250 248
251 249 edit the history
252 250 $ hg histedit 3 --commands $EDITED 2>&1 | fixbundle
253 251 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 252 merging e
255 253 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
256 254 Fix up the change (roll 39522b764e3d)
257 255 (hg histedit --continue to resume)
258 256
259 257 fix up
260 258 $ echo 'I can haz no commute' > e
261 259 $ hg resolve --mark e
262 260 (no more unresolved files)
263 261 continue: hg histedit --continue
264 262 $ hg histedit --continue 2>&1 | fixbundle | grep -v '2 files removed'
265 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 263 merging e
268 264 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
269 265 Fix up the change (pick 7b4e2f4b7bcd)
270 266 (hg histedit --continue to resume)
271 267
272 268 just continue this time
273 269 $ hg revert -r 'p1()' e
274 270 $ hg resolve --mark e
275 271 (no more unresolved files)
276 272 continue: hg histedit --continue
277 273 $ hg histedit --continue 2>&1 | fixbundle
278 274 7b4e2f4b7bcd: empty changeset
279 275
280 276 log after edit
281 277 $ hg log --graph
282 278 @ changeset: 5:e7c4f5d4eb75
283 279 | tag: tip
284 280 | user: test
285 281 | date: Thu Jan 01 00:00:00 1970 +0000
286 282 | summary: f
287 283 |
288 284 o changeset: 4:803d1bb561fc
289 285 | user: test
290 286 | date: Thu Jan 01 00:00:00 1970 +0000
291 287 | summary: d
292 288 |
293 289 o changeset: 3:65a9a84f33fd
294 290 | user: test
295 291 | date: Thu Jan 01 00:00:00 1970 +0000
296 292 | summary: c
297 293 |
298 294 o changeset: 2:da6535b52e45
299 295 | user: test
300 296 | date: Thu Jan 01 00:00:00 1970 +0000
301 297 | summary: b
302 298 |
303 299 o changeset: 1:c1f09da44841
304 300 | user: test
305 301 | date: Thu Jan 01 00:00:00 1970 +0000
306 302 | summary: a
307 303 |
308 304 o changeset: 0:1715188a53c7
309 305 user: test
310 306 date: Thu Jan 01 00:00:00 1970 +0000
311 307 summary: Initial commit
312 308
313 309
314 310 contents of e
315 311 $ hg cat e
316 312 I can haz no commute
317 313
318 314 manifest
319 315 $ hg manifest
320 316 a
321 317 b
322 318 c
323 319 d
324 320 e
325 321 f
326 322
327 323 description is taken from rollup target commit
328 324
329 325 $ hg log --debug --rev 4
330 326 changeset: 4:803d1bb561fceac3129ec778db9da249a3106fc3
331 327 phase: draft
332 328 parent: 3:65a9a84f33fdeb1ad5679b3941ec885d2b24027b
333 329 parent: -1:0000000000000000000000000000000000000000
334 330 manifest: 4:b068a323d969f22af1296ec6a5ea9384cef437ac
335 331 user: test
336 332 date: Thu Jan 01 00:00:00 1970 +0000
337 333 files: d e
338 334 extra: branch=default
339 335 extra: histedit_source=00f1c53839651fa5c76d423606811ea5455a79d0,39522b764e3d26103f08bd1fa2ccd3e3d7dbcf4e
340 336 description:
341 337 d
342 338
343 339
344 340
345 341 done with repo r2
346 342
347 343 $ cd ..
@@ -1,563 +1,535 b''
1 1 Test histedit extension: Fold commands
2 2 ======================================
3 3
4 4 This test file is dedicated to testing the fold command in non conflicting
5 5 case.
6 6
7 7 Initialization
8 8 ---------------
9 9
10 10
11 11 $ . "$TESTDIR/histedit-helpers.sh"
12 12
13 13 $ cat >> $HGRCPATH <<EOF
14 14 > [alias]
15 15 > logt = log --template '{rev}:{node|short} {desc|firstline}\n'
16 16 > [extensions]
17 17 > histedit=
18 18 > EOF
19 19
20 20
21 21 Simple folding
22 22 --------------------
23 23 $ initrepo ()
24 24 > {
25 25 > hg init r
26 26 > cd r
27 27 > for x in a b c d e f ; do
28 28 > echo $x > $x
29 29 > hg add $x
30 30 > hg ci -m $x
31 31 > done
32 32 > }
33 33
34 34 $ initrepo
35 35
36 36 log before edit
37 37 $ hg logt --graph
38 38 @ 5:652413bf663e f
39 39 |
40 40 o 4:e860deea161a e
41 41 |
42 42 o 3:055a42cdd887 d
43 43 |
44 44 o 2:177f92b77385 c
45 45 |
46 46 o 1:d2ae7f538514 b
47 47 |
48 48 o 0:cb9a9f314b8b a
49 49
50 50
51 51 $ hg histedit 177f92b77385 --commands - 2>&1 <<EOF | fixbundle
52 52 > pick e860deea161a e
53 53 > pick 652413bf663e f
54 54 > fold 177f92b77385 c
55 55 > pick 055a42cdd887 d
56 56 > EOF
57 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
58 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 57
61 58 log after edit
62 59 $ hg logt --graph
63 60 @ 4:9c277da72c9b d
64 61 |
65 62 o 3:6de59d13424a f
66 63 |
67 64 o 2:ee283cb5f2d5 e
68 65 |
69 66 o 1:d2ae7f538514 b
70 67 |
71 68 o 0:cb9a9f314b8b a
72 69
73 70
74 71 post-fold manifest
75 72 $ hg manifest
76 73 a
77 74 b
78 75 c
79 76 d
80 77 e
81 78 f
82 79
83 80
84 81 check histedit_source
85 82
86 83 $ hg log --debug --rev 3
87 84 changeset: 3:6de59d13424a8a13acd3e975514aed29dd0d9b2d
88 85 phase: draft
89 86 parent: 2:ee283cb5f2d5955443f23a27b697a04339e9a39a
90 87 parent: -1:0000000000000000000000000000000000000000
91 88 manifest: 3:81eede616954057198ead0b2c73b41d1f392829a
92 89 user: test
93 90 date: Thu Jan 01 00:00:00 1970 +0000
94 91 files+: c f
95 92 extra: branch=default
96 93 extra: histedit_source=a4f7421b80f79fcc59fff01bcbf4a53d127dd6d3,177f92b773850b59254aa5e923436f921b55483b
97 94 description:
98 95 f
99 96 ***
100 97 c
101 98
102 99
103 100
104 101 rollup will fold without preserving the folded commit's message
105 102
106 103 $ OLDHGEDITOR=$HGEDITOR
107 104 $ HGEDITOR=false
108 105 $ hg histedit d2ae7f538514 --commands - 2>&1 <<EOF | fixbundle
109 106 > pick d2ae7f538514 b
110 107 > roll ee283cb5f2d5 e
111 108 > pick 6de59d13424a f
112 109 > pick 9c277da72c9b d
113 110 > EOF
114 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
115 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
116 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 111
118 112 $ HGEDITOR=$OLDHGEDITOR
119 113
120 114 log after edit
121 115 $ hg logt --graph
122 116 @ 3:c4a9eb7989fc d
123 117 |
124 118 o 2:8e03a72b6f83 f
125 119 |
126 120 o 1:391ee782c689 b
127 121 |
128 122 o 0:cb9a9f314b8b a
129 123
130 124
131 125 description is taken from rollup target commit
132 126
133 127 $ hg log --debug --rev 1
134 128 changeset: 1:391ee782c68930be438ccf4c6a403daedbfbffa5
135 129 phase: draft
136 130 parent: 0:cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
137 131 parent: -1:0000000000000000000000000000000000000000
138 132 manifest: 1:b5e112a3a8354e269b1524729f0918662d847c38
139 133 user: test
140 134 date: Thu Jan 01 00:00:00 1970 +0000
141 135 files+: b e
142 136 extra: branch=default
143 137 extra: histedit_source=d2ae7f538514cd87c17547b0de4cea71fe1af9fb,ee283cb5f2d5955443f23a27b697a04339e9a39a
144 138 description:
145 139 b
146 140
147 141
148 142
149 143 check saving last-message.txt
150 144
151 145 $ cat > $TESTTMP/abortfolding.py <<EOF
152 146 > from mercurial import util
153 147 > def abortfolding(ui, repo, hooktype, **kwargs):
154 148 > ctx = repo[kwargs.get('node')]
155 149 > if set(ctx.files()) == set(['c', 'd', 'f']):
156 150 > return True # abort folding commit only
157 151 > ui.warn('allow non-folding commit\\n')
158 152 > EOF
159 153 $ cat > .hg/hgrc <<EOF
160 154 > [hooks]
161 155 > pretxncommit.abortfolding = python:$TESTTMP/abortfolding.py:abortfolding
162 156 > EOF
163 157
164 158 $ cat > $TESTTMP/editor.sh << EOF
165 159 > echo "==== before editing"
166 160 > cat \$1
167 161 > echo "===="
168 162 > echo "check saving last-message.txt" >> \$1
169 163 > EOF
170 164
171 165 $ rm -f .hg/last-message.txt
172 166 $ hg status --rev '8e03a72b6f83^1::c4a9eb7989fc'
173 167 A c
174 168 A d
175 169 A f
176 170 $ HGEDITOR="sh $TESTTMP/editor.sh" hg histedit 8e03a72b6f83 --commands - 2>&1 <<EOF
177 171 > pick 8e03a72b6f83 f
178 172 > fold c4a9eb7989fc d
179 173 > EOF
180 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 adding d
182 174 allow non-folding commit
183 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
184 175 ==== before editing
185 176 f
186 177 ***
187 178 c
188 179 ***
189 180 d
190 181
191 182
192 183
193 184 HG: Enter commit message. Lines beginning with 'HG:' are removed.
194 185 HG: Leave message empty to abort commit.
195 186 HG: --
196 187 HG: user: test
197 188 HG: branch 'default'
198 189 HG: added c
199 190 HG: added d
200 191 HG: added f
201 192 ====
202 193 transaction abort!
203 194 rollback completed
204 195 abort: pretxncommit.abortfolding hook failed
205 196 [255]
206 197
207 198 $ cat .hg/last-message.txt
208 199 f
209 200 ***
210 201 c
211 202 ***
212 203 d
213 204
214 205
215 206
216 207 check saving last-message.txt
217 208
218 209 $ cd ..
219 210 $ rm -r r
220 211
221 212 folding preserves initial author
222 213 --------------------------------
223 214
224 215 $ initrepo
225 216
226 217 $ hg ci --user "someone else" --amend --quiet
227 218
228 219 tip before edit
229 220 $ hg log --rev .
230 221 changeset: 5:a00ad806cb55
231 222 tag: tip
232 223 user: someone else
233 224 date: Thu Jan 01 00:00:00 1970 +0000
234 225 summary: f
235 226
236 227
237 228 $ hg --config progress.debug=1 --debug \
238 229 > histedit e860deea161a --commands - 2>&1 <<EOF | \
239 230 > egrep 'editing|unresolved'
240 231 > pick e860deea161a e
241 232 > fold a00ad806cb55 f
242 233 > EOF
243 234 editing: pick e860deea161a 4 e 1/2 changes (50.00%)
244 235 editing: fold a00ad806cb55 5 f 2/2 changes (100.00%)
245 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
246 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
247 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 236
249 237 tip after edit
250 238 $ hg log --rev .
251 239 changeset: 4:698d4e8040a1
252 240 tag: tip
253 241 user: test
254 242 date: Thu Jan 01 00:00:00 1970 +0000
255 243 summary: e
256 244
257 245
258 246 $ cd ..
259 247 $ rm -r r
260 248
261 249 folding and creating no new change doesn't break:
262 250 -------------------------------------------------
263 251
264 252 folded content is dropped during a merge. The folded commit should properly disappear.
265 253
266 254 $ mkdir fold-to-empty-test
267 255 $ cd fold-to-empty-test
268 256 $ hg init
269 257 $ printf "1\n2\n3\n" > file
270 258 $ hg add file
271 259 $ hg commit -m '1+2+3'
272 260 $ echo 4 >> file
273 261 $ hg commit -m '+4'
274 262 $ echo 5 >> file
275 263 $ hg commit -m '+5'
276 264 $ echo 6 >> file
277 265 $ hg commit -m '+6'
278 266 $ hg logt --graph
279 267 @ 3:251d831eeec5 +6
280 268 |
281 269 o 2:888f9082bf99 +5
282 270 |
283 271 o 1:617f94f13c0f +4
284 272 |
285 273 o 0:0189ba417d34 1+2+3
286 274
287 275
288 276 $ hg histedit 1 --commands - << EOF
289 277 > pick 617f94f13c0f 1 +4
290 278 > drop 888f9082bf99 2 +5
291 279 > fold 251d831eeec5 3 +6
292 280 > EOF
293 281 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
294 282 merging file
295 283 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
296 284 Fix up the change (fold 251d831eeec5)
297 285 (hg histedit --continue to resume)
298 286 [1]
299 287 There were conflicts, we keep P1 content. This
300 288 should effectively drop the changes from +6.
301 289 $ hg status
302 290 M file
303 291 ? file.orig
304 292 $ hg resolve -l
305 293 U file
306 294 $ hg revert -r 'p1()' file
307 295 $ hg resolve --mark file
308 296 (no more unresolved files)
309 297 continue: hg histedit --continue
310 298 $ hg histedit --continue
311 299 251d831eeec5: empty changeset
312 300 saved backup bundle to $TESTTMP/*-backup.hg (glob)
313 301 $ hg logt --graph
314 302 @ 1:617f94f13c0f +4
315 303 |
316 304 o 0:0189ba417d34 1+2+3
317 305
318 306
319 307 $ cd ..
320 308
321 309
322 310 Test fold through dropped
323 311 -------------------------
324 312
325 313
326 314 Test corner case where folded revision is separated from its parent by a
327 315 dropped revision.
328 316
329 317
330 318 $ hg init fold-with-dropped
331 319 $ cd fold-with-dropped
332 320 $ printf "1\n2\n3\n" > file
333 321 $ hg commit -Am '1+2+3'
334 322 adding file
335 323 $ echo 4 >> file
336 324 $ hg commit -m '+4'
337 325 $ echo 5 >> file
338 326 $ hg commit -m '+5'
339 327 $ echo 6 >> file
340 328 $ hg commit -m '+6'
341 329 $ hg logt -G
342 330 @ 3:251d831eeec5 +6
343 331 |
344 332 o 2:888f9082bf99 +5
345 333 |
346 334 o 1:617f94f13c0f +4
347 335 |
348 336 o 0:0189ba417d34 1+2+3
349 337
350 338 $ hg histedit 1 --commands - << EOF
351 339 > pick 617f94f13c0f 1 +4
352 340 > drop 888f9082bf99 2 +5
353 341 > fold 251d831eeec5 3 +6
354 342 > EOF
355 343 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 344 merging file
357 345 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
358 346 Fix up the change (fold 251d831eeec5)
359 347 (hg histedit --continue to resume)
360 348 [1]
361 349 $ cat > file << EOF
362 350 > 1
363 351 > 2
364 352 > 3
365 353 > 4
366 354 > 5
367 355 > EOF
368 356 $ hg resolve --mark file
369 357 (no more unresolved files)
370 358 continue: hg histedit --continue
371 359 $ hg commit -m '+5.2'
372 360 created new head
373 361 $ echo 6 >> file
374 362 $ HGEDITOR=cat hg histedit --continue
375 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 363 +4
377 364 ***
378 365 +5.2
379 366 ***
380 367 +6
381 368
382 369
383 370
384 371 HG: Enter commit message. Lines beginning with 'HG:' are removed.
385 372 HG: Leave message empty to abort commit.
386 373 HG: --
387 374 HG: user: test
388 375 HG: branch 'default'
389 376 HG: changed file
390 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
391 377 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/55c8d8dc79ce-4066cd98-backup.hg (glob)
392 378 saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-a35700fc-backup.hg (glob)
393 379 $ hg logt -G
394 380 @ 1:10c647b2cdd5 +4
395 381 |
396 382 o 0:0189ba417d34 1+2+3
397 383
398 384 $ hg export tip
399 385 # HG changeset patch
400 386 # User test
401 387 # Date 0 0
402 388 # Thu Jan 01 00:00:00 1970 +0000
403 389 # Node ID 10c647b2cdd54db0603ecb99b2ff5ce66d5a5323
404 390 # Parent 0189ba417d34df9dda55f88b637dcae9917b5964
405 391 +4
406 392 ***
407 393 +5.2
408 394 ***
409 395 +6
410 396
411 397 diff -r 0189ba417d34 -r 10c647b2cdd5 file
412 398 --- a/file Thu Jan 01 00:00:00 1970 +0000
413 399 +++ b/file Thu Jan 01 00:00:00 1970 +0000
414 400 @@ -1,3 +1,6 @@
415 401 1
416 402 2
417 403 3
418 404 +4
419 405 +5
420 406 +6
421 407 $ cd ..
422 408
423 409
424 410 Folding with initial rename (issue3729)
425 411 ---------------------------------------
426 412
427 413 $ hg init fold-rename
428 414 $ cd fold-rename
429 415 $ echo a > a.txt
430 416 $ hg add a.txt
431 417 $ hg commit -m a
432 418 $ hg rename a.txt b.txt
433 419 $ hg commit -m rename
434 420 $ echo b >> b.txt
435 421 $ hg commit -m b
436 422
437 423 $ hg logt --follow b.txt
438 424 2:e0371e0426bc b
439 425 1:1c4f440a8085 rename
440 426 0:6c795aa153cb a
441 427
442 428 $ hg histedit 1c4f440a8085 --commands - 2>&1 << EOF | fixbundle
443 429 > pick 1c4f440a8085 rename
444 430 > fold e0371e0426bc b
445 431 > EOF
446 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
447 reverting b.txt
448 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
449 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
450 432
451 433 $ hg logt --follow b.txt
452 434 1:cf858d235c76 rename
453 435 0:6c795aa153cb a
454 436
455 437 $ cd ..
456 438
457 439 Folding with swapping
458 440 ---------------------
459 441
460 442 This is an excuse to test hook with histedit temporary commit (issue4422)
461 443
462 444
463 445 $ hg init issue4422
464 446 $ cd issue4422
465 447 $ echo a > a.txt
466 448 $ hg add a.txt
467 449 $ hg commit -m a
468 450 $ echo b > b.txt
469 451 $ hg add b.txt
470 452 $ hg commit -m b
471 453 $ echo c > c.txt
472 454 $ hg add c.txt
473 455 $ hg commit -m c
474 456
475 457 $ hg logt
476 458 2:a1a953ffb4b0 c
477 459 1:199b6bb90248 b
478 460 0:6c795aa153cb a
479 461
480 462 Setup the proper environment variable symbol for the platform, to be subbed
481 463 into the hook command.
482 464 #if windows
483 465 $ NODE="%HG_NODE%"
484 466 #else
485 467 $ NODE="\$HG_NODE"
486 468 #endif
487 469 $ hg histedit 6c795aa153cb --config hooks.commit="echo commit $NODE" --commands - 2>&1 << EOF | fixbundle
488 470 > pick 199b6bb90248 b
489 471 > fold a1a953ffb4b0 c
490 472 > pick 6c795aa153cb a
491 473 > EOF
492 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
493 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
494 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
495 474 commit 9599899f62c05f4377548c32bf1c9f1a39634b0c
496 475
497 476 $ hg logt
498 477 1:9599899f62c0 a
499 478 0:79b99e9c8e49 b
500 479
501 480 $ echo "foo" > amended.txt
502 481 $ hg add amended.txt
503 482 $ hg ci -q --config extensions.largefiles= --amend -I amended.txt
504 483
505 484 Test that folding multiple changes in a row doesn't show multiple
506 485 editors.
507 486
508 487 $ echo foo >> foo
509 488 $ hg add foo
510 489 $ hg ci -m foo1
511 490 $ echo foo >> foo
512 491 $ hg ci -m foo2
513 492 $ echo foo >> foo
514 493 $ hg ci -m foo3
515 494 $ hg logt
516 495 4:21679ff7675c foo3
517 496 3:b7389cc4d66e foo2
518 497 2:0e01aeef5fa8 foo1
519 498 1:578c7455730c a
520 499 0:79b99e9c8e49 b
521 500 $ cat > "$TESTTMP/editor.sh" <<EOF
522 501 > echo ran editor >> "$TESTTMP/editorlog.txt"
523 502 > cat \$1 >> "$TESTTMP/editorlog.txt"
524 503 > echo END >> "$TESTTMP/editorlog.txt"
525 504 > echo merged foos > \$1
526 505 > EOF
527 506 $ HGEDITOR="sh \"$TESTTMP/editor.sh\"" hg histedit 1 --commands - 2>&1 <<EOF | fixbundle
528 507 > pick 578c7455730c 1 a
529 508 > pick 0e01aeef5fa8 2 foo1
530 509 > fold b7389cc4d66e 3 foo2
531 510 > fold 21679ff7675c 4 foo3
532 511 > EOF
533 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
534 reverting foo
535 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
536 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
537 merging foo
538 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
539 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
540 512 $ hg logt
541 513 2:e8bedbda72c1 merged foos
542 514 1:578c7455730c a
543 515 0:79b99e9c8e49 b
544 516 Editor should have run only once
545 517 $ cat $TESTTMP/editorlog.txt
546 518 ran editor
547 519 foo1
548 520 ***
549 521 foo2
550 522 ***
551 523 foo3
552 524
553 525
554 526
555 527 HG: Enter commit message. Lines beginning with 'HG:' are removed.
556 528 HG: Leave message empty to abort commit.
557 529 HG: --
558 530 HG: user: test
559 531 HG: branch 'default'
560 532 HG: added foo
561 533 END
562 534
563 535 $ cd ..
@@ -1,158 +1,157 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > EOF
7 7
8 8 $ initrepo ()
9 9 > {
10 10 > hg init r
11 11 > cd r
12 12 > for x in a b c d e f ; do
13 13 > echo $x > $x
14 14 > hg add $x
15 15 > hg ci -m $x
16 16 > done
17 17 > echo a >> e
18 18 > hg ci -m 'does not commute with e'
19 19 > cd ..
20 20 > }
21 21
22 22 $ initrepo
23 23 $ cd r
24 24
25 25 log before edit
26 26 $ hg log --graph
27 27 @ changeset: 6:bfa474341cc9
28 28 | tag: tip
29 29 | user: test
30 30 | date: Thu Jan 01 00:00:00 1970 +0000
31 31 | summary: does not commute with e
32 32 |
33 33 o changeset: 5:652413bf663e
34 34 | user: test
35 35 | date: Thu Jan 01 00:00:00 1970 +0000
36 36 | summary: f
37 37 |
38 38 o changeset: 4:e860deea161a
39 39 | user: test
40 40 | date: Thu Jan 01 00:00:00 1970 +0000
41 41 | summary: e
42 42 |
43 43 o changeset: 3:055a42cdd887
44 44 | user: test
45 45 | date: Thu Jan 01 00:00:00 1970 +0000
46 46 | summary: d
47 47 |
48 48 o changeset: 2:177f92b77385
49 49 | user: test
50 50 | date: Thu Jan 01 00:00:00 1970 +0000
51 51 | summary: c
52 52 |
53 53 o changeset: 1:d2ae7f538514
54 54 | user: test
55 55 | date: Thu Jan 01 00:00:00 1970 +0000
56 56 | summary: b
57 57 |
58 58 o changeset: 0:cb9a9f314b8b
59 59 user: test
60 60 date: Thu Jan 01 00:00:00 1970 +0000
61 61 summary: a
62 62
63 63
64 64 edit the history
65 65 $ hg histedit 177f92b77385 --commands - 2>&1 <<EOF | fixbundle
66 66 > pick 177f92b77385 c
67 67 > pick 055a42cdd887 d
68 68 > pick bfa474341cc9 does not commute with e
69 69 > pick e860deea161a e
70 70 > pick 652413bf663e f
71 71 > EOF
72 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
73 72 merging e
74 73 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
75 74 Fix up the change (pick e860deea161a)
76 75 (hg histedit --continue to resume)
77 76
78 77 insert unsupported advisory merge record
79 78 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -x
80 79 $ hg debugmergestate
81 80 * version 2 records
82 81 local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
83 82 other: e860deea161a2f77de56603b340ebbb4536308ae
84 83 unrecognized entry: x advisory record
85 84 file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
86 85 local path: e (flags "")
87 86 ancestor path: e (node null)
88 87 other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
89 88 $ hg resolve -l
90 89 U e
91 90
92 91 insert unsupported mandatory merge record
93 92 $ hg --config extensions.fakemergerecord=$TESTDIR/fakemergerecord.py fakemergerecord -X
94 93 $ hg debugmergestate
95 94 * version 2 records
96 95 local: 8f7551c7e4a2f2efe0bc8c741baf7f227d65d758
97 96 other: e860deea161a2f77de56603b340ebbb4536308ae
98 97 file: e (record type "F", state "u", hash 58e6b3a414a1e090dfc6029add0f3555ccba127f)
99 98 local path: e (flags "")
100 99 ancestor path: e (node null)
101 100 other path: e (node 6b67ccefd5ce6de77e7ead4f5292843a0255329f)
102 101 unrecognized entry: X mandatory record
103 102 $ hg resolve -l
104 103 abort: unsupported merge state records: X
105 104 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
106 105 [255]
107 106 $ hg resolve -ma
108 107 abort: unsupported merge state records: X
109 108 (see https://mercurial-scm.org/wiki/MergeStateRecords for more information)
110 109 [255]
111 110
112 111 abort the edit (should clear out merge state)
113 112 $ hg histedit --abort 2>&1 | fixbundle
114 113 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
115 114 $ hg debugmergestate
116 115 no merge state found
117 116
118 117 log after abort
119 118 $ hg resolve -l
120 119 $ hg log --graph
121 120 @ changeset: 6:bfa474341cc9
122 121 | tag: tip
123 122 | user: test
124 123 | date: Thu Jan 01 00:00:00 1970 +0000
125 124 | summary: does not commute with e
126 125 |
127 126 o changeset: 5:652413bf663e
128 127 | user: test
129 128 | date: Thu Jan 01 00:00:00 1970 +0000
130 129 | summary: f
131 130 |
132 131 o changeset: 4:e860deea161a
133 132 | user: test
134 133 | date: Thu Jan 01 00:00:00 1970 +0000
135 134 | summary: e
136 135 |
137 136 o changeset: 3:055a42cdd887
138 137 | user: test
139 138 | date: Thu Jan 01 00:00:00 1970 +0000
140 139 | summary: d
141 140 |
142 141 o changeset: 2:177f92b77385
143 142 | user: test
144 143 | date: Thu Jan 01 00:00:00 1970 +0000
145 144 | summary: c
146 145 |
147 146 o changeset: 1:d2ae7f538514
148 147 | user: test
149 148 | date: Thu Jan 01 00:00:00 1970 +0000
150 149 | summary: b
151 150 |
152 151 o changeset: 0:cb9a9f314b8b
153 152 user: test
154 153 date: Thu Jan 01 00:00:00 1970 +0000
155 154 summary: a
156 155
157 156
158 157 $ cd ..
@@ -1,435 +1,424 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 Enable obsolete
4 4
5 5 $ cat >> $HGRCPATH << EOF
6 6 > [ui]
7 7 > logtemplate= {rev}:{node|short} {desc|firstline}
8 8 > [phases]
9 9 > publish=False
10 10 > [experimental]
11 11 > evolution=createmarkers,allowunstable
12 12 > [extensions]
13 13 > histedit=
14 14 > rebase=
15 15 > EOF
16 16
17 17 $ hg init base
18 18 $ cd base
19 19
20 20 $ for x in a b c d e f ; do
21 21 > echo $x > $x
22 22 > hg add $x
23 23 > hg ci -m $x
24 24 > done
25 25
26 26 $ hg log --graph
27 27 @ 5:652413bf663e f
28 28 |
29 29 o 4:e860deea161a e
30 30 |
31 31 o 3:055a42cdd887 d
32 32 |
33 33 o 2:177f92b77385 c
34 34 |
35 35 o 1:d2ae7f538514 b
36 36 |
37 37 o 0:cb9a9f314b8b a
38 38
39 39
40 40 $ HGEDITOR=cat hg histedit 1
41 41 pick d2ae7f538514 1 b
42 42 pick 177f92b77385 2 c
43 43 pick 055a42cdd887 3 d
44 44 pick e860deea161a 4 e
45 45 pick 652413bf663e 5 f
46 46
47 47 # Edit history between d2ae7f538514 and 652413bf663e
48 48 #
49 49 # Commits are listed from least to most recent
50 50 #
51 51 # Commands:
52 52 #
53 53 # e, edit = use commit, but stop for amending
54 54 # m, mess = edit commit message without changing commit content
55 55 # p, pick = use commit
56 56 # d, drop = remove commit from history
57 57 # f, fold = use commit, but combine it with the one above
58 58 # r, roll = like fold, but discard this commit's description
59 59 #
60 60 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
61 61 > pick 177f92b77385 2 c
62 62 > drop d2ae7f538514 1 b
63 63 > pick 055a42cdd887 3 d
64 64 > fold e860deea161a 4 e
65 65 > pick 652413bf663e 5 f
66 66 > EOF
67 67 [1]
68 68 $ hg log --graph --hidden
69 69 @ 10:cacdfd884a93 f
70 70 |
71 71 o 9:59d9f330561f d
72 72 |
73 73 | x 8:b558abc46d09 fold-temp-revision e860deea161a
74 74 | |
75 75 | x 7:96e494a2d553 d
76 76 |/
77 77 o 6:b346ab9a313d c
78 78 |
79 79 | x 5:652413bf663e f
80 80 | |
81 81 | x 4:e860deea161a e
82 82 | |
83 83 | x 3:055a42cdd887 d
84 84 | |
85 85 | x 2:177f92b77385 c
86 86 | |
87 87 | x 1:d2ae7f538514 b
88 88 |/
89 89 o 0:cb9a9f314b8b a
90 90
91 91 $ hg debugobsolete
92 92 96e494a2d553dd05902ba1cee1d94d4cb7b8faed 0 {b346ab9a313db8537ecf96fca3ca3ca984ef3bd7} (*) {'user': 'test'} (glob)
93 93 b558abc46d09c30f57ac31e85a8a3d64d2e906e4 0 {96e494a2d553dd05902ba1cee1d94d4cb7b8faed} (*) {'user': 'test'} (glob)
94 94 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (*) {'user': 'test'} (glob)
95 95 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (*) {'user': 'test'} (glob)
96 96 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
97 97 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
98 98 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (*) {'user': 'test'} (glob)
99 99
100 100
101 101 Ensure hidden revision does not prevent histedit
102 102 -------------------------------------------------
103 103
104 104 create an hidden revision
105 105
106 106 $ hg histedit 6 --commands - << EOF
107 107 > pick b346ab9a313d 6 c
108 108 > drop 59d9f330561f 7 d
109 109 > pick cacdfd884a93 8 f
110 110 > EOF
111 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
112 111 $ hg log --graph
113 112 @ 11:c13eb81022ca f
114 113 |
115 114 o 6:b346ab9a313d c
116 115 |
117 116 o 0:cb9a9f314b8b a
118 117
119 118 check hidden revision are ignored (6 have hidden children 7 and 8)
120 119
121 120 $ hg histedit 6 --commands - << EOF
122 121 > pick b346ab9a313d 6 c
123 122 > pick c13eb81022ca 8 f
124 123 > EOF
125 124
126 125
127 126
128 127 Test that rewriting leaving instability behind is allowed
129 128 ---------------------------------------------------------------------
130 129
131 130 $ hg up '.^'
132 131 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
133 132 $ hg log -r 'children(.)'
134 133 11:c13eb81022ca f (no-eol)
135 134 $ hg histedit -r '.' --commands - <<EOF
136 135 > edit b346ab9a313d 6 c
137 136 > EOF
138 137 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
139 138 adding c
140 139 Editing (b346ab9a313d), you may commit or record as needed now.
141 140 (hg histedit --continue to resume)
142 141 [1]
143 142 $ echo c >> c
144 143 $ hg histedit --continue
145 144
146 145 $ hg log -r 'unstable()'
147 146 11:c13eb81022ca f (no-eol)
148 147
149 148 stabilise
150 149
151 150 $ hg rebase -r 'unstable()' -d .
152 151 rebasing 11:c13eb81022ca "f"
153 152 $ hg up tip -q
154 153
155 154 Test dropping of changeset on the top of the stack
156 155 -------------------------------------------------------
157 156
158 157 Nothing is rewritten below, the working directory parent must be change for the
159 158 dropped changeset to be hidden.
160 159
161 160 $ cd ..
162 161 $ hg clone base droplast
163 162 updating to branch default
164 163 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 164 $ cd droplast
166 165 $ hg histedit -r '40db8afa467b' --commands - << EOF
167 166 > pick 40db8afa467b 10 c
168 167 > drop b449568bf7fc 11 f
169 168 > EOF
170 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
171 169 $ hg log -G
172 170 @ 12:40db8afa467b c
173 171 |
174 172 o 0:cb9a9f314b8b a
175 173
176 174
177 175 With rewritten ancestors
178 176
179 177 $ echo e > e
180 178 $ hg add e
181 179 $ hg commit -m g
182 180 $ echo f > f
183 181 $ hg add f
184 182 $ hg commit -m h
185 183 $ hg histedit -r '40db8afa467b' --commands - << EOF
186 184 > pick 47a8561c0449 12 g
187 185 > pick 40db8afa467b 10 c
188 186 > drop 1b3b05f35ff0 13 h
189 187 > EOF
190 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
191 188 $ hg log -G
192 189 @ 17:ee6544123ab8 c
193 190 |
194 191 o 16:269e713e9eae g
195 192 |
196 193 o 0:cb9a9f314b8b a
197 194
198 195 $ cd ../base
199 196
200 197
201 198
202 199 Test phases support
203 200 ===========================================
204 201
205 202 Check that histedit respect immutability
206 203 -------------------------------------------
207 204
208 205 $ cat >> $HGRCPATH << EOF
209 206 > [ui]
210 207 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
211 208 > EOF
212 209
213 210 $ hg ph -pv '.^'
214 211 phase changed for 2 changesets
215 212 $ hg log -G
216 213 @ 13:b449568bf7fc (draft) f
217 214 |
218 215 o 12:40db8afa467b (public) c
219 216 |
220 217 o 0:cb9a9f314b8b (public) a
221 218
222 219 $ hg histedit -r '.~2'
223 220 abort: cannot edit public changeset: cb9a9f314b8b
224 221 (see "hg help phases" for details)
225 222 [255]
226 223
227 224
228 225 Prepare further testing
229 226 -------------------------------------------
230 227
231 228 $ for x in g h i j k ; do
232 229 > echo $x > $x
233 230 > hg add $x
234 231 > hg ci -m $x
235 232 > done
236 233 $ hg phase --force --secret .~2
237 234 $ hg log -G
238 235 @ 18:ee118ab9fa44 (secret) k
239 236 |
240 237 o 17:3a6c53ee7f3d (secret) j
241 238 |
242 239 o 16:b605fb7503f2 (secret) i
243 240 |
244 241 o 15:7395e1ff83bd (draft) h
245 242 |
246 243 o 14:6b70183d2492 (draft) g
247 244 |
248 245 o 13:b449568bf7fc (draft) f
249 246 |
250 247 o 12:40db8afa467b (public) c
251 248 |
252 249 o 0:cb9a9f314b8b (public) a
253 250
254 251 $ cd ..
255 252
256 253 simple phase conservation
257 254 -------------------------------------------
258 255
259 256 Resulting changeset should conserve the phase of the original one whatever the
260 257 phases.new-commit option is.
261 258
262 259 New-commit as draft (default)
263 260
264 261 $ cp -r base simple-draft
265 262 $ cd simple-draft
266 263 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
267 264 > edit b449568bf7fc 11 f
268 265 > pick 6b70183d2492 12 g
269 266 > pick 7395e1ff83bd 13 h
270 267 > pick b605fb7503f2 14 i
271 268 > pick 3a6c53ee7f3d 15 j
272 269 > pick ee118ab9fa44 16 k
273 270 > EOF
274 271 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
275 272 adding f
276 273 Editing (b449568bf7fc), you may commit or record as needed now.
277 274 (hg histedit --continue to resume)
278 275 [1]
279 276 $ echo f >> f
280 277 $ hg histedit --continue
281 278 $ hg log -G
282 279 @ 24:12e89af74238 (secret) k
283 280 |
284 281 o 23:636a8687b22e (secret) j
285 282 |
286 283 o 22:ccaf0a38653f (secret) i
287 284 |
288 285 o 21:11a89d1c2613 (draft) h
289 286 |
290 287 o 20:c1dec7ca82ea (draft) g
291 288 |
292 289 o 19:087281e68428 (draft) f
293 290 |
294 291 o 12:40db8afa467b (public) c
295 292 |
296 293 o 0:cb9a9f314b8b (public) a
297 294
298 295 $ cd ..
299 296
300 297
301 298 New-commit as draft (default)
302 299
303 300 $ cp -r base simple-secret
304 301 $ cd simple-secret
305 302 $ cat >> .hg/hgrc << EOF
306 303 > [phases]
307 304 > new-commit=secret
308 305 > EOF
309 306 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
310 307 > edit b449568bf7fc 11 f
311 308 > pick 6b70183d2492 12 g
312 309 > pick 7395e1ff83bd 13 h
313 310 > pick b605fb7503f2 14 i
314 311 > pick 3a6c53ee7f3d 15 j
315 312 > pick ee118ab9fa44 16 k
316 313 > EOF
317 314 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
318 315 adding f
319 316 Editing (b449568bf7fc), you may commit or record as needed now.
320 317 (hg histedit --continue to resume)
321 318 [1]
322 319 $ echo f >> f
323 320 $ hg histedit --continue
324 321 $ hg log -G
325 322 @ 24:12e89af74238 (secret) k
326 323 |
327 324 o 23:636a8687b22e (secret) j
328 325 |
329 326 o 22:ccaf0a38653f (secret) i
330 327 |
331 328 o 21:11a89d1c2613 (draft) h
332 329 |
333 330 o 20:c1dec7ca82ea (draft) g
334 331 |
335 332 o 19:087281e68428 (draft) f
336 333 |
337 334 o 12:40db8afa467b (public) c
338 335 |
339 336 o 0:cb9a9f314b8b (public) a
340 337
341 338 $ cd ..
342 339
343 340
344 341 Changeset reordering
345 342 -------------------------------------------
346 343
347 344 If a secret changeset is put before a draft one, all descendant should be secret.
348 345 It seems more important to present the secret phase.
349 346
350 347 $ cp -r base reorder
351 348 $ cd reorder
352 349 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
353 350 > pick b449568bf7fc 11 f
354 351 > pick 3a6c53ee7f3d 15 j
355 352 > pick 6b70183d2492 12 g
356 353 > pick b605fb7503f2 14 i
357 354 > pick 7395e1ff83bd 13 h
358 355 > pick ee118ab9fa44 16 k
359 356 > EOF
360 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
361 357 $ hg log -G
362 358 @ 23:558246857888 (secret) k
363 359 |
364 360 o 22:28bd44768535 (secret) h
365 361 |
366 362 o 21:d5395202aeb9 (secret) i
367 363 |
368 364 o 20:21edda8e341b (secret) g
369 365 |
370 366 o 19:5ab64f3a4832 (secret) j
371 367 |
372 368 o 13:b449568bf7fc (draft) f
373 369 |
374 370 o 12:40db8afa467b (public) c
375 371 |
376 372 o 0:cb9a9f314b8b (public) a
377 373
378 374 $ cd ..
379 375
380 376 Changeset folding
381 377 -------------------------------------------
382 378
383 379 Folding a secret changeset with a draft one turn the result secret (again,
384 380 better safe than sorry). Folding between same phase changeset still works
385 381
386 382 Note that there is a few reordering in this series for more extensive test
387 383
388 384 $ cp -r base folding
389 385 $ cd folding
390 386 $ cat >> .hg/hgrc << EOF
391 387 > [phases]
392 388 > new-commit=secret
393 389 > EOF
394 390 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
395 391 > pick 7395e1ff83bd 13 h
396 392 > fold b449568bf7fc 11 f
397 393 > pick 6b70183d2492 12 g
398 394 > fold 3a6c53ee7f3d 15 j
399 395 > pick b605fb7503f2 14 i
400 396 > fold ee118ab9fa44 16 k
401 397 > EOF
402 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
403 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
404 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
406 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
408 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
409 398 $ hg log -G
410 399 @ 27:f9daec13fb98 (secret) i
411 400 |
412 401 o 24:49807617f46a (secret) g
413 402 |
414 403 o 21:050280826e04 (draft) h
415 404 |
416 405 o 12:40db8afa467b (public) c
417 406 |
418 407 o 0:cb9a9f314b8b (public) a
419 408
420 409 $ hg co 49807617f46a
421 410 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
422 411 $ echo wat >> wat
423 412 $ hg add wat
424 413 $ hg ci -m 'add wat'
425 414 created new head
426 415 $ hg merge f9daec13fb98
427 416 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 417 (branch merge, don't forget to commit)
429 418 $ hg ci -m 'merge'
430 419 $ echo not wat > wat
431 420 $ hg ci -m 'modify wat'
432 421 $ hg histedit 050280826e04
433 422 abort: cannot edit history that contains merges
434 423 [255]
435 424 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now