Is it possible to undo an accidental "hg unshelve"? - mercurial

Now my there's an extra few hundred lines of code in my project, scattered over half a dozen classes, and I'm not looking forward to tracking them down one by one.

No, but this is an avoidable problem
Instead of doing hg shelve, do hg commit --secret. This will create a "real" commit in the repository, which can be rebased or stripped (hg strip) when you no longer need it. It can also be folded into another changeset using hg histedit or hg fold (the latter requires the Evolve extension, which is experimental at the time of writing, while the former is interactive and may be hard to script).
The --secret flag ensures the changeset will not be accidentally pushed until you manually run hg phase -d REV on the changeset.

Yes, with a few steps. The help for unshelve specifies "If a shelved change is applied successfully, the bundle that contains the shelved changes is moved to a backup location (.hg/shelve-backup)." So hg update --clean will return you to the pre-unshelve state, unless you added changes in the mean-time, in which case you need to figure out how to recover the world prior to the unwanted patches. Then you can re-apply the patches stored in the backup individually if you wish to essentially re-do the unshelve under whatever conditions you prefer. In any event, the back-up contains the data you thought was missing.

Related

What to do instead of squashing commits in Mercurial

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>.

How do I put a bunch of uncommitted changes aside while working on something else

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.

What is the standard commit process for Hg?

Is it
pull
update
merge
commit
push
? Or can you do the commit first?
I don't like the idea of pulling and merging without having a version of my local code backed up somewhere in case the merge explodes, but presumably you have to do the merge before you can do a push, because you can't have conflicts in the central repo. Not quite understanding this whole process yet; used to my nice simple SVN.
I recommend to always commit before pulling in changes to your working directory, unless you are 100% sure that your changes and the changes to be merged into your working directory will not conflict.
If you do an updating pull (hg pull; hg update, or shorter hg -u pull) and have any outstanding non-committed changes, any changes coming from outside will be combined with your changes. When conflicts happen, it might be difficult to decide how the merge result should look like, because you can't easily distinguish between your changes and the changes merged in.
When you did commit first, it is much easier to decide how the merge result should look like, because you can always look at both parents of the merge.
So, in effect it is:
hg commit
hg pull -u (if no merge necessary, go to 5)
hg merge
hg commit
hg push
Update: As Martin Geisler has pointed out, it is possible to get at the "original" changed version of a file using:
hg resolve --unmark the-file
hg resolve --tool internal:local the-file
or for all files at the same time:
hg resolve --unmark --all
hg resolve --tool internal:local -all
Still, I find the "commit first" system nicer. At the end, it is personal preference...
I don't know as there's a standard per se, but one of the ideas behind Mercurial is that you can commit as often as you like since it goes to your local repository. So you can commit to your heart's content as much as you like before you pull updates.
I tend not to commit very often, saving up for when I'm preparing to push, but that's me. I can see the utility of committing early and often. I do pull updates frequently as I work to cut down on merge fun.
One other thing I do is to keep a parallel clone of my working repo (cloned from the same repository as my working repo, not cloned from my working repo) so that I can check the original state of a file easily, and if need-be check in an out-of-band emergency fix or what-have-you without complicating my current change set.
Do edits
Commit
Goto 1 until satisfied
Pull
Merge & commit
Push if you want to.
Definitely commit before trying to do something complex like a merge. I don't think mercurial will allow you to merge before committing, but even if it did, what if the merge goes wrong. You have no pre-merge revision to go back to.
Commit early, commit often.
If you don't, you are missing out on a huge benefit of a DVCS.
but presumably you have to do the merge before you can do a push, because you can't have conflicts in the central repo
Wrong statement and poor understanding of distributed workflow and parallel development.
You can merge heads before push, but not have or must. Push can put any data to repo, if it needed and intended to be so
By default, push will not allow creation of new heads at the destination,
since multiple heads would make it unclear which head to use. In this
situation, it is recommended to pull and merge before pushing.
(NB: "recommended to pull and merge before" statement)
You can use commit-pull-merge, stash-pull-unstash-merge, perform fetch with modified WC and merge on the fly, don't merge heads at all or sporadically and push --force with +1 heads - there are not common rule for everybody. And any and every such workflow doesn't produce "conflicts in the central repo", but only different DAG.
Each point of divergence, which appear in case of existing your and other changeset from commmon parent in your (or even central) repo is a point of starting anonymous branches in Hg, which (technically) are absolutely legal, applicable and usual way. How they handled is defined by policy and agreement between developers, PM, QA-team and others
I, personally, prefer finish my task (in one or more amount of commits), after it pull and maybe merge, when it approved by development-policy

