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