mercurial -- "merge" by abandoning one changeset - mercurial

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.

Related

Mark changes as already merged or deliberately ignored with hg pull/push/merge/graft?

I'm transitioning to Mercurial from Subversion, where I'm used to using svnmerge.py to track changes that have already been merged, or which have been blocked from being merged:
# Mark change 123 as having already been merged; it will not be merged again, even if a range
# that contains it is subsequently specified.
svnmerge.py merge -M -r123
#
# Block change 326 from being considered for merges.
svnmerge.py merge -X -r326
#
# Show changes that are available for merging from the source branch.
svnmerge.py avail
#
# Do a catchall merge of the remaining changes. Neither change 123 nor change 326 will be
# considered for merging.
svnmerge.py merge
I want to be able to do something similar for hg pull/push/merge/graft, so that if I know that I never want to merge a given change, I can just block it from consideration, making subsequent cherry-picking, merging, etc., into a more fire-and-forget affair. I have done a lot of googling, but have not found a way to do this.
There also appears to be no way to view a list of as-yet-ungrafted changes.
As I'm often tidying up after other developers and helping them with their merges, it's immensely helpful to be able to do these kinds of things, which one might well consider "inverse cherry-picking;" i.e., marking changes that you do NOT want to merge, and then doing a bulk merge of the remainder.
DAG-based systems like Mercurial ans Git are all or nothing: when you merge two branches, you do a three-way merge of the common ancestor and the two branches.
The three-way merge is only concerned with the final stage of each branch. For instance, it doesn't matter if you make your changes in 10 it 1000 steps — the merge result will be the same.
This implies that the only way to ignore a changeset is to back it out before the merge:
$ hg backout BAD
That will cancel the changeset on the branch, making it appear that it was never made from the perspective of the three-way merge.
If you have a whole branch that you want to merge, but ignore, then you can do a dummy merge:
$ hg merge --tool internal:local --non-interactive
$ hg revert --all --rev .
That goes through the merge, but reverts back to the old state before committing.
The best advice I can give you is to structure your workflow so that the above backouts aren't necessary. This means committing a bugfix on the oldest applicative branch. If a bug is found while creating feature X, then use hg bisect to figure out when the bug was introduced. Now updated back to the oldest branch where you still want to fix the bug:
$ hg update 2.0
# fix bug
$ hg commit -m "Fixed issue-123"
then merge the bugfix into all later branches:
$ hg update 2.1
$ hg merge 2.0
$ hg commit -m "Merge with 2.0 to get bugfix for issue-123"
$ hg update 2.2
$ hg merge 2.1
$ hg commit -m "Merge with 2.1 to get bugfix for issue-123"
If the bugfix no longer applies, then you should still merge, but throw away the unrelated changes:
$ hg update 3.0
$ hg merge 2.2 --tool internal:local --non-interactive
$ hg revert --all --rev .
$ hg commit -m "Dummy merge with 2.2"
That ensures that you can always use
$ hg log -r "::2.2 - ::3.0"
to see changesets on the 2.2 branch that haven't been merged into 3.0 yet.

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

ignore non-existant files when merging in 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.

Mercurial Getting out of a bad merge

I just merged branch A into B, and for some reason the merge did not go well. I want to revert B back to where it was before the merge and try again like it never happened before. I was thinking of just doing
hg clone myrepo newrepo -r A -r 12345
where 12345 is the revision number before B's bad merge commit
I think this works, but I have a lot of other branches (most of which are closed using commit --close-branch) and this puts those branches back to an inactive state.
Is there a way to clone everything except revision 123456 or something? (where 123456 is the bad commit on B)
Assuming you have not pushed the merge changeset to any public location, the easiest solution is to use the hg strip command that comes with the Mercurial Queues (i.e. mq) extension.
From the wiki:
hg strip rev removes the rev revision
and all its descendants from a
repository. To remove an unwanted
branch, you would specify the first
revision specific to that branch. By
default, hg strip will place a backup
in the .hg/strip-backup/ directory. If
strip turned out to be a bad idea, you
can restore with hg unbundle
.hg/strip-backup/filename.
It might not be as nice as hg rollback, but usually what I do is update to head A, merge in previous head B, check that I got it right this time, and then dummy-merge away the bad merge.
I hope I'm understanding your situation correctly. If I am you should be able to update to the revision of B before the merge, give that revision a new branch name, merge A in to it, and continue on. You'll probably want to mark the original B branch as closed.
$ hg up 12345 #12345 is the revision of B prior to the merge
$ hg branch B-take2
$ hg merge A
$ hg commit -m 'merge A in to B-take2'
$ hg up B
$ hg commit --close-branch -m 'mark original B branch as closed'
Is it too late to use the hg rollback command? If so, try the hg backout command.