Mercurial: how to amend the last commit?
Solution 1
With the release of Mercurial 2.2, you can use the --amend
option with hg commit
to update the last commit with the current working directory
From the command line reference:
The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by hg status, if there are any. The old commit is stored in a backup bundle in .hg/strip-backup (see hg help bundle and hg help unbundle on how to restore it).
Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit.
The great thing is that this mechanism is "safe", because it relies on the relatively new "Phases" feature to prevent updates that would change history that's already been made available outside of the local repository.
Solution 2
You have 3 options to edit commits in Mercurial:
hg strip --keep --rev -1
undo the last (1) commit(s), so you can do it again (see this answer for more information).Using the MQ extension, which is shipped with Mercurial
Even if it isn't shipped with Mercurial, the Histedit extension is worth mentioning
You can also have a look on the Editing History page of the Mercurial wiki.
In short, editing history is really hard and discouraged. And if you've already pushed your changes, there's barely nothing you can do, except if you have total control of all the other clones.
I'm not really familiar with the git commit --amend
command, but AFAIK, Histedit is what seems to be the closest approach, but sadly it isn't shipped with Mercurial. MQ is really complicated to use, but you can do nearly anything with it.
Solution 3
GUI equivalent for hg commit --amend
:
This also works from TortoiseHG's GUI (I'm using v2.5):
Swich to the 'Commit' view or, in the workbench view, select the 'working directory' entry. The 'Commit' button has an option named 'Amend current revision' (click the button's drop-down arrow to find it).
||
||
\/
Caveat emptor:
This extra option will only be enabled if the mercurial version is at least 2.2.0, and if the current revision is not public, is not a patch and has no children. [...]
Clicking the button will call 'commit --amend' to 'amend' the revision.
More info about this on the THG dev channel
Solution 4
I'm tuning into what krtek has written. More specifically solution 1:
Assumptions:
- you've committed one (!) changeset but have not pushed it yet
- you want to modify this changeset (e.g. add, remove or change files and/or the commit message)
Solution:
- use
hg rollback
to undo the last commit - commit again with the new changes in place
The rollback really undoes the last operation. Its way of working is quite simple: normal operations in HG will only append to files; this includes a commit. Mercurial keeps track of the file lengths of the last transaction and can therefore completely undo one step by truncating the files back to their old lengths.
Solution 5
Assuming that you have not yet propagated your changes, here is what you can do.
Add to your .hgrc:
[extensions] mq =
In your repository:
hg qimport -r0:tip hg qpop -a
Of course you need not start with revision zero or pop all patches, for the last just one pop (
hg qpop
) suffices (see below).remove the last entry in the
.hg/patches/series
file, or the patches you do not like. Reordering is possible too.hg qpush -a; hg qfinish -a
- remove the
.diff
files (unapplied patches) still in .hg/patches (should be one in your case).
If you don't want to take back all of your patch, you can edit it by using hg qimport -r0:tip
(or similar), then edit stuff and use hg qrefresh
to merge the changes into the topmost patch on your stack. Read hg help qrefresh
.
By editing .hg/patches/series
, you can even remove several patches, or reorder some. If your last revision is 99, you may just use hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a
.
Of course, this procedure is highly discouraged and risky. Make a backup of everything before you do this!
As a sidenote, I've done it zillions of times on private-only repositories.
Related videos on Youtube
mstrap
Updated on July 08, 2022Comments
-
mstrap almost 2 years
I'm looking for a counter-part of
git commit --amend
in Mercurial, i.e. a way to modify the commit which my working copy is linked to. I'm only interested in the last commit, not an arbitrary earlier commit.The requirements for this amend-procedure are:
if possible, it should not require any extensions. It must not require non-default extensions, i.e. extensions which do not come with an official Mercurial installation.
if the commit to amend is one head of my current branch, no new head should be created. If the commit is not head, a new head may be created.
the procedure should be safe in a way that if for whatever reasons the amending fails, I want to have the same working copy and repository state restored as before the amending. With other words, if the amending itself can fail, there should be a fail-safe procedure to restore the working copy and repository state. I'm referring to "failures" which lie in the nature of the amend-procedure (like e.g. conflicts), not to file-system-related problems (like access restrictions, not being able to lock a file for writing, ...)
Update (1):
- the procedure must be automatable, so it can be performed by a GUI client without any user interaction required.
Update (2):
- files in the working directory must not be touched (there may be file system locks on certain modified files). This especially means, that a possible approach may at no point require a clean working directory.
-
mstrap over 12 yearsI'm not sure why I missed rollback, but it seems to do (almost) what I want. The only problem is, when a file has been removed for my original commit and it has been resurrected for my amended commit: before the rollback it will be unversioned, after the rollback, it will be scheduled for removal (but the file still exists in the working directory)
-
mstrap over 12 yearsThanks for tuning solution (1); there is just a small problem left with rollback, please see my comment at krtek's solution.
-
mstrap over 12 yearsI had also considered using mq-extension, however it requires quite many operations for which some of them may fail (e.g. if binary files are involved). Furthermore, having to edit .hg/patch/series won't be acceptable, as this procedure should be used within a GUI client (I've updated requirements above)
-
hochl over 12 yearsHmmm, sorry that this won't for you, on a private repository this really kicks ass (with backups -- I've already fatfinger-destroyed a rep with it ^^). It's quite cool to combine patches into one before pushing the local changes using
hg qfold
, btw -
Paul S over 12 years+1 for using MQ, but I think you've gone overboard. He's only asking about amending the last commit. Also that import will keel over as soon as it hits a merge. 'qimport -r tip; <edit stuff>; qrefresh -e; qfin -a' will do the job (-e to edit the commit message)
-
hochl over 12 yearstrue, merges are a problem, I usually only pop one patch and use
hg import -r<prev>:tip
. A pitty there is no shortcut for the previous version, like in subversion. -
Paul S over 12 yearsOne thing to emphasise on rollback, because it catches people out, is that it's the last transaction on the repo that gets rolled back, not the last commit. So if something else has caused a write to the repo, rollback won't help. It's a subtle but important thing to remember. MQ and histedit can help once the rollback window has been closed, but still only up to a certain point.
-
krtek over 12 years@Marc I'm not sure I understand your problem, but have a look on the forget command, I think it's what you looking for.
-
mstrap over 12 yearsI don't think "forget" will be helpful here. Here's the problem in more detail: (1) I'm at revision 2 (2) Remove "file" and have some other changes (3) Commit changes, resulting in revision 3 (4) Now I'll change my mind and decide "file" should not be removed from the commit, so I want to amend revision 3. Hence, I'll re-add "file" which is now unversioned (5) Now I perform rollback: it will reset the dirstate and mark "file" as removed. (6) When performing "hg commit" again now, "file" will remain as removed, though it shouldn't be anymore. How could an automated fix for that look like?
-
krtek over 12 yearsFor the automated part I don't know, but you can do
hg revert myfile
to undo the deletion. Maybe re-adding withhg add
the file after therollback
also works. -
mstrap over 12 yearsUnfortunately, both "hg rollback" and MQ extension won't work for following scenario: - revision 2 has been committed with changes to file1 and file2. - working copy contains changes for file2 and file3. - only the changes for file3, but not for file2 should be committed. "hg rollback" obviously isn't able to do that. Whatever I tried with patch queues, local modifications need to be stashed away, i.e. my working directory needs to be touched. "git commit --amend" can cope with that scenario.
-
krtek over 12 years@Marc Like I said, history editing is discouraged with Mercurial. It's one of the main difference with Git. You can do what you want by stashing away your changes (have a look at the Shelve extension) or saving the diff to a patch file that you can reapply with the patch command before using MQ, but what you want to do is not in the Mercurial philosophy : The history is sacred !
-
mstrap over 12 yearsI agree that editing history of published changes should be avoided, but editing of my local history is one of the highlights of a DVCS. MQ with its qimport is pure history-editing, AFAICT.
-
Martin Geisler about 10 yearsGood answer! The experimental evolve extension allows you to safely amend non-head commits. The old commit will be marked obsolete and hidden. With a non-publishing server, you can even do this safely after you've pushed the changesets.
-
Jay Sheth almost 8 yearsTo just update the message on the last commit: hg commit --amend -m "this is my new message"
-
StayOnTarget over 7 yearsVery helpful, thanks. THG is smart enough to default the commit (amend) message to the message from the prior commit - just what I wanted.
-
mpen almost 5 yearsRe-using the same commit message is a nice feature!
-
darw almost 4 yearsuncommit is an experimental extension
-
darw almost 4 yearsstrip is an extension