Mercurial: how to amend the last commit?

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).

Why does mercurial merge committed changes in the working copy?

I've been using Mercurial for a few weeks now and don't understand why when Mercurial comes to merge committed changes from two repositories it does it in the working copy?
Surely the merge could happen without the use of the working copy removing the need to shelf changes etc.
It just doesn't seem necessary to involve the working copy. Am I missing something?
There is only one working copy per repository, by definition:
The working directory is the top-level directory in a repository, in which
the plain versions of files are available to read, edit and build.
Unless your file system descends from Schrödinger's cat, you cannot have two versions of the same file at the same time, thus you cannot have two working copies.
Nevertheless, it's indeed theoretically possible to use something like a ephemeral clone (per #Ry4an) to act as the working copy of a merge, resolve conflicts there, commit, then make it disappear. You'd get a beautiful merge changeset and your intact working copy.
I can think of several ways to achieve this:
Petition hg team to do it in core
Write an extension to implement the ephemeral clone or some other way
Shelve with a temporary changeset
Shelve with MQ
I would strongly recommend #4, as I would for almost all workflow scenarios. It took me a few good days to grok MQ, but once I did I've never had to turn back.
In an MQ workflow, your working copy is always the current patch. So for the merge situation you would do:
hg qrefresh
hg qpop -a
hg update -r<merge first parent>
hg merge [-r<merge second parent>]
hg commit
hg update qparent
hg qgo <working copy patch>
You don't have to pop all patches in #2. I always do that whenever I need to deal with real changesets to avoid mixing them up with patches.
Solution #3 is really the same as #4, since a patch is a temporary changeset by definition (this is really the only thing you need for understanding MQ). It's just different commands:
hg commit -A
hg update -r<merge first parent>
hg merge [-r<merge second parent>]
hg commit
hg update -r<working copy changeset parent>
hg revert -a -r<working copy changeset>
hg strip <working copy changeset>
If you want to keep the working copy changeset and continue to commit, simply update to it in #5.
From your question it seems like you already know #4 but don't like shelving. I think shelving is good because merging is a fundamentally different task than coding (changing working copy), and shelving makes the context switch explicit and safe.
I didn't write Mercurial, so I can't say why they did it that way, but here are some of the positive results of that decision:
you can look over the results of the merge before you commit it
you can edit the results of the merge before you commit it
you're encouraged to commit frequently
If you really want to do a merge and have stuff in your working dir that you can't bear to commit don't bother with shelve just do:
cd ..
hg clone myrepo myrepo-mergeclone
hg -R myrepo-mergeclone merge
hg -R myrepo-mergeclone push myrepo
On the same file system clone is near instantaneous and uses hardlinks under the covers so it takes up almost no space past that of the temporary working copy.
As mentioned in the chapter "Merge" of HgInit:
The merge command, hg merge, took the two heads and combined them.
Then it left the result in my working directory.
It did not commit it. That gives me a chance to check that the merge is correct.
Such check can include conflicts in merge, that the user has to review:
In KDiff3, you see four panes
The top left is the original file.
Top center shows Rose her version.
Top right shows Rose my version.
The bottom pane is an editor where Rose constructs a merged file with the conflicts resolved.
So you need a working directory (a view for the merge) in order to resolve fully a merge.