ignore non-existant files when merging in Mercurial - mercurial

I am working with a repository with stable and experimental branches. Sometimes I add a file on the experimental branch that is not yet ready for the stable branch. When I merge, I want to merge the changes in the files that are common to both branches, but ignore the files that don't exist on one of the branches.
Here's a simple example:
hg init
hg branch stable
(create file A)
hg add A
hg commit -m "Added A"
hg branch exp
(create file B)
hg add B
hg commit -m "Added B, which is really experimental"
(modify file A)
hg commit -m "Some changes to A"
hg update stable
hg merge exp
However I change the merge tool configuration, Mercurial always seems to take B along with the merge. Since it doesn't exist on the stable branch, it's never a conflict.
I could do the following:
hg update stable
hg merge exp
hg commit -m "Merged"
hg revert -r 0 B
but that requires me to know which files need reverting.
Any thoughts on the simplest way to make the merge ignore files that don't exist on one branch, and preferably do it automatically?

You cannot ask Mercurial to do what you want.
You do not merge individual files, you merge the entire branch, which means that all the files that are part of the branch becomes part of the merge. This is how Mercurial is designed and how it operates.
Now, you could revert/forget/delete the files you don't want before you commit, but then you're just setting yourself up for disaster later.
I recommend you separate things you want to keep from things you don't know if you want to keep so that you can merge one branch and let the other be separate for now.

Related

How to Switch the branch an commit with Tortoise Hg?

