Why does mercurial merge committed changes in the working copy? - mercurial

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.

Related

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.

how to undo a commit pull

I faced today my first mercurial problem. I was in my repo, I modified a file and I did a
hg commit
hg pull
followed by
hg update
hg rollback
to repair what I've done, (but actually I didn't push anything)
The problem is that when I did the pull (that I should do before the commit, the head changed and so hg heads looks like :
- Modif from yesterday
- My modif
- Modif from last week
and now I see that someone also did another modification (via the http interface). What should I do to repair my local repo, (if possible modifying my summary) and push it after the 2 others modifications.
Thanks a lot. Quiet confusing, was easier on my "one-man" repo..
Your local repo doesn't need "repairing". This is a very standard case that you will see often if you use Mercurial a lot.
The issue is multiple heads.
You can either merge your heads, assuming your working directory is your version and there is only the other head:
hg merge
This will result in a merge changeset (same as if you were merging across branches).
Or you can enable the rebase extension to re-base your version onto the tip of the branch (the other head):
hg rebase --source<YourVersionNumber> --dest<TipVersionNumber>
This will not result in a merge changeset, and will simply transplant your changes on top of the changeset you specify as if they were born of that changeset all along (hence rebase or "new"basing).
Multiple heads is a funny sort of inner-branch branching... you can continue checking stuff in against your own head and change between heads using hg update. We block multiple heads per branch on our server, so our push would fail. I'd advise keeping multiple heads local as they are less clear-cut than branches.
I tend to work with Mercurial in one of two ways:
If the work is large in scale I will branch it off and follow Continuous Integration practices (constantly merging the main branch into my own etc). I then re-merge back into main when I am happy with the end result.
If the work is small in scale I will simply work against main branch and "merge" heads every so often. I say "merge" as I usually use rebase. Re-basing works great if the changes are simple and conflicts are unlikely.
My hard and fast rule: if I can't use rebase I put it on a branch born of main.

Merging from parent branch, did hg incoming path/to/baseline, how do I just merge?

I'm in my branch, and I do this:
hg incoming /path/to/baseline
And I get a few changesets in the output.
Now do I just merge to pull in the changsets to my branch?
hg merge /path/to/baseline
Will my history show what was merged?
As long as I didn't touch those files, it will be automatic right?
You should really try all this out yourself! Make a couple of test repositories:
hg init main
hg clone main clone
and then experiment away. It's easy and safe since you're only playing around on your own machine. (This is actually what happens "behind the scenes" when you ask a question here: I try to make sure that the advice I give really works, so I normally have to run a few tests on a new repository to double-check.)
If you ran the tests, you would see that
You cannot give hg merge a path name (or URL) as argument. It takes a revision as the only argument. You need to hg pull /path/to/baseline to copy the changesets into your local repository and then hg merge.
The history will indeed show what was merged. A merge becomes a merge commit in Mercurial. That is a changeset with two ancestor changesets — both lines of development leading up to the merge are still in the repository.
Merges are without conflicts ("automatic") if the changes made in the two branhces don't overlap. If you edit different files in the two branches then there's certainly no overlap. But you can also edit different regions within the same file and still have a merge without conflicts.
There is a fine tutorial on the wiki and I've also written a beginners guide. I hope that helps.

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

How can I create a branch for a non-tip revision in Mercurial?

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.