Edit Mercurial Earlier Commit Message - TortoiseHg - mercurial

Is there a way to edit a commit message in Mercurial on a commit after other commits have occured using TortoiseHg? I've read these posts:
How to edit incorrect commit message in Mercurial?
Mercurial: how to amend the last commit?
and have been able to update a "regular" commit message when it is the latest commit on a branch (using TortoiseHg). However, I haven't been able to figure out how to edit a commit message when other commits have occurred after the one I want to edit. It always edits the last commit.
Based on Ed Cottrell's comment, I did a test where I made two commits without pushing to the central repo, and I still have the same issue - only the last commit message can be edited.
EDIT: I should clarify that I am looking to update a changeset that has been pushed.

Histedit extension (bundled with TortoiseHG now) has a mess command for changing the commit message of historical changesets.
Unfortunately, this command is not supported by the TortoiseHG GUI so you need to run the command from command line.

As long as the change in question is local and hasn't been pushed anywhere, it is possible.
The commit message is used to compute the globally unique hash id that is used for all repositories to determine whether or not they already have a changeset. If you change the commit message, you change the unique hash id and every repo will see it as a "new" changeset. All other repositories that had the old changeset will try to get the new one and ask that you merge it with itself.... This is not a good thing, so the short answer to your question is "don't do it".
If you could definitively purge that change from all other repos, so that only the local copy is left you could essentially get to the "draft" state. Note that if any repo has the "old" changeset, it will be pushed to the central repo someday and cause the mess that we are trying to avoid.
If the changeset is still local (e.g. in draft status), you can use hg commit --amend if it is the parent of the working directory.
If there are changes after it, I would use mq and hg qimport all the changes down to and including the one where you want to edit the commit message. hg qpop -a and then hg qpush to get to the patch that represents the changeset you want to edit. Then hg qrefresh -e and make your changes. Then just hg qfin -a and you should be good to go.

The advice from Edward is good — if you've pushed your changes to another repository, you should consider them set in stone and not update the commit message or any other aspect of them.
However, we're working on changing this in Mercurial. There is an experimental extension that will allow you to do more extensive history editing and push those edits to other repositories. It is called the Evolve Extension and it enables some behavior that is partly in the core of Mercurial and partly outside core.
When using evolve, you can edit the second-to-last commit message like this
$ hg update .^
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg commit --amend -m 'new commit message'
1 new unstable changesets
$ hg stabilize
more:[5] old tip changeset
atop:[6] new commit message
The extension allows you to do this as long as the changesets are in the draft phase. To keep them in the draft phase after pushing them somewhere, the repository you push to need to be configured as a non-publishing repository. You can read more about this in the Changeset Evolution Documentation.

Related

Safe way to purge history - Mercurial

