Excluding a file while merging in Mercurial - mercurial

I use Mercurial with TortoiseHg. I have two branches (A and B) with two files (toto.cs and titi.cs).
Is there a way, when I want to merge B with A, to exclude titi.cs from merging, and only merge toto.cs? If it's possible, how can I do that?

Sure. Not merging titi.cs is really just using titi.cs exactly as it exists in A or in B (your choice). You could do that manually like this:
hg update A
hg merge B
hg revert --rev A titi.cs
(swap A and B to go the other direction).
Or you could do that automatically in your Merge Tool Confguration with something like this in your .hgrc.
[merge-patterns]
titi.cs = internal:local
Which tells Mercurial to always use "this one not that one" for files matching that pattern.

If you're talking about a certain file which you want to be exempt from merging, then see #Ry4chan's answer. This is perfectly valid: some files, like build-relating configs, can be autogenerated, and conflict resolution for them is useless. Or, you might actually want to drop changes made in a source file.
If, on the other hand, you are trying to mimic file- or directory-limited merges of SVN-like systems, be careful. The revert trick does not mean you merged some files and didn't merge other: you merged everything, the revert was just a kind of merge.
If you want to move some changes from one branch to another (say, backport a fix in a certain subsystem to an old stable branch), you don't merge the files with the fix; instead, you merge the changesets containing the fix; hg graft will help you do so.

Related

Mercurial: How to re-include ignored files after the merge

