##// END OF EJS Templates
histedit: shorten new fold message...
Matt Mackall -
r20511:5840da87 default
parent child Browse files
Show More
@@ -1,920 +1,920 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 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
38 # f, fold = use commit, but combine it with the one above
39 39 # d, drop = remove commit from history
40 40 # m, mess = edit message without changing commit content
41 41 #
42 42
43 43 In this file, lines beginning with ``#`` are ignored. You must specify a rule
44 44 for each revision in your history. For example, if you had meant to add gamma
45 45 before beta, and then wanted to add delta in the same revision as beta, you
46 46 would reorganize the file to look like this::
47 47
48 48 pick 030b686bedc4 Add gamma
49 49 pick c561b4e977df Add beta
50 50 fold 7c2fd3b9020c Add delta
51 51
52 52 # Edit history between c561b4e977df and 7c2fd3b9020c
53 53 #
54 54 # Commits are listed from least to most recent
55 55 #
56 56 # Commands:
57 57 # p, pick = use commit
58 58 # e, edit = use commit, but stop for amending
59 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
59 # f, fold = use commit, but combine it with the one above
60 60 # d, drop = remove commit from history
61 61 # m, mess = edit message without changing commit content
62 62 #
63 63
64 64 At which point you close the editor and ``histedit`` starts working. When you
65 65 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
66 66 those revisions together, offering you a chance to clean up the commit message::
67 67
68 68 Add beta
69 69 ***
70 70 Add delta
71 71
72 72 Edit the commit message to your liking, then close the editor. For
73 73 this example, let's assume that the commit message was changed to
74 74 ``Add beta and delta.`` After histedit has run and had a chance to
75 75 remove any old or temporary revisions it needed, the history looks
76 76 like this::
77 77
78 78 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
79 79 | Add beta and delta.
80 80 |
81 81 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
82 82 | Add gamma
83 83 |
84 84 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
85 85 Add alpha
86 86
87 87 Note that ``histedit`` does *not* remove any revisions (even its own temporary
88 88 ones) until after it has completed all the editing operations, so it will
89 89 probably perform several strip operations when it's done. For the above example,
90 90 it had to run strip twice. Strip can be slow depending on a variety of factors,
91 91 so you might need to be a little patient. You can choose to keep the original
92 92 revisions by passing the ``--keep`` flag.
93 93
94 94 The ``edit`` operation will drop you back to a command prompt,
95 95 allowing you to edit files freely, or even use ``hg record`` to commit
96 96 some changes as a separate commit. When you're done, any remaining
97 97 uncommitted changes will be committed as well. When done, run ``hg
98 98 histedit --continue`` to finish this step. You'll be prompted for a
99 99 new commit message, but the default commit message will be the
100 100 original message for the ``edit`` ed revision.
101 101
102 102 The ``message`` operation will give you a chance to revise a commit
103 103 message without changing the contents. It's a shortcut for doing
104 104 ``edit`` immediately followed by `hg histedit --continue``.
105 105
106 106 If ``histedit`` encounters a conflict when moving a revision (while
107 107 handling ``pick`` or ``fold``), it'll stop in a similar manner to
108 108 ``edit`` with the difference that it won't prompt you for a commit
109 109 message when done. If you decide at this point that you don't like how
110 110 much work it will be to rearrange history, or that you made a mistake,
111 111 you can use ``hg histedit --abort`` to abandon the new changes you
112 112 have made and return to the state before you attempted to edit your
113 113 history.
114 114
115 115 If we clone the histedit-ed example repository above and add four more
116 116 changes, such that we have the following history::
117 117
118 118 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
119 119 | Add theta
120 120 |
121 121 o 5 140988835471 2009-04-27 18:04 -0500 stefan
122 122 | Add eta
123 123 |
124 124 o 4 122930637314 2009-04-27 18:04 -0500 stefan
125 125 | Add zeta
126 126 |
127 127 o 3 836302820282 2009-04-27 18:04 -0500 stefan
128 128 | Add epsilon
129 129 |
130 130 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
131 131 | Add beta and delta.
132 132 |
133 133 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
134 134 | Add gamma
135 135 |
136 136 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
137 137 Add alpha
138 138
139 139 If you run ``hg histedit --outgoing`` on the clone then it is the same
140 140 as running ``hg histedit 836302820282``. If you need plan to push to a
141 141 repository that Mercurial does not detect to be related to the source
142 142 repo, you can add a ``--force`` option.
143 143 """
144 144
145 145 try:
146 146 import cPickle as pickle
147 147 pickle.dump # import now
148 148 except ImportError:
149 149 import pickle
150 150 import os
151 151 import sys
152 152
153 153 from mercurial import cmdutil
154 154 from mercurial import discovery
155 155 from mercurial import error
156 156 from mercurial import copies
157 157 from mercurial import context
158 158 from mercurial import hg
159 159 from mercurial import lock as lockmod
160 160 from mercurial import node
161 161 from mercurial import repair
162 162 from mercurial import scmutil
163 163 from mercurial import util
164 164 from mercurial import obsolete
165 165 from mercurial import merge as mergemod
166 166 from mercurial.lock import release
167 167 from mercurial.i18n import _
168 168
169 169 cmdtable = {}
170 170 command = cmdutil.command(cmdtable)
171 171
172 172 testedwith = 'internal'
173 173
174 174 # i18n: command names and abbreviations must remain untranslated
175 175 editcomment = _("""# Edit history between %s and %s
176 176 #
177 177 # Commits are listed from least to most recent
178 178 #
179 179 # Commands:
180 180 # p, pick = use commit
181 181 # e, edit = use commit, but stop for amending
182 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
182 # f, fold = use commit, but combine it with the one above
183 183 # d, drop = remove commit from history
184 184 # m, mess = edit message without changing commit content
185 185 #
186 186 """)
187 187
188 188 def commitfuncfor(repo, src):
189 189 """Build a commit function for the replacement of <src>
190 190
191 191 This function ensure we apply the same treatment to all changesets.
192 192
193 193 - Add a 'histedit_source' entry in extra.
194 194
195 195 Note that fold have its own separated logic because its handling is a bit
196 196 different and not easily factored out of the fold method.
197 197 """
198 198 phasemin = src.phase()
199 199 def commitfunc(**kwargs):
200 200 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
201 201 try:
202 202 repo.ui.setconfig('phases', 'new-commit', phasemin)
203 203 extra = kwargs.get('extra', {}).copy()
204 204 extra['histedit_source'] = src.hex()
205 205 kwargs['extra'] = extra
206 206 return repo.commit(**kwargs)
207 207 finally:
208 208 repo.ui.restoreconfig(phasebackup)
209 209 return commitfunc
210 210
211 211
212 212
213 213 def applychanges(ui, repo, ctx, opts):
214 214 """Merge changeset from ctx (only) in the current working directory"""
215 215 wcpar = repo.dirstate.parents()[0]
216 216 if ctx.p1().node() == wcpar:
217 217 # edition ar "in place" we do not need to make any merge,
218 218 # just applies changes on parent for edition
219 219 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
220 220 stats = None
221 221 else:
222 222 try:
223 223 # ui.forcemerge is an internal variable, do not document
224 224 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
225 225 stats = mergemod.update(repo, ctx.node(), True, True, False,
226 226 ctx.p1().node())
227 227 finally:
228 228 repo.ui.setconfig('ui', 'forcemerge', '')
229 229 repo.setparents(wcpar, node.nullid)
230 230 repo.dirstate.write()
231 231 # fix up dirstate for copies and renames
232 232 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
233 233 return stats
234 234
235 235 def collapse(repo, first, last, commitopts):
236 236 """collapse the set of revisions from first to last as new one.
237 237
238 238 Expected commit options are:
239 239 - message
240 240 - date
241 241 - username
242 242 Commit message is edited in all cases.
243 243
244 244 This function works in memory."""
245 245 ctxs = list(repo.set('%d::%d', first, last))
246 246 if not ctxs:
247 247 return None
248 248 base = first.parents()[0]
249 249
250 250 # commit a new version of the old changeset, including the update
251 251 # collect all files which might be affected
252 252 files = set()
253 253 for ctx in ctxs:
254 254 files.update(ctx.files())
255 255
256 256 # Recompute copies (avoid recording a -> b -> a)
257 257 copied = copies.pathcopies(base, last)
258 258
259 259 # prune files which were reverted by the updates
260 260 def samefile(f):
261 261 if f in last.manifest():
262 262 a = last.filectx(f)
263 263 if f in base.manifest():
264 264 b = base.filectx(f)
265 265 return (a.data() == b.data()
266 266 and a.flags() == b.flags())
267 267 else:
268 268 return False
269 269 else:
270 270 return f not in base.manifest()
271 271 files = [f for f in files if not samefile(f)]
272 272 # commit version of these files as defined by head
273 273 headmf = last.manifest()
274 274 def filectxfn(repo, ctx, path):
275 275 if path in headmf:
276 276 fctx = last[path]
277 277 flags = fctx.flags()
278 278 mctx = context.memfilectx(fctx.path(), fctx.data(),
279 279 islink='l' in flags,
280 280 isexec='x' in flags,
281 281 copied=copied.get(path))
282 282 return mctx
283 283 raise IOError()
284 284
285 285 if commitopts.get('message'):
286 286 message = commitopts['message']
287 287 else:
288 288 message = first.description()
289 289 user = commitopts.get('user')
290 290 date = commitopts.get('date')
291 291 extra = commitopts.get('extra')
292 292
293 293 parents = (first.p1().node(), first.p2().node())
294 294 new = context.memctx(repo,
295 295 parents=parents,
296 296 text=message,
297 297 files=files,
298 298 filectxfn=filectxfn,
299 299 user=user,
300 300 date=date,
301 301 extra=extra)
302 302 new._text = cmdutil.commitforceeditor(repo, new, [])
303 303 return repo.commitctx(new)
304 304
305 305 def pick(ui, repo, ctx, ha, opts):
306 306 oldctx = repo[ha]
307 307 if oldctx.parents()[0] == ctx:
308 308 ui.debug('node %s unchanged\n' % ha)
309 309 return oldctx, []
310 310 hg.update(repo, ctx.node())
311 311 stats = applychanges(ui, repo, oldctx, opts)
312 312 if stats and stats[3] > 0:
313 313 raise error.InterventionRequired(_('Fix up the change and run '
314 314 'hg histedit --continue'))
315 315 # drop the second merge parent
316 316 commit = commitfuncfor(repo, oldctx)
317 317 n = commit(text=oldctx.description(), user=oldctx.user(),
318 318 date=oldctx.date(), extra=oldctx.extra())
319 319 if n is None:
320 320 ui.warn(_('%s: empty changeset\n')
321 321 % node.hex(ha))
322 322 return ctx, []
323 323 new = repo[n]
324 324 return new, [(oldctx.node(), (n,))]
325 325
326 326
327 327 def edit(ui, repo, ctx, ha, opts):
328 328 oldctx = repo[ha]
329 329 hg.update(repo, ctx.node())
330 330 applychanges(ui, repo, oldctx, opts)
331 331 raise error.InterventionRequired(
332 332 _('Make changes as needed, you may commit or record as needed now.\n'
333 333 'When you are finished, run hg histedit --continue to resume.'))
334 334
335 335 def fold(ui, repo, ctx, ha, opts):
336 336 oldctx = repo[ha]
337 337 hg.update(repo, ctx.node())
338 338 stats = applychanges(ui, repo, oldctx, opts)
339 339 if stats and stats[3] > 0:
340 340 raise error.InterventionRequired(
341 341 _('Fix up the change and run hg histedit --continue'))
342 342 n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
343 343 date=oldctx.date(), extra=oldctx.extra())
344 344 if n is None:
345 345 ui.warn(_('%s: empty changeset')
346 346 % node.hex(ha))
347 347 return ctx, []
348 348 return finishfold(ui, repo, ctx, oldctx, n, opts, [])
349 349
350 350 def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
351 351 parent = ctx.parents()[0].node()
352 352 hg.update(repo, parent)
353 353 ### prepare new commit data
354 354 commitopts = opts.copy()
355 355 # username
356 356 if ctx.user() == oldctx.user():
357 357 username = ctx.user()
358 358 else:
359 359 username = ui.username()
360 360 commitopts['user'] = username
361 361 # commit message
362 362 newmessage = '\n***\n'.join(
363 363 [ctx.description()] +
364 364 [repo[r].description() for r in internalchanges] +
365 365 [oldctx.description()]) + '\n'
366 366 commitopts['message'] = newmessage
367 367 # date
368 368 commitopts['date'] = max(ctx.date(), oldctx.date())
369 369 extra = ctx.extra().copy()
370 370 # histedit_source
371 371 # note: ctx is likely a temporary commit but that the best we can do here
372 372 # This is sufficient to solve issue3681 anyway
373 373 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
374 374 commitopts['extra'] = extra
375 375 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
376 376 try:
377 377 phasemin = max(ctx.phase(), oldctx.phase())
378 378 repo.ui.setconfig('phases', 'new-commit', phasemin)
379 379 n = collapse(repo, ctx, repo[newnode], commitopts)
380 380 finally:
381 381 repo.ui.restoreconfig(phasebackup)
382 382 if n is None:
383 383 return ctx, []
384 384 hg.update(repo, n)
385 385 replacements = [(oldctx.node(), (newnode,)),
386 386 (ctx.node(), (n,)),
387 387 (newnode, (n,)),
388 388 ]
389 389 for ich in internalchanges:
390 390 replacements.append((ich, (n,)))
391 391 return repo[n], replacements
392 392
393 393 def drop(ui, repo, ctx, ha, opts):
394 394 return ctx, [(repo[ha].node(), ())]
395 395
396 396
397 397 def message(ui, repo, ctx, ha, opts):
398 398 oldctx = repo[ha]
399 399 hg.update(repo, ctx.node())
400 400 stats = applychanges(ui, repo, oldctx, opts)
401 401 if stats and stats[3] > 0:
402 402 raise error.InterventionRequired(
403 403 _('Fix up the change and run hg histedit --continue'))
404 404 message = oldctx.description() + '\n'
405 405 message = ui.edit(message, ui.username())
406 406 commit = commitfuncfor(repo, oldctx)
407 407 new = commit(text=message, user=oldctx.user(), date=oldctx.date(),
408 408 extra=oldctx.extra())
409 409 newctx = repo[new]
410 410 if oldctx.node() != newctx.node():
411 411 return newctx, [(oldctx.node(), (new,))]
412 412 # We didn't make an edit, so just indicate no replaced nodes
413 413 return newctx, []
414 414
415 415 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
416 416 """utility function to find the first outgoing changeset
417 417
418 418 Used by initialisation code"""
419 419 dest = ui.expandpath(remote or 'default-push', remote or 'default')
420 420 dest, revs = hg.parseurl(dest, None)[:2]
421 421 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
422 422
423 423 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
424 424 other = hg.peer(repo, opts, dest)
425 425
426 426 if revs:
427 427 revs = [repo.lookup(rev) for rev in revs]
428 428
429 429 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
430 430 if not outgoing.missing:
431 431 raise util.Abort(_('no outgoing ancestors'))
432 432 roots = list(repo.revs("roots(%ln)", outgoing.missing))
433 433 if 1 < len(roots):
434 434 msg = _('there are ambiguous outgoing revisions')
435 435 hint = _('see "hg help histedit" for more detail')
436 436 raise util.Abort(msg, hint=hint)
437 437 return repo.lookup(roots[0])
438 438
439 439 actiontable = {'p': pick,
440 440 'pick': pick,
441 441 'e': edit,
442 442 'edit': edit,
443 443 'f': fold,
444 444 'fold': fold,
445 445 'd': drop,
446 446 'drop': drop,
447 447 'm': message,
448 448 'mess': message,
449 449 }
450 450
451 451 @command('histedit',
452 452 [('', 'commands', '',
453 453 _('Read history edits from the specified file.')),
454 454 ('c', 'continue', False, _('continue an edit already in progress')),
455 455 ('k', 'keep', False,
456 456 _("don't strip old nodes after edit is complete")),
457 457 ('', 'abort', False, _('abort an edit in progress')),
458 458 ('o', 'outgoing', False, _('changesets not found in destination')),
459 459 ('f', 'force', False,
460 460 _('force outgoing even for unrelated repositories')),
461 461 ('r', 'rev', [], _('first revision to be edited'))],
462 462 _("ANCESTOR | --outgoing [URL]"))
463 463 def histedit(ui, repo, *freeargs, **opts):
464 464 """interactively edit changeset history
465 465
466 466 This command edits changesets between ANCESTOR and the parent of
467 467 the working directory.
468 468
469 469 With --outgoing, this edits changesets not found in the
470 470 destination repository. If URL of the destination is omitted, the
471 471 'default-push' (or 'default') path will be used.
472 472
473 473 For safety, this command is aborted, also if there are ambiguous
474 474 outgoing revisions which may confuse users: for example, there are
475 475 multiple branches containing outgoing revisions.
476 476
477 477 Use "min(outgoing() and ::.)" or similar revset specification
478 478 instead of --outgoing to specify edit target revision exactly in
479 479 such ambiguous situation. See :hg:`help revsets` for detail about
480 480 selecting revisions.
481 481
482 482 Returns 0 on success, 1 if user intervention is required (not only
483 483 for intentional "edit" command, but also for resolving unexpected
484 484 conflicts).
485 485 """
486 486 lock = wlock = None
487 487 try:
488 488 wlock = repo.wlock()
489 489 lock = repo.lock()
490 490 _histedit(ui, repo, *freeargs, **opts)
491 491 finally:
492 492 release(lock, wlock)
493 493
494 494 def _histedit(ui, repo, *freeargs, **opts):
495 495 # TODO only abort if we try and histedit mq patches, not just
496 496 # blanket if mq patches are applied somewhere
497 497 mq = getattr(repo, 'mq', None)
498 498 if mq and mq.applied:
499 499 raise util.Abort(_('source has mq patches applied'))
500 500
501 501 # basic argument incompatibility processing
502 502 outg = opts.get('outgoing')
503 503 cont = opts.get('continue')
504 504 abort = opts.get('abort')
505 505 force = opts.get('force')
506 506 rules = opts.get('commands', '')
507 507 revs = opts.get('rev', [])
508 508 goal = 'new' # This invocation goal, in new, continue, abort
509 509 if force and not outg:
510 510 raise util.Abort(_('--force only allowed with --outgoing'))
511 511 if cont:
512 512 if util.any((outg, abort, revs, freeargs, rules)):
513 513 raise util.Abort(_('no arguments allowed with --continue'))
514 514 goal = 'continue'
515 515 elif abort:
516 516 if util.any((outg, revs, freeargs, rules)):
517 517 raise util.Abort(_('no arguments allowed with --abort'))
518 518 goal = 'abort'
519 519 else:
520 520 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
521 521 raise util.Abort(_('history edit already in progress, try '
522 522 '--continue or --abort'))
523 523 if outg:
524 524 if revs:
525 525 raise util.Abort(_('no revisions allowed with --outgoing'))
526 526 if len(freeargs) > 1:
527 527 raise util.Abort(
528 528 _('only one repo argument allowed with --outgoing'))
529 529 else:
530 530 revs.extend(freeargs)
531 531 if len(revs) != 1:
532 532 raise util.Abort(
533 533 _('histedit requires exactly one ancestor revision'))
534 534
535 535
536 536 if goal == 'continue':
537 537 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
538 538 parentctx = repo[parentctxnode]
539 539 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts)
540 540 replacements.extend(repl)
541 541 elif goal == 'abort':
542 542 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
543 543 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements)
544 544 ui.debug('restore wc to old parent %s\n' % node.short(topmost))
545 545 # check whether we should update away
546 546 parentnodes = [c.node() for c in repo[None].parents()]
547 547 for n in leafs | set([parentctxnode]):
548 548 if n in parentnodes:
549 549 hg.clean(repo, topmost)
550 550 break
551 551 else:
552 552 pass
553 553 cleanupnode(ui, repo, 'created', tmpnodes)
554 554 cleanupnode(ui, repo, 'temp', leafs)
555 555 os.unlink(os.path.join(repo.path, 'histedit-state'))
556 556 return
557 557 else:
558 558 cmdutil.checkunfinished(repo)
559 559 cmdutil.bailifchanged(repo)
560 560
561 561 topmost, empty = repo.dirstate.parents()
562 562 if outg:
563 563 if freeargs:
564 564 remote = freeargs[0]
565 565 else:
566 566 remote = None
567 567 root = findoutgoing(ui, repo, remote, force, opts)
568 568 else:
569 569 root = revs[0]
570 570 root = scmutil.revsingle(repo, root).node()
571 571
572 572 keep = opts.get('keep', False)
573 573 revs = between(repo, root, topmost, keep)
574 574 if not revs:
575 575 raise util.Abort(_('%s is not an ancestor of working directory') %
576 576 node.short(root))
577 577
578 578 ctxs = [repo[r] for r in revs]
579 579 if not rules:
580 580 rules = '\n'.join([makedesc(c) for c in ctxs])
581 581 rules += '\n\n'
582 582 rules += editcomment % (node.short(root), node.short(topmost))
583 583 rules = ui.edit(rules, ui.username())
584 584 # Save edit rules in .hg/histedit-last-edit.txt in case
585 585 # the user needs to ask for help after something
586 586 # surprising happens.
587 587 f = open(repo.join('histedit-last-edit.txt'), 'w')
588 588 f.write(rules)
589 589 f.close()
590 590 else:
591 591 if rules == '-':
592 592 f = sys.stdin
593 593 else:
594 594 f = open(rules)
595 595 rules = f.read()
596 596 f.close()
597 597 rules = [l for l in (r.strip() for r in rules.splitlines())
598 598 if l and not l[0] == '#']
599 599 rules = verifyrules(rules, repo, ctxs)
600 600
601 601 parentctx = repo[root].parents()[0]
602 602 keep = opts.get('keep', False)
603 603 replacements = []
604 604
605 605
606 606 while rules:
607 607 writestate(repo, parentctx.node(), rules, keep, topmost, replacements)
608 608 action, ha = rules.pop(0)
609 609 ui.debug('histedit: processing %s %s\n' % (action, ha))
610 610 actfunc = actiontable[action]
611 611 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts)
612 612 replacements.extend(replacement_)
613 613
614 614 hg.update(repo, parentctx.node())
615 615
616 616 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements)
617 617 if mapping:
618 618 for prec, succs in mapping.iteritems():
619 619 if not succs:
620 620 ui.debug('histedit: %s is dropped\n' % node.short(prec))
621 621 else:
622 622 ui.debug('histedit: %s is replaced by %s\n' % (
623 623 node.short(prec), node.short(succs[0])))
624 624 if len(succs) > 1:
625 625 m = 'histedit: %s'
626 626 for n in succs[1:]:
627 627 ui.debug(m % node.short(n))
628 628
629 629 if not keep:
630 630 if mapping:
631 631 movebookmarks(ui, repo, mapping, topmost, ntm)
632 632 # TODO update mq state
633 633 if obsolete._enabled:
634 634 markers = []
635 635 # sort by revision number because it sound "right"
636 636 for prec in sorted(mapping, key=repo.changelog.rev):
637 637 succs = mapping[prec]
638 638 markers.append((repo[prec],
639 639 tuple(repo[s] for s in succs)))
640 640 if markers:
641 641 obsolete.createmarkers(repo, markers)
642 642 else:
643 643 cleanupnode(ui, repo, 'replaced', mapping)
644 644
645 645 cleanupnode(ui, repo, 'temp', tmpnodes)
646 646 os.unlink(os.path.join(repo.path, 'histedit-state'))
647 647 if os.path.exists(repo.sjoin('undo')):
648 648 os.unlink(repo.sjoin('undo'))
649 649
650 650
651 651 def bootstrapcontinue(ui, repo, parentctx, rules, opts):
652 652 action, currentnode = rules.pop(0)
653 653 ctx = repo[currentnode]
654 654 # is there any new commit between the expected parent and "."
655 655 #
656 656 # note: does not take non linear new change in account (but previous
657 657 # implementation didn't used them anyway (issue3655)
658 658 newchildren = [c.node() for c in repo.set('(%d::.)', parentctx)]
659 659 if parentctx.node() != node.nullid:
660 660 if not newchildren:
661 661 # `parentctxnode` should match but no result. This means that
662 662 # currentnode is not a descendant from parentctxnode.
663 663 msg = _('%s is not an ancestor of working directory')
664 664 hint = _('use "histedit --abort" to clear broken state')
665 665 raise util.Abort(msg % parentctx, hint=hint)
666 666 newchildren.pop(0) # remove parentctxnode
667 667 # Commit dirty working directory if necessary
668 668 new = None
669 669 m, a, r, d = repo.status()[:4]
670 670 if m or a or r or d:
671 671 # prepare the message for the commit to comes
672 672 if action in ('f', 'fold'):
673 673 message = 'fold-temp-revision %s' % currentnode
674 674 else:
675 675 message = ctx.description() + '\n'
676 676 if action in ('e', 'edit', 'm', 'mess'):
677 677 editor = cmdutil.commitforceeditor
678 678 else:
679 679 editor = False
680 680 commit = commitfuncfor(repo, ctx)
681 681 new = commit(text=message, user=ctx.user(),
682 682 date=ctx.date(), extra=ctx.extra(),
683 683 editor=editor)
684 684 if new is not None:
685 685 newchildren.append(new)
686 686
687 687 replacements = []
688 688 # track replacements
689 689 if ctx.node() not in newchildren:
690 690 # note: new children may be empty when the changeset is dropped.
691 691 # this happen e.g during conflicting pick where we revert content
692 692 # to parent.
693 693 replacements.append((ctx.node(), tuple(newchildren)))
694 694
695 695 if action in ('f', 'fold'):
696 696 if newchildren:
697 697 # finalize fold operation if applicable
698 698 if new is None:
699 699 new = newchildren[-1]
700 700 else:
701 701 newchildren.pop() # remove new from internal changes
702 702 parentctx, repl = finishfold(ui, repo, parentctx, ctx, new, opts,
703 703 newchildren)
704 704 replacements.extend(repl)
705 705 else:
706 706 # newchildren is empty if the fold did not result in any commit
707 707 # this happen when all folded change are discarded during the
708 708 # merge.
709 709 replacements.append((ctx.node(), (parentctx.node(),)))
710 710 elif newchildren:
711 711 # otherwise update "parentctx" before proceeding to further operation
712 712 parentctx = repo[newchildren[-1]]
713 713 return parentctx, replacements
714 714
715 715
716 716 def between(repo, old, new, keep):
717 717 """select and validate the set of revision to edit
718 718
719 719 When keep is false, the specified set can't have children."""
720 720 ctxs = list(repo.set('%n::%n', old, new))
721 721 if ctxs and not keep:
722 722 if (not obsolete._enabled and
723 723 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
724 724 raise util.Abort(_('cannot edit history that would orphan nodes'))
725 725 if repo.revs('(%ld) and merge()', ctxs):
726 726 raise util.Abort(_('cannot edit history that contains merges'))
727 727 root = ctxs[0] # list is already sorted by repo.set
728 728 if not root.phase():
729 729 raise util.Abort(_('cannot edit immutable changeset: %s') % root)
730 730 return [c.node() for c in ctxs]
731 731
732 732
733 733 def writestate(repo, parentnode, rules, keep, topmost, replacements):
734 734 fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
735 735 pickle.dump((parentnode, rules, keep, topmost, replacements), fp)
736 736 fp.close()
737 737
738 738 def readstate(repo):
739 739 """Returns a tuple of (parentnode, rules, keep, topmost, replacements).
740 740 """
741 741 fp = open(os.path.join(repo.path, 'histedit-state'))
742 742 return pickle.load(fp)
743 743
744 744
745 745 def makedesc(c):
746 746 """build a initial action line for a ctx `c`
747 747
748 748 line are in the form:
749 749
750 750 pick <hash> <rev> <summary>
751 751 """
752 752 summary = ''
753 753 if c.description():
754 754 summary = c.description().splitlines()[0]
755 755 line = 'pick %s %d %s' % (c, c.rev(), summary)
756 756 return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
757 757
758 758 def verifyrules(rules, repo, ctxs):
759 759 """Verify that there exists exactly one edit rule per given changeset.
760 760
761 761 Will abort if there are to many or too few rules, a malformed rule,
762 762 or a rule on a changeset outside of the user-given range.
763 763 """
764 764 parsed = []
765 765 expected = set(str(c) for c in ctxs)
766 766 seen = set()
767 767 for r in rules:
768 768 if ' ' not in r:
769 769 raise util.Abort(_('malformed line "%s"') % r)
770 770 action, rest = r.split(' ', 1)
771 771 ha = rest.strip().split(' ', 1)[0]
772 772 try:
773 773 ha = str(repo[ha]) # ensure its a short hash
774 774 except error.RepoError:
775 775 raise util.Abort(_('unknown changeset %s listed') % ha)
776 776 if ha not in expected:
777 777 raise util.Abort(
778 778 _('may not use changesets other than the ones listed'))
779 779 if ha in seen:
780 780 raise util.Abort(_('duplicated command for changeset %s') % ha)
781 781 seen.add(ha)
782 782 if action not in actiontable:
783 783 raise util.Abort(_('unknown action "%s"') % action)
784 784 parsed.append([action, ha])
785 785 missing = sorted(expected - seen) # sort to stabilize output
786 786 if missing:
787 787 raise util.Abort(_('missing rules for changeset %s') % missing[0],
788 788 hint=_('do you want to use the drop action?'))
789 789 return parsed
790 790
791 791 def processreplacement(repo, replacements):
792 792 """process the list of replacements to return
793 793
794 794 1) the final mapping between original and created nodes
795 795 2) the list of temporary node created by histedit
796 796 3) the list of new commit created by histedit"""
797 797 allsuccs = set()
798 798 replaced = set()
799 799 fullmapping = {}
800 800 # initialise basic set
801 801 # fullmapping record all operation recorded in replacement
802 802 for rep in replacements:
803 803 allsuccs.update(rep[1])
804 804 replaced.add(rep[0])
805 805 fullmapping.setdefault(rep[0], set()).update(rep[1])
806 806 new = allsuccs - replaced
807 807 tmpnodes = allsuccs & replaced
808 808 # Reduce content fullmapping into direct relation between original nodes
809 809 # and final node created during history edition
810 810 # Dropped changeset are replaced by an empty list
811 811 toproceed = set(fullmapping)
812 812 final = {}
813 813 while toproceed:
814 814 for x in list(toproceed):
815 815 succs = fullmapping[x]
816 816 for s in list(succs):
817 817 if s in toproceed:
818 818 # non final node with unknown closure
819 819 # We can't process this now
820 820 break
821 821 elif s in final:
822 822 # non final node, replace with closure
823 823 succs.remove(s)
824 824 succs.update(final[s])
825 825 else:
826 826 final[x] = succs
827 827 toproceed.remove(x)
828 828 # remove tmpnodes from final mapping
829 829 for n in tmpnodes:
830 830 del final[n]
831 831 # we expect all changes involved in final to exist in the repo
832 832 # turn `final` into list (topologically sorted)
833 833 nm = repo.changelog.nodemap
834 834 for prec, succs in final.items():
835 835 final[prec] = sorted(succs, key=nm.get)
836 836
837 837 # computed topmost element (necessary for bookmark)
838 838 if new:
839 839 newtopmost = sorted(new, key=repo.changelog.rev)[-1]
840 840 elif not final:
841 841 # Nothing rewritten at all. we won't need `newtopmost`
842 842 # It is the same as `oldtopmost` and `processreplacement` know it
843 843 newtopmost = None
844 844 else:
845 845 # every body died. The newtopmost is the parent of the root.
846 846 newtopmost = repo[sorted(final, key=repo.changelog.rev)[0]].p1().node()
847 847
848 848 return final, tmpnodes, new, newtopmost
849 849
850 850 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
851 851 """Move bookmark from old to newly created node"""
852 852 if not mapping:
853 853 # if nothing got rewritten there is not purpose for this function
854 854 return
855 855 moves = []
856 856 for bk, old in sorted(repo._bookmarks.iteritems()):
857 857 if old == oldtopmost:
858 858 # special case ensure bookmark stay on tip.
859 859 #
860 860 # This is arguably a feature and we may only want that for the
861 861 # active bookmark. But the behavior is kept compatible with the old
862 862 # version for now.
863 863 moves.append((bk, newtopmost))
864 864 continue
865 865 base = old
866 866 new = mapping.get(base, None)
867 867 if new is None:
868 868 continue
869 869 while not new:
870 870 # base is killed, trying with parent
871 871 base = repo[base].p1().node()
872 872 new = mapping.get(base, (base,))
873 873 # nothing to move
874 874 moves.append((bk, new[-1]))
875 875 if moves:
876 876 marks = repo._bookmarks
877 877 for mark, new in moves:
878 878 old = marks[mark]
879 879 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
880 880 % (mark, node.short(old), node.short(new)))
881 881 marks[mark] = new
882 882 marks.write()
883 883
884 884 def cleanupnode(ui, repo, name, nodes):
885 885 """strip a group of nodes from the repository
886 886
887 887 The set of node to strip may contains unknown nodes."""
888 888 ui.debug('should strip %s nodes %s\n' %
889 889 (name, ', '.join([node.short(n) for n in nodes])))
890 890 lock = None
891 891 try:
892 892 lock = repo.lock()
893 893 # Find all node that need to be stripped
894 894 # (we hg %lr instead of %ln to silently ignore unknown item
895 895 nm = repo.changelog.nodemap
896 896 nodes = [n for n in nodes if n in nm]
897 897 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
898 898 for c in roots:
899 899 # We should process node in reverse order to strip tip most first.
900 900 # but this trigger a bug in changegroup hook.
901 901 # This would reduce bundle overhead
902 902 repair.strip(ui, repo, c)
903 903 finally:
904 904 lockmod.release(lock)
905 905
906 906 def summaryhook(ui, repo):
907 907 if not os.path.exists(repo.join('histedit-state')):
908 908 return
909 909 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo)
910 910 if rules:
911 911 # i18n: column positioning for "hg summary"
912 912 ui.write(_('hist: %s (histedit --continue)\n') %
913 913 (ui.label(_('%d remaining'), 'histedit.remaining') %
914 914 len(rules)))
915 915
916 916 def extsetup(ui):
917 917 cmdutil.summaryhooks.add('histedit', summaryhook)
918 918 cmdutil.unfinishedstates.append(
919 919 ['histedit-state', False, True, _('histedit in progress'),
920 920 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
@@ -1,200 +1,200 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 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
45 45 --------------------------------------------------------------------
46 46
47 47 $ HGEDITOR=cat hg histedit "tip^^"
48 48 pick eb57da33312f 2 three
49 49 pick c8e68270e35a 3 four
50 50 pick 08d98a8350f3 4 five
51 51
52 52 # Edit history between eb57da33312f and 08d98a8350f3
53 53 #
54 54 # Commits are listed from least to most recent
55 55 #
56 56 # Commands:
57 57 # p, pick = use commit
58 58 # e, edit = use commit, but stop for amending
59 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
59 # f, fold = use commit, but combine it with the one above
60 60 # d, drop = remove commit from history
61 61 # m, mess = edit message without changing commit content
62 62 #
63 63 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64
65 65 Run on a revision not ancestors of the current working directory.
66 66 --------------------------------------------------------------------
67 67
68 68 $ hg up 2
69 69 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 70 $ hg histedit -r 4
71 71 abort: 08d98a8350f3 is not an ancestor of working directory
72 72 [255]
73 73 $ hg up --quiet
74 74
75 75 Run on a revision not descendants of the initial parent
76 76 --------------------------------------------------------------------
77 77
78 78 Test the message shown for inconsistent histedit state, which may be
79 79 created (and forgotten) by Mercurial earlier than 2.7. This emulates
80 80 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
81 81 temporarily.
82 82
83 83 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
84 84 > edit 08d98a8350f3 4 five
85 85 > EOF
86 86 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 87 reverting alpha
88 88 Make changes as needed, you may commit or record as needed now.
89 89 When you are finished, run hg histedit --continue to resume.
90 90 [1]
91 91
92 92 $ mv .hg/histedit-state .hg/histedit-state.back
93 93 $ hg update --quiet --clean 2
94 94 $ mv .hg/histedit-state.back .hg/histedit-state
95 95
96 96 $ hg histedit --continue
97 97 abort: c8e68270e35a is not an ancestor of working directory
98 98 (use "histedit --abort" to clear broken state)
99 99 [255]
100 100
101 101 $ hg histedit --abort
102 102 $ hg update --quiet --clean
103 103
104 104 Test that missing revisions are detected
105 105 ---------------------------------------
106 106
107 107 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
108 108 > pick eb57da33312f 2 three
109 109 > pick 08d98a8350f3 4 five
110 110 > EOF
111 111 abort: missing rules for changeset c8e68270e35a
112 112 (do you want to use the drop action?)
113 113 [255]
114 114
115 115 Test that extra revisions are detected
116 116 ---------------------------------------
117 117
118 118 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
119 119 > pick 6058cbb6cfd7 0 one
120 120 > pick c8e68270e35a 3 four
121 121 > pick 08d98a8350f3 4 five
122 122 > EOF
123 123 abort: may not use changesets other than the ones listed
124 124 [255]
125 125
126 126 Test malformed line
127 127 ---------------------------------------
128 128
129 129 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
130 130 > pickeb57da33312f2three
131 131 > pick c8e68270e35a 3 four
132 132 > pick 08d98a8350f3 4 five
133 133 > EOF
134 134 abort: malformed line "pickeb57da33312f2three"
135 135 [255]
136 136
137 137 Test unknown changeset
138 138 ---------------------------------------
139 139
140 140 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
141 141 > pick 0123456789ab 2 three
142 142 > pick c8e68270e35a 3 four
143 143 > pick 08d98a8350f3 4 five
144 144 > EOF
145 145 abort: unknown changeset 0123456789ab listed
146 146 [255]
147 147
148 148 Test unknown command
149 149 ---------------------------------------
150 150
151 151 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
152 152 > coin eb57da33312f 2 three
153 153 > pick c8e68270e35a 3 four
154 154 > pick 08d98a8350f3 4 five
155 155 > EOF
156 156 abort: unknown action "coin"
157 157 [255]
158 158
159 159 Test duplicated changeset
160 160 ---------------------------------------
161 161
162 162 So one is missing and one appear twice.
163 163
164 164 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
165 165 > pick eb57da33312f 2 three
166 166 > pick eb57da33312f 2 three
167 167 > pick 08d98a8350f3 4 five
168 168 > EOF
169 169 abort: duplicated command for changeset eb57da33312f
170 170 [255]
171 171
172 172 Test short version of command
173 173 ---------------------------------------
174 174
175 175 Note: we use varying amounts of white space between command name and changeset
176 176 short hash. This tests issue3893.
177 177
178 178 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
179 179 > pick eb57da33312f 2 three
180 180 > p c8e68270e35a 3 four
181 181 > f 08d98a8350f3 4 five
182 182 > EOF
183 183 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 184 reverting alpha
185 185 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 186 four
187 187 ***
188 188 five
189 189
190 190
191 191
192 192 HG: Enter commit message. Lines beginning with 'HG:' are removed.
193 193 HG: Leave message empty to abort commit.
194 194 HG: --
195 195 HG: user: test
196 196 HG: branch 'default'
197 197 HG: changed alpha
198 198 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
199 199 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 200 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
@@ -1,180 +1,180 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 # Commands:
73 73 # p, pick = use commit
74 74 # e, edit = use commit, but stop for amending
75 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
75 # f, fold = use commit, but combine it with the one above
76 76 # d, drop = remove commit from history
77 77 # m, mess = edit message without changing commit content
78 78 #
79 79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 80 $ hg histedit 1 --commands - --verbose << EOF | grep histedit
81 81 > pick 177f92b77385 2 c
82 82 > drop d2ae7f538514 1 b
83 83 > pick 055a42cdd887 3 d
84 84 > fold e860deea161a 4 e
85 85 > pick 652413bf663e 5 f
86 86 > EOF
87 87 histedit: moving bookmarks also-two from 177f92b77385 to b346ab9a313d
88 88 histedit: moving bookmarks five from 652413bf663e to cacdfd884a93
89 89 histedit: moving bookmarks four from e860deea161a to 59d9f330561f
90 90 histedit: moving bookmarks three from 055a42cdd887 to 59d9f330561f
91 91 histedit: moving bookmarks two from 177f92b77385 to b346ab9a313d
92 92 histedit: moving bookmarks will-move-backwards from d2ae7f538514 to cb9a9f314b8b
93 93 saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-backup.hg (glob)
94 94 saved backup bundle to $TESTTMP/r/.hg/strip-backup/96e494a2d553-backup.hg (glob)
95 95 $ hg log --graph
96 96 @ changeset: 3:cacdfd884a93
97 97 | bookmark: five
98 98 | tag: tip
99 99 | user: test
100 100 | date: Thu Jan 01 00:00:00 1970 +0000
101 101 | summary: f
102 102 |
103 103 o changeset: 2:59d9f330561f
104 104 | bookmark: four
105 105 | bookmark: three
106 106 | user: test
107 107 | date: Thu Jan 01 00:00:00 1970 +0000
108 108 | summary: d
109 109 |
110 110 o changeset: 1:b346ab9a313d
111 111 | bookmark: also-two
112 112 | bookmark: two
113 113 | user: test
114 114 | date: Thu Jan 01 00:00:00 1970 +0000
115 115 | summary: c
116 116 |
117 117 o changeset: 0:cb9a9f314b8b
118 118 bookmark: will-move-backwards
119 119 user: test
120 120 date: Thu Jan 01 00:00:00 1970 +0000
121 121 summary: a
122 122
123 123 $ HGEDITOR=cat hg histedit 1
124 124 pick b346ab9a313d 1 c
125 125 pick 59d9f330561f 2 d
126 126 pick cacdfd884a93 3 f
127 127
128 128 # Edit history between b346ab9a313d and cacdfd884a93
129 129 #
130 130 # Commits are listed from least to most recent
131 131 #
132 132 # Commands:
133 133 # p, pick = use commit
134 134 # e, edit = use commit, but stop for amending
135 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
135 # f, fold = use commit, but combine it with the one above
136 136 # d, drop = remove commit from history
137 137 # m, mess = edit message without changing commit content
138 138 #
139 139 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 140 $ hg histedit 1 --commands - --verbose << EOF | grep histedit
141 141 > pick b346ab9a313d 1 c
142 142 > pick cacdfd884a93 3 f
143 143 > pick 59d9f330561f 2 d
144 144 > EOF
145 145 histedit: moving bookmarks five from cacdfd884a93 to c04e50810e4b
146 146 histedit: moving bookmarks four from 59d9f330561f to c04e50810e4b
147 147 histedit: moving bookmarks three from 59d9f330561f to c04e50810e4b
148 148 saved backup bundle to $TESTTMP/r/.hg/strip-backup/59d9f330561f-backup.hg (glob)
149 149
150 150 We expect 'five' to stay at tip, since the tipmost bookmark is most
151 151 likely the useful signal.
152 152
153 153 $ hg log --graph
154 154 @ changeset: 3:c04e50810e4b
155 155 | bookmark: five
156 156 | bookmark: four
157 157 | bookmark: three
158 158 | tag: tip
159 159 | user: test
160 160 | date: Thu Jan 01 00:00:00 1970 +0000
161 161 | summary: d
162 162 |
163 163 o changeset: 2:c13eb81022ca
164 164 | user: test
165 165 | date: Thu Jan 01 00:00:00 1970 +0000
166 166 | summary: f
167 167 |
168 168 o changeset: 1:b346ab9a313d
169 169 | bookmark: also-two
170 170 | bookmark: two
171 171 | user: test
172 172 | date: Thu Jan 01 00:00:00 1970 +0000
173 173 | summary: c
174 174 |
175 175 o changeset: 0:cb9a9f314b8b
176 176 bookmark: will-move-backwards
177 177 user: test
178 178 date: Thu Jan 01 00:00:00 1970 +0000
179 179 summary: a
180 180
@@ -1,358 +1,358 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > histedit=
6 6 > EOF
7 7
8 8 $ initrepo ()
9 9 > {
10 10 > hg init r
11 11 > cd r
12 12 > for x in a b c d e f ; do
13 13 > echo $x > $x
14 14 > hg add $x
15 15 > hg ci -m $x
16 16 > done
17 17 > }
18 18
19 19 $ initrepo
20 20
21 21 log before edit
22 22 $ hg log --graph
23 23 @ changeset: 5:652413bf663e
24 24 | tag: tip
25 25 | user: test
26 26 | date: Thu Jan 01 00:00:00 1970 +0000
27 27 | summary: f
28 28 |
29 29 o changeset: 4:e860deea161a
30 30 | user: test
31 31 | date: Thu Jan 01 00:00:00 1970 +0000
32 32 | summary: e
33 33 |
34 34 o changeset: 3:055a42cdd887
35 35 | user: test
36 36 | date: Thu Jan 01 00:00:00 1970 +0000
37 37 | summary: d
38 38 |
39 39 o changeset: 2:177f92b77385
40 40 | user: test
41 41 | date: Thu Jan 01 00:00:00 1970 +0000
42 42 | summary: c
43 43 |
44 44 o changeset: 1:d2ae7f538514
45 45 | user: test
46 46 | date: Thu Jan 01 00:00:00 1970 +0000
47 47 | summary: b
48 48 |
49 49 o changeset: 0:cb9a9f314b8b
50 50 user: test
51 51 date: Thu Jan 01 00:00:00 1970 +0000
52 52 summary: a
53 53
54 54
55 55 show the edit commands offered
56 56 $ HGEDITOR=cat hg histedit 177f92b77385
57 57 pick 177f92b77385 2 c
58 58 pick 055a42cdd887 3 d
59 59 pick e860deea161a 4 e
60 60 pick 652413bf663e 5 f
61 61
62 62 # Edit history between 177f92b77385 and 652413bf663e
63 63 #
64 64 # Commits are listed from least to most recent
65 65 #
66 66 # Commands:
67 67 # p, pick = use commit
68 68 # e, edit = use commit, but stop for amending
69 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
69 # f, fold = use commit, but combine it with the one above
70 70 # d, drop = remove commit from history
71 71 # m, mess = edit message without changing commit content
72 72 #
73 73 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
74 74
75 75 edit the history
76 76 (use a hacky editor to check histedit-last-edit.txt backup)
77 77
78 78 $ EDITED="$TESTTMP/editedhistory"
79 79 $ cat > $EDITED <<EOF
80 80 > pick 177f92b77385 c
81 81 > pick e860deea161a e
82 82 > pick 652413bf663e f
83 83 > pick 055a42cdd887 d
84 84 > EOF
85 85 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
86 86 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
87 87 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 89 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90
91 91 rules should end up in .hg/histedit-last-edit.txt:
92 92 $ cat .hg/histedit-last-edit.txt
93 93 pick 177f92b77385 c
94 94 pick e860deea161a e
95 95 pick 652413bf663e f
96 96 pick 055a42cdd887 d
97 97
98 98 log after edit
99 99 $ hg log --graph
100 100 @ changeset: 5:07114f51870f
101 101 | tag: tip
102 102 | user: test
103 103 | date: Thu Jan 01 00:00:00 1970 +0000
104 104 | summary: d
105 105 |
106 106 o changeset: 4:8ade9693061e
107 107 | user: test
108 108 | date: Thu Jan 01 00:00:00 1970 +0000
109 109 | summary: f
110 110 |
111 111 o changeset: 3:d8249471110a
112 112 | user: test
113 113 | date: Thu Jan 01 00:00:00 1970 +0000
114 114 | summary: e
115 115 |
116 116 o changeset: 2:177f92b77385
117 117 | user: test
118 118 | date: Thu Jan 01 00:00:00 1970 +0000
119 119 | summary: c
120 120 |
121 121 o changeset: 1:d2ae7f538514
122 122 | user: test
123 123 | date: Thu Jan 01 00:00:00 1970 +0000
124 124 | summary: b
125 125 |
126 126 o changeset: 0:cb9a9f314b8b
127 127 user: test
128 128 date: Thu Jan 01 00:00:00 1970 +0000
129 129 summary: a
130 130
131 131
132 132 put things back
133 133
134 134 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
135 135 > pick 177f92b77385 c
136 136 > pick 07114f51870f d
137 137 > pick d8249471110a e
138 138 > pick 8ade9693061e f
139 139 > EOF
140 140 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
141 141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
143 143 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
144 144
145 145 $ hg log --graph
146 146 @ changeset: 5:7eca9b5b1148
147 147 | tag: tip
148 148 | user: test
149 149 | date: Thu Jan 01 00:00:00 1970 +0000
150 150 | summary: f
151 151 |
152 152 o changeset: 4:915da888f2de
153 153 | user: test
154 154 | date: Thu Jan 01 00:00:00 1970 +0000
155 155 | summary: e
156 156 |
157 157 o changeset: 3:10517e47bbbb
158 158 | user: test
159 159 | date: Thu Jan 01 00:00:00 1970 +0000
160 160 | summary: d
161 161 |
162 162 o changeset: 2:177f92b77385
163 163 | user: test
164 164 | date: Thu Jan 01 00:00:00 1970 +0000
165 165 | summary: c
166 166 |
167 167 o changeset: 1:d2ae7f538514
168 168 | user: test
169 169 | date: Thu Jan 01 00:00:00 1970 +0000
170 170 | summary: b
171 171 |
172 172 o changeset: 0:cb9a9f314b8b
173 173 user: test
174 174 date: Thu Jan 01 00:00:00 1970 +0000
175 175 summary: a
176 176
177 177
178 178 slightly different this time
179 179
180 180 $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle
181 181 > pick 10517e47bbbb d
182 182 > pick 7eca9b5b1148 f
183 183 > pick 915da888f2de e
184 184 > pick 177f92b77385 c
185 185 > EOF
186 186 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
187 187 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
188 188 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 189 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 190 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
191 191 $ hg log --graph
192 192 @ changeset: 5:38b92f448761
193 193 | tag: tip
194 194 | user: test
195 195 | date: Thu Jan 01 00:00:00 1970 +0000
196 196 | summary: c
197 197 |
198 198 o changeset: 4:de71b079d9ce
199 199 | user: test
200 200 | date: Thu Jan 01 00:00:00 1970 +0000
201 201 | summary: e
202 202 |
203 203 o changeset: 3:be9ae3a309c6
204 204 | user: test
205 205 | date: Thu Jan 01 00:00:00 1970 +0000
206 206 | summary: f
207 207 |
208 208 o changeset: 2:799205341b6b
209 209 | user: test
210 210 | date: Thu Jan 01 00:00:00 1970 +0000
211 211 | summary: d
212 212 |
213 213 o changeset: 1:d2ae7f538514
214 214 | user: test
215 215 | date: Thu Jan 01 00:00:00 1970 +0000
216 216 | summary: b
217 217 |
218 218 o changeset: 0:cb9a9f314b8b
219 219 user: test
220 220 date: Thu Jan 01 00:00:00 1970 +0000
221 221 summary: a
222 222
223 223
224 224 keep prevents stripping dead revs
225 225 $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle
226 226 > pick 799205341b6b d
227 227 > pick be9ae3a309c6 f
228 228 > pick 38b92f448761 c
229 229 > pick de71b079d9ce e
230 230 > EOF
231 231 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
232 232 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 233 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 234 $ hg log --graph
235 235 @ changeset: 7:803ef1c6fcfd
236 236 | tag: tip
237 237 | user: test
238 238 | date: Thu Jan 01 00:00:00 1970 +0000
239 239 | summary: e
240 240 |
241 241 o changeset: 6:ece0b8d93dda
242 242 | parent: 3:be9ae3a309c6
243 243 | user: test
244 244 | date: Thu Jan 01 00:00:00 1970 +0000
245 245 | summary: c
246 246 |
247 247 | o changeset: 5:38b92f448761
248 248 | | user: test
249 249 | | date: Thu Jan 01 00:00:00 1970 +0000
250 250 | | summary: c
251 251 | |
252 252 | o changeset: 4:de71b079d9ce
253 253 |/ user: test
254 254 | date: Thu Jan 01 00:00:00 1970 +0000
255 255 | summary: e
256 256 |
257 257 o changeset: 3:be9ae3a309c6
258 258 | user: test
259 259 | date: Thu Jan 01 00:00:00 1970 +0000
260 260 | summary: f
261 261 |
262 262 o changeset: 2:799205341b6b
263 263 | user: test
264 264 | date: Thu Jan 01 00:00:00 1970 +0000
265 265 | summary: d
266 266 |
267 267 o changeset: 1:d2ae7f538514
268 268 | user: test
269 269 | date: Thu Jan 01 00:00:00 1970 +0000
270 270 | summary: b
271 271 |
272 272 o changeset: 0:cb9a9f314b8b
273 273 user: test
274 274 date: Thu Jan 01 00:00:00 1970 +0000
275 275 summary: a
276 276
277 277
278 278 try with --rev
279 279 $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle
280 280 > pick de71b079d9ce e
281 281 > pick 38b92f448761 c
282 282 > EOF
283 283 abort: may not use changesets other than the ones listed
284 284 $ hg log --graph
285 285 @ changeset: 7:803ef1c6fcfd
286 286 | tag: tip
287 287 | user: test
288 288 | date: Thu Jan 01 00:00:00 1970 +0000
289 289 | summary: e
290 290 |
291 291 o changeset: 6:ece0b8d93dda
292 292 | parent: 3:be9ae3a309c6
293 293 | user: test
294 294 | date: Thu Jan 01 00:00:00 1970 +0000
295 295 | summary: c
296 296 |
297 297 | o changeset: 5:38b92f448761
298 298 | | user: test
299 299 | | date: Thu Jan 01 00:00:00 1970 +0000
300 300 | | summary: c
301 301 | |
302 302 | o changeset: 4:de71b079d9ce
303 303 |/ user: test
304 304 | date: Thu Jan 01 00:00:00 1970 +0000
305 305 | summary: e
306 306 |
307 307 o changeset: 3:be9ae3a309c6
308 308 | user: test
309 309 | date: Thu Jan 01 00:00:00 1970 +0000
310 310 | summary: f
311 311 |
312 312 o changeset: 2:799205341b6b
313 313 | user: test
314 314 | date: Thu Jan 01 00:00:00 1970 +0000
315 315 | summary: d
316 316 |
317 317 o changeset: 1:d2ae7f538514
318 318 | user: test
319 319 | date: Thu Jan 01 00:00:00 1970 +0000
320 320 | summary: b
321 321 |
322 322 o changeset: 0:cb9a9f314b8b
323 323 user: test
324 324 date: Thu Jan 01 00:00:00 1970 +0000
325 325 summary: a
326 326
327 327
328 328 should also work if a commit message is missing
329 329 $ BUNDLE="$TESTDIR/missing-comment.hg"
330 330 $ hg init missing
331 331 $ cd missing
332 332 $ hg unbundle $BUNDLE
333 333 adding changesets
334 334 adding manifests
335 335 adding file changes
336 336 added 3 changesets with 3 changes to 1 files
337 337 (run 'hg update' to get a working copy)
338 338 $ hg co tip
339 339 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 340 $ hg log --graph
341 341 @ changeset: 2:bd22688093b3
342 342 | tag: tip
343 343 | user: Robert Altman <robert.altman@telventDTN.com>
344 344 | date: Mon Nov 28 16:40:04 2011 +0000
345 345 | summary: Update file.
346 346 |
347 347 o changeset: 1:3b3e956f9171
348 348 | user: Robert Altman <robert.altman@telventDTN.com>
349 349 | date: Mon Nov 28 16:37:57 2011 +0000
350 350 |
351 351 o changeset: 0:141947992243
352 352 user: Robert Altman <robert.altman@telventDTN.com>
353 353 date: Mon Nov 28 16:35:28 2011 +0000
354 354 summary: Checked in text file
355 355
356 356 $ hg histedit 0
357 357 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 358 $ cd ..
@@ -1,462 +1,462 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 Enable obsolete
4 4
5 5 $ cat > ${TESTTMP}/obs.py << EOF
6 6 > import mercurial.obsolete
7 7 > mercurial.obsolete._enabled = True
8 8 > EOF
9 9
10 10 $ cat >> $HGRCPATH << EOF
11 11 > [ui]
12 12 > logtemplate= {rev}:{node|short} {desc|firstline}
13 13 > [phases]
14 14 > publish=False
15 15 > [extensions]'
16 16 > histedit=
17 17 > rebase=
18 18 >
19 19 > obs=${TESTTMP}/obs.py
20 20 > EOF
21 21
22 22 $ hg init base
23 23 $ cd base
24 24
25 25 $ for x in a b c d e f ; do
26 26 > echo $x > $x
27 27 > hg add $x
28 28 > hg ci -m $x
29 29 > done
30 30
31 31 $ hg log --graph
32 32 @ 5:652413bf663e f
33 33 |
34 34 o 4:e860deea161a e
35 35 |
36 36 o 3:055a42cdd887 d
37 37 |
38 38 o 2:177f92b77385 c
39 39 |
40 40 o 1:d2ae7f538514 b
41 41 |
42 42 o 0:cb9a9f314b8b a
43 43
44 44
45 45 $ HGEDITOR=cat hg histedit 1
46 46 pick d2ae7f538514 1 b
47 47 pick 177f92b77385 2 c
48 48 pick 055a42cdd887 3 d
49 49 pick e860deea161a 4 e
50 50 pick 652413bf663e 5 f
51 51
52 52 # Edit history between d2ae7f538514 and 652413bf663e
53 53 #
54 54 # Commits are listed from least to most recent
55 55 #
56 56 # Commands:
57 57 # p, pick = use commit
58 58 # e, edit = use commit, but stop for amending
59 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
59 # f, fold = use commit, but combine it with the one above
60 60 # d, drop = remove commit from history
61 61 # m, mess = edit message without changing commit content
62 62 #
63 63 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 64 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
65 65 > pick 177f92b77385 2 c
66 66 > drop d2ae7f538514 1 b
67 67 > pick 055a42cdd887 3 d
68 68 > fold e860deea161a 4 e
69 69 > pick 652413bf663e 5 f
70 70 > EOF
71 71 saved backup bundle to $TESTTMP/base/.hg/strip-backup/96e494a2d553-backup.hg (glob)
72 72 $ hg log --graph --hidden
73 73 @ 8:cacdfd884a93 f
74 74 |
75 75 o 7:59d9f330561f d
76 76 |
77 77 o 6:b346ab9a313d c
78 78 |
79 79 | x 5:652413bf663e f
80 80 | |
81 81 | x 4:e860deea161a e
82 82 | |
83 83 | x 3:055a42cdd887 d
84 84 | |
85 85 | x 2:177f92b77385 c
86 86 | |
87 87 | x 1:d2ae7f538514 b
88 88 |/
89 89 o 0:cb9a9f314b8b a
90 90
91 91 $ hg debugobsolete
92 92 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {'date': '* *', 'user': 'test'} (glob)
93 93 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 {'date': '* *', 'user': 'test'} (glob)
94 94 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 {'date': '* *', 'user': 'test'} (glob)
95 95 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 {'date': '* *', 'user': 'test'} (glob)
96 96 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 {'date': '* *', 'user': 'test'} (glob)
97 97
98 98
99 99 Ensure hidden revision does not prevent histedit
100 100 -------------------------------------------------
101 101
102 102 create an hidden revision
103 103
104 104 $ hg histedit 6 --commands - << EOF
105 105 > pick b346ab9a313d 6 c
106 106 > drop 59d9f330561f 7 d
107 107 > pick cacdfd884a93 8 f
108 108 > EOF
109 109 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
110 110 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 $ hg log --graph
112 112 @ 9:c13eb81022ca f
113 113 |
114 114 o 6:b346ab9a313d c
115 115 |
116 116 o 0:cb9a9f314b8b a
117 117
118 118 check hidden revision are ignored (6 have hidden children 7 and 8)
119 119
120 120 $ hg histedit 6 --commands - << EOF
121 121 > pick b346ab9a313d 6 c
122 122 > pick c13eb81022ca 8 f
123 123 > EOF
124 124 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 125
126 126
127 127
128 128 Test that rewriting leaving instability behind is allowed
129 129 ---------------------------------------------------------------------
130 130
131 131 $ hg up '.^'
132 132 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
133 133 $ hg log -r 'children(.)'
134 134 9:c13eb81022ca f (no-eol)
135 135 $ hg histedit -r '.' --commands - <<EOF
136 136 > edit b346ab9a313d 6 c
137 137 > EOF
138 138 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
139 139 adding c
140 140 Make changes as needed, you may commit or record as needed now.
141 141 When you are finished, run hg histedit --continue to resume.
142 142 [1]
143 143 $ echo c >> c
144 144 $ hg histedit --continue
145 145 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 146
147 147 $ hg log -r 'unstable()'
148 148 9:c13eb81022ca f (no-eol)
149 149
150 150 stabilise
151 151
152 152 $ hg rebase -r 'unstable()' -d .
153 153 $ hg up tip -q
154 154
155 155 Test dropping of changeset on the top of the stack
156 156 -------------------------------------------------------
157 157
158 158 Nothing is rewritten below, the working directory parent must be change for the
159 159 dropped changeset to be hidden.
160 160
161 161 $ cd ..
162 162 $ hg clone base droplast
163 163 updating to branch default
164 164 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 165 $ cd droplast
166 166 $ hg histedit -r '40db8afa467b' --commands - << EOF
167 167 > pick 40db8afa467b 10 c
168 168 > drop b449568bf7fc 11 f
169 169 > EOF
170 170 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
171 171 $ hg log -G
172 172 @ 10:40db8afa467b c
173 173 |
174 174 o 0:cb9a9f314b8b a
175 175
176 176
177 177 With rewritten ancestors
178 178
179 179 $ echo e > e
180 180 $ hg add e
181 181 $ hg commit -m g
182 182 $ echo f > f
183 183 $ hg add f
184 184 $ hg commit -m h
185 185 $ hg histedit -r '40db8afa467b' --commands - << EOF
186 186 > pick 47a8561c0449 12 g
187 187 > pick 40db8afa467b 10 c
188 188 > drop 1b3b05f35ff0 13 h
189 189 > EOF
190 190 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
191 191 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 192 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 $ hg log -G
194 194 @ 15:ee6544123ab8 c
195 195 |
196 196 o 14:269e713e9eae g
197 197 |
198 198 o 0:cb9a9f314b8b a
199 199
200 200 $ cd ../base
201 201
202 202
203 203
204 204 Test phases support
205 205 ===========================================
206 206
207 207 Check that histedit respect immutability
208 208 -------------------------------------------
209 209
210 210 $ cat >> $HGRCPATH << EOF
211 211 > [ui]
212 212 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
213 213 > EOF
214 214
215 215 $ hg ph -pv '.^'
216 216 phase changed for 2 changesets
217 217 $ hg log -G
218 218 @ 11:b449568bf7fc (draft) f
219 219 |
220 220 o 10:40db8afa467b (public) c
221 221 |
222 222 o 0:cb9a9f314b8b (public) a
223 223
224 224 $ hg histedit -r '.~2'
225 225 abort: cannot edit immutable changeset: cb9a9f314b8b
226 226 [255]
227 227
228 228
229 229 Prepare further testing
230 230 -------------------------------------------
231 231
232 232 $ for x in g h i j k ; do
233 233 > echo $x > $x
234 234 > hg add $x
235 235 > hg ci -m $x
236 236 > done
237 237 $ hg phase --force --secret .~2
238 238 $ hg log -G
239 239 @ 16:ee118ab9fa44 (secret) k
240 240 |
241 241 o 15:3a6c53ee7f3d (secret) j
242 242 |
243 243 o 14:b605fb7503f2 (secret) i
244 244 |
245 245 o 13:7395e1ff83bd (draft) h
246 246 |
247 247 o 12:6b70183d2492 (draft) g
248 248 |
249 249 o 11:b449568bf7fc (draft) f
250 250 |
251 251 o 10:40db8afa467b (public) c
252 252 |
253 253 o 0:cb9a9f314b8b (public) a
254 254
255 255 $ cd ..
256 256
257 257 simple phase conservation
258 258 -------------------------------------------
259 259
260 260 Resulting changeset should conserve the phase of the original one whatever the
261 261 phases.new-commit option is.
262 262
263 263 New-commit as draft (default)
264 264
265 265 $ cp -r base simple-draft
266 266 $ cd simple-draft
267 267 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
268 268 > edit b449568bf7fc 11 f
269 269 > pick 6b70183d2492 12 g
270 270 > pick 7395e1ff83bd 13 h
271 271 > pick b605fb7503f2 14 i
272 272 > pick 3a6c53ee7f3d 15 j
273 273 > pick ee118ab9fa44 16 k
274 274 > EOF
275 275 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
276 276 adding f
277 277 Make changes as needed, you may commit or record as needed now.
278 278 When you are finished, run hg histedit --continue to resume.
279 279 [1]
280 280 $ echo f >> f
281 281 $ hg histedit --continue
282 282 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
283 283 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 284 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 285 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 286 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 287 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 288 $ hg log -G
289 289 @ 22:12e89af74238 (secret) k
290 290 |
291 291 o 21:636a8687b22e (secret) j
292 292 |
293 293 o 20:ccaf0a38653f (secret) i
294 294 |
295 295 o 19:11a89d1c2613 (draft) h
296 296 |
297 297 o 18:c1dec7ca82ea (draft) g
298 298 |
299 299 o 17:087281e68428 (draft) f
300 300 |
301 301 o 10:40db8afa467b (public) c
302 302 |
303 303 o 0:cb9a9f314b8b (public) a
304 304
305 305 $ cd ..
306 306
307 307
308 308 New-commit as draft (default)
309 309
310 310 $ cp -r base simple-secret
311 311 $ cd simple-secret
312 312 $ cat >> .hg/hgrc << EOF
313 313 > [phases]
314 314 > new-commit=secret
315 315 > EOF
316 316 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
317 317 > edit b449568bf7fc 11 f
318 318 > pick 6b70183d2492 12 g
319 319 > pick 7395e1ff83bd 13 h
320 320 > pick b605fb7503f2 14 i
321 321 > pick 3a6c53ee7f3d 15 j
322 322 > pick ee118ab9fa44 16 k
323 323 > EOF
324 324 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
325 325 adding f
326 326 Make changes as needed, you may commit or record as needed now.
327 327 When you are finished, run hg histedit --continue to resume.
328 328 [1]
329 329 $ echo f >> f
330 330 $ hg histedit --continue
331 331 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
332 332 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
333 333 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 334 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
335 335 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
336 336 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 337 $ hg log -G
338 338 @ 22:12e89af74238 (secret) k
339 339 |
340 340 o 21:636a8687b22e (secret) j
341 341 |
342 342 o 20:ccaf0a38653f (secret) i
343 343 |
344 344 o 19:11a89d1c2613 (draft) h
345 345 |
346 346 o 18:c1dec7ca82ea (draft) g
347 347 |
348 348 o 17:087281e68428 (draft) f
349 349 |
350 350 o 10:40db8afa467b (public) c
351 351 |
352 352 o 0:cb9a9f314b8b (public) a
353 353
354 354 $ cd ..
355 355
356 356
357 357 Changeset reordering
358 358 -------------------------------------------
359 359
360 360 If a secret changeset is put before a draft one, all descendant should be secret.
361 361 It seems more important to present the secret phase.
362 362
363 363 $ cp -r base reorder
364 364 $ cd reorder
365 365 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
366 366 > pick b449568bf7fc 11 f
367 367 > pick 3a6c53ee7f3d 15 j
368 368 > pick 6b70183d2492 12 g
369 369 > pick b605fb7503f2 14 i
370 370 > pick 7395e1ff83bd 13 h
371 371 > pick ee118ab9fa44 16 k
372 372 > EOF
373 373 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
374 374 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
375 375 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
376 376 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 377 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 378 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
379 379 $ hg log -G
380 380 @ 21:558246857888 (secret) k
381 381 |
382 382 o 20:28bd44768535 (secret) h
383 383 |
384 384 o 19:d5395202aeb9 (secret) i
385 385 |
386 386 o 18:21edda8e341b (secret) g
387 387 |
388 388 o 17:5ab64f3a4832 (secret) j
389 389 |
390 390 o 11:b449568bf7fc (draft) f
391 391 |
392 392 o 10:40db8afa467b (public) c
393 393 |
394 394 o 0:cb9a9f314b8b (public) a
395 395
396 396 $ cd ..
397 397
398 398 Changeset folding
399 399 -------------------------------------------
400 400
401 401 Folding a secret changeset with a draft one turn the result secret (again,
402 402 better safe than sorry). Folding between same phase changeset still works
403 403
404 404 Note that there is a few reordering in this series for more extensive test
405 405
406 406 $ cp -r base folding
407 407 $ cd folding
408 408 $ cat >> .hg/hgrc << EOF
409 409 > [phases]
410 410 > new-commit=secret
411 411 > EOF
412 412 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
413 413 > pick 7395e1ff83bd 13 h
414 414 > fold b449568bf7fc 11 f
415 415 > pick 6b70183d2492 12 g
416 416 > fold 3a6c53ee7f3d 15 j
417 417 > pick b605fb7503f2 14 i
418 418 > fold ee118ab9fa44 16 k
419 419 > EOF
420 420 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
421 421 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 422 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
423 423 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 424 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 425 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 426 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
427 427 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 428 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
429 429 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 430 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
431 431 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
432 432 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
433 433 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/58019c66f35f-backup.hg (glob)
434 434 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/83d1858e070b-backup.hg (glob)
435 435 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/859969f5ed7e-backup.hg (glob)
436 436 $ hg log -G
437 437 @ 19:f9daec13fb98 (secret) i
438 438 |
439 439 o 18:49807617f46a (secret) g
440 440 |
441 441 o 17:050280826e04 (draft) h
442 442 |
443 443 o 10:40db8afa467b (public) c
444 444 |
445 445 o 0:cb9a9f314b8b (public) a
446 446
447 447 $ hg co 18
448 448 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
449 449 $ echo wat >> wat
450 450 $ hg add wat
451 451 $ hg ci -m 'add wat'
452 452 created new head
453 453 $ hg merge 19
454 454 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
455 455 (branch merge, don't forget to commit)
456 456 $ hg ci -m 'merge'
457 457 $ echo not wat > wat
458 458 $ hg ci -m 'modify wat'
459 459 $ hg histedit 17
460 460 abort: cannot edit history that contains merges
461 461 [255]
462 462 $ cd ..
@@ -1,144 +1,144 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 # Commands:
49 49 # p, pick = use commit
50 50 # e, edit = use commit, but stop for amending
51 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
51 # f, fold = use commit, but combine it with the one above
52 52 # d, drop = remove commit from history
53 53 # m, mess = edit message without changing commit content
54 54 #
55 55 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 56 $ cd ..
57 57
58 58 show the error from unrelated repos
59 59 $ cd r3
60 60 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
61 61 abort: repository is unrelated
62 62 [1]
63 63 $ cd ..
64 64
65 65 show the error from unrelated repos
66 66 $ cd r3
67 67 $ HGEDITOR=cat hg histedit --force --outgoing ../r
68 68 comparing with ../r
69 69 searching for changes
70 70 warning: repository is unrelated
71 71 pick 2a4042b45417 0 g
72 72 pick 68c46b4927ce 1 h
73 73 pick 51281e65ba79 2 i
74 74
75 75 # Edit history between 2a4042b45417 and 51281e65ba79
76 76 #
77 77 # Commits are listed from least to most recent
78 78 #
79 79 # Commands:
80 80 # p, pick = use commit
81 81 # e, edit = use commit, but stop for amending
82 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
82 # f, fold = use commit, but combine it with the one above
83 83 # d, drop = remove commit from history
84 84 # m, mess = edit message without changing commit content
85 85 #
86 86 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 87 $ cd ..
88 88
89 89 test sensitivity to branch in URL:
90 90
91 91 $ cd r2
92 92 $ hg -q update 2
93 93 $ hg -q branch foo
94 94 $ hg commit -m 'create foo branch'
95 95 $ HGEDITOR=cat hg histedit --outgoing '../r#foo' | grep -v comparing | grep -v searching
96 96 pick f26599ee3441 6 create foo branch
97 97
98 98 # Edit history between f26599ee3441 and f26599ee3441
99 99 #
100 100 # Commits are listed from least to most recent
101 101 #
102 102 # Commands:
103 103 # p, pick = use commit
104 104 # e, edit = use commit, but stop for amending
105 # f, fold = use commit, but fold into previous commit (combines this commit with the one above)
105 # f, fold = use commit, but combine it with the one above
106 106 # d, drop = remove commit from history
107 107 # m, mess = edit message without changing commit content
108 108 #
109 109 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 110
111 111 test to check number of roots in outgoing revisions
112 112
113 113 $ hg -q outgoing -G --template '{node|short}({branch})' '../r'
114 114 @ f26599ee3441(foo)
115 115
116 116 o 652413bf663e(default)
117 117 |
118 118 o e860deea161a(default)
119 119 |
120 120 o 055a42cdd887(default)
121 121
122 122 $ HGEDITOR=cat hg -q histedit --outgoing '../r'
123 123 abort: there are ambiguous outgoing revisions
124 124 (see "hg help histedit" for more detail)
125 125 [255]
126 126
127 127 $ hg -q update -C 2
128 128 $ echo aa >> a
129 129 $ hg -q commit -m 'another head on default'
130 130 $ hg -q outgoing -G --template '{node|short}({branch})' '../r#default'
131 131 @ 3879dc049647(default)
132 132
133 133 o 652413bf663e(default)
134 134 |
135 135 o e860deea161a(default)
136 136 |
137 137 o 055a42cdd887(default)
138 138
139 139 $ HGEDITOR=cat hg -q histedit --outgoing '../r#default'
140 140 abort: there are ambiguous outgoing revisions
141 141 (see "hg help histedit" for more detail)
142 142 [255]
143 143
144 144 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now