tags:

views:

64

answers:

3

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?

+1  A: 

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:

alt text

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.

VonC
Will I get 1k rep for quoting Joel the Apostle? :)
Geoffrey Zheng
+3  A: 

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.

Ry4an
My only concern: you probably want to test the merge if there's significant conflict, but testing a different repository can require a lot of setup, and if you test in `myrepo` you're back to square 1.
Geoffrey Zheng
+1 for the pragmatic reason.
VonC
Geoffrey, yeah, keep a symlink named CURRENT that I point to my repo-of-the-moment, which I uses in all my PATH and test configurations, but I recognize that'd easier with some toolchains than with others.
Ry4an
+2  A: 

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:

  1. Petition hg team to do it in core
  2. Write an extension to implement the ephemeral clone or some other way
  3. Shelve with a temporary changeset
  4. 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:

  1. hg qrefresh
  2. hg qpop -a
  3. hg update -r<merge first parent>
  4. hg merge [-r<merge second parent>]
  5. hg commit
  6. hg update qparent
  7. 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:

  1. hg commit -A
  2. hg update -r<merge first parent>
  3. hg merge [-r<merge second parent>]
  4. hg commit
  5. hg update -r<working copy changeset parent>
  6. hg revert -a -r<working copy changeset>
  7. 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.

Geoffrey Zheng
"Will I get 1k rep for quoting Joel the Apostle?"... Maybe not. Just a +1 for a complete answer ;)
VonC
Good answer and you're right I don't like shelving. Shelving was fine until I started using Mercurial in a busy development environment. Since then I've been getting a lot of unshelve aborts as files in the shelve have changed as part of the merge. Meaning what I think is called rebasing of the diff files (read edit of the shelve file in .hg). I assume MQ would have similar problems as its patch based? Think the answer may be to simply clone for individual features.
One-clone-per-feature is a viable workflow with its own problems (see, e.g. my question http://stackoverflow.com/questions/3719019/how-to-manage-concurrent-development-with-mercurial on hg workflows). If you reapply a patch on a new base and there's conflict, MQ saves the conflicting hunk and you'll need to resolve it. I think it's necessary pain because you want to know and resolve this kind of conflict. BTW `hg rebase` can work with applied patches directly and bring up merge tool to resolve conflicts.
Geoffrey Zheng
At what point do you ask another question in StackOverflow? I've looked very briefly at hg rebase. I've seen a number of people mysteriously mention that it is dangerous but I'm not sure of exactly how.
Feel free to ask another question. People will kill it if they don't like it :) `rebase` is usually considered dangerous because it changes history, but it's totally legit to use with patches since patches are not permanent.
Geoffrey Zheng