Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 724
cbehanna Avatar asked Dec 21 '11 05:12

cbehanna


1 Answers

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.

like image 152
Martin Geisler Avatar answered Oct 31 '22 23:10

Martin Geisler