Reputation: 45
I have a branch a1 whose parent branch is A. Now I have created another branch a2 from same parent A. I want to merge a1 with a2 excluding a chengeset from a1. Is there a way to partially merge a branch excluding a changeset?
Upvotes: 0
Views: 200
Reputation: 487735
The short answer is no: merges work according to the graph topology, not the branch names.
The longer answer is still no, but it's worth drawing the topology. Your description talks about branch parentage, but branches themselves do not have parent/child relationships. Branches are just collections of commits, with each commit containing its branch name. (Commits have parent/child, or more generally, ancestor/descendant, relationships.) So:
I have a branch a1 whose parent branch is A.
This really means that you have some commit(s) <rev-number(s)> whose branch name is a1
, and one or more of those commits have parent commit(s) <different-rev-number(s)> whose branch name is A
. If we draw them we get, e.g.:
A: ...--o--o--o
\
a1: o--o
Now I have created another branch a2 from same parent A.
Again, this just means that you have yet more commits (with their rev-numbers) whose branch name is a2
; at least one such commit has a parent whose branch-name is A
; we might draw this any number of ways, but let's try this one:
a2: o--o
/
A: ...--o--o--o
\
a1: o--o
I want to merge a1 with a2 excluding a chengeset from a1.
You don't really merge branches. Instead, you merge commits. You do this while having some commit checked-out into the working tree—this working-tree copy is the proposed next commit—and you are, by definition, on some branch. You then run hg merge
and specify some other revision: a commit that is on this or any other branch. Assuming the merge makes sense, Mercurial begins the merging process. When the merge is done, the new commit will have your current branch as its branch, and will have the working-tree's parent commit as its first parent, and the supplied revision as its second-parent.
So here, you might hg update a2
to select the tip of a2
as the commit copied into the working tree. The working-tree is a proposed, but not yet actual, new commit for a2
, and its parent is the commit now marked @
:
a2: o--@
/
A: ...--o--o--o
\
a1: o--o
Is there a way to partially merge a branch excluding a changeset?
You may select either of the two (we've only drawn two here) commits that are on a1
as your target for the hg merge
operation. Let's say you select the second one, using hg merge a1
:
a2: o--@
/
A: ...--*--o--o
\
a1: o--●
The filled-in circle is the commit to merge; @
is the parent of the working tree; so *
is their merge base—the best common-ancestor commit. Mercurial will compare the contents of the snapshot in *
to the contents of the snapshot in @
(or the working tree—these contents should be the same when you start the process), and also compare the contents of the snapshot in *
to those in ●
. These two comparisons each produce an en-masse changeset: the contents in ●
are modified from those in the o
between *
and ●
. Merge will simply combine these two en-masse changesets (which are then applied to the base's content).
You can, of course, select the other a1
commit for merging, but then you'll be using its snapshot, not the one for ●
. This will effectively exclude the changes in ●
—probably not what you want.
Let's draw yet a third graph, since this particular one is a bit shallow for our purposes. Suppose instead of just the two commits exclusive to a1
, we have many. But one of them, which we'll draw as x
here, we'd like to exclude when doing the merge into @
:
a2: o--@
/
A: ...--*--o--o
\
a1: o--o--o--x--o--o--o [want to merge this commit]
To achieve what you do want, you must first make new commits, perhaps on a1
, perhaps on a new branch entirely. These new commits will look like this:
a2: o--@
/
A: ...--*--o--o
\
o--o--o--x--o--o--o
a1: \
----o--o--o
In this case, I've drawn the new commits as a new mini-branch within a1
, but perhaps it would be clearer to make them on a new branch a3
:
a2: o--@
/
A: ...--*--o--o
\
a1: o--o--o--x--o--o--o
\
a3: ----o--o--o
The three new commits, in both cases, are simply copies of the changesets that occur after commit x
. When using a3
, the easy way to make these copies is to use hg graft
. Now we can pick the tip commit of a3
as the one to merge into a1
:
a2: o--@
/
A: ...--*--o--o
\
a1: o--o--o--x--o--o--o
\
a3: ----o--o--●
and the result of the merge will be a merge commit, which will tie back to both the old @
—the new merge is the current commit so it is now @
—and ●
:
a2: o--o---------------@
/ /
A: ...--*--o--o /
\ /
a1: o--o--o--x--o--o--o /
\ /
a3: ----o--o---●
The merge still used the topology, not the branch names.
(This is a key realization for Mercurial: branch names merely group commits together; but it's the graph topology that really controls things.)
Upvotes: 4