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
Related
I have a project, which is using Mercurial, I work alone on it and often I find myself in the same situation over and over: during working on something, I realize, that other changes should be fixed and committed before I finish my current work. So, I tried to create an "Anonymous branch"
hg update --check PREVIOUS_REVISION
but unfortunately, it doesn't work with the uncommitted changes (and I really don't want to commit unfinished work). So, every time, I copy directory with sources, revert to the previous revision, fix, commit, switch back to my working copy, pull the change and continue my work... But it takes too much time, so maybe there are better ways to do it and just with one copy of sources? Thank you.
P.S. Probably, it's the same question as How do I put a bunch of uncommitted changes aside while working on something else but my idea is "Is it possible just to leave unfinished work in the default branch in 'as is' state and then work on it later, without loading external patch?" (anyway, feel free to close it if it's a duplicate)
Three ways to solve task of "intermitted work"
Using shelve extension: Save all current changes hg shelve --all, make needed independent changes in clean WC, commit, restore intermediate results of work, saved on step 1 hg unshelve
Using anonymous branch: commit "as is" your WC, hg up <REV> to previous commit, make "must be before" changes, commit, return to older head, merge heads, continue work
Using MQ extension: use MQ Tutorial as starting point, chapter One: "Mq for the impatient" (between qrefresh and qfinish will be pure commit in your case)
I'll prefer (and always use for different tasks) MQ
Addition:
For fans and admirers of "clean history" one possible change in anonymous branching workflow (avoiding merge)
hg commit -m "Unfinished work" (rev M)
hg up -r "tip^1"
...
hg commit -m "Base changes" (rev N)
hg rebase -r M -d N (linearize history)
hg up
...
hg commit --amend -m "Full dependent change"
PS: I can't see anything bad in reversed set of changeset, i.e write a = something(data) in CSET, and function something (int subject) {...} in CSET+1
Having commits that aren't finished isn't a bad thing. It helps you survive things like your hard drive crashing. Why not commit your in-progress code with a commit message like "this is in progress", do whatever else you need to, then continue working on it?
Old commits are, by definition, unfinished. That's why you committed more things after them. Don't worry about the state of your old repository.
Of course, you should make cure you're working in feature branches, so you can push your code to a branch that isn't default.
If the 'must' changes are different hunks of codes, why not hg record, and select the 'must' hunks and commit before the others.
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>.
If I have a bunch of uncommitted changes and want to set it aside while working on something else instead, and then later (f.i. after several days) come back to it and proceed working. What would be the easiest workflow to accomplish this? (So far I have only experience with Mercurial's basic functionality). My usual method was to create a new branch using clone, but there might be better ways.
You have a handful options:
Shelve the items. This saves the changes and removes them from the working directory so the branch can continue. It doesn't create a change-set.
hg shelve --all --name "UnfinishedChanges"
hg unshelve --name "UnfinishedChanges"
Update/Edit: Newer versions of mercurial may need to use
hg shelve -n "UnfinishedChanges"
hg unshelve "UnfinishedChanges"
You can still use --name as an alternative to -n, but mercurial doesn't seem to like --name anymore. Additionally, the --all is no longer required and mercurial will in fact freak out over it.
Patch queue the items using mq. This isn't too dissimilar to shelve in some respects, but behaves differently. The end result is the same, changes are removed and can be optionally re-applied later. When pushed, the patches are logical change-sets, when popped they are saved elsewhere and are not part of change-set history.
hg qnew "UnfinishedWork"
hg qrefresh
hg qpop
hg qpush "UnfinishedWork"
Commit them locally, update to the previous change-set and continue working and make use of anonymous branches (or multiple heads). If you then want the changes, you can merge heads. If you don't want the changes, you can strip the change-set.
hg commit -m"Commiting unfinished work in-line."
hg update -r<previous revision>
hg strip -r<revision of temporary commit>
Commit them to a named branch. The workflow then becomes the same as option 3 - merge or strip when you are ready.
hg branch "NewBranch"
hg commit -m"Commiting unfinished work to temporary named branch."
hg update <previous branch name>
Personally I use option 3 or 4 as I don't mind stripping change-sets or checking-in partial code (so long as that doesn't eventually get pushed). This can be used in conjunction with the new Phase stuff to hide your local change-sets from other users if need-be.
I also use the rebase command to move change-sets around to avoid merges where a merge wouldn't add anything to the history of the code. Merges I tend to save for activity between important branches (such as release branches), or activity from a longer-lived feature branch. There is also the histedit command I use for compressing change-sets where the "chattiness" of them reduces the value.
Patch queues are also a common mechanism for doing this, but they have stack semantics. You push and pop patches, but a patch that is "underneath" another patch in the stack requires that the one on top of it be pushed also.
Warning, as with all these options, if the files have more changes since the temporary changes that you've shelved / queued / branched, there will be merge resolution required when un-shelving / pushing / merging.
Personally, I don't like any of the answers posted so far:
I don't like clone branching because I like each project to have only one directory. Working on different directories at the same time completly messes the history of recent files of my editors. I always end up changing the wrong file. So I don't do that anymore.
I use shelve for quick fixes (just to move my uncommited changes to another branch, if I realize I'm at the wrong one). You are talking about days, no way I'd shelve something for days.
I think mq is too complicated for such an ordinary sittuation
I think the best way is to simply commit your changes, than you go back to the changeset before you start these changes and work from there. There are some minor issues, let me illustrate:
Let's say you have the changeset A. Than you start your changes. At this point you want set it aside for a while. First of all, commit your work:
hg ci -m "Working on new stuff"
If you want, you can add a bookmark to make it easier to come back later. I always create bookmarks to my anonymous branches.
hg bookmark new-stuff
Go back to the changeset before these modifications
hg update A
From here, you work and generate the changeset C. Now you have 2 heads (B and C), you'll be warned when you try to push. You can push only one branch by specifying the head of that branch:
hg push -r C
Or you can change the phase of the new-stuff branch to secret. Secret changesets won't be pushed.
hg phase -r new-stuff --secret --force
To keep local uncommited changes, easiest way for me is just to save them as a patch file.
hg diff > /tmp/`hg id -i`.patch
and when you need to return to previous state:
hg up <REV_WHERE_SAVED>
hg patch --no-commit /tmp/<REV_WHERE_SAVED>.patch
You can just clone your repo multiple times. I tend to have a root clone, then multiple childs from there. Example:
MyProject.Root
MyProject.BugFix1
MyProject.BugFix2
MyProject.FeatureChange1
MyProject.FeatureChange2
The 4 childs are all cloned from the root and push/pull to/from the root. The root then push/pulls from the master repo on the network/internet somewhere. The root acts as your sort of personal staging area.
So in your case, you'd just clone up a new repo and start working. Leave your 'shelved' work alone in the other repo. It's that simple.
The only downside is disk space usage, but if that were a concern you'd not be using DVCS at all anyway ;) Oh and it does kind of pollute your Visual Studio "recent projects" list, but what the hey.
[Edit following comments] :-
To conclude then... what you're doing is completely fine and normal. I would argue it is the best possible way to work when the following are true: 1) it is short-lived 2) you don't need to collaborate with other developers 3) the changes don't need to leave your PC until commit/push time.
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).
In my repo, I have the revisions 1 to 10. I've pushed up to 5 (so the next hg push would publish revisions 6-10).
But I have to interrupt my work now and the result isn't 100% complete. So I'd like to move the revisions 6-10 into a new "experimental" branch to allow someone else to complete the work without disrupting the sources for everyone.
How can I add a branch to a non-tip revision (in my case: Starting with revision 6)? Or should I use a completely different approach?
You cannot apply a branch name after the fact without modifying your history.
The most simple approach is to ask the other users to use revision 5 as the parent for any changes they create. For example, the other users would:
hg clone <your repo> or even hg clone --rev 5
hg update -r 5
work, work, work
hg commit
When they commit a change, it will create a second head on the default branch, but that should not create any problems. You will simply need to merge the two heads together once your experimental changes are complete.
That being said, moving your changesets onto a branch can be accomplished using Mercurial Queues (MQ). The following sequence shows how it be done:
hg qinit (Create a new patch queue)
hg qimport --rev 6:10 (import r6-10 into a new patch queue)
hg qpop -a (remove all patches from your working copy)
hg branch <branch name> (create your new experimental branch)
hg qpush -a (apply all the patches to your branch)
hg qfinish -a (convert all patches to permanent changesets)
Tim already has good suggestions. Additionally you could push your experimental changes into a distinct experimental clone on your central server (I guess you use one). This clone could also be used by other developers to push their not-yet-finished work in order to let others review or continue it. It is also clear that this clone's code is not ready to be used. Once some task is finished, the corresponding changesets can be pushed to the stable repository.
Actually named branches are a good idea for your case, but the fact that their names are burned into history mostly is more a problem than a feature. IMHO Git's branch names are more practically. However, to some extend you could also handle your case with bookmarks, which are pushable since Mercurial 1.7 (not sure here). That is you bookmark revision 5 with something like stable (or whatever you agree on in your team) and revision 10 gets bookmarked with something like Aarons-not-finished-work. The other developers would then just pull stable, except your colleague who is supposed to continue your work, who would pull the other bookmark. However, personally I did not use a such workflow yet, so I cannot say if it performs well in practice.