Mercurial Queues recently got a new feature, which allows mq patches to be pushed and popped when there are local changes, provided the patches don't conflict with the local changes. This is controlled by the --keep-changes flag. I'd like to make this behavior the default. Generally, I avoid setting defaults, since that would mean hg works differently on my machine than on other machines, but in this case it seems harmless.
hg help qpop says, for example
With --keep-changes, abort only if the uncommitted files overlap with
patched files.
[...]
--keep-changes tolerate non-conflicting local changes
So, can someone tell what to put in .hgrc so that --keep-changes is default for qpush and qpop?
See Bug 2780 - qpop should work if the local changes and the mq patches are unrelated for the history of this feature. It is possible there is no option to set this, in which case it is less useful. There isn't even a one letter alias.
The defaults section of hgrc should do what you want (untested though):
[defaults]
qpush = --keep-changes
qpop = --keep-changes
But.... defaults are "depreciated", and people are encouraged to use aliases instead. I don't think that will mean they disappear though, it would "break work-flows" which is a mortal sin in mercurial development.
Read about alias section and pay special attention to note in this chapter
Note
It is possible to create aliases with the same names as existing
commands, which will then override the original definitions. This is
almost always a bad idea!
Related
This is my setup:
I have a main Mercurial repository (call it trunk). When I want to work on a feature, I do a clone and start working on it (usually add a bookmark as well).
I use various tools to do my work, which tend to generate convenient text files in the directory. It would be very helpful for me to track those files as well. However, I need to ensure those files do not get pushed to trunk.
In a sense, I'd like a "parallel" Mercurial repository in that directory where I can track these files.
How do people manage this? I'm open to using (stable) Mercurial extensions. Ideally, I do not want to "remember" to remove stuff before pushing to trunk.
There are two possibilities: patch queues and phases. Probably for what you want to do, phases are the less-friction approach. But there is no really "parallel" solution to my knowledge.
Have a look at hg help phases for an overview and hg phase for the command to manipulate the phase of a changeset.
To be sure you never push to trunk inadvertently, you have to make your commits "secret" by default (in your HOME/.hgrc):
[phases]
new-commit = secret
Then hg push will never allow to push anything by default, you would have to selectively change the phase of the changesets you want to push.
You could also not use the above configuration and use the --secret option of hg commit when committing something you want to keep for you, but it is too risky to forget.
Note that, both with patch queues and phases, you have to be proficient with history rewriting with hg histedit, to reshuffle around the commits.
In Mercurial changesets with a successor are marked obsolete.
Can this marker be (forcefully) removed somehow?
After using hg strip to remove the successor, the original changeset is still marked as obsolete and extinct). hg evolve refuses to re-create the successor.
You can't unmark a change as obsolete, but you can bring it back to life using "hg touch <rev> --hidden", where touch is a new command that is part of the evolve extension.
FWIW. It's also possible to rebase the obsolete change, but you'll probably get fewer complications if you use touch.
Yes! you can remove obsolescence markers now. Perhaps it wasn't possible in 2015, when the question was asked.
The solution is in the Apr 18, 2018 answer from user377178: hg --hidden debugobsolete --delete <revindex>
Note that you do not use a changeset ID. You must use an index number (at least, with Mercurial 4.1.3). Run hg --hidden debugobsolete --index to get a list of changesets with obsolescence markers. Find the index of the changeset that you want to become non-obsolete, then run hg --hidden debugobsolete --delete <revindex> with the index substituted for revindex. Then your obsolete changeset is no longer obsolete. (It might be in the list multiple times--I have not thoroughly investigated.)
The debugobsolete command is currently not listed in the evolve user guide, even in the section on recovering obsolete changesets. However, it is listed in the Evolve How To guide.
Many thanks to user377178.
I have come up with the following bash function - a hack and likely dependents on mercurial version but has been working well for years. Takes revset as parameter.
function hg_unhide() {
for i in $(hg --hidden log --template '{node}\n' -r "$1"); do
hg --hidden debugobsolete --index | grep $i | awk '{ print $1;}' | \
xargs -r hg --hidden debugobsolete --delete
done
}
The current Mercurial documentation Evolve: User Guide has a discussion on this.
Recover an obsolete changeset
Sometimes you might obsolete a changeset, and then change your mind. You’ll probably start looking for an “unobsolete” command to restore a
changeset to normal state. For complicated implementation reasons,
that command doesn’t exist. ...
Instead, evolve provides a touch command to resurrect an obsolete
changeset.
An unexpected quirk: you almost certainly need to use
--hidden, since obsolete changesets tend to be hidden, and you can’t
reference a hidden changeset otherwise.
Typical usage thus looks like
$ hg --hidden touch REV
This creates a new, normal changeset which is the same as REV—except
with a different changeset ID. The new changeset will have the same
parent as REV, and will be a successor of REV.
The current implementation of hg touch is not ideal, and is likely
to change in the future. ...
(emphasis added)
That said, personally I have found it simpler to graft the obsolete changesets back into usage. You can do this easily in TortoiseHG for example by just selecting the obsoletes that you want and grafting them all in one batch. Grafting may technically work outside (or skirt around) the obsolescence system but given the complexities of using touch as alluded to above I find the simplicity worth it.
The most current or maybe even "bleeding edge" approach to this seems to be using:
hg rewind
or its alias
hg undo
I say "bleeding edge" because although this command is released and seems to work, I can't find much documented about it, though the hg command line help does include it (below). It may still be considered experimental (as of 2022).
hg rewind seems potentially intended to replace hg touch.
Here's an example of it in use:
% hg rebase --dest=25707 --source=26017
rebasing 26017:5c78e2af32cb "message 1"
rebasing 26018:f156002253b3 "message 2"
...
[command completed successfully Fri Apr 29 09:07:15 2022]
% hg rewind
rewound to 33 changesets
(33 changesets obsoleted)
working directory is now at 0ad018a90b93
The net effect of these commands were to leave things exactly equivalent to where I started. The rewind took about as long as the rebase.
Built in help states the following:
% hg undo --help
hg rewind [--as-divergence] [--exact] [--keep] [--to REV]... [--from REV]...
aliases: undo
rewind a stack of changesets to a previous state
This command can be used to restore stacks of changesets to an obsolete
state, creating identical copies.
There are two main ways to select the rewind target. Rewinding "from"
changesets will restore the direct predecessors of these changesets (and
obsolete the changeset you rewind from). Rewinding "to" will restore the
changeset you have selected (and obsolete their latest successors).
By default, we rewind from the working directory parents, restoring its
predecessor.
...
% hg --version
Mercurial Distributed SCM (version 5.9.2)
...
Importantly, the help also includes the following cautions:
Current rough edges:
fold: rewinding to only some of the initially folded changesets
will
be problematic. The fold result is marked obsolete and the part not
rewinded to are "lost". Please use --as-divergence when you need to
perform such operation.
'hg rewind' might affect changesets outside the current stack.
Without
--exact, we also restore ancestors of the rewind target, obsoleting
their latest successors (unless --as-divergent is provided). In some
case, these latest successors will be on branches unrelated to the
changeset you rewind from. (We plan to automatically detect this case
in the future)
Some Mercurial developer-oriented notes about rewind are:
https://www.mercurial-scm.org/wiki/CEDConcept
https://octobus.net/blog/2018-09-03-evolve-status-2018-08.html
https://www.mail-archive.com/evolve-testers#mercurial-scm.org/msg00052.html
I've got my IDE set to commit locally every time I save anything. I'd ideally like to keep an uncensored record of my idiot fumblings for the rare occasions they may be useful. But most of the time it makes my history way to detailed.
I'd like to know a good strategy to keep that history but be able to ignore it most of the time. My IDE is running my own script every time I save, so I have control over that.
I'm pretty new to Mercurial, so a basic answer might be all I need here. But what are all the steps I should do when committing, merging, and reporting to be able to mostly ignore these automatic commits, but without actually squashing them? Or am I better off giving up and just squashing?
Related question about how to squash with highly rated comment suggesting it might be better to keep that history
Edit - My point here is that if Mercurial wants to keep all your history (which I agree with), it should let you filter that history to avoid seeing the stuff you might be tempted to squash. I would prefer not to squash, I'm just asking for help in a strategy to (in regular usage, though not quite always) make it look as much as possible like I did squash my history.
You want to keep a detailed history in your repo, but you want to have (and be able to export) an idealized history that only contains "reasonable" revsets, right? I can sympathize.
Solution 1: Use tags to mark interesting points in the history, and learn to ignore all the messy bits between them.
Solution 2: Use two branches and merge. Do your development in branch default, and keep a parallel branch release. (You could call it clean, but in effect you are managing releases). Whenever default is in a stable state that you want to checkpoint, switch to branch release and merge into it the current state of default-- in batches, if you wish. If you never commit anything directly to release, there will never be a merge conflict.
(original branch) --o--o--o--o--o--o--o (default)
\ \ \
r ... ... --r--------r (release)
Result: You can update to any revision of release and expect a functioning state. You can run hg log -r release and you will only see the chosen checkpoints. You can examine the full log to see how everything happened. Drawbacks: Because the release branch depends on default, you can't push it to another repo without bringing default with it. Also hg glog -r release will look weird because of the repeated merges.
Solution 3: Use named branches as above, but use the rebase extension instead of merging. It has an option to copy, rather than move outright, the rebased changesets; and it has an option --collapse that will convert a set of revisions into a single one. Whenever you have a set of revisions r1:tip you want to finalize, copy them from default to release as follows:
hg rebase --source r1 --dest release --keep --collapse
This pushes ONE revision at the head of release that is equivalent to the entire changeset from r1 to the head of default. The --keep option makes it a copy, not a destructive rewrite. The advantage is that the release branch looks just as you wanted: nice and clean, and you can push it without dragging the default branch with it. The disadvantage is that you cannot relate its stages to the revisions in default, so I'd recommend method 2 unless you really have to hide the intermediate revisions. (Also: it's not as easy to squash your history in multiple batches, since rebase will move/copy all descendants of the "source" revision.)
All of these require you to do some extra work. This is inevitable, since mercurial has no way of knowing which revsets you'd like to squash.
it should let you filter that history to avoid seeing the stuff you might be tempted to squash
Mercurial has the tools for this. If you just don't want see (in hg log, I suppose) - filter these changesets with revsets:
hg log -r "not desc('autosave')"
Or if you use TortoiseHg, just go View -> Filter Toolbar, and type in "not desc('autosave')" in the toolbar. Voila, your autosave entries are hidden from the main list.
If you actually do want to keep all the tiny changes from every Ctrl-S in the repo history and only have log show the subset of the important ones, you could always tag the "important" changesets and then alias log to log -r tagged(). Or you could use the same principle with some other revset descriptor, such as including the text 'autosave' in the auto-committed messages and using log -r keyword(autosave), which would show you all non-autosaved commits.
To accomplish your goal, at least as I'd approach it, I'd use the mq extension and auto-commit the patch queue repository on every save. Then when you've finished your "idiot fumblings" you can hg qfinish the patch as a single changeset that can be pushed. You should (as always!) keep the changes centered around a single concept or step (e.g. "fixing the save button"), but this will capture all the little steps it took to get you there.
You'd need to
hg qinit --mq once to initialze the patch queue repo (fyi: stored at \.hg\patches\)
hg qnew fixing-the-save-btn creates a patch
then every time you save in your IDE
hg qrefresh to update the patch
hg commit --mq to make the small changeset in the patch queue repo
and when you are done
hg qfinish fixing-the-save-btn converts the patch into a changeset to be pushed
This keeps your fumblings local to your repo complete with what was changed every time you saved, but only pushes a changeset when it is complete. You could also qpop or qpush to change which item you were working on.
If you were to try the squash method, you'd lose the fumbling history when you squashed the changesets down. Either that or you'd be stuck trying to migrate work to/from the 'real' repository, which, I can tell you from experience, you don't want to do. :)
I would suggest you to use branches. When you start a new feature, you create a new branch. You can commit as many and often as you like within that branch. When you are done, you merge the feature branch into your trunk. In this way, you basically separate the history into two categories: one in fine-grain (history in feature branches), and the other in coarse-grain (history in the trunk). You can easily look at either one of them using the command: hg log --branch <branch-name>.
As I understand it, you can't really fix a comment in Hg. So what I would like to do instead is re-push the exact same changes (or at least "touch" the same files and commit & push again).
The reason this is necessary is because we have a bug tracking and build system that relies on specific comment patterns, and we need to make sure the right files get included in the build, but if I forget to update the bug # in my comment from my last commit, and I accidentally commit and push it under the wrong # because i'm overzealous, how can I re-push those same files again without manually going into each one and adding a space or line break just to create a diff?
To clarify, I can't "rollback" or something; it's already been pushed with the wrong message.
As far as I know, current Mercurial features provide no support for this. After the changeset has been pushed, there's little you can do to un-push it, besides stripping it from the server repo and any other developer's repo.
I guess you you should ask those who set up this workflow in your shop; they should've come up with some exception handlers for it.
We usually just ignore issues like this, and close the bug by hand, making sure the bug links to the correct changeset. If the changeset is really messed up (usually this means bad changes, not a malformed commit message), we resort to stripping.
Since your change has already been pushed you can't use a simple fix, like "hg commit --amend", but you can do something similar. Basically, the following commands re-do the commit with Mercurial's help:
CSET=...the changeset to re-do...
hg up -r "p1($CSET)" # Update the working directory to the parent revision
hg log -r "$CSET" -p > changes.patch
hg import --no-commit changes.patch
hg commit # And use the appropriate commit message.
Then, merge and push.
The only way that I could think of doing this is to commit two more changes, one would be an hg backout of the incorrect revision and the other would be an hg backout of that revision with the corrected comment.
I don't like that idea though and wouldn't recommend it if there was any way to fix the problem in your bug tracking system.
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.
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.
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.
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
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.
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.
Recent versions of Mercurial include the evolve extension which provides the hg amend command. This allows amending a commit without losing the pre-amend history in your version control.
hg amend [OPTION]... [FILE]...
aliases: refresh
combine a changeset with updates and replace it with a new one
Commits a new changeset incorporating both the changes to the given files
and all the changes from the current parent changeset into the repository.
See 'hg commit' for details about committing changes.
If you don't specify -m, the parent's message will be reused.
Behind the scenes, Mercurial first commits the update as a regular child
of the current parent. Then it creates a new commit on the parent's
parents with the updated contents. Then it changes the working copy parent
to this new combined changeset. Finally, the old changeset and its update
are hidden from 'hg log' (unless you use --hidden with log).
See https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve for a complete description of the evolve extension.
Might not solve all the problems in the original question, but since this seems to be the de facto post on how mercurial can amend to previous commit, I'll add my 2 cents worth of information.
If you are like me, and only wish to modify the previous commit message (fix a typo etc) without adding any files, this will work
hg commit -X 'glob:**' --amend
Without any include or exclude patterns hg commit will by default include all files in working directory. Applying pattern -X 'glob:**' will exclude all possible files, allowing only to modify the commit message.
Functionally it is same as git commit --amend when there are no files in index/stage.
Another solution could be use the uncommit command to exclude specific file from current commit.
hg uncommit [file/directory]
This is very helpful when you want to keep current commit and deselect some files from commit (especially helpful for files/directories have been deleted).