I have two branches named X and Y. X is previously created and y is the latest one. Now I am at branch 'Y' and I have modified some files. I need to commit the files which I have modified and I need to commit the changes to 'X' branch instead of 'Y' branch. How can I do this with Mercury Hg or Tortoise Hg? (How to Switch to 'x' branch and commit the changes?)
You could
Commit it to Y branch
Then transplant/grave it to X branch
Then strip changeset from Y branch
As result it's look like you commited it to X instead of Y
If you have not committed the changes then you should be able to do the following:
hg update X
hg commit
Unless you've changed some of the same files in the same places within the file, this should be sufficient. If you have, then your hg update X command should fail. You have a few options at this point.
You can use Mercurial Queues (mq).
You can put your current changes into a patch queue:
hg qnew stufftomove -e
You'll be presented with whatever editor you use to create a commit message. Go ahead and fill in your commit message with whatever you want it to be when it is ultimately committed. If you don't use -e, it will just create the patch queue with no commit message. Later you can make a commit message with hg qrefresh -e if you want.
After the patch queue is created, pop it off with:
hg qpop
Now move to the X branch:
hg update X
Now push the patch queue on. This will apply the patch file:
hg qpush
This can either go on cleanly, or if you did have merge conflicts, you'll get a message that .rej files have been created. Those show you the parts of the changes that could not be applied automatically.
If you have .rej files, manually apply the pieces that didn't work and then run
hg qrefresh
This will update the patch file with the new changes.
When everything looks like what you want it to, you can convert the patch queue to a real commit with
hg qfinish stufftomove
Your file changes should now be committed on the branch you want them to be on.
Alternatively, if you already committed the changes on the wrong branch, and have not pushed your changes (or had them pulled) to a remote repository, you can do this:
hg qimport -r <revision to import>
At this point, continue the above instructions starting with hg qpop.
If you can be more specific about the state of the files and branches, I can help more specifically. In any case, I hope this helps.
Side note: If you've got merge conflicts in the steps above and don't like dealing with .rej files, you can use hg rebase which will allow you to resolve the conflicts in your favorite merge tool (or whatever tool you've got configured). Hope this helps.
I use Shelving for such operation: https://tortoisehg.bitbucket.io/manual/2.9/shelve.html
While you're on 'Y' branch, put all your changes to shelf. Then switch to 'X' branch and restore changes from your shelf.

Mercurial: Switch working directory to branch without losing changes?

Let's say that I have a named branch 'B1' which I'm doing feature development on.
I am at a good stopping point before a demo though not done with the feature so I:
hg up default
hg merge B1
hg ci -m "merged in feature drop"
hg push
Now I continue working for a half an hour or so and go to commit only to realize that I forgot to update back to B1 and that my current working directory is on default - uhoh. In theory I should be able to just mark my working directory parent as the tip of B1 - is there an easy way to do this?
I could of course commit, update back to B1, and merge my changes back, but then there's an unstable changeset in default and this happens often enough to me that I would like a real solution.
Two ways. First, the obvious way:
hg diff > foo
hg up -C b1
hg import --no-commit foo
rm foo
Second, the magical way:
hg up -r 'ancestor(., b1)' # take working dir back to the fork point
hg up b1 # take it forward to the branch head
This way involves merges. Depending on how much your branches have diverged, this may be painless. Or it may be complicated, and you may make a mess of your changes that you haven't saved anywhere. Which is why even magicians like myself prefer to do it the first way.
I would use the shelve extension. I think it’s distributed along with TortoiseHg, you can also use it from the UI:
hg shelve --all
hg up B1
hg unshelve
Rebase extension allow you to change parent for any commit for wrongly commited changeset.
If you want just change branch for future commit - MQ (as mentioned) or Shelve
Typically for this sort of dynamic approach, I favor mercurial queues.
In your situation, what I would do would be to create a patch on default with the changes, pop the patch off, switch over to B1, and apply the patch.
It goes something like:
hg qnew OOPSPATCH
hg qrefresh
hg qpop
hg up B1
hg qpush
<hack hack>
hg qrefresh
hg qfinish
All you need is simple hg up -m B1
From hg up --help:
options:
…
-m --merge merge uncommitted changes
…

Can changes made by strip be reflected in the public repo?

Let's say I have this:
hg clone public_repo my_repo
touch a b c d
hg add .
hg commit -m a a
hg commit -m b b
hg commit -m c c
hg commit -m d d
hg push
# let's say revX is the revision that added a
hg strip revX
In my repository's history, the commits are gone. However, if I try to do a push after the strip, it tells me no changes found. Can the strip be applied to the public repo?
EDIT: the question is just hypothetical :). I am not in this situation, I just thought it would be interesting to find out how this works.
You need to run hg strip there. If it's bitbucket repo, you can do it from the admin panel. If not, SSH.
This is not possible. You have to strip the public repo as well.
hg strip just takes away changesets. It does not perform an "adding" operation which could be pushed.
In the exact example you provide, you could just hg rollback on the public repo.
Let me pretend you're asking a slightly different question: I've stripped part of my repository history, how do I propagate this change to other dev's repositories?
Mercurial offers no way of propagating stripping in the same way it propagates adding changesets. So you have to come up with your own way.
Bitbucket, for example, has an updates list (on the repo summary page, if my memory serves me well). If you do a strip, Bitbucket displays there something like this:
Changeset 12345abcdef was stripped from the repository, please run hg strip 12345abcdef locally.
When we had to propagate stripping of part of an old branch in our shop, here's what we did:
Stripped the changesets on the server.
Created a batch file named strip.bat on the default branch of the repo containing the command we'd run on the server, i.e. hg strip 1234567890.
Told everybody that whenever they cannot push because “push creates new remote heads” for no apparent reason, this means they should run strip.bat.
In case we ever need to strip something again, we just add another hg strip in the batch file.
P.S. Yes it's better not to strip, e.g. use backout to commit inverse changesets. Sometimes that's not an option: if you mistakenly merged branch a into branch b when you wanted the merge b into a, you're trapped. Even if you backout the merge changeset on feature, the merged changesets from a are already marked as merged, and you'll have a hard time doing another merge of these branches.
The correct and safe way to remove unwanted changes that made it to the wild is to hg backout them.
Sometimes it is not possible to strip or backout a change, for example if it was a merge revision and you don't have admin access to the server. One method that does work is to branch the last good revision and then close the bad head.
hg up badrev^ (Go to the revision before the bad one)
touch dummy && hg add dummy && hg ci -m 'dummy' && hg rm dummy && hg ci --amend -m 'Fix accidental merge' (Create a second head with no changes from the good revision)
hg up badrev (Go to the bad revision)
hg ci --close-branch -m 'Close bad head' (Close the bad head)
hg push

Using Mercurial, do we need to "hg merge -r 6880" if there is an extra branch?

