Consider this tree below. Excuse the crappy diagram, but I think you get the idea.
I have a MASTER
branch and a MY BRANCH
. I create a PATCH
branch from MASTER
and make my fixes on this branch. I then merge the fixes into both MASTER
and MY BRANCH
. When I do this, MY BRANCH
also gets commits C
& D
along with it.
MASTER----A----B----C----D-------------MERGE
\ \ /
\ PATCH----E----F
\ \
MY BRANCH----G----H-------MERGE
How can I merge only the commits in the patch into both MASTER
and MY BRANCH
?
As RomainValeri noted, the graph can be drawn better. I'll borrow his variant to start, but draw what we have before you try to do any merging:
E---F <<< patch
/
A---B---C---D <<< master
\
G---H <<< my-branch
The name patch
refers to existing commit F
and the names master
and my-branch
refer to existing commit H
.
The problem you have occurs because git merge
works based on the commit graph , not on the branch names . The names find commits, which is OK as far as that goes, but if you run git merge <commit F>
, the result will include commit F
, which will include commit E
, which will include commit D
, which will include commit C
, which will include commit B
, and so on.
That is, any successful merge of commit F
automatically includes every commit leading up to and including F
itself .
You can use git cherry-pick
to copy individual commits. Remember, a commit is a snapshot , a complete set of all files. But every commit has a parent commit too. 1 If you compare the snapshot in commit E
with the snapshot in its parent commit D
, you'll see what you changed in commit E
. If you compare the snapshot in commit F
with its parent in commit E
, you'll see what you changed in commit F
. A cherry-pick essentially allows you to "replay" these changes elsewhere.
This leads to the recommendation that people will often make, to use cherry-pick here. If you make a new branch named patch2
, starting at commit H
, and copy E
and F
to new and improved commits E'
and F'
, where E'
and F'
come after commit H
, you get:
E---F <-- patch
/
A---B---C---D <-- master
\
G---H <-- my-branch
\
E'-F' <-- patch2
You can now merge the original patch into master
and the new-and-different (supposedly improved) patch2
branch into my-branch
.
There is a better way! Well, often , anyway. Let's step back for a moment and ask: Why did you write commits E
and F
in the first place?
Chances are that it is to fix a bug that is in commit A
, or commit B
, or maybe both. Or maybe it adds a feature that is just lacking in commit B
. Whatever the case, you believe that it can be applied without commits C
and D
.
What you should do now is prove this . Make a new branch patch2
but don't point it at existing commit H
. Start it instead at commit B
. Then copy E
and F
to your new-and-improved E'
and F'
, giving:
E--F <-- patch
/
C--D <-- master
/
A--B--E'-F' <-- patch2
\
G--H <-- my-branch
Note that all the existing commits are still there, exactly the same. (You have not merged anything anywhere yet.) But if patch2
really works, we can now throw away the branch named patch
, abandoning commits EF
:
E--F [abandoned]
/
C--D <-- master
/
A--B--E'-F' <-- patch2
\
G--H <-- my-branch
Now that we've done that, let's stop drawing them, and rename existing branch patch2
to patch
:
C--D <-- master
/
A--B--E'-F' <-- patch
\
G--H <-- my-branch
The copied (cherry-picked or rebased ) commits E'-F'
are now in a position in which we can run git checkout master; git merge patch
git checkout master; git merge patch
and git checkout my-branch; git merge patch
git checkout my-branch; git merge patch
. When we do, we'll get two merge commits:
C--D--M1 <-- master
/ /
A--B--E'-F' <-- patch
\ \
G--H--M2 <-- my-branch
These merges behave the way you like: they bring only the patch into the branch, not any other commits unrelated to the patch.
If you're going to fix a bug or add a shared feature, find the earliest commit in your graph where the bug or feature can go. In this case, that was just after commit B
. (It might be possible to put it just after commit A
, and if so, you can do that instead. If the bug or feature requires part of commit B
, though, commit B
is the furthest back you can go.)
The reason for doing this is that the resulting patch can be cleanly merged to any commit that is "downstream" of that point. In this case, you wanted to merge the patch into commits D
, on master
, downstream of B
, and into H
, on my-branch
, downstream of B
. Since patch
, at commit F'
, is downstream of B
, merging patch
into any branch will bring in commit B
. But commit B
is the first place at which branch patch
is useful . Any branch whose tip isn't downstream of B
cannot use the patch at all! So by placing the patch itself immediately after the first place it could possibly be used , every other branch that can use the patch at all, can use the patch easily and cleanly.
使用rebase
或cherry-pick
手动将E
和F
应用到MYBRANCH
或仅以E
和F
开始正确创建PATCH
分支。
You can use a range to designate which commits you want on the destination branch :
git checkout my-branch
git cherry-pick master..patch
where master..patch
means :
everything on patch
MINUS everything already known on master
As a sidenote, suggestion for a better tree schema
E---F <<< patch
/ \
A---B---C---D-------I <<< master
\
G---H <<< my-branch
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.