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