For Mercurial, right now there is default branch and newfeature branch... is it true that if I am on
the newfeature branch, and do an hg pull and hg update, it will always ask me to merge? (if there are changesets that I pulled)
Also, it seems that I cannot just do hg merge? I need to use hg heads and then look at what the newfeature branch's head is (say it is revision 6880),
then I need to hg merge -r 6880? Because otherwise, will Mercurial merge the newfeature branch with the default branch automatically? I cannot do hg merge -b newfeature, it seems, as there is no -b option for hg merge.
Is there an easier way other than using hg heads to look for the revision to merge? Isn't there a more automatic way?
You've got two questions there, let me take them one at a time (with a little paraphrasing):
Q. When I hg pull and get a new head Mercurial suggest I hg merge. Do I have to?
A. No. Mercurial is just warning you you have more heads than than you did, and that if you don't like that arrangement you can merge to stop it. Named branches are heads, so you'll see that warning if pulling gets you a new head
Q. If I want to merge one named branch into another do I have to provide the revision number?
A. No. It's true that hg merge will only automatically select heads on the same named branch, but you can do hg merge -r newfeature and that merges in the changeset from the point of divergence up to the head on newfeature (6880 in your example) exactly the same as hg update -r 6880 would.
In either case, after committing that merge you'll have no heads on newfeature (the new, resulting head is on default because that was the branch name of your parent before you started the merge. However, just doing this after the merge:
hg update newfeature
...code....
hg commit
will create a new head on the newfeature branch, and you're right back as you were before the merge, except all of the changes that were on new feature are also available in default now.
If you pull a changeset or changesets from one branch into another branch that share the same root changeset. Mercurial will have multiple heads as you have so noticed. It will only suggest that you merge when you do an hg update on one of the branches.
You shouldn't have to specify which revision to merge to, assuming that you want to merge the tips of each of the branches. hg merge should suffice.
Your command structure should look as follow
hg pull -b 'branchYouWantToPullFrom`
hg update
hg merge
hg commit
hg merge works in your working copy, which is always connected to a specific branch.
You have to specify a branch name only if you want to merge your current branch with another branch: hg merge branch_name.
hg pull updates your repository with all remote changes. Then you have to update your working copy, that is connected to a specific branch. So, when you type hg update command, you update your working copy with all changes in your current branch.
If you want to switch to another branch you have to type hg update branch_name. You can type hg branch to know your current branch.
The only reason to merge with a specific revision is when you have three or more heads, a strange situation probably caused by some hg push -f (extremely bad practice). If you are in this situation, the right way to know which revisions you have to merge is hg heads. In a normal situation hg heads returns one head per branch, so you don't have to merge two heads of different branches if you don't want.
If you're working on a branch and someone has committed and pushed some changes on the same branch, you have to pull and merge before your push, simply with hg merge, no revision or branch.
I hope this will help you.

mercurial -- "merge" by abandoning one changeset

I have two heads, let's call them "A" (the good head) and "B" (the bad head). I want to merge them by taking everything from A and nothing from B. Basically, my merge of A and B is A.
When I try hg merge, it starts asking me about this file and that, and inevitably I get into trouble. I don't want any of that! How can I tell it to merge them and end up with A, preferably without any intermediate steps?
From the Mercurial tips at section 22. Keep "My" or "Their" files when doing a merge.
https://www.mercurial-scm.org/wiki/TipsAndTricks
Occasionally you want to merge two heads, but you want to throw away all changes from one of the heads, a so-called dummy merge. You can override the merge by using the ui.merge configuration entry:
$ hg --config ui.merge=internal:local merge #keep my files
$ hg --config ui.merge=internal:other merge #keep their files
Here local means parent of working directory, other is the head you want to merge with. This will leave out updates from the other head.
To merge X into the current revision without letting any of the changes from X come through, do:
hg --config ui.merge=internal:fail merge X
hg revert --all --rev .
The other approach is mentioned in : https://www.mercurial-scm.org/wiki/PruningDeadBranches
$ hg update -C tip # jump to one head
$ hg merge otherhead # merge in the other head
$ hg revert -a -r tip # undo all the changes from the merge
$ hg commit -m "eliminate other head" # create new tip identical to the old
One thing I came across and started using recently on some personal repos was just using the close-branch switch with commit. e.g.
$ hg update B
$ hg commit --close-branch -m "Abandoning branch"
In my reasoning, if you're blowing away one branch in favor of the other entirely, it's simply not a merge and it's silly to call it that. I'm relatively new to hg myself, and I seem to recall that --close-branch has not been around since the beginning and maybe that's why it doesn't have as much traction as the merging gyrations I usually see.