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