In mercurial, the merge ignored some files (possibly a human mistake). How I can 're-include' the ignored files?
Scenario: the merge target(rev #47) has 5 files, but the merge(rev #50) has included just 1 file and others are ignored.
Apparently, when I check the resulting code, the code from Rev# 47 is missing.
You probably did the merge in a way you didn't intend, e.g. telling hg to only accept changes from one branch, discarding the changes from the merged rev47.
The easiest way is to start with a repo where the merge did not happen and re-do the merge. This is not possible, if it is a shared repo and that merge is already shared with others. If it everything local, but if that repo is your only copy, make a new clone without the merge: hg clone -r49 OLDREPO .
Thus if you cannot simply re-do the merge on a new repo without that merge, you'll have to go a longer way which is summarized in this answer. Note the screenshot in the accepted answer and DO NOT discard the changes from the merge target.

Splitting out multiple intermediate changes in Mercurial

We have a very unfortunate situation where a new feature branch was made from a second, unrelated feature branch that had been put on hold rather than from default. There's multiple changesets within both feature branches and while most changed files are unrelated several high level project files that have edits on both branches. Default has also had several updates and merges during this time too. Thankfully the intermediate feature hasn't been updated concurrently with the new feature. I've looked into various commands and options available and lost how to best fix this situation. The tree currently looks roughly like:
Default -- Various edits and merges -- tip
\
\-- Named Branch 1 (15 changes) -- Named Branch 2 (30 edits)
I want to get to a point where default has the changes from Named Branch 2 but none from Named Branch 1. I also want a branch that has the changes from Named Branch 1 available for when we return to this feature.
I suspect there's no nice easy way to do this, and there's going to be some messy parts in the history, however I am at a loss at how to start going about this task.
hg graft can cherry-pick changesets from one branch to another. Update to the destination branch, then graft the revisions you want to copy by running, for example:
hg graft 8 9 10
Conflicts will be handled using the normal merge process.
If you use TortoiseHg, select the changesets to graft to the current selected changeset, then right-click and select Graft Selected to local...:
Result:
Since you want to move the entire branch 2, you could consider using rebase. Make a clone of the repository and try this:
hg rebase --source <first branch2 rev> --dest <new parent in default> --keepbranches
This will in principle transform the history to what it should have been. However:
You may have to resolve conflicts arising when <first branch2 ver> gets moved to a new parent.
Since rebase rewrites history, you'll have to get everyone to cooperate in synchronizing their repositories. Whether that's feasible or worth the trouble in your case I can't say, but it's not that difficult: Assuming everyone has pushed any changes in branch 2, they can pull the new history and then get rid of the obsolete version of branch 2 with hg strip:
hg strip <first branch2 rev>

How to diff two revisions of a file that got renamed in between and Mercurial doesn't know about the renaming?

I accidentally renamed a file outside of Mercurial. When I committed the change, Mercurial treated the change as two unrelated files (ie. a remove and a add). I need to go back to diff the two revisions but I don't know how to do so when Mercurial sees them as two respective files across different revisions. What can I do to diff the files?
You didn't say what operating system you were using. The following will work with bash on Linux:
diff <(hg cat -r rev1 file1) <(hg cat -r rev2 file2)
You can replace diff with another program like vimdiff if you want a visual diff.
If you want to actually fix the history so that Mercurial is aware of the rename (and can use that information in future merges if needed), there's a way to do so documented on the Tips and Tricks page on the Mercurial wiki.
Current contents copied here for ease of use (and in case the link gets broken later):
Steps:
Update your working directory to before you did the rename
Do an actual "hg rename" which will create a new head
Merge that new head into the revision where you did the "manual" rename (not the head revision!)
Then finally merge the head revision into this merge result.
Advice:
Make a clone first and work on that, just in case!
After finishing the steps, use a file compare tool to check that the original and the clone are identical
Check the file history of any moved file to make sure it is now restored
That being said, if all you want to do is compare the contents at the point in time, you can definitely accomplish that without making Mercurial aware of the rename (as mentioned in Stephen Rasku's answer). In fact, you can use a combination of "hg cat" and an external comparison tool to compare any files, not just ones that Mercurial knows about.
Fix history:
Update to first changeset with new-filename, save file outside WC
Update to parent of bad replacement changeset, replace file correctly (with rename tracking), commit, got second head
Rebase all changesets from old anonymous branch on top of fresh good changeset
--close-branch on bad-replacement changeset or delete this unwanted changeset or leave inactive head intact

Mercurial: How to deal with one branch that has two heads

What if one branch has two heads? I came to this situation weeks ago for some reason I didn't merged them back then and just continued developing on one head. Now I want to get rid of the other head. What should I do? Should I just merge them after so many changesets?
So you have this:
o--o--o--A--B <- older unnecessary head
\
1--2--3--4--5--6 <- newer ‘good’ head
...and you don't need A and B, absolutely, 100% sure. If you aren't sure, and there are possibly salvageable things in A and B, better merge the heads to combine the changes. As Aaron said, Mercurial is good at it.
Now you have two options:
get rid of the old head, or
do a dummy merge of two heads that ignores head B.
If changesets A and B are present in other repositories you don't control, e.g. if other people pulled A and B into their repositories, or you pushed A and B to a public repository (say, Bitbucket), then you've released A and B into the wild, and can't get rid of them. You should do a dummy merge:
$ hg up 6
$ hg --config ui.merge=internal:local merge
This will ignore any changes from A and B when merging.
If, on the other hand, A and B are private, you can either strip them:
$ hg strip A
(Rev A and descendants stripped; enable the MQ extension to make strip available.)
Or, make a new clone of your repository without changesets A and B:
$ hg clone myrepo myrepo2-clone -r 6
(Only rev 6 and ancestors added to the clone.)
A head is created if you commit some changes (add a changeset to an existing changeset). If that happens several times for the same changeset, then several heads are created. This is nothing unusual or bad.
The usual solution is to merge them. Mercurial is very good at merging. Chances are that the result will still compile and run without any manual work.
Just run hg merge and then hg status and hg diff to see what changed between the two. If you like the result, commit it. If you don't like it, update to either head to clean your workspace.
To get rid of a head, the docs explain how to do that.
Using MQ, you can also turn the second head into an independent branch (so you can keep it around but it won't bother you). See this answer: How can I create a branch for a non-tip revision in Mercurial?
If you need to keep the changes on the old branch, you should merge them, and it should not be a problem.

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.