I cloned a project to my local directory and made a lot of changes. My current goal is to push my changed code to a new branch in the remote repository. And eventually this new branch will be merged back to default.
My problem is, because of some stupid effort in the past a few weeks to try to recover some missing files, I end up with a few branch names that I don't want being shown in public.
Here's what I have:
$hg branches
dev-v02 197:xxxxx
dev2 194:xxxxx
dev 183:xxxxx
qa 189:xxxxx
$hg branch
dev-v02
My question is, if I push my current branch dev-v02 to the remote repository by "hg push --new-branch", and this branch later get merged back to default, will the unwanted branches show up in history of default? And if so, is there a safe way to purge them?
I do NOT want to discard my changes. I just don't want the unwanted branches showing up in "hg branches" or "hg his" commands by whoever later clones the project from the remote repository. I searched online and found "hg strip" but I couldn't tell from the article if it would also remove the changes I've made. Thanks.
Edit: I just cloned my local repository by "hg clone -r 197 original-dir dest-dir" as suggested by both kevin and chessbot and now hg branches shows:
dev-02 192:xxxxx
qa 187:xxxxx (inactive)
I guess "qa" remains because I had pushed it to the remote as a QA branch and closed it later, and I just have to live with that. I will push from this new directory from now on. Thanks guys for your help.
Try hg push --new-branch -b dev-v02 to specify that you're pushing only that branch.
(See: https://www.mercurial-scm.org/repo/hg/help/push)
Another thing you could do: Clone the repository locally on your machine, strip out the branches you don't want, and then push that clone to the server. Then you retain your history locally without pushing it to everyone else.
It depends.
Branches are permanently associated with a commit. The branch is part of the commit, and contributes to the hash. Changing the branch of a commit in the past would alter all commit hashes from that point forward. This is quite different from Git, where a branch is little more than an ephemeral pointer to a HEAD. Such pointers are implemented in Mercurial as bookmarks.
If the unwanted branches appear on commits which are (ancestors of) commits you want to publish, there is very little you can do, short of recreating the history with all-new hashes. This could (for instance) be done with hg export and hg import, along with local cloning and (probably) a certain amount of shell scripting. More efficiently, you could use the convert extension to automate the process. Since this changes commit hashes, it will likely cause serious problems if any of the commits have already been distributed publicly.
If you have no interest in sharing the offending commits, you can simply not publish them. This can be done with selective pushing. However, since you'll always have to manually exclude those commits every time you push, it's probably safer to clone and strip (or clone selectively with the -r flag). You can then push from your partial clone with impunity. Assuming you have a sufficiently recent version of Mercurial, you can also force the commits into the secret phase, so that they will not be pushed:
hg phase -fs revisions
You don't want to use hg strip, because it permanently removes the commits from the history (see Editing History in the Mercurial wiki)
If I were you, I would close the branches instead:
hg up -C badbranch
hg commit --close-branch -m 'close badbranch, this approach never worked'
hg up -C default
(source: Pruning branches in the Mercurial wiki)
After closing a branch, hg branches doesn't show it anymore.
If you do want to see closed branches, use the -c parameter:
hg branches -c
Disadvantage:
hg his still shows closed branches.
You could use the -b parameter though, to show only the default branch:
hg his -b default

Mercurial (hg) equivalent of git reset (--mixed or --soft)

what would be an equivalent mercurial command (or workflow) for
git reset --mixed HEAD^
or
git reset --soft HEAD^
i.e. I want leave the working tree intact but get the repository back into the state it was before the last commit. Surprisingly I did not find anything useful on stackoverflow or with google.
Note that I cannot use
hg rollback
as I've done some history rewriting using HistEdit after the last commit.
Added to clarify:
After some rebasing and history editing I had ended up with A<--B<--C. Then I used HistEdit to squash B and C together, obtaining A<--C'. Now I want to split up the commit C' (I committed the wrong files in B). I figured the easiest way to do this was to get the repository back to state A (which technically never existed in the repository because of all the rebasing and history editing before hand) and the working tree to the state of C' and then doing two commits.
The right way to replicate git reset --soft HEAD^ (undo the current commit but keep changes in the working copy) is:
hg strip --keep -r .
-1 will only work if the commit you want to strip is the very last commit that entered the repository. . refers to the currently checked out commit, which is the closest equivalent Mercurial has to Git's HEAD.
Note that if . has descendants, those will get stripped away too. If you'd like to keep the commit around, then once you have the commit ID, you can instead:
hg update .^
hg revert --all -r <commit id>
This will update to the commit's parent and then replace the files in the working copy with the versions at that commit.
I believe the more modern and simpler way to do this now is hg uncommit. Note this leaves behind an empty commit which can be useful if you want to reuse the commit message later. If you don't, use hg uncommit --no-keep to not leave the empty commit.
hg uncommit [OPTION]... [FILE]...
uncommit part or all of a local changeset
This command undoes the effect of a local commit, returning the affected
files to their uncommitted state. This means that files modified or
deleted in the changeset will be left unchanged, and so will remain
modified in the working directory.
If no files are specified, the commit will be left empty, unless --no-keep

View/undo a Hg commit?

How do I view commits that are about to be pushed?
I'd made a local commit. Pull a change. And no it requires a merge.
I prefer not to merge and would like to undo the commit,
Pull,
Update changes,
Then commit again.
How do I do it since rollback only undo the last command which is pull?
That's really the way Mercurial works, and you shouldnt fight it in the name of a straight linear history, but there are tools that can edit history. Enable the rebase extension and just run hg rebase after your pull. It will move your local commit to the tip automatically in the simple case you described.
How do I view commits that are about to be pushed?
Use hg outgoing. That shows what hg push would have sent to the server. The opposite command is hg incoming, which shows what hg pull would have retrieved.
I'd made a local commit. Pull a change. And no it requires a merge. I prefer not to merge and would like to undo the commit, Pull, Update changes, Then commit again.
Like Mark says, you're looking for the rebase extension. Enable it with
[extensions]
rebase =
in your config file and then run
$ hg pull
$ hg rebase
to move your local work (this can be multiple changesets, not just a single as in your work around!) on top of the changesets you just pulled down.
How do I do it since rollback only undo the last command which is pull?
Please don't use hg rollback as a general undo mechanism. It's a low-level command that should not be used as much as it is, especially not by new users. The rollback command removes the last transaction from the repository — a transaction in Mercurial is typically the last changeset you made or the last changesets (plural) you pulled into the repository.

After pushing to a review repository, "abort: can't rebase immutable changeset" on rebase

We have a code review repository where people hg push -f all sorts of stuff. After reviews are complete, we pull from the project's central repository, rebase, and push. I recently upgraded to mercurial 2.1 and get this message:
abort: can't rebase immutable changeset 43ab8134e7af
(see hg help phases for details)
when I try to hg pull --rebase from the central repository. How do I fix it?
In the review repository's .hg/hgrc file, add these lines:
[phases]
publish = False
The problem is due to a new feature in mercurial 2.1 called phases. It's great. Here is a nice introduction to its use.
To make the changesets in question mutable right now, use hg phase -f -d REV to force REV to be mutable again. Once the hgrc file has been changed, you shouldn't have to do that any more.
As a side note, hg push -f is lame. Make an alias hg review that pushes with -f to that repository.
I don't think disabling phase support on the server is the correct solution, but your problem sounds weird.
Pull --rebase should rebase your local changes, on top of the remote changes, which should be allowed, even if phases are supported by the client, as long as these changes have not been seen by anyone else, eg. they haven't been pushed out anywhere.
Is it possible that you have already pushed your your own changes, to somewhere else (which set them to public phase), and after that tried pulling from the testing repo? Because then, this is the correct behaviour that you are seeing.
Most of the time it is a bad idea to mess with phases manually (with hg phase -f), because it can easily lead to a history rewrite, which can lead to duplicated changesets, or various errors when other people try to pull/push. If a changeset became marked as public (as in your case), it probably happened for a good reason.
I've encountered such behaviour with collapsed rebase. Phasing out back to draft hadn't helped me. So I've just pulled up (hg pull -u) to sync with remote repo, then just grafted the problem commit (hg graft <problem_commit>) and then amended this very new commit.

How do you delete a commit in Mercurial?

I want to completely delete a Mercurial commit as if it was never entered in the repository and move back to my prior commit.
Is this possible?
If it was your last commit and you haven't pushed it anywhere, you can do that with rollback. Otherwise, no. Not really. Time to change your passwords.
Edit: It has been pointed out that you can clone from an older revision and merge in the changes you want to keep. That's also true, unless you have pushed it to a repo you don't control. Once you push, your data is very likely to be very hard to get back.
You can try to remove mq info about your commit.
For this you need to go File->Settings->Extensions.
There check mq and restart gui.
After that just right click on unneeded commit and
ModifyHistory->Strip
To edit the history I would use the Histedit Extension extension.
hg histedit 45:c3a3a271d11c
However keep in mind this only makes sense in a situation where you have not yet pushed the commits to the public repository, you own the public repository and/or you can account for all the clones out there. If you receive the following error:
abort: can't rebase immutable changeset 43ab8134e7af
It means that Mecurial thinks this is a public changeset (see phases) that has already been pushed - you can force it to be a draft again doing:
hg phase -f -d 45:c3a3a271d11c
I encounter this fairly often. I make a commit and then pull to push. But then there is something incoming that makes my newly made commit unnecessary. A plain hg rollback isn't enough because it only undoes the pull...
This is the thing to do:
hg strip <rev>
Things are painless when you don't push your changesets anywhere.
If it's more than one commit and/or you already pushed it somewhere else, you can clone your repository and specify the last changeset that should be cloned.
See my answer here how to do this:
Mercurial: Fix a borked history
If you only committed locally and didn't push, you can just create a clone locally (as described in my link) and you're done.
If you already pushed to some remote repository, you would have to replace that with your clone.
Of course it depends if you are able (or allowed) to do this.
You can use "hg backout" to do a reverse merge basically. All options are discussed in the freely available book "Mercurial: The Definitive Guide":
http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html
If using tortoise you can use modify history > strip...
Yes. Unless I am mistaken, as of v2.3 (rel. 2012/08/01) you can use the HisteditExtension with a drop command to drop a commit, along with strip or backout to remove changes.
A simple Google search on the feature: https://www.google.com/webhp#q=histedit+drop
In 2022 I do use evolve extension. It is one of the best extensions for this purpose.
To prune unwanted changeset, if you for example did a quick hack to get the code working:
$ echo 'debug hack' >> file1.c
$ hg commit -m 'debug hack'
Now you have a proper patch you can do hg prune .:
$ hg prune .
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
working directory is now at 2a39221aaebb
1 changesets pruned
If you push the change to the remote repository you will find only obsolescence markers:
$ hg push
searching for changes
no changes found
remote: 1 new obsolescence markers
To check the changes to your local repo you can pull from the remote one:
$ hg pull
pulling from ssh://userid#server/repo
searching for changes
no changes found