Joda Maki
Joda Maki

Reputation: 5869

Mercurial Reverting Back, then Reapplying Changesets

I have commits A, B, C, D, and E. I realize that something very bad happened in commit B, so I want to revert back to A, make the change correctly this time that screwed up B before, and then reapply C, D, and E automatically.

You may be wondering why I don't revert back to B and make the fix there, then remerge back in to E (is this ever a good idea?). The reason is not well understood by me, but it has something to do with the problem occurring in a set of special visual studio files (that should only be edited via some GUI screens in visual studio) that don't play well with simply correcting the file after an error occurred... I would give more details if I knew them

Upvotes: 3

Views: 1963

Answers (3)

bambams
bambams

Reputation: 765

You can also use the HisteditExtension (instead of MqExtension). MqExtension is much more powerful, but it's also much more complicated, I think. HisteditExtension is a bit more like git rebase --interactive.

# Ordinarily it would be something like (I'd normally do -r -5, instead):
hg histedit d0844102a010

Your text editor will be opened with a file that looks something like this:

pick d0844102a010 A
pick a9448f0ba534 B
pick b754f9f2513b C
pick 736f7f2363ff D
pick 05bb58f48597 E

# Edit history between d0844102a010 and 05bb58f48597
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  f, fold = use commit, but fold into previous commit
#  d, drop = remove commit from history
#

Each line corresponds to a commit. The first word refers to a command to be applied to that commit. The default "pick" just keeps the commit as is, unchanged. Use "edit" to make changes (including commit log changes), "fold" to combine it with the previous commit, and "drop" to delete it entirely.

In your case, you'd probably just need to change the first line to "edit".

Remember that you must use hg histedit --continue instead of hg commit (if you're "editing" or if there's a merge conflict). :) If you get conflicts and things aren't looking good and you just want to cancel then you can use hg histedit --abort.

# Fix up files...
vim foo bar baz

# Finished; apply the changes (and pray for a clean merge ;).
hg histedit --continue

Edit history at own risk, of course. I recommend that you create a backup tarball or zip of your source tree prior to editing history until you're familiar with the commands.

Upvotes: 3

Robert Jeppesen
Robert Jeppesen

Reputation: 7877

Just make a backout of what you did in B and commit it as F. This way, history will be intact, and your peers will get the change without having to know about it.

If B is a service release, do make the change there and merge it into F afterwards.

Upvotes: 5

Tim Henigan
Tim Henigan

Reputation: 62188

This can be done using Mercurial Queues (mq). You want to:

  1. Import changesets B through E to mq
  2. Unapply changesets C through E
  3. Fix changeset B and refresh the patch
  4. Reapply C through E
  5. Finalize the patches

This is done as follows:

  1. cd <project>
  2. hg qinit
  3. hg qimport --rev B:E
  4. hg qpop --all
  5. hg qpush <patch name for B>
  6. ...fix the problems you found in B
  7. hg qrefresh
  8. hg qpush --all
  9. hg qfinish --applied

This all assumes that B through E have not been pushed to any public repositories. If they have already been pushed, then your best bet is to simply fix the problem in a new changeset (F).

Upvotes: 4

Related Questions