##// END OF EJS Templates
histedit: improve discoverability of edit commit message
timeless@mozdev.org -
r26100:5706d130 default
parent child Browse files
Show More
@@ -1,1180 +1,1180 b''
1 1 # histedit.py - interactive history editing for mercurial
2 2 #
3 3 # Copyright 2009 Augie Fackler <raf@durin42.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """interactive history editing
8 8
9 9 With this extension installed, Mercurial gains one new command: histedit. Usage
10 10 is as follows, assuming the following history::
11 11
12 12 @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
13 13 | Add delta
14 14 |
15 15 o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
16 16 | Add gamma
17 17 |
18 18 o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
19 19 | Add beta
20 20 |
21 21 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
22 22 Add alpha
23 23
24 24 If you were to run ``hg histedit c561b4e977df``, you would see the following
25 25 file open in your editor::
26 26
27 27 pick c561b4e977df Add beta
28 28 pick 030b686bedc4 Add gamma
29 29 pick 7c2fd3b9020c Add delta
30 30
31 31 # Edit history between c561b4e977df and 7c2fd3b9020c
32 32 #
33 33 # Commits are listed from least to most recent
34 34 #
35 35 # Commands:
36 36 # p, pick = use commit
37 37 # e, edit = use commit, but stop for amending
38 38 # f, fold = use commit, but combine it with the one above
39 39 # r, roll = like fold, but discard this commit's description
40 40 # d, drop = remove commit from history
41 # m, mess = edit message without changing commit content
41 # m, mess = edit commit message without changing commit content
42 42 #
43 43
44 44 In this file, lines beginning with ``#`` are ignored. You must specify a rule
45 45 for each revision in your history. For example, if you had meant to add gamma
46 46 before beta, and then wanted to add delta in the same revision as beta, you
47 47 would reorganize the file to look like this::
48 48
49 49 pick 030b686bedc4 Add gamma
50 50 pick c561b4e977df Add beta
51 51 fold 7c2fd3b9020c Add delta
52 52
53 53 # Edit history between c561b4e977df and 7c2fd3b9020c
54 54 #
55 55 # Commits are listed from least to most recent
56 56 #
57 57 # Commands:
58 58 # p, pick = use commit
59 59 # e, edit = use commit, but stop for amending
60 60 # f, fold = use commit, but combine it with the one above
61 61 # r, roll = like fold, but discard this commit's description
62 62 # d, drop = remove commit from history
63 # m, mess = edit message without changing commit content
63 # m, mess = edit commit message without changing commit content
64 64 #
65 65
66 66 At which point you close the editor and ``histedit`` starts working. When you
67 67 specify a ``fold`` operation, ``histedit`` will open an editor when it folds
68 68 those revisions together, offering you a chance to clean up the commit message::
69 69
70 70 Add beta
71 71 ***
72 72 Add delta
73 73
74 74 Edit the commit message to your liking, then close the editor. For
75 75 this example, let's assume that the commit message was changed to
76 76 ``Add beta and delta.`` After histedit has run and had a chance to
77 77 remove any old or temporary revisions it needed, the history looks
78 78 like this::
79 79
80 80 @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
81 81 | Add beta and delta.
82 82 |
83 83 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
84 84 | Add gamma
85 85 |
86 86 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
87 87 Add alpha
88 88
89 89 Note that ``histedit`` does *not* remove any revisions (even its own temporary
90 90 ones) until after it has completed all the editing operations, so it will
91 91 probably perform several strip operations when it's done. For the above example,
92 92 it had to run strip twice. Strip can be slow depending on a variety of factors,
93 93 so you might need to be a little patient. You can choose to keep the original
94 94 revisions by passing the ``--keep`` flag.
95 95
96 96 The ``edit`` operation will drop you back to a command prompt,
97 97 allowing you to edit files freely, or even use ``hg record`` to commit
98 98 some changes as a separate commit. When you're done, any remaining
99 99 uncommitted changes will be committed as well. When done, run ``hg
100 100 histedit --continue`` to finish this step. You'll be prompted for a
101 101 new commit message, but the default commit message will be the
102 102 original message for the ``edit`` ed revision.
103 103
104 104 The ``message`` operation will give you a chance to revise a commit
105 105 message without changing the contents. It's a shortcut for doing
106 106 ``edit`` immediately followed by `hg histedit --continue``.
107 107
108 108 If ``histedit`` encounters a conflict when moving a revision (while
109 109 handling ``pick`` or ``fold``), it'll stop in a similar manner to
110 110 ``edit`` with the difference that it won't prompt you for a commit
111 111 message when done. If you decide at this point that you don't like how
112 112 much work it will be to rearrange history, or that you made a mistake,
113 113 you can use ``hg histedit --abort`` to abandon the new changes you
114 114 have made and return to the state before you attempted to edit your
115 115 history.
116 116
117 117 If we clone the histedit-ed example repository above and add four more
118 118 changes, such that we have the following history::
119 119
120 120 @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
121 121 | Add theta
122 122 |
123 123 o 5 140988835471 2009-04-27 18:04 -0500 stefan
124 124 | Add eta
125 125 |
126 126 o 4 122930637314 2009-04-27 18:04 -0500 stefan
127 127 | Add zeta
128 128 |
129 129 o 3 836302820282 2009-04-27 18:04 -0500 stefan
130 130 | Add epsilon
131 131 |
132 132 o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
133 133 | Add beta and delta.
134 134 |
135 135 o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
136 136 | Add gamma
137 137 |
138 138 o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
139 139 Add alpha
140 140
141 141 If you run ``hg histedit --outgoing`` on the clone then it is the same
142 142 as running ``hg histedit 836302820282``. If you need plan to push to a
143 143 repository that Mercurial does not detect to be related to the source
144 144 repo, you can add a ``--force`` option.
145 145
146 146 Histedit rule lines are truncated to 80 characters by default. You
147 147 can customise this behavior by setting a different length in your
148 148 configuration file::
149 149
150 150 [histedit]
151 151 linelen = 120 # truncate rule lines at 120 characters
152 152 """
153 153
154 154 try:
155 155 import cPickle as pickle
156 156 pickle.dump # import now
157 157 except ImportError:
158 158 import pickle
159 159 import errno
160 160 import os
161 161 import sys
162 162
163 163 from mercurial import cmdutil
164 164 from mercurial import discovery
165 165 from mercurial import error
166 166 from mercurial import changegroup
167 167 from mercurial import copies
168 168 from mercurial import context
169 169 from mercurial import exchange
170 170 from mercurial import extensions
171 171 from mercurial import hg
172 172 from mercurial import node
173 173 from mercurial import repair
174 174 from mercurial import scmutil
175 175 from mercurial import util
176 176 from mercurial import obsolete
177 177 from mercurial import merge as mergemod
178 178 from mercurial.lock import release
179 179 from mercurial.i18n import _
180 180
181 181 cmdtable = {}
182 182 command = cmdutil.command(cmdtable)
183 183
184 184 # Note for extension authors: ONLY specify testedwith = 'internal' for
185 185 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
186 186 # be specifying the version(s) of Mercurial they are tested with, or
187 187 # leave the attribute unspecified.
188 188 testedwith = 'internal'
189 189
190 190 # i18n: command names and abbreviations must remain untranslated
191 191 editcomment = _("""# Edit history between %s and %s
192 192 #
193 193 # Commits are listed from least to most recent
194 194 #
195 195 # Commands:
196 196 # p, pick = use commit
197 197 # e, edit = use commit, but stop for amending
198 198 # f, fold = use commit, but combine it with the one above
199 199 # r, roll = like fold, but discard this commit's description
200 200 # d, drop = remove commit from history
201 # m, mess = edit message without changing commit content
201 # m, mess = edit commit message without changing commit content
202 202 #
203 203 """)
204 204
205 205 class histeditstate(object):
206 206 def __init__(self, repo, parentctxnode=None, rules=None, keep=None,
207 207 topmost=None, replacements=None, lock=None, wlock=None):
208 208 self.repo = repo
209 209 self.rules = rules
210 210 self.keep = keep
211 211 self.topmost = topmost
212 212 self.parentctxnode = parentctxnode
213 213 self.lock = lock
214 214 self.wlock = wlock
215 215 self.backupfile = None
216 216 if replacements is None:
217 217 self.replacements = []
218 218 else:
219 219 self.replacements = replacements
220 220
221 221 def read(self):
222 222 """Load histedit state from disk and set fields appropriately."""
223 223 try:
224 224 fp = self.repo.vfs('histedit-state', 'r')
225 225 except IOError as err:
226 226 if err.errno != errno.ENOENT:
227 227 raise
228 228 raise util.Abort(_('no histedit in progress'))
229 229
230 230 try:
231 231 data = pickle.load(fp)
232 232 parentctxnode, rules, keep, topmost, replacements = data
233 233 backupfile = None
234 234 except pickle.UnpicklingError:
235 235 data = self._load()
236 236 parentctxnode, rules, keep, topmost, replacements, backupfile = data
237 237
238 238 self.parentctxnode = parentctxnode
239 239 self.rules = rules
240 240 self.keep = keep
241 241 self.topmost = topmost
242 242 self.replacements = replacements
243 243 self.backupfile = backupfile
244 244
245 245 def write(self):
246 246 fp = self.repo.vfs('histedit-state', 'w')
247 247 fp.write('v1\n')
248 248 fp.write('%s\n' % node.hex(self.parentctxnode))
249 249 fp.write('%s\n' % node.hex(self.topmost))
250 250 fp.write('%s\n' % self.keep)
251 251 fp.write('%d\n' % len(self.rules))
252 252 for rule in self.rules:
253 253 fp.write('%s\n' % rule[0]) # action
254 254 fp.write('%s\n' % rule[1]) # remainder
255 255 fp.write('%d\n' % len(self.replacements))
256 256 for replacement in self.replacements:
257 257 fp.write('%s%s\n' % (node.hex(replacement[0]), ''.join(node.hex(r)
258 258 for r in replacement[1])))
259 259 backupfile = self.backupfile
260 260 if not backupfile:
261 261 backupfile = ''
262 262 fp.write('%s\n' % backupfile)
263 263 fp.close()
264 264
265 265 def _load(self):
266 266 fp = self.repo.vfs('histedit-state', 'r')
267 267 lines = [l[:-1] for l in fp.readlines()]
268 268
269 269 index = 0
270 270 lines[index] # version number
271 271 index += 1
272 272
273 273 parentctxnode = node.bin(lines[index])
274 274 index += 1
275 275
276 276 topmost = node.bin(lines[index])
277 277 index += 1
278 278
279 279 keep = lines[index] == 'True'
280 280 index += 1
281 281
282 282 # Rules
283 283 rules = []
284 284 rulelen = int(lines[index])
285 285 index += 1
286 286 for i in xrange(rulelen):
287 287 ruleaction = lines[index]
288 288 index += 1
289 289 rule = lines[index]
290 290 index += 1
291 291 rules.append((ruleaction, rule))
292 292
293 293 # Replacements
294 294 replacements = []
295 295 replacementlen = int(lines[index])
296 296 index += 1
297 297 for i in xrange(replacementlen):
298 298 replacement = lines[index]
299 299 original = node.bin(replacement[:40])
300 300 succ = [node.bin(replacement[i:i + 40]) for i in
301 301 range(40, len(replacement), 40)]
302 302 replacements.append((original, succ))
303 303 index += 1
304 304
305 305 backupfile = lines[index]
306 306 index += 1
307 307
308 308 fp.close()
309 309
310 310 return parentctxnode, rules, keep, topmost, replacements, backupfile
311 311
312 312 def clear(self):
313 313 self.repo.vfs.unlink('histedit-state')
314 314
315 315 class histeditaction(object):
316 316 def __init__(self, state, node):
317 317 self.state = state
318 318 self.repo = state.repo
319 319 self.node = node
320 320
321 321 @classmethod
322 322 def fromrule(cls, state, rule):
323 323 """Parses the given rule, returning an instance of the histeditaction.
324 324 """
325 325 repo = state.repo
326 326 rulehash = rule.strip().split(' ', 1)[0]
327 327 try:
328 328 node = repo[rulehash].node()
329 329 except error.RepoError:
330 330 raise util.Abort(_('unknown changeset %s listed') % rulehash[:12])
331 331 return cls(state, node)
332 332
333 333 def run(self):
334 334 """Runs the action. The default behavior is simply apply the action's
335 335 rulectx onto the current parentctx."""
336 336 self.applychange()
337 337 self.continuedirty()
338 338 return self.continueclean()
339 339
340 340 def applychange(self):
341 341 """Applies the changes from this action's rulectx onto the current
342 342 parentctx, but does not commit them."""
343 343 repo = self.repo
344 344 rulectx = repo[self.node]
345 345 hg.update(repo, self.state.parentctxnode)
346 346 stats = applychanges(repo.ui, repo, rulectx, {})
347 347 if stats and stats[3] > 0:
348 348 raise error.InterventionRequired(_('Fix up the change and run '
349 349 'hg histedit --continue'))
350 350
351 351 def continuedirty(self):
352 352 """Continues the action when changes have been applied to the working
353 353 copy. The default behavior is to commit the dirty changes."""
354 354 repo = self.repo
355 355 rulectx = repo[self.node]
356 356
357 357 editor = self.commiteditor()
358 358 commit = commitfuncfor(repo, rulectx)
359 359
360 360 commit(text=rulectx.description(), user=rulectx.user(),
361 361 date=rulectx.date(), extra=rulectx.extra(), editor=editor)
362 362
363 363 def commiteditor(self):
364 364 """The editor to be used to edit the commit message."""
365 365 return False
366 366
367 367 def continueclean(self):
368 368 """Continues the action when the working copy is clean. The default
369 369 behavior is to accept the current commit as the new version of the
370 370 rulectx."""
371 371 ctx = self.repo['.']
372 372 if ctx.node() == self.state.parentctxnode:
373 373 self.repo.ui.warn(_('%s: empty changeset\n') %
374 374 node.short(self.node))
375 375 return ctx, [(self.node, tuple())]
376 376 if ctx.node() == self.node:
377 377 # Nothing changed
378 378 return ctx, []
379 379 return ctx, [(self.node, (ctx.node(),))]
380 380
381 381 def commitfuncfor(repo, src):
382 382 """Build a commit function for the replacement of <src>
383 383
384 384 This function ensure we apply the same treatment to all changesets.
385 385
386 386 - Add a 'histedit_source' entry in extra.
387 387
388 388 Note that fold has its own separated logic because its handling is a bit
389 389 different and not easily factored out of the fold method.
390 390 """
391 391 phasemin = src.phase()
392 392 def commitfunc(**kwargs):
393 393 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
394 394 try:
395 395 repo.ui.setconfig('phases', 'new-commit', phasemin,
396 396 'histedit')
397 397 extra = kwargs.get('extra', {}).copy()
398 398 extra['histedit_source'] = src.hex()
399 399 kwargs['extra'] = extra
400 400 return repo.commit(**kwargs)
401 401 finally:
402 402 repo.ui.restoreconfig(phasebackup)
403 403 return commitfunc
404 404
405 405 def applychanges(ui, repo, ctx, opts):
406 406 """Merge changeset from ctx (only) in the current working directory"""
407 407 wcpar = repo.dirstate.parents()[0]
408 408 if ctx.p1().node() == wcpar:
409 409 # edition ar "in place" we do not need to make any merge,
410 410 # just applies changes on parent for edition
411 411 cmdutil.revert(ui, repo, ctx, (wcpar, node.nullid), all=True)
412 412 stats = None
413 413 else:
414 414 try:
415 415 # ui.forcemerge is an internal variable, do not document
416 416 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
417 417 'histedit')
418 418 stats = mergemod.graft(repo, ctx, ctx.p1(), ['local', 'histedit'])
419 419 finally:
420 420 repo.ui.setconfig('ui', 'forcemerge', '', 'histedit')
421 421 return stats
422 422
423 423 def collapse(repo, first, last, commitopts, skipprompt=False):
424 424 """collapse the set of revisions from first to last as new one.
425 425
426 426 Expected commit options are:
427 427 - message
428 428 - date
429 429 - username
430 430 Commit message is edited in all cases.
431 431
432 432 This function works in memory."""
433 433 ctxs = list(repo.set('%d::%d', first, last))
434 434 if not ctxs:
435 435 return None
436 436 for c in ctxs:
437 437 if not c.mutable():
438 438 raise util.Abort(
439 439 _("cannot fold into public change %s") % node.short(c.node()))
440 440 base = first.parents()[0]
441 441
442 442 # commit a new version of the old changeset, including the update
443 443 # collect all files which might be affected
444 444 files = set()
445 445 for ctx in ctxs:
446 446 files.update(ctx.files())
447 447
448 448 # Recompute copies (avoid recording a -> b -> a)
449 449 copied = copies.pathcopies(base, last)
450 450
451 451 # prune files which were reverted by the updates
452 452 def samefile(f):
453 453 if f in last.manifest():
454 454 a = last.filectx(f)
455 455 if f in base.manifest():
456 456 b = base.filectx(f)
457 457 return (a.data() == b.data()
458 458 and a.flags() == b.flags())
459 459 else:
460 460 return False
461 461 else:
462 462 return f not in base.manifest()
463 463 files = [f for f in files if not samefile(f)]
464 464 # commit version of these files as defined by head
465 465 headmf = last.manifest()
466 466 def filectxfn(repo, ctx, path):
467 467 if path in headmf:
468 468 fctx = last[path]
469 469 flags = fctx.flags()
470 470 mctx = context.memfilectx(repo,
471 471 fctx.path(), fctx.data(),
472 472 islink='l' in flags,
473 473 isexec='x' in flags,
474 474 copied=copied.get(path))
475 475 return mctx
476 476 return None
477 477
478 478 if commitopts.get('message'):
479 479 message = commitopts['message']
480 480 else:
481 481 message = first.description()
482 482 user = commitopts.get('user')
483 483 date = commitopts.get('date')
484 484 extra = commitopts.get('extra')
485 485
486 486 parents = (first.p1().node(), first.p2().node())
487 487 editor = None
488 488 if not skipprompt:
489 489 editor = cmdutil.getcommiteditor(edit=True, editform='histedit.fold')
490 490 new = context.memctx(repo,
491 491 parents=parents,
492 492 text=message,
493 493 files=files,
494 494 filectxfn=filectxfn,
495 495 user=user,
496 496 date=date,
497 497 extra=extra,
498 498 editor=editor)
499 499 return repo.commitctx(new)
500 500
501 501 class pick(histeditaction):
502 502 def run(self):
503 503 rulectx = self.repo[self.node]
504 504 if rulectx.parents()[0].node() == self.state.parentctxnode:
505 505 self.repo.ui.debug('node %s unchanged\n' % node.short(self.node))
506 506 return rulectx, []
507 507
508 508 return super(pick, self).run()
509 509
510 510 class edit(histeditaction):
511 511 def run(self):
512 512 repo = self.repo
513 513 rulectx = repo[self.node]
514 514 hg.update(repo, self.state.parentctxnode)
515 515 applychanges(repo.ui, repo, rulectx, {})
516 516 raise error.InterventionRequired(
517 517 _('Make changes as needed, you may commit or record as needed '
518 518 'now.\nWhen you are finished, run hg histedit --continue to '
519 519 'resume.'))
520 520
521 521 def commiteditor(self):
522 522 return cmdutil.getcommiteditor(edit=True, editform='histedit.edit')
523 523
524 524 class fold(histeditaction):
525 525 def continuedirty(self):
526 526 repo = self.repo
527 527 rulectx = repo[self.node]
528 528
529 529 commit = commitfuncfor(repo, rulectx)
530 530 commit(text='fold-temp-revision %s' % node.short(self.node),
531 531 user=rulectx.user(), date=rulectx.date(),
532 532 extra=rulectx.extra())
533 533
534 534 def continueclean(self):
535 535 repo = self.repo
536 536 ctx = repo['.']
537 537 rulectx = repo[self.node]
538 538 parentctxnode = self.state.parentctxnode
539 539 if ctx.node() == parentctxnode:
540 540 repo.ui.warn(_('%s: empty changeset\n') %
541 541 node.short(self.node))
542 542 return ctx, [(self.node, (parentctxnode,))]
543 543
544 544 parentctx = repo[parentctxnode]
545 545 newcommits = set(c.node() for c in repo.set('(%d::. - %d)', parentctx,
546 546 parentctx))
547 547 if not newcommits:
548 548 repo.ui.warn(_('%s: cannot fold - working copy is not a '
549 549 'descendant of previous commit %s\n') %
550 550 (node.short(self.node), node.short(parentctxnode)))
551 551 return ctx, [(self.node, (ctx.node(),))]
552 552
553 553 middlecommits = newcommits.copy()
554 554 middlecommits.discard(ctx.node())
555 555
556 556 return self.finishfold(repo.ui, repo, parentctx, rulectx, ctx.node(),
557 557 middlecommits)
558 558
559 559 def skipprompt(self):
560 560 return False
561 561
562 562 def finishfold(self, ui, repo, ctx, oldctx, newnode, internalchanges):
563 563 parent = ctx.parents()[0].node()
564 564 hg.update(repo, parent)
565 565 ### prepare new commit data
566 566 commitopts = {}
567 567 commitopts['user'] = ctx.user()
568 568 # commit message
569 569 if self.skipprompt():
570 570 newmessage = ctx.description()
571 571 else:
572 572 newmessage = '\n***\n'.join(
573 573 [ctx.description()] +
574 574 [repo[r].description() for r in internalchanges] +
575 575 [oldctx.description()]) + '\n'
576 576 commitopts['message'] = newmessage
577 577 # date
578 578 commitopts['date'] = max(ctx.date(), oldctx.date())
579 579 extra = ctx.extra().copy()
580 580 # histedit_source
581 581 # note: ctx is likely a temporary commit but that the best we can do
582 582 # here. This is sufficient to solve issue3681 anyway.
583 583 extra['histedit_source'] = '%s,%s' % (ctx.hex(), oldctx.hex())
584 584 commitopts['extra'] = extra
585 585 phasebackup = repo.ui.backupconfig('phases', 'new-commit')
586 586 try:
587 587 phasemin = max(ctx.phase(), oldctx.phase())
588 588 repo.ui.setconfig('phases', 'new-commit', phasemin, 'histedit')
589 589 n = collapse(repo, ctx, repo[newnode], commitopts,
590 590 skipprompt=self.skipprompt())
591 591 finally:
592 592 repo.ui.restoreconfig(phasebackup)
593 593 if n is None:
594 594 return ctx, []
595 595 hg.update(repo, n)
596 596 replacements = [(oldctx.node(), (newnode,)),
597 597 (ctx.node(), (n,)),
598 598 (newnode, (n,)),
599 599 ]
600 600 for ich in internalchanges:
601 601 replacements.append((ich, (n,)))
602 602 return repo[n], replacements
603 603
604 604 class rollup(fold):
605 605 def skipprompt(self):
606 606 return True
607 607
608 608 class drop(histeditaction):
609 609 def run(self):
610 610 parentctx = self.repo[self.state.parentctxnode]
611 611 return parentctx, [(self.node, tuple())]
612 612
613 613 class message(histeditaction):
614 614 def commiteditor(self):
615 615 return cmdutil.getcommiteditor(edit=True, editform='histedit.mess')
616 616
617 617 def findoutgoing(ui, repo, remote=None, force=False, opts={}):
618 618 """utility function to find the first outgoing changeset
619 619
620 620 Used by initialisation code"""
621 621 dest = ui.expandpath(remote or 'default-push', remote or 'default')
622 622 dest, revs = hg.parseurl(dest, None)[:2]
623 623 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
624 624
625 625 revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
626 626 other = hg.peer(repo, opts, dest)
627 627
628 628 if revs:
629 629 revs = [repo.lookup(rev) for rev in revs]
630 630
631 631 outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
632 632 if not outgoing.missing:
633 633 raise util.Abort(_('no outgoing ancestors'))
634 634 roots = list(repo.revs("roots(%ln)", outgoing.missing))
635 635 if 1 < len(roots):
636 636 msg = _('there are ambiguous outgoing revisions')
637 637 hint = _('see "hg help histedit" for more detail')
638 638 raise util.Abort(msg, hint=hint)
639 639 return repo.lookup(roots[0])
640 640
641 641 actiontable = {'p': pick,
642 642 'pick': pick,
643 643 'e': edit,
644 644 'edit': edit,
645 645 'f': fold,
646 646 'fold': fold,
647 647 'r': rollup,
648 648 'roll': rollup,
649 649 'd': drop,
650 650 'drop': drop,
651 651 'm': message,
652 652 'mess': message,
653 653 }
654 654
655 655 @command('histedit',
656 656 [('', 'commands', '',
657 657 _('read history edits from the specified file'), _('FILE')),
658 658 ('c', 'continue', False, _('continue an edit already in progress')),
659 659 ('', 'edit-plan', False, _('edit remaining actions list')),
660 660 ('k', 'keep', False,
661 661 _("don't strip old nodes after edit is complete")),
662 662 ('', 'abort', False, _('abort an edit in progress')),
663 663 ('o', 'outgoing', False, _('changesets not found in destination')),
664 664 ('f', 'force', False,
665 665 _('force outgoing even for unrelated repositories')),
666 666 ('r', 'rev', [], _('first revision to be edited'), _('REV'))],
667 667 _("ANCESTOR | --outgoing [URL]"))
668 668 def histedit(ui, repo, *freeargs, **opts):
669 669 """interactively edit changeset history
670 670
671 671 This command edits changesets between ANCESTOR and the parent of
672 672 the working directory.
673 673
674 674 With --outgoing, this edits changesets not found in the
675 675 destination repository. If URL of the destination is omitted, the
676 676 'default-push' (or 'default') path will be used.
677 677
678 678 For safety, this command is also aborted if there are ambiguous
679 679 outgoing revisions which may confuse users: for example, if there
680 680 are multiple branches containing outgoing revisions.
681 681
682 682 Use "min(outgoing() and ::.)" or similar revset specification
683 683 instead of --outgoing to specify edit target revision exactly in
684 684 such ambiguous situation. See :hg:`help revsets` for detail about
685 685 selecting revisions.
686 686
687 687 Returns 0 on success, 1 if user intervention is required (not only
688 688 for intentional "edit" command, but also for resolving unexpected
689 689 conflicts).
690 690 """
691 691 state = histeditstate(repo)
692 692 try:
693 693 state.wlock = repo.wlock()
694 694 state.lock = repo.lock()
695 695 _histedit(ui, repo, state, *freeargs, **opts)
696 696 finally:
697 697 release(state.lock, state.wlock)
698 698
699 699 def _histedit(ui, repo, state, *freeargs, **opts):
700 700 # TODO only abort if we try and histedit mq patches, not just
701 701 # blanket if mq patches are applied somewhere
702 702 mq = getattr(repo, 'mq', None)
703 703 if mq and mq.applied:
704 704 raise util.Abort(_('source has mq patches applied'))
705 705
706 706 # basic argument incompatibility processing
707 707 outg = opts.get('outgoing')
708 708 cont = opts.get('continue')
709 709 editplan = opts.get('edit_plan')
710 710 abort = opts.get('abort')
711 711 force = opts.get('force')
712 712 rules = opts.get('commands', '')
713 713 revs = opts.get('rev', [])
714 714 goal = 'new' # This invocation goal, in new, continue, abort
715 715 if force and not outg:
716 716 raise util.Abort(_('--force only allowed with --outgoing'))
717 717 if cont:
718 718 if any((outg, abort, revs, freeargs, rules, editplan)):
719 719 raise util.Abort(_('no arguments allowed with --continue'))
720 720 goal = 'continue'
721 721 elif abort:
722 722 if any((outg, revs, freeargs, rules, editplan)):
723 723 raise util.Abort(_('no arguments allowed with --abort'))
724 724 goal = 'abort'
725 725 elif editplan:
726 726 if any((outg, revs, freeargs)):
727 727 raise util.Abort(_('only --commands argument allowed with '
728 728 '--edit-plan'))
729 729 goal = 'edit-plan'
730 730 else:
731 731 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
732 732 raise util.Abort(_('history edit already in progress, try '
733 733 '--continue or --abort'))
734 734 if outg:
735 735 if revs:
736 736 raise util.Abort(_('no revisions allowed with --outgoing'))
737 737 if len(freeargs) > 1:
738 738 raise util.Abort(
739 739 _('only one repo argument allowed with --outgoing'))
740 740 else:
741 741 revs.extend(freeargs)
742 742 if len(revs) == 0:
743 743 # experimental config: histedit.defaultrev
744 744 histeditdefault = ui.config('histedit', 'defaultrev')
745 745 if histeditdefault:
746 746 revs.append(histeditdefault)
747 747 if len(revs) != 1:
748 748 raise util.Abort(
749 749 _('histedit requires exactly one ancestor revision'))
750 750
751 751
752 752 replacements = []
753 753 state.keep = opts.get('keep', False)
754 754 supportsmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
755 755
756 756 # rebuild state
757 757 if goal == 'continue':
758 758 state.read()
759 759 state = bootstrapcontinue(ui, state, opts)
760 760 elif goal == 'edit-plan':
761 761 state.read()
762 762 if not rules:
763 763 comment = editcomment % (node.short(state.parentctxnode),
764 764 node.short(state.topmost))
765 765 rules = ruleeditor(repo, ui, state.rules, comment)
766 766 else:
767 767 if rules == '-':
768 768 f = sys.stdin
769 769 else:
770 770 f = open(rules)
771 771 rules = f.read()
772 772 f.close()
773 773 rules = [l for l in (r.strip() for r in rules.splitlines())
774 774 if l and not l.startswith('#')]
775 775 rules = verifyrules(rules, repo, [repo[c] for [_a, c] in state.rules])
776 776 state.rules = rules
777 777 state.write()
778 778 return
779 779 elif goal == 'abort':
780 780 state.read()
781 781 tmpnodes, leafs = newnodestoabort(state)
782 782 ui.debug('restore wc to old parent %s\n' % node.short(state.topmost))
783 783
784 784 # Recover our old commits if necessary
785 785 if not state.topmost in repo and state.backupfile:
786 786 backupfile = repo.join(state.backupfile)
787 787 f = hg.openpath(ui, backupfile)
788 788 gen = exchange.readbundle(ui, f, backupfile)
789 789 changegroup.addchangegroup(repo, gen, 'histedit',
790 790 'bundle:' + backupfile)
791 791 os.remove(backupfile)
792 792
793 793 # check whether we should update away
794 794 if repo.unfiltered().revs('parents() and (%n or %ln::)',
795 795 state.parentctxnode, leafs | tmpnodes):
796 796 hg.clean(repo, state.topmost)
797 797 cleanupnode(ui, repo, 'created', tmpnodes)
798 798 cleanupnode(ui, repo, 'temp', leafs)
799 799 state.clear()
800 800 return
801 801 else:
802 802 cmdutil.checkunfinished(repo)
803 803 cmdutil.bailifchanged(repo)
804 804
805 805 topmost, empty = repo.dirstate.parents()
806 806 if outg:
807 807 if freeargs:
808 808 remote = freeargs[0]
809 809 else:
810 810 remote = None
811 811 root = findoutgoing(ui, repo, remote, force, opts)
812 812 else:
813 813 rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
814 814 if len(rr) != 1:
815 815 raise util.Abort(_('The specified revisions must have '
816 816 'exactly one common root'))
817 817 root = rr[0].node()
818 818
819 819 revs = between(repo, root, topmost, state.keep)
820 820 if not revs:
821 821 raise util.Abort(_('%s is not an ancestor of working directory') %
822 822 node.short(root))
823 823
824 824 ctxs = [repo[r] for r in revs]
825 825 if not rules:
826 826 comment = editcomment % (node.short(root), node.short(topmost))
827 827 rules = ruleeditor(repo, ui, [['pick', c] for c in ctxs], comment)
828 828 else:
829 829 if rules == '-':
830 830 f = sys.stdin
831 831 else:
832 832 f = open(rules)
833 833 rules = f.read()
834 834 f.close()
835 835 rules = [l for l in (r.strip() for r in rules.splitlines())
836 836 if l and not l.startswith('#')]
837 837 rules = verifyrules(rules, repo, ctxs)
838 838
839 839 parentctxnode = repo[root].parents()[0].node()
840 840
841 841 state.parentctxnode = parentctxnode
842 842 state.rules = rules
843 843 state.topmost = topmost
844 844 state.replacements = replacements
845 845
846 846 # Create a backup so we can always abort completely.
847 847 backupfile = None
848 848 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
849 849 backupfile = repair._bundle(repo, [parentctxnode], [topmost], root,
850 850 'histedit')
851 851 state.backupfile = backupfile
852 852
853 853 while state.rules:
854 854 state.write()
855 855 action, ha = state.rules.pop(0)
856 856 ui.debug('histedit: processing %s %s\n' % (action, ha[:12]))
857 857 actobj = actiontable[action].fromrule(state, ha)
858 858 parentctx, replacement_ = actobj.run()
859 859 state.parentctxnode = parentctx.node()
860 860 state.replacements.extend(replacement_)
861 861 state.write()
862 862
863 863 hg.update(repo, state.parentctxnode)
864 864
865 865 mapping, tmpnodes, created, ntm = processreplacement(state)
866 866 if mapping:
867 867 for prec, succs in mapping.iteritems():
868 868 if not succs:
869 869 ui.debug('histedit: %s is dropped\n' % node.short(prec))
870 870 else:
871 871 ui.debug('histedit: %s is replaced by %s\n' % (
872 872 node.short(prec), node.short(succs[0])))
873 873 if len(succs) > 1:
874 874 m = 'histedit: %s'
875 875 for n in succs[1:]:
876 876 ui.debug(m % node.short(n))
877 877
878 878 if not state.keep:
879 879 if mapping:
880 880 movebookmarks(ui, repo, mapping, state.topmost, ntm)
881 881 # TODO update mq state
882 882 if supportsmarkers:
883 883 markers = []
884 884 # sort by revision number because it sound "right"
885 885 for prec in sorted(mapping, key=repo.changelog.rev):
886 886 succs = mapping[prec]
887 887 markers.append((repo[prec],
888 888 tuple(repo[s] for s in succs)))
889 889 if markers:
890 890 obsolete.createmarkers(repo, markers)
891 891 else:
892 892 cleanupnode(ui, repo, 'replaced', mapping)
893 893
894 894 cleanupnode(ui, repo, 'temp', tmpnodes)
895 895 state.clear()
896 896 if os.path.exists(repo.sjoin('undo')):
897 897 os.unlink(repo.sjoin('undo'))
898 898
899 899 def bootstrapcontinue(ui, state, opts):
900 900 repo = state.repo
901 901 if state.rules:
902 902 action, currentnode = state.rules.pop(0)
903 903
904 904 actobj = actiontable[action].fromrule(state, currentnode)
905 905
906 906 s = repo.status()
907 907 if s.modified or s.added or s.removed or s.deleted:
908 908 actobj.continuedirty()
909 909 s = repo.status()
910 910 if s.modified or s.added or s.removed or s.deleted:
911 911 raise util.Abort(_("working copy still dirty"))
912 912
913 913 parentctx, replacements = actobj.continueclean()
914 914
915 915 state.parentctxnode = parentctx.node()
916 916 state.replacements.extend(replacements)
917 917
918 918 return state
919 919
920 920 def between(repo, old, new, keep):
921 921 """select and validate the set of revision to edit
922 922
923 923 When keep is false, the specified set can't have children."""
924 924 ctxs = list(repo.set('%n::%n', old, new))
925 925 if ctxs and not keep:
926 926 if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
927 927 repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
928 928 raise util.Abort(_('cannot edit history that would orphan nodes'))
929 929 if repo.revs('(%ld) and merge()', ctxs):
930 930 raise util.Abort(_('cannot edit history that contains merges'))
931 931 root = ctxs[0] # list is already sorted by repo.set
932 932 if not root.mutable():
933 933 raise util.Abort(_('cannot edit public changeset: %s') % root,
934 934 hint=_('see "hg help phases" for details'))
935 935 return [c.node() for c in ctxs]
936 936
937 937 def makedesc(repo, action, rev):
938 938 """build a initial action line for a ctx
939 939
940 940 line are in the form:
941 941
942 942 <action> <hash> <rev> <summary>
943 943 """
944 944 ctx = repo[rev]
945 945 summary = ''
946 946 if ctx.description():
947 947 summary = ctx.description().splitlines()[0]
948 948 line = '%s %s %d %s' % (action, ctx, ctx.rev(), summary)
949 949 # trim to 80 columns so it's not stupidly wide in my editor
950 950 maxlen = repo.ui.configint('histedit', 'linelen', default=80)
951 951 maxlen = max(maxlen, 22) # avoid truncating hash
952 952 return util.ellipsis(line, maxlen)
953 953
954 954 def ruleeditor(repo, ui, rules, editcomment=""):
955 955 """open an editor to edit rules
956 956
957 957 rules are in the format [ [act, ctx], ...] like in state.rules
958 958 """
959 959 rules = '\n'.join([makedesc(repo, act, rev) for [act, rev] in rules])
960 960 rules += '\n\n'
961 961 rules += editcomment
962 962 rules = ui.edit(rules, ui.username())
963 963
964 964 # Save edit rules in .hg/histedit-last-edit.txt in case
965 965 # the user needs to ask for help after something
966 966 # surprising happens.
967 967 f = open(repo.join('histedit-last-edit.txt'), 'w')
968 968 f.write(rules)
969 969 f.close()
970 970
971 971 return rules
972 972
973 973 def verifyrules(rules, repo, ctxs):
974 974 """Verify that there exists exactly one edit rule per given changeset.
975 975
976 976 Will abort if there are to many or too few rules, a malformed rule,
977 977 or a rule on a changeset outside of the user-given range.
978 978 """
979 979 parsed = []
980 980 expected = set(c.hex() for c in ctxs)
981 981 seen = set()
982 982 for r in rules:
983 983 if ' ' not in r:
984 984 raise util.Abort(_('malformed line "%s"') % r)
985 985 action, rest = r.split(' ', 1)
986 986 ha = rest.strip().split(' ', 1)[0]
987 987 try:
988 988 ha = repo[ha].hex()
989 989 except error.RepoError:
990 990 raise util.Abort(_('unknown changeset %s listed') % ha[:12])
991 991 if ha not in expected:
992 992 raise util.Abort(
993 993 _('may not use changesets other than the ones listed'))
994 994 if ha in seen:
995 995 raise util.Abort(_('duplicated command for changeset %s') %
996 996 ha[:12])
997 997 seen.add(ha)
998 998 if action not in actiontable:
999 999 raise util.Abort(_('unknown action "%s"') % action)
1000 1000 parsed.append([action, ha])
1001 1001 missing = sorted(expected - seen) # sort to stabilize output
1002 1002 if missing:
1003 1003 raise util.Abort(_('missing rules for changeset %s') %
1004 1004 missing[0][:12],
1005 1005 hint=_('do you want to use the drop action?'))
1006 1006 return parsed
1007 1007
1008 1008 def newnodestoabort(state):
1009 1009 """process the list of replacements to return
1010 1010
1011 1011 1) the list of final node
1012 1012 2) the list of temporary node
1013 1013
1014 1014 This meant to be used on abort as less data are required in this case.
1015 1015 """
1016 1016 replacements = state.replacements
1017 1017 allsuccs = set()
1018 1018 replaced = set()
1019 1019 for rep in replacements:
1020 1020 allsuccs.update(rep[1])
1021 1021 replaced.add(rep[0])
1022 1022 newnodes = allsuccs - replaced
1023 1023 tmpnodes = allsuccs & replaced
1024 1024 return newnodes, tmpnodes
1025 1025
1026 1026
1027 1027 def processreplacement(state):
1028 1028 """process the list of replacements to return
1029 1029
1030 1030 1) the final mapping between original and created nodes
1031 1031 2) the list of temporary node created by histedit
1032 1032 3) the list of new commit created by histedit"""
1033 1033 replacements = state.replacements
1034 1034 allsuccs = set()
1035 1035 replaced = set()
1036 1036 fullmapping = {}
1037 1037 # initialize basic set
1038 1038 # fullmapping records all operations recorded in replacement
1039 1039 for rep in replacements:
1040 1040 allsuccs.update(rep[1])
1041 1041 replaced.add(rep[0])
1042 1042 fullmapping.setdefault(rep[0], set()).update(rep[1])
1043 1043 new = allsuccs - replaced
1044 1044 tmpnodes = allsuccs & replaced
1045 1045 # Reduce content fullmapping into direct relation between original nodes
1046 1046 # and final node created during history edition
1047 1047 # Dropped changeset are replaced by an empty list
1048 1048 toproceed = set(fullmapping)
1049 1049 final = {}
1050 1050 while toproceed:
1051 1051 for x in list(toproceed):
1052 1052 succs = fullmapping[x]
1053 1053 for s in list(succs):
1054 1054 if s in toproceed:
1055 1055 # non final node with unknown closure
1056 1056 # We can't process this now
1057 1057 break
1058 1058 elif s in final:
1059 1059 # non final node, replace with closure
1060 1060 succs.remove(s)
1061 1061 succs.update(final[s])
1062 1062 else:
1063 1063 final[x] = succs
1064 1064 toproceed.remove(x)
1065 1065 # remove tmpnodes from final mapping
1066 1066 for n in tmpnodes:
1067 1067 del final[n]
1068 1068 # we expect all changes involved in final to exist in the repo
1069 1069 # turn `final` into list (topologically sorted)
1070 1070 nm = state.repo.changelog.nodemap
1071 1071 for prec, succs in final.items():
1072 1072 final[prec] = sorted(succs, key=nm.get)
1073 1073
1074 1074 # computed topmost element (necessary for bookmark)
1075 1075 if new:
1076 1076 newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
1077 1077 elif not final:
1078 1078 # Nothing rewritten at all. we won't need `newtopmost`
1079 1079 # It is the same as `oldtopmost` and `processreplacement` know it
1080 1080 newtopmost = None
1081 1081 else:
1082 1082 # every body died. The newtopmost is the parent of the root.
1083 1083 r = state.repo.changelog.rev
1084 1084 newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
1085 1085
1086 1086 return final, tmpnodes, new, newtopmost
1087 1087
1088 1088 def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
1089 1089 """Move bookmark from old to newly created node"""
1090 1090 if not mapping:
1091 1091 # if nothing got rewritten there is not purpose for this function
1092 1092 return
1093 1093 moves = []
1094 1094 for bk, old in sorted(repo._bookmarks.iteritems()):
1095 1095 if old == oldtopmost:
1096 1096 # special case ensure bookmark stay on tip.
1097 1097 #
1098 1098 # This is arguably a feature and we may only want that for the
1099 1099 # active bookmark. But the behavior is kept compatible with the old
1100 1100 # version for now.
1101 1101 moves.append((bk, newtopmost))
1102 1102 continue
1103 1103 base = old
1104 1104 new = mapping.get(base, None)
1105 1105 if new is None:
1106 1106 continue
1107 1107 while not new:
1108 1108 # base is killed, trying with parent
1109 1109 base = repo[base].p1().node()
1110 1110 new = mapping.get(base, (base,))
1111 1111 # nothing to move
1112 1112 moves.append((bk, new[-1]))
1113 1113 if moves:
1114 1114 marks = repo._bookmarks
1115 1115 for mark, new in moves:
1116 1116 old = marks[mark]
1117 1117 ui.note(_('histedit: moving bookmarks %s from %s to %s\n')
1118 1118 % (mark, node.short(old), node.short(new)))
1119 1119 marks[mark] = new
1120 1120 marks.write()
1121 1121
1122 1122 def cleanupnode(ui, repo, name, nodes):
1123 1123 """strip a group of nodes from the repository
1124 1124
1125 1125 The set of node to strip may contains unknown nodes."""
1126 1126 ui.debug('should strip %s nodes %s\n' %
1127 1127 (name, ', '.join([node.short(n) for n in nodes])))
1128 1128 lock = None
1129 1129 try:
1130 1130 lock = repo.lock()
1131 1131 # do not let filtering get in the way of the cleanse
1132 1132 # we should probably get ride of obsolescence marker created during the
1133 1133 # histedit, but we currently do not have such information.
1134 1134 repo = repo.unfiltered()
1135 1135 # Find all node that need to be stripped
1136 1136 # (we hg %lr instead of %ln to silently ignore unknown item
1137 1137 nm = repo.changelog.nodemap
1138 1138 nodes = sorted(n for n in nodes if n in nm)
1139 1139 roots = [c.node() for c in repo.set("roots(%ln)", nodes)]
1140 1140 for c in roots:
1141 1141 # We should process node in reverse order to strip tip most first.
1142 1142 # but this trigger a bug in changegroup hook.
1143 1143 # This would reduce bundle overhead
1144 1144 repair.strip(ui, repo, c)
1145 1145 finally:
1146 1146 release(lock)
1147 1147
1148 1148 def stripwrapper(orig, ui, repo, nodelist, *args, **kwargs):
1149 1149 if isinstance(nodelist, str):
1150 1150 nodelist = [nodelist]
1151 1151 if os.path.exists(os.path.join(repo.path, 'histedit-state')):
1152 1152 state = histeditstate(repo)
1153 1153 state.read()
1154 1154 histedit_nodes = set([repo[rulehash].node() for (action, rulehash)
1155 1155 in state.rules if rulehash in repo])
1156 1156 strip_nodes = set([repo[n].node() for n in nodelist])
1157 1157 common_nodes = histedit_nodes & strip_nodes
1158 1158 if common_nodes:
1159 1159 raise util.Abort(_("histedit in progress, can't strip %s")
1160 1160 % ', '.join(node.short(x) for x in common_nodes))
1161 1161 return orig(ui, repo, nodelist, *args, **kwargs)
1162 1162
1163 1163 extensions.wrapfunction(repair, 'strip', stripwrapper)
1164 1164
1165 1165 def summaryhook(ui, repo):
1166 1166 if not os.path.exists(repo.join('histedit-state')):
1167 1167 return
1168 1168 state = histeditstate(repo)
1169 1169 state.read()
1170 1170 if state.rules:
1171 1171 # i18n: column positioning for "hg summary"
1172 1172 ui.write(_('hist: %s (histedit --continue)\n') %
1173 1173 (ui.label(_('%d remaining'), 'histedit.remaining') %
1174 1174 len(state.rules)))
1175 1175
1176 1176 def extsetup(ui):
1177 1177 cmdutil.summaryhooks.add('histedit', summaryhook)
1178 1178 cmdutil.unfinishedstates.append(
1179 1179 ['histedit-state', False, True, _('histedit in progress'),
1180 1180 _("use 'hg histedit --continue' or 'hg histedit --abort'")])
@@ -1,326 +1,326 b''
1 1 Test argument handling and various data parsing
2 2 ==================================================
3 3
4 4
5 5 Enable extensions used by this test.
6 6 $ cat >>$HGRCPATH <<EOF
7 7 > [extensions]
8 8 > histedit=
9 9 > EOF
10 10
11 11 Repo setup.
12 12 $ hg init foo
13 13 $ cd foo
14 14 $ echo alpha >> alpha
15 15 $ hg addr
16 16 adding alpha
17 17 $ hg ci -m one
18 18 $ echo alpha >> alpha
19 19 $ hg ci -m two
20 20 $ echo alpha >> alpha
21 21 $ hg ci -m three
22 22 $ echo alpha >> alpha
23 23 $ hg ci -m four
24 24 $ echo alpha >> alpha
25 25 $ hg ci -m five
26 26
27 27 $ hg log --style compact --graph
28 28 @ 4[tip] 08d98a8350f3 1970-01-01 00:00 +0000 test
29 29 | five
30 30 |
31 31 o 3 c8e68270e35a 1970-01-01 00:00 +0000 test
32 32 | four
33 33 |
34 34 o 2 eb57da33312f 1970-01-01 00:00 +0000 test
35 35 | three
36 36 |
37 37 o 1 579e40513370 1970-01-01 00:00 +0000 test
38 38 | two
39 39 |
40 40 o 0 6058cbb6cfd7 1970-01-01 00:00 +0000 test
41 41 one
42 42
43 43
44 44 histedit --continue/--abort with no existing state
45 45 --------------------------------------------------
46 46
47 47 $ hg histedit --continue
48 48 abort: no histedit in progress
49 49 [255]
50 50 $ hg histedit --abort
51 51 abort: no histedit in progress
52 52 [255]
53 53
54 54 Run a dummy edit to make sure we get tip^^ correctly via revsingle.
55 55 --------------------------------------------------------------------
56 56
57 57 $ HGEDITOR=cat hg histedit "tip^^"
58 58 pick eb57da33312f 2 three
59 59 pick c8e68270e35a 3 four
60 60 pick 08d98a8350f3 4 five
61 61
62 62 # Edit history between eb57da33312f and 08d98a8350f3
63 63 #
64 64 # Commits are listed from least to most recent
65 65 #
66 66 # Commands:
67 67 # p, pick = use commit
68 68 # e, edit = use commit, but stop for amending
69 69 # f, fold = use commit, but combine it with the one above
70 70 # r, roll = like fold, but discard this commit's description
71 71 # d, drop = remove commit from history
72 # m, mess = edit message without changing commit content
72 # m, mess = edit commit message without changing commit content
73 73 #
74 74 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 75
76 76 Run on a revision not ancestors of the current working directory.
77 77 --------------------------------------------------------------------
78 78
79 79 $ hg up 2
80 80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 $ hg histedit -r 4
82 82 abort: 08d98a8350f3 is not an ancestor of working directory
83 83 [255]
84 84 $ hg up --quiet
85 85
86 86
87 87 Test that we pick the minimum of a revrange
88 88 ---------------------------------------
89 89
90 90 $ HGEDITOR=cat hg histedit '2::' --commands - << EOF
91 91 > pick eb57da33312f 2 three
92 92 > pick c8e68270e35a 3 four
93 93 > pick 08d98a8350f3 4 five
94 94 > EOF
95 95 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 96 $ hg up --quiet
97 97
98 98 $ HGEDITOR=cat hg histedit 'tip:2' --commands - << EOF
99 99 > pick eb57da33312f 2 three
100 100 > pick c8e68270e35a 3 four
101 101 > pick 08d98a8350f3 4 five
102 102 > EOF
103 103 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 104 $ hg up --quiet
105 105
106 106 Test config specified default
107 107 -----------------------------
108 108
109 109 $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
110 110 > pick c8e68270e35a 3 four
111 111 > pick 08d98a8350f3 4 five
112 112 > EOF
113 113 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
114 114
115 115 Run on a revision not descendants of the initial parent
116 116 --------------------------------------------------------------------
117 117
118 118 Test the message shown for inconsistent histedit state, which may be
119 119 created (and forgotten) by Mercurial earlier than 2.7. This emulates
120 120 Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
121 121 temporarily.
122 122
123 123 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
124 124 @ 4 08d9 five
125 125 |
126 126 o 3 c8e6 four
127 127 |
128 128 o 2 eb57 three
129 129 |
130 130 $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
131 131 > edit 08d98a8350f3 4 five
132 132 > EOF
133 133 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
134 134 reverting alpha
135 135 Make changes as needed, you may commit or record as needed now.
136 136 When you are finished, run hg histedit --continue to resume.
137 137 [1]
138 138
139 139 $ mv .hg/histedit-state .hg/histedit-state.back
140 140 $ hg update --quiet --clean 2
141 141 $ echo alpha >> alpha
142 142 $ mv .hg/histedit-state.back .hg/histedit-state
143 143
144 144 $ hg histedit --continue
145 145 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 146 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg (glob)
147 147 $ hg log -G -T '{rev} {shortest(node)} {desc}\n' -r 2::
148 148 @ 4 f5ed five
149 149 |
150 150 | o 3 c8e6 four
151 151 |/
152 152 o 2 eb57 three
153 153 |
154 154
155 155 $ hg unbundle -q $TESTTMP/foo/.hg/strip-backup/08d98a8350f3-02594089-backup.hg
156 156 $ hg strip -q -r f5ed --config extensions.strip=
157 157 $ hg up -q 08d98a8350f3
158 158
159 159 Test that missing revisions are detected
160 160 ---------------------------------------
161 161
162 162 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
163 163 > pick eb57da33312f 2 three
164 164 > pick 08d98a8350f3 4 five
165 165 > EOF
166 166 abort: missing rules for changeset c8e68270e35a
167 167 (do you want to use the drop action?)
168 168 [255]
169 169
170 170 Test that extra revisions are detected
171 171 ---------------------------------------
172 172
173 173 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
174 174 > pick 6058cbb6cfd7 0 one
175 175 > pick c8e68270e35a 3 four
176 176 > pick 08d98a8350f3 4 five
177 177 > EOF
178 178 abort: may not use changesets other than the ones listed
179 179 [255]
180 180
181 181 Test malformed line
182 182 ---------------------------------------
183 183
184 184 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
185 185 > pickeb57da33312f2three
186 186 > pick c8e68270e35a 3 four
187 187 > pick 08d98a8350f3 4 five
188 188 > EOF
189 189 abort: malformed line "pickeb57da33312f2three"
190 190 [255]
191 191
192 192 Test unknown changeset
193 193 ---------------------------------------
194 194
195 195 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
196 196 > pick 0123456789ab 2 three
197 197 > pick c8e68270e35a 3 four
198 198 > pick 08d98a8350f3 4 five
199 199 > EOF
200 200 abort: unknown changeset 0123456789ab listed
201 201 [255]
202 202
203 203 Test unknown command
204 204 ---------------------------------------
205 205
206 206 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
207 207 > coin eb57da33312f 2 three
208 208 > pick c8e68270e35a 3 four
209 209 > pick 08d98a8350f3 4 five
210 210 > EOF
211 211 abort: unknown action "coin"
212 212 [255]
213 213
214 214 Test duplicated changeset
215 215 ---------------------------------------
216 216
217 217 So one is missing and one appear twice.
218 218
219 219 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
220 220 > pick eb57da33312f 2 three
221 221 > pick eb57da33312f 2 three
222 222 > pick 08d98a8350f3 4 five
223 223 > EOF
224 224 abort: duplicated command for changeset eb57da33312f
225 225 [255]
226 226
227 227 Test short version of command
228 228 ---------------------------------------
229 229
230 230 Note: we use varying amounts of white space between command name and changeset
231 231 short hash. This tests issue3893.
232 232
233 233 $ HGEDITOR=cat hg histedit "tip^^" --commands - << EOF
234 234 > pick eb57da33312f 2 three
235 235 > p c8e68270e35a 3 four
236 236 > f 08d98a8350f3 4 five
237 237 > EOF
238 238 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 239 reverting alpha
240 240 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
241 241 four
242 242 ***
243 243 five
244 244
245 245
246 246
247 247 HG: Enter commit message. Lines beginning with 'HG:' are removed.
248 248 HG: Leave message empty to abort commit.
249 249 HG: --
250 250 HG: user: test
251 251 HG: branch 'default'
252 252 HG: changed alpha
253 253 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 254 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 255 saved backup bundle to $TESTTMP/foo/.hg/strip-backup/*-backup.hg (glob)
256 256
257 257 $ hg update -q 2
258 258 $ echo x > x
259 259 $ hg add x
260 260 $ hg commit -m'x' x
261 261 created new head
262 262 $ hg histedit -r 'heads(all())'
263 263 abort: The specified revisions must have exactly one common root
264 264 [255]
265 265
266 266 Test that trimming description using multi-byte characters
267 267 --------------------------------------------------------------------
268 268
269 269 $ python <<EOF
270 270 > fp = open('logfile', 'w')
271 271 > fp.write('12345678901234567890123456789012345678901234567890' +
272 272 > '12345') # there are 5 more columns for 80 columns
273 273 >
274 274 > # 2 x 4 = 8 columns, but 3 x 4 = 12 bytes
275 275 > fp.write(u'\u3042\u3044\u3046\u3048'.encode('utf-8'))
276 276 >
277 277 > fp.close()
278 278 > EOF
279 279 $ echo xx >> x
280 280 $ hg --encoding utf-8 commit --logfile logfile
281 281
282 282 $ HGEDITOR=cat hg --encoding utf-8 histedit tip
283 283 pick 3d3ea1f3a10b 5 1234567890123456789012345678901234567890123456789012345\xe3\x81\x82... (esc)
284 284
285 285 # Edit history between 3d3ea1f3a10b and 3d3ea1f3a10b
286 286 #
287 287 # Commits are listed from least to most recent
288 288 #
289 289 # Commands:
290 290 # p, pick = use commit
291 291 # e, edit = use commit, but stop for amending
292 292 # f, fold = use commit, but combine it with the one above
293 293 # r, roll = like fold, but discard this commit's description
294 294 # d, drop = remove commit from history
295 # m, mess = edit message without changing commit content
295 # m, mess = edit commit message without changing commit content
296 296 #
297 297 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 298
299 299 Test --continue with --keep
300 300
301 301 $ hg strip -q -r . --config extensions.strip=
302 302 $ hg histedit '.^' -q --keep --commands - << EOF
303 303 > edit eb57da33312f 2 three
304 304 > pick f3cfcca30c44 4 x
305 305 > EOF
306 306 Make changes as needed, you may commit or record as needed now.
307 307 When you are finished, run hg histedit --continue to resume.
308 308 [1]
309 309 $ echo edit >> alpha
310 310 $ hg histedit -q --continue
311 311 $ hg log -G -T '{rev}:{node|short} {desc}'
312 312 @ 6:8fda0c726bf2 x
313 313 |
314 314 o 5:63379946892c three
315 315 |
316 316 | o 4:f3cfcca30c44 x
317 317 | |
318 318 | | o 3:2a30f3cfee78 four
319 319 | |/ ***
320 320 | | five
321 321 | o 2:eb57da33312f three
322 322 |/
323 323 o 1:579e40513370 two
324 324 |
325 325 o 0:6058cbb6cfd7 one
326 326
@@ -1,182 +1,182 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 75 # f, fold = use commit, but combine it with the one above
76 76 # r, roll = like fold, but discard this commit's description
77 77 # d, drop = remove commit from history
78 # m, mess = edit message without changing commit content
78 # m, mess = edit commit message without changing commit content
79 79 #
80 80 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 $ hg histedit 1 --commands - --verbose << EOF | grep histedit
82 82 > pick 177f92b77385 2 c
83 83 > drop d2ae7f538514 1 b
84 84 > pick 055a42cdd887 3 d
85 85 > fold e860deea161a 4 e
86 86 > pick 652413bf663e 5 f
87 87 > EOF
88 88 histedit: moving bookmarks also-two from 177f92b77385 to b346ab9a313d
89 89 histedit: moving bookmarks five from 652413bf663e to cacdfd884a93
90 90 histedit: moving bookmarks four from e860deea161a to 59d9f330561f
91 91 histedit: moving bookmarks three from 055a42cdd887 to 59d9f330561f
92 92 histedit: moving bookmarks two from 177f92b77385 to b346ab9a313d
93 93 histedit: moving bookmarks will-move-backwards from d2ae7f538514 to cb9a9f314b8b
94 94 saved backup bundle to $TESTTMP/r/.hg/strip-backup/d2ae7f538514-48787b8d-backup.hg (glob)
95 95 saved backup bundle to $TESTTMP/r/.hg/strip-backup/96e494a2d553-3c6c5d92-backup.hg (glob)
96 96 $ hg log --graph
97 97 @ changeset: 3:cacdfd884a93
98 98 | bookmark: five
99 99 | tag: tip
100 100 | user: test
101 101 | date: Thu Jan 01 00:00:00 1970 +0000
102 102 | summary: f
103 103 |
104 104 o changeset: 2:59d9f330561f
105 105 | bookmark: four
106 106 | bookmark: three
107 107 | user: test
108 108 | date: Thu Jan 01 00:00:00 1970 +0000
109 109 | summary: d
110 110 |
111 111 o changeset: 1:b346ab9a313d
112 112 | bookmark: also-two
113 113 | bookmark: two
114 114 | user: test
115 115 | date: Thu Jan 01 00:00:00 1970 +0000
116 116 | summary: c
117 117 |
118 118 o changeset: 0:cb9a9f314b8b
119 119 bookmark: will-move-backwards
120 120 user: test
121 121 date: Thu Jan 01 00:00:00 1970 +0000
122 122 summary: a
123 123
124 124 $ HGEDITOR=cat hg histedit 1
125 125 pick b346ab9a313d 1 c
126 126 pick 59d9f330561f 2 d
127 127 pick cacdfd884a93 3 f
128 128
129 129 # Edit history between b346ab9a313d and cacdfd884a93
130 130 #
131 131 # Commits are listed from least to most recent
132 132 #
133 133 # Commands:
134 134 # p, pick = use commit
135 135 # e, edit = use commit, but stop for amending
136 136 # f, fold = use commit, but combine it with the one above
137 137 # r, roll = like fold, but discard this commit's description
138 138 # d, drop = remove commit from history
139 # m, mess = edit message without changing commit content
139 # m, mess = edit commit message without changing commit content
140 140 #
141 141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ hg histedit 1 --commands - --verbose << EOF | grep histedit
143 143 > pick b346ab9a313d 1 c
144 144 > pick cacdfd884a93 3 f
145 145 > pick 59d9f330561f 2 d
146 146 > EOF
147 147 histedit: moving bookmarks five from cacdfd884a93 to c04e50810e4b
148 148 histedit: moving bookmarks four from 59d9f330561f to c04e50810e4b
149 149 histedit: moving bookmarks three from 59d9f330561f to c04e50810e4b
150 150 saved backup bundle to $TESTTMP/r/.hg/strip-backup/59d9f330561f-073008af-backup.hg (glob)
151 151
152 152 We expect 'five' to stay at tip, since the tipmost bookmark is most
153 153 likely the useful signal.
154 154
155 155 $ hg log --graph
156 156 @ changeset: 3:c04e50810e4b
157 157 | bookmark: five
158 158 | bookmark: four
159 159 | bookmark: three
160 160 | tag: tip
161 161 | user: test
162 162 | date: Thu Jan 01 00:00:00 1970 +0000
163 163 | summary: d
164 164 |
165 165 o changeset: 2:c13eb81022ca
166 166 | user: test
167 167 | date: Thu Jan 01 00:00:00 1970 +0000
168 168 | summary: f
169 169 |
170 170 o changeset: 1:b346ab9a313d
171 171 | bookmark: also-two
172 172 | bookmark: two
173 173 | user: test
174 174 | date: Thu Jan 01 00:00:00 1970 +0000
175 175 | summary: c
176 176 |
177 177 o changeset: 0:cb9a9f314b8b
178 178 bookmark: will-move-backwards
179 179 user: test
180 180 date: Thu Jan 01 00:00:00 1970 +0000
181 181 summary: a
182 182
@@ -1,459 +1,459 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 69 # f, fold = use commit, but combine it with the one above
70 70 # r, roll = like fold, but discard this commit's description
71 71 # d, drop = remove commit from history
72 # m, mess = edit message without changing commit content
72 # m, mess = edit commit message without changing commit content
73 73 #
74 74 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 75
76 76 edit the history
77 77 (use a hacky editor to check histedit-last-edit.txt backup)
78 78
79 79 $ EDITED="$TESTTMP/editedhistory"
80 80 $ cat > $EDITED <<EOF
81 81 > pick 177f92b77385 c
82 82 > pick e860deea161a e
83 83 > pick 652413bf663e f
84 84 > pick 055a42cdd887 d
85 85 > EOF
86 86 $ HGEDITOR="cat \"$EDITED\" > " hg histedit 177f92b77385 2>&1 | fixbundle
87 87 0 files updated, 0 files merged, 3 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 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 91
92 92 rules should end up in .hg/histedit-last-edit.txt:
93 93 $ cat .hg/histedit-last-edit.txt
94 94 pick 177f92b77385 c
95 95 pick e860deea161a e
96 96 pick 652413bf663e f
97 97 pick 055a42cdd887 d
98 98
99 99 log after edit
100 100 $ hg log --graph
101 101 @ changeset: 5:07114f51870f
102 102 | tag: tip
103 103 | user: test
104 104 | date: Thu Jan 01 00:00:00 1970 +0000
105 105 | summary: d
106 106 |
107 107 o changeset: 4:8ade9693061e
108 108 | user: test
109 109 | date: Thu Jan 01 00:00:00 1970 +0000
110 110 | summary: f
111 111 |
112 112 o changeset: 3:d8249471110a
113 113 | user: test
114 114 | date: Thu Jan 01 00:00:00 1970 +0000
115 115 | summary: e
116 116 |
117 117 o changeset: 2:177f92b77385
118 118 | user: test
119 119 | date: Thu Jan 01 00:00:00 1970 +0000
120 120 | summary: c
121 121 |
122 122 o changeset: 1:d2ae7f538514
123 123 | user: test
124 124 | date: Thu Jan 01 00:00:00 1970 +0000
125 125 | summary: b
126 126 |
127 127 o changeset: 0:cb9a9f314b8b
128 128 user: test
129 129 date: Thu Jan 01 00:00:00 1970 +0000
130 130 summary: a
131 131
132 132
133 133 put things back
134 134
135 135 $ hg histedit 177f92b77385 --commands - 2>&1 << EOF | fixbundle
136 136 > pick 177f92b77385 c
137 137 > pick 07114f51870f d
138 138 > pick d8249471110a e
139 139 > pick 8ade9693061e f
140 140 > EOF
141 141 0 files updated, 0 files merged, 3 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 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 145
146 146 $ hg log --graph
147 147 @ changeset: 5:7eca9b5b1148
148 148 | tag: tip
149 149 | user: test
150 150 | date: Thu Jan 01 00:00:00 1970 +0000
151 151 | summary: f
152 152 |
153 153 o changeset: 4:915da888f2de
154 154 | user: test
155 155 | date: Thu Jan 01 00:00:00 1970 +0000
156 156 | summary: e
157 157 |
158 158 o changeset: 3:10517e47bbbb
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:177f92b77385
164 164 | user: test
165 165 | date: Thu Jan 01 00:00:00 1970 +0000
166 166 | summary: c
167 167 |
168 168 o changeset: 1:d2ae7f538514
169 169 | user: test
170 170 | date: Thu Jan 01 00:00:00 1970 +0000
171 171 | summary: b
172 172 |
173 173 o changeset: 0:cb9a9f314b8b
174 174 user: test
175 175 date: Thu Jan 01 00:00:00 1970 +0000
176 176 summary: a
177 177
178 178
179 179 slightly different this time
180 180
181 181 $ hg histedit 177f92b77385 --commands - << EOF 2>&1 | fixbundle
182 182 > pick 10517e47bbbb d
183 183 > pick 7eca9b5b1148 f
184 184 > pick 915da888f2de e
185 185 > pick 177f92b77385 c
186 186 > EOF
187 187 0 files updated, 0 files merged, 4 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 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 192 $ hg log --graph
193 193 @ changeset: 5:38b92f448761
194 194 | tag: tip
195 195 | user: test
196 196 | date: Thu Jan 01 00:00:00 1970 +0000
197 197 | summary: c
198 198 |
199 199 o changeset: 4:de71b079d9ce
200 200 | user: test
201 201 | date: Thu Jan 01 00:00:00 1970 +0000
202 202 | summary: e
203 203 |
204 204 o changeset: 3:be9ae3a309c6
205 205 | user: test
206 206 | date: Thu Jan 01 00:00:00 1970 +0000
207 207 | summary: f
208 208 |
209 209 o changeset: 2:799205341b6b
210 210 | user: test
211 211 | date: Thu Jan 01 00:00:00 1970 +0000
212 212 | summary: d
213 213 |
214 214 o changeset: 1:d2ae7f538514
215 215 | user: test
216 216 | date: Thu Jan 01 00:00:00 1970 +0000
217 217 | summary: b
218 218 |
219 219 o changeset: 0:cb9a9f314b8b
220 220 user: test
221 221 date: Thu Jan 01 00:00:00 1970 +0000
222 222 summary: a
223 223
224 224
225 225 keep prevents stripping dead revs
226 226 $ hg histedit 799205341b6b --keep --commands - 2>&1 << EOF | fixbundle
227 227 > pick 799205341b6b d
228 228 > pick be9ae3a309c6 f
229 229 > pick 38b92f448761 c
230 230 > pick de71b079d9ce e
231 231 > EOF
232 232 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
233 233 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 234 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
235 235 $ hg log --graph
236 236 @ changeset: 7:803ef1c6fcfd
237 237 | tag: tip
238 238 | user: test
239 239 | date: Thu Jan 01 00:00:00 1970 +0000
240 240 | summary: e
241 241 |
242 242 o changeset: 6:ece0b8d93dda
243 243 | parent: 3:be9ae3a309c6
244 244 | user: test
245 245 | date: Thu Jan 01 00:00:00 1970 +0000
246 246 | summary: c
247 247 |
248 248 | o changeset: 5:38b92f448761
249 249 | | user: test
250 250 | | date: Thu Jan 01 00:00:00 1970 +0000
251 251 | | summary: c
252 252 | |
253 253 | o changeset: 4:de71b079d9ce
254 254 |/ user: test
255 255 | date: Thu Jan 01 00:00:00 1970 +0000
256 256 | summary: e
257 257 |
258 258 o changeset: 3:be9ae3a309c6
259 259 | user: test
260 260 | date: Thu Jan 01 00:00:00 1970 +0000
261 261 | summary: f
262 262 |
263 263 o changeset: 2:799205341b6b
264 264 | user: test
265 265 | date: Thu Jan 01 00:00:00 1970 +0000
266 266 | summary: d
267 267 |
268 268 o changeset: 1:d2ae7f538514
269 269 | user: test
270 270 | date: Thu Jan 01 00:00:00 1970 +0000
271 271 | summary: b
272 272 |
273 273 o changeset: 0:cb9a9f314b8b
274 274 user: test
275 275 date: Thu Jan 01 00:00:00 1970 +0000
276 276 summary: a
277 277
278 278
279 279 try with --rev
280 280 $ hg histedit --commands - --rev -2 2>&1 <<EOF | fixbundle
281 281 > pick de71b079d9ce e
282 282 > pick 38b92f448761 c
283 283 > EOF
284 284 abort: may not use changesets other than the ones listed
285 285 $ hg log --graph
286 286 @ changeset: 7:803ef1c6fcfd
287 287 | tag: tip
288 288 | user: test
289 289 | date: Thu Jan 01 00:00:00 1970 +0000
290 290 | summary: e
291 291 |
292 292 o changeset: 6:ece0b8d93dda
293 293 | parent: 3:be9ae3a309c6
294 294 | user: test
295 295 | date: Thu Jan 01 00:00:00 1970 +0000
296 296 | summary: c
297 297 |
298 298 | o changeset: 5:38b92f448761
299 299 | | user: test
300 300 | | date: Thu Jan 01 00:00:00 1970 +0000
301 301 | | summary: c
302 302 | |
303 303 | o changeset: 4:de71b079d9ce
304 304 |/ user: test
305 305 | date: Thu Jan 01 00:00:00 1970 +0000
306 306 | summary: e
307 307 |
308 308 o changeset: 3:be9ae3a309c6
309 309 | user: test
310 310 | date: Thu Jan 01 00:00:00 1970 +0000
311 311 | summary: f
312 312 |
313 313 o changeset: 2:799205341b6b
314 314 | user: test
315 315 | date: Thu Jan 01 00:00:00 1970 +0000
316 316 | summary: d
317 317 |
318 318 o changeset: 1:d2ae7f538514
319 319 | user: test
320 320 | date: Thu Jan 01 00:00:00 1970 +0000
321 321 | summary: b
322 322 |
323 323 o changeset: 0:cb9a9f314b8b
324 324 user: test
325 325 date: Thu Jan 01 00:00:00 1970 +0000
326 326 summary: a
327 327
328 328 Verify that revsetalias entries work with histedit:
329 329 $ cat >> $HGRCPATH <<EOF
330 330 > [revsetalias]
331 331 > grandparent(ARG) = p1(p1(ARG))
332 332 > EOF
333 333 $ echo extra commit >> c
334 334 $ hg ci -m 'extra commit to c'
335 335 $ HGEDITOR=cat hg histedit 'grandparent(.)'
336 336 pick ece0b8d93dda 6 c
337 337 pick 803ef1c6fcfd 7 e
338 338 pick 9c863c565126 8 extra commit to c
339 339
340 340 # Edit history between ece0b8d93dda and 9c863c565126
341 341 #
342 342 # Commits are listed from least to most recent
343 343 #
344 344 # Commands:
345 345 # p, pick = use commit
346 346 # e, edit = use commit, but stop for amending
347 347 # f, fold = use commit, but combine it with the one above
348 348 # r, roll = like fold, but discard this commit's description
349 349 # d, drop = remove commit from history
350 # m, mess = edit message without changing commit content
350 # m, mess = edit commit message without changing commit content
351 351 #
352 352 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
353 353
354 354 should also work if a commit message is missing
355 355 $ BUNDLE="$TESTDIR/missing-comment.hg"
356 356 $ hg init missing
357 357 $ cd missing
358 358 $ hg unbundle $BUNDLE
359 359 adding changesets
360 360 adding manifests
361 361 adding file changes
362 362 added 3 changesets with 3 changes to 1 files
363 363 (run 'hg update' to get a working copy)
364 364 $ hg co tip
365 365 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
366 366 $ hg log --graph
367 367 @ changeset: 2:bd22688093b3
368 368 | tag: tip
369 369 | user: Robert Altman <robert.altman@telventDTN.com>
370 370 | date: Mon Nov 28 16:40:04 2011 +0000
371 371 | summary: Update file.
372 372 |
373 373 o changeset: 1:3b3e956f9171
374 374 | user: Robert Altman <robert.altman@telventDTN.com>
375 375 | date: Mon Nov 28 16:37:57 2011 +0000
376 376 |
377 377 o changeset: 0:141947992243
378 378 user: Robert Altman <robert.altman@telventDTN.com>
379 379 date: Mon Nov 28 16:35:28 2011 +0000
380 380 summary: Checked in text file
381 381
382 382 $ hg histedit 0
383 383 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
384 384 $ cd ..
385 385
386 386 $ cd ..
387 387
388 388
389 389 Test to make sure folding renames doesn't cause bogus conflicts (issue4251):
390 390 $ hg init issue4251
391 391 $ cd issue4251
392 392
393 393 $ mkdir initial-dir
394 394 $ echo foo > initial-dir/initial-file
395 395 $ hg add initial-dir/initial-file
396 396 $ hg commit -m "initial commit"
397 397
398 398 Move the file to a new directory, and in the same commit, change its content:
399 399 $ mkdir another-dir
400 400 $ hg mv initial-dir/initial-file another-dir/
401 401 $ echo changed > another-dir/initial-file
402 402 $ hg commit -m "moved and changed"
403 403
404 404 Rename the file:
405 405 $ hg mv another-dir/initial-file another-dir/renamed-file
406 406 $ hg commit -m "renamed"
407 407
408 408 Now, let's try to fold the second commit into the first:
409 409 $ cat > editor.sh <<EOF
410 410 > #!/bin/sh
411 411 > cat > \$1 <<ENDOF
412 412 > pick b0f4233702ca 0 initial commit
413 413 > fold 5e8704a8f2d2 1 moved and changed
414 414 > pick 40e7299e8fa7 2 renamed
415 415 > ENDOF
416 416 > EOF
417 417
418 418 $ HGEDITOR="sh ./editor.sh" hg histedit 0
419 419 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
420 420 adding another-dir/initial-file (glob)
421 421 removing initial-dir/initial-file (glob)
422 422 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
423 423 1 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 saved backup bundle to $TESTTMP/issue4251/.hg/strip-backup/*-backup.hg (glob)
427 427
428 428 $ hg --config diff.git=yes export 0
429 429 # HG changeset patch
430 430 # User test
431 431 # Date 0 0
432 432 # Thu Jan 01 00:00:00 1970 +0000
433 433 # Node ID fffadc26f8f85623ce60b028a3f1ccc3730f8530
434 434 # Parent 0000000000000000000000000000000000000000
435 435 pick b0f4233702ca 0 initial commit
436 436 fold 5e8704a8f2d2 1 moved and changed
437 437 pick 40e7299e8fa7 2 renamed
438 438
439 439 diff --git a/another-dir/initial-file b/another-dir/initial-file
440 440 new file mode 100644
441 441 --- /dev/null
442 442 +++ b/another-dir/initial-file
443 443 @@ -0,0 +1,1 @@
444 444 +changed
445 445
446 446 $ hg --config diff.git=yes export 1
447 447 # HG changeset patch
448 448 # User test
449 449 # Date 0 0
450 450 # Thu Jan 01 00:00:00 1970 +0000
451 451 # Node ID 9b730d82b00af8a2766facebfa47cc124405a118
452 452 # Parent fffadc26f8f85623ce60b028a3f1ccc3730f8530
453 453 renamed
454 454
455 455 diff --git a/another-dir/initial-file b/another-dir/renamed-file
456 456 rename from another-dir/initial-file
457 457 rename to another-dir/renamed-file
458 458
459 459 $ cd ..
@@ -1,460 +1,460 b''
1 1 $ . "$TESTDIR/histedit-helpers.sh"
2 2
3 3 Enable obsolete
4 4
5 5 $ cat >> $HGRCPATH << EOF
6 6 > [ui]
7 7 > logtemplate= {rev}:{node|short} {desc|firstline}
8 8 > [phases]
9 9 > publish=False
10 10 > [experimental]
11 11 > evolution=createmarkers,allowunstable
12 12 > [extensions]
13 13 > histedit=
14 14 > rebase=
15 15 > EOF
16 16
17 17 $ hg init base
18 18 $ cd base
19 19
20 20 $ for x in a b c d e f ; do
21 21 > echo $x > $x
22 22 > hg add $x
23 23 > hg ci -m $x
24 24 > done
25 25
26 26 $ hg log --graph
27 27 @ 5:652413bf663e f
28 28 |
29 29 o 4:e860deea161a e
30 30 |
31 31 o 3:055a42cdd887 d
32 32 |
33 33 o 2:177f92b77385 c
34 34 |
35 35 o 1:d2ae7f538514 b
36 36 |
37 37 o 0:cb9a9f314b8b a
38 38
39 39
40 40 $ HGEDITOR=cat hg histedit 1
41 41 pick d2ae7f538514 1 b
42 42 pick 177f92b77385 2 c
43 43 pick 055a42cdd887 3 d
44 44 pick e860deea161a 4 e
45 45 pick 652413bf663e 5 f
46 46
47 47 # Edit history between d2ae7f538514 and 652413bf663e
48 48 #
49 49 # Commits are listed from least to most recent
50 50 #
51 51 # Commands:
52 52 # p, pick = use commit
53 53 # e, edit = use commit, but stop for amending
54 54 # f, fold = use commit, but combine it with the one above
55 55 # r, roll = like fold, but discard this commit's description
56 56 # d, drop = remove commit from history
57 # m, mess = edit message without changing commit content
57 # m, mess = edit commit message without changing commit content
58 58 #
59 59 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 $ hg histedit 1 --commands - --verbose <<EOF | grep histedit
61 61 > pick 177f92b77385 2 c
62 62 > drop d2ae7f538514 1 b
63 63 > pick 055a42cdd887 3 d
64 64 > fold e860deea161a 4 e
65 65 > pick 652413bf663e 5 f
66 66 > EOF
67 67 saved backup bundle to $TESTTMP/base/.hg/strip-backup/96e494a2d553-3c6c5d92-backup.hg (glob)
68 68 $ hg log --graph --hidden
69 69 @ 8:cacdfd884a93 f
70 70 |
71 71 o 7:59d9f330561f d
72 72 |
73 73 o 6:b346ab9a313d c
74 74 |
75 75 | x 5:652413bf663e f
76 76 | |
77 77 | x 4:e860deea161a e
78 78 | |
79 79 | x 3:055a42cdd887 d
80 80 | |
81 81 | x 2:177f92b77385 c
82 82 | |
83 83 | x 1:d2ae7f538514 b
84 84 |/
85 85 o 0:cb9a9f314b8b a
86 86
87 87 $ hg debugobsolete
88 88 d2ae7f538514cd87c17547b0de4cea71fe1af9fb 0 {cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b} (*) {'user': 'test'} (glob)
89 89 177f92b773850b59254aa5e923436f921b55483b b346ab9a313db8537ecf96fca3ca3ca984ef3bd7 0 (*) {'user': 'test'} (glob)
90 90 055a42cdd88768532f9cf79daa407fc8d138de9b 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
91 91 e860deea161a2f77de56603b340ebbb4536308ae 59d9f330561fd6c88b1a6b32f0e45034d88db784 0 (*) {'user': 'test'} (glob)
92 92 652413bf663ef2a641cab26574e46d5f5a64a55a cacdfd884a9321ec4e1de275ef3949fa953a1f83 0 (*) {'user': 'test'} (glob)
93 93
94 94
95 95 Ensure hidden revision does not prevent histedit
96 96 -------------------------------------------------
97 97
98 98 create an hidden revision
99 99
100 100 $ hg histedit 6 --commands - << EOF
101 101 > pick b346ab9a313d 6 c
102 102 > drop 59d9f330561f 7 d
103 103 > pick cacdfd884a93 8 f
104 104 > EOF
105 105 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
106 106 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107 $ hg log --graph
108 108 @ 9:c13eb81022ca f
109 109 |
110 110 o 6:b346ab9a313d c
111 111 |
112 112 o 0:cb9a9f314b8b a
113 113
114 114 check hidden revision are ignored (6 have hidden children 7 and 8)
115 115
116 116 $ hg histedit 6 --commands - << EOF
117 117 > pick b346ab9a313d 6 c
118 118 > pick c13eb81022ca 8 f
119 119 > EOF
120 120 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 121
122 122
123 123
124 124 Test that rewriting leaving instability behind is allowed
125 125 ---------------------------------------------------------------------
126 126
127 127 $ hg up '.^'
128 128 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
129 129 $ hg log -r 'children(.)'
130 130 9:c13eb81022ca f (no-eol)
131 131 $ hg histedit -r '.' --commands - <<EOF
132 132 > edit b346ab9a313d 6 c
133 133 > EOF
134 134 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
135 135 adding c
136 136 Make changes as needed, you may commit or record as needed now.
137 137 When you are finished, run hg histedit --continue to resume.
138 138 [1]
139 139 $ echo c >> c
140 140 $ hg histedit --continue
141 141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142
143 143 $ hg log -r 'unstable()'
144 144 9:c13eb81022ca f (no-eol)
145 145
146 146 stabilise
147 147
148 148 $ hg rebase -r 'unstable()' -d .
149 149 rebasing 9:c13eb81022ca "f"
150 150 $ hg up tip -q
151 151
152 152 Test dropping of changeset on the top of the stack
153 153 -------------------------------------------------------
154 154
155 155 Nothing is rewritten below, the working directory parent must be change for the
156 156 dropped changeset to be hidden.
157 157
158 158 $ cd ..
159 159 $ hg clone base droplast
160 160 updating to branch default
161 161 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
162 162 $ cd droplast
163 163 $ hg histedit -r '40db8afa467b' --commands - << EOF
164 164 > pick 40db8afa467b 10 c
165 165 > drop b449568bf7fc 11 f
166 166 > EOF
167 167 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
168 168 $ hg log -G
169 169 @ 10:40db8afa467b c
170 170 |
171 171 o 0:cb9a9f314b8b a
172 172
173 173
174 174 With rewritten ancestors
175 175
176 176 $ echo e > e
177 177 $ hg add e
178 178 $ hg commit -m g
179 179 $ echo f > f
180 180 $ hg add f
181 181 $ hg commit -m h
182 182 $ hg histedit -r '40db8afa467b' --commands - << EOF
183 183 > pick 47a8561c0449 12 g
184 184 > pick 40db8afa467b 10 c
185 185 > drop 1b3b05f35ff0 13 h
186 186 > EOF
187 187 0 files updated, 0 files merged, 3 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 $ hg log -G
191 191 @ 15:ee6544123ab8 c
192 192 |
193 193 o 14:269e713e9eae g
194 194 |
195 195 o 0:cb9a9f314b8b a
196 196
197 197 $ cd ../base
198 198
199 199
200 200
201 201 Test phases support
202 202 ===========================================
203 203
204 204 Check that histedit respect immutability
205 205 -------------------------------------------
206 206
207 207 $ cat >> $HGRCPATH << EOF
208 208 > [ui]
209 209 > logtemplate= {rev}:{node|short} ({phase}) {desc|firstline}\n
210 210 > EOF
211 211
212 212 $ hg ph -pv '.^'
213 213 phase changed for 2 changesets
214 214 $ hg log -G
215 215 @ 11:b449568bf7fc (draft) f
216 216 |
217 217 o 10:40db8afa467b (public) c
218 218 |
219 219 o 0:cb9a9f314b8b (public) a
220 220
221 221 $ hg histedit -r '.~2'
222 222 abort: cannot edit public changeset: cb9a9f314b8b
223 223 (see "hg help phases" for details)
224 224 [255]
225 225
226 226
227 227 Prepare further testing
228 228 -------------------------------------------
229 229
230 230 $ for x in g h i j k ; do
231 231 > echo $x > $x
232 232 > hg add $x
233 233 > hg ci -m $x
234 234 > done
235 235 $ hg phase --force --secret .~2
236 236 $ hg log -G
237 237 @ 16:ee118ab9fa44 (secret) k
238 238 |
239 239 o 15:3a6c53ee7f3d (secret) j
240 240 |
241 241 o 14:b605fb7503f2 (secret) i
242 242 |
243 243 o 13:7395e1ff83bd (draft) h
244 244 |
245 245 o 12:6b70183d2492 (draft) g
246 246 |
247 247 o 11:b449568bf7fc (draft) f
248 248 |
249 249 o 10:40db8afa467b (public) c
250 250 |
251 251 o 0:cb9a9f314b8b (public) a
252 252
253 253 $ cd ..
254 254
255 255 simple phase conservation
256 256 -------------------------------------------
257 257
258 258 Resulting changeset should conserve the phase of the original one whatever the
259 259 phases.new-commit option is.
260 260
261 261 New-commit as draft (default)
262 262
263 263 $ cp -r base simple-draft
264 264 $ cd simple-draft
265 265 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
266 266 > edit b449568bf7fc 11 f
267 267 > pick 6b70183d2492 12 g
268 268 > pick 7395e1ff83bd 13 h
269 269 > pick b605fb7503f2 14 i
270 270 > pick 3a6c53ee7f3d 15 j
271 271 > pick ee118ab9fa44 16 k
272 272 > EOF
273 273 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
274 274 adding f
275 275 Make changes as needed, you may commit or record as needed now.
276 276 When you are finished, run hg histedit --continue to resume.
277 277 [1]
278 278 $ echo f >> f
279 279 $ hg histedit --continue
280 280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
281 281 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
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 $ hg log -G
287 287 @ 22:12e89af74238 (secret) k
288 288 |
289 289 o 21:636a8687b22e (secret) j
290 290 |
291 291 o 20:ccaf0a38653f (secret) i
292 292 |
293 293 o 19:11a89d1c2613 (draft) h
294 294 |
295 295 o 18:c1dec7ca82ea (draft) g
296 296 |
297 297 o 17:087281e68428 (draft) f
298 298 |
299 299 o 10:40db8afa467b (public) c
300 300 |
301 301 o 0:cb9a9f314b8b (public) a
302 302
303 303 $ cd ..
304 304
305 305
306 306 New-commit as draft (default)
307 307
308 308 $ cp -r base simple-secret
309 309 $ cd simple-secret
310 310 $ cat >> .hg/hgrc << EOF
311 311 > [phases]
312 312 > new-commit=secret
313 313 > EOF
314 314 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
315 315 > edit b449568bf7fc 11 f
316 316 > pick 6b70183d2492 12 g
317 317 > pick 7395e1ff83bd 13 h
318 318 > pick b605fb7503f2 14 i
319 319 > pick 3a6c53ee7f3d 15 j
320 320 > pick ee118ab9fa44 16 k
321 321 > EOF
322 322 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
323 323 adding f
324 324 Make changes as needed, you may commit or record as needed now.
325 325 When you are finished, run hg histedit --continue to resume.
326 326 [1]
327 327 $ echo f >> f
328 328 $ hg histedit --continue
329 329 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
330 330 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
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 $ hg log -G
336 336 @ 22:12e89af74238 (secret) k
337 337 |
338 338 o 21:636a8687b22e (secret) j
339 339 |
340 340 o 20:ccaf0a38653f (secret) i
341 341 |
342 342 o 19:11a89d1c2613 (draft) h
343 343 |
344 344 o 18:c1dec7ca82ea (draft) g
345 345 |
346 346 o 17:087281e68428 (draft) f
347 347 |
348 348 o 10:40db8afa467b (public) c
349 349 |
350 350 o 0:cb9a9f314b8b (public) a
351 351
352 352 $ cd ..
353 353
354 354
355 355 Changeset reordering
356 356 -------------------------------------------
357 357
358 358 If a secret changeset is put before a draft one, all descendant should be secret.
359 359 It seems more important to present the secret phase.
360 360
361 361 $ cp -r base reorder
362 362 $ cd reorder
363 363 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
364 364 > pick b449568bf7fc 11 f
365 365 > pick 3a6c53ee7f3d 15 j
366 366 > pick 6b70183d2492 12 g
367 367 > pick b605fb7503f2 14 i
368 368 > pick 7395e1ff83bd 13 h
369 369 > pick ee118ab9fa44 16 k
370 370 > EOF
371 371 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
372 372 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 373 0 files updated, 0 files merged, 0 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 $ hg log -G
378 378 @ 21:558246857888 (secret) k
379 379 |
380 380 o 20:28bd44768535 (secret) h
381 381 |
382 382 o 19:d5395202aeb9 (secret) i
383 383 |
384 384 o 18:21edda8e341b (secret) g
385 385 |
386 386 o 17:5ab64f3a4832 (secret) j
387 387 |
388 388 o 11:b449568bf7fc (draft) f
389 389 |
390 390 o 10:40db8afa467b (public) c
391 391 |
392 392 o 0:cb9a9f314b8b (public) a
393 393
394 394 $ cd ..
395 395
396 396 Changeset folding
397 397 -------------------------------------------
398 398
399 399 Folding a secret changeset with a draft one turn the result secret (again,
400 400 better safe than sorry). Folding between same phase changeset still works
401 401
402 402 Note that there is a few reordering in this series for more extensive test
403 403
404 404 $ cp -r base folding
405 405 $ cd folding
406 406 $ cat >> .hg/hgrc << EOF
407 407 > [phases]
408 408 > new-commit=secret
409 409 > EOF
410 410 $ hg histedit -r 'b449568bf7fc' --commands - << EOF
411 411 > pick 7395e1ff83bd 13 h
412 412 > fold b449568bf7fc 11 f
413 413 > pick 6b70183d2492 12 g
414 414 > fold 3a6c53ee7f3d 15 j
415 415 > pick b605fb7503f2 14 i
416 416 > fold ee118ab9fa44 16 k
417 417 > EOF
418 418 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
419 419 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
420 420 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
421 421 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 422 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
423 423 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 424 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
425 425 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 426 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 427 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 428 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
429 429 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 430 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 431 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/58019c66f35f-96092fce-backup.hg (glob)
432 432 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/83d1858e070b-f3469cf8-backup.hg (glob)
433 433 saved backup bundle to $TESTTMP/folding/.hg/strip-backup/859969f5ed7e-d89a19d7-backup.hg (glob)
434 434 $ hg log -G
435 435 @ 19:f9daec13fb98 (secret) i
436 436 |
437 437 o 18:49807617f46a (secret) g
438 438 |
439 439 o 17:050280826e04 (draft) h
440 440 |
441 441 o 10:40db8afa467b (public) c
442 442 |
443 443 o 0:cb9a9f314b8b (public) a
444 444
445 445 $ hg co 18
446 446 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
447 447 $ echo wat >> wat
448 448 $ hg add wat
449 449 $ hg ci -m 'add wat'
450 450 created new head
451 451 $ hg merge 19
452 452 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 453 (branch merge, don't forget to commit)
454 454 $ hg ci -m 'merge'
455 455 $ echo not wat > wat
456 456 $ hg ci -m 'modify wat'
457 457 $ hg histedit 17
458 458 abort: cannot edit history that contains merges
459 459 [255]
460 460 $ cd ..
@@ -1,147 +1,147 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 51 # f, fold = use commit, but combine it with the one above
52 52 # r, roll = like fold, but discard this commit's description
53 53 # d, drop = remove commit from history
54 # m, mess = edit message without changing commit content
54 # m, mess = edit commit message without changing commit content
55 55 #
56 56 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 57 $ cd ..
58 58
59 59 show the error from unrelated repos
60 60 $ cd r3
61 61 $ HGEDITOR=cat hg histedit --outgoing ../r | grep -v comparing | grep -v searching
62 62 abort: repository is unrelated
63 63 [1]
64 64 $ cd ..
65 65
66 66 show the error from unrelated repos
67 67 $ cd r3
68 68 $ HGEDITOR=cat hg histedit --force --outgoing ../r
69 69 comparing with ../r
70 70 searching for changes
71 71 warning: repository is unrelated
72 72 pick 2a4042b45417 0 g
73 73 pick 68c46b4927ce 1 h
74 74 pick 51281e65ba79 2 i
75 75
76 76 # Edit history between 2a4042b45417 and 51281e65ba79
77 77 #
78 78 # Commits are listed from least to most recent
79 79 #
80 80 # Commands:
81 81 # p, pick = use commit
82 82 # e, edit = use commit, but stop for amending
83 83 # f, fold = use commit, but combine it with the one above
84 84 # r, roll = like fold, but discard this commit's description
85 85 # d, drop = remove commit from history
86 # m, mess = edit message without changing commit content
86 # m, mess = edit commit message without changing commit content
87 87 #
88 88 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 89 $ cd ..
90 90
91 91 test sensitivity to branch in URL:
92 92
93 93 $ cd r2
94 94 $ hg -q update 2
95 95 $ hg -q branch foo
96 96 $ hg commit -m 'create foo branch'
97 97 $ HGEDITOR=cat hg histedit --outgoing '../r#foo' | grep -v comparing | grep -v searching
98 98 pick f26599ee3441 6 create foo branch
99 99
100 100 # Edit history between f26599ee3441 and f26599ee3441
101 101 #
102 102 # Commits are listed from least to most recent
103 103 #
104 104 # Commands:
105 105 # p, pick = use commit
106 106 # e, edit = use commit, but stop for amending
107 107 # f, fold = use commit, but combine it with the one above
108 108 # r, roll = like fold, but discard this commit's description
109 109 # d, drop = remove commit from history
110 # m, mess = edit message without changing commit content
110 # m, mess = edit commit message without changing commit content
111 111 #
112 112 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 113
114 114 test to check number of roots in outgoing revisions
115 115
116 116 $ hg -q outgoing -G --template '{node|short}({branch})' '../r'
117 117 @ f26599ee3441(foo)
118 118
119 119 o 652413bf663e(default)
120 120 |
121 121 o e860deea161a(default)
122 122 |
123 123 o 055a42cdd887(default)
124 124
125 125 $ HGEDITOR=cat hg -q histedit --outgoing '../r'
126 126 abort: there are ambiguous outgoing revisions
127 127 (see "hg help histedit" for more detail)
128 128 [255]
129 129
130 130 $ hg -q update -C 2
131 131 $ echo aa >> a
132 132 $ hg -q commit -m 'another head on default'
133 133 $ hg -q outgoing -G --template '{node|short}({branch})' '../r#default'
134 134 @ 3879dc049647(default)
135 135
136 136 o 652413bf663e(default)
137 137 |
138 138 o e860deea161a(default)
139 139 |
140 140 o 055a42cdd887(default)
141 141
142 142 $ HGEDITOR=cat hg -q histedit --outgoing '../r#default'
143 143 abort: there are ambiguous outgoing revisions
144 144 (see "hg help histedit" for more detail)
145 145 [255]
146 146
147 147 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now