1

At work I use my working E-Mail address when creating commit messages, but at home I use my private account for creating commits.
I forgot to change back to my working email address before committing and pushing and then I realized that I pushed a Merge commit with my private email address and I would like to basically modify this commit message to my working email address.
In Git it's extremely easy when I needed to rebase from certain child commit and then force push it and it works perfectly fine, but in Mercurial it seems to be more restricted and I'm pretty sure there must be a way, as I saw many similar questions in Stackoverflow like amending commit, I also read some articles using some of the famous Mercurial extensions like: histedit, evolve and rebase.
Some other things I tried also failed:

  • Using the hgext.convert creates a new repository which I don't want and it also failed to finish in one of my changesets.
  • Using histedit throws an error that it can't modify public changes.
  • Using hgext.mq throws an error that it cannot import merge revision.

Why is it so complicated in mercurial?
One of the articles that I also checked is this one:
https://book.mercurial-scm.org/read/changing-history.html

Note that I already understand the meaning of it and how dangerous it can be when others working on this repository, so please try to save this from me :)

Another note that I normally use SourceTree and sometimes command line, so if you're the kind of SourceTree person, I would also be happy to hear your solutions.

And please try to avoid marking this question as duplicate, as I've already used the search and have seen many different solutions that didn't really clarified this.

Is there a similar way as in Git to basically rebase from specific commit and force push so I can change my email back to my working email address?

Thanks in advance for your time and effort to help! :)

EDIT 1:
My attempt to reproduce it on test public repository with pushed changes based on @DaveInCaz Solution, but failed:
Steps:

// Updating to the wrong commit message.
hg update -r B

// Throwing all changes that has been made on the wrong commit.
hg strip B

// Re-creating the Merge again with the correct mail address.
hg merge new-feature

// Assuming now it creates revision B2
hg commit -m 'Merge'

// Pulling the changes again
hg pull

// Making the correct commits afterwards to be the children from B2 instead B.
hg rebase -s C -d B2

// Removing the old B
hg strip B

And now of course if I try hg push, I get the following abort: push creates new remote head.

  • 2
    Possible duplicate of [Can I change the username on a mercurial changeset?](https://stackoverflow.com/questions/732819/can-i-change-the-username-on-a-mercurial-changeset) – phd Jul 03 '18 at 10:38
  • @phd, Thanks for the reference! But I've already seen this one and tried all of their solutions. Using the `hgext.convert` creates a new repository and also failed to finish in one of my changesets. Using `histedit` throws an error that it can't modify public changes. Using `hgext.mq` throws an error that it cannot import merge revision. Why is it so complicated in mercurial? In Git I simply rebase and force push and finito. Do you have any idea? –  Jul 03 '18 at 12:10
  • hg is not git. Mercurial values history too much. If you want git — use git. I hated hg from the day one and when 3 years later I discovered git — I switched all my development at the job and at home and never looked back. – phd Jul 03 '18 at 12:50
  • @phd To be completely honest, I totally agree with what you say! In my private projects I only use Git instead of Mercurial due to these restrictions. I wish they were more flexible on that side, as the source-control suppose to serve you and not we to satisfy the source-control and that it makes us such a hard time for a simple task. I don't like to edit history and it kind of kills the idea of using source-control, but there are certain situations where you simply want to do that. I guess that's what differentiate between the two. –  Jul 03 '18 at 13:06
  • 1
    The failure of the convert extension would be a bug. The evolve extension should be able to do this (cleanly, by outdating the original published commit; you'll have both the original commit and its replacement, which in this case is probably unnecessary for your purposes). You can also forcibly change the *phase* of a commit from published to draft or secret, then use histedit. That's probably the simplest and best method. – torek Jul 03 '18 at 14:18
  • 1
    I see you put the [tag:git] tag back. I don't think it belongs here: your question is not *about* Git, you are just using Git as a reference for how you'd like things to work. (Meanwhile, have you tried forcing the phase back to draft? Note: you'll probably also want to do this on the target of any push. This is where evolve is superior: lots less manual fiddling.) – torek Jul 03 '18 at 16:53
  • @torek Thanks for the heads up! :) Yea, sorry about it, I brought the `git` tag back as I think it it brings more attention from the people who uses git and are experienced with version control in general and are already aware of the trick to do it in git in comparison to hg. I'm not sure if I understand you correctly by `forcing the phase back to the draft?`. –  Jul 05 '18 at 12:52
  • @GiladReich the other question may be a duplicate even if you didn't like any of the answers there... – StayOnTarget Jul 05 '18 at 13:51
  • @DaveInCaz Thanks for your answer! P.S: I'll try out your solution on some test repo pushed to public and see if I encounter any problems reproducing the situation and let you know :) –  Jul 05 '18 at 14:04
  • 1
    You can use `hg phase -f draft ` to set the given revisions back to draft phase. You can then edit them as you like. Note that if you have transferred them to another hg repository, though, you must remove them from that repository as well. If they are at the tip you can strip them safely; if not, be sure whatever repository you are manipulating has all subsequent commits, so that you can strip everything and put it back from the surgically altered now-correct repo. (And of course if they're not in draft phase that normally means you *have* transferred them.) – torek Jul 05 '18 at 18:47
  • @torek, Thanks for the heads up. I'll have a look about phases and see if I can reproduce it. Reading here: https://www.mercurial-scm.org/wiki/Phases#Available_Phases –  Jul 06 '18 at 12:51
  • @torek My published changes have their phase set to public. When I do `hg phase -f --draft 10:13` it changes them locally perfectly fine, so then I do `hg push` and then it changes them back to `public` from `draft` on my local machine and the repository does not get affected. I assume because published changes that are already public cannot be modified at all? I read somewhere that that's the reason people set them to be `secret` by default using the `mq` extension. –  Jul 06 '18 at 14:04
  • Right: once you have them in draft or secret phase, you can edit them. However, draft-phase commits get pushed and in the process become public-phase. That's precisely *how* Mercurial keeps you from editing published history. If you force local commits back to draft (or secret) you can change them again, but the ones you published are loose, just as would happen in Git. Unlike Git, you can't remove them with a force-push, you must log in on the other machine and strip them. – torek Jul 06 '18 at 14:31
  • @torek, Thanks for the info's. Does `evolve` can by any chance help in this situation? Or is there any other way? –  Jul 06 '18 at 15:11
  • 1
    Evolve *should* help, though I've never used it. Functionally, the idea behind evolve is that a new commit can *outdate* a previous commit, hiding it from view. The old commit remains, and is transferred to all other clones, but all clones that are using the evolve extension understand that the outdated commit is outdated and the new replacement is the one to use. The drawback here is that every repository has to support the extension. – torek Jul 06 '18 at 15:27
  • Thanks a lot for the help @torek, even though I haven't got my problem sorted, I've learned a lot about mercurial in depth due to the situation. I guess I should be extra cautious next time when working with mercurial repositories and this also convinces me in my private projects sticking to git. A lesson learned is to always define manually the first time in the `hgrc` file the username and email when cloning a new repository at work without depending on the global email and needing to change between the two. –  Jul 06 '18 at 15:37

1 Answers1

2

A possible solution is to re-create the commit you want to change, and then rebase anything which followed it to that new commit.

This will only work if you know all the clones that the unwanted commit has propagated to, AND you have the ability to modify them directly.


Let's assume you have the following history:

A-B-C-D

where B was the commit with the error.

Steps:

  1. Update to A
  2. Revert local changes to be the same as B

    (e.g., in THG you can right-click in the history and select "Revert All Files...")

  3. Fix the original problem - i.e., set the email address correctly

  4. Commit. Now your history looks like this:

    A-B-C-D \ B'

  5. Rebase C to B' (hg rebase ... command - also THG has screens for this). So now you have:

    A-B \ B'-C-D

  6. Remove the original B using hg strip (LOCAL clone only)

    A-B'-C-D

  7. Remove B, C, and D on ALL OTHER CLONES using hg strip (1)

  8. Push


(1) This is only possible if you have access to those clones and no one has committed anything following revision D. Or if they have, you can rebase it or otherwise get it out of the way.

Also just be cautious with strip because it is destructive.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
  • Hey @DaveInCaz, I assume you base your answer on repository without published changes? I edited the original question so you can see my steps to try and reproduce what you say here. –  Jul 06 '18 at 12:39
  • Step 7 addressed the issue of having already pushed local changes to other clones (publishing). In the question edit it doesn't look like that was taken care of before pulling again. – StayOnTarget Jul 09 '18 at 11:31