The command you're looking for is git rebase
, specifically the -i/--interactive
option.
I'm going to assume you want to leave commit c on branch A, and that you really do mean you want to move the other commits to the other branches, rather than merging, since merges are straightforward. Let's start by manipulating branch A.
git rebase -i <SHA1 of commit a>^ branchA
The ^
means the previous commit, so this command says to rebase branch A using the commit before "a" as the base. Git will present you with a list of the commits in this range. Reorder them and tell git to squash the appropriate ones:
pick c ...
pick a ...
squash d ...
squash e ...
squash g ...
pick b
squash f
Now the history should look like this:
c - [a+d+e+g] - [b+f] (branchA)
/
--o-x-x-x-x-x-x-x-x-x-x (master)
Now, let's grab the newly-squashed commit b+f for branchB.
git checkout branchB
git cherry-pick branchA # cherry-pick one commit, the tip of branchA
And the same for a+d+e+g for master:
git checkout master
git cherry-pick branchA^
Finally, update branchA so it points to c:
git branch -f branchA branchA^^
We should now have:
c (branch A) - [a+d+e+g] - [b+f] (dangling commits)
/
--o-x-x-x-x-x-x-x-x-x-x-[a+d+e+g] (master)
x-x-x-x-x-[b+f] (branchB)
Note that if you had multiple commits you wanted to move between branches, you could use rebase again (non-interactively):
# create a temporary branch
git branch fromAtoB branchA
# move branchA back two commits
git branch -f branchA branchA~2
# rebase those two commits onto branchB
git rebase --onto branchB branchA fromAtoB
# merge (fast-forward) these into branchB
git checkout branchB
git merge fromAtoB
# clean up
git branch -d fromAtoB
Finally, a disclaimer: It's quite possible to reorder commits in such a way that some no longer apply cleanly. This could be because you chose a bad order (putting a patch before the commit introducing the feature it patched); in that case you'll want to abort the rebase (git rebase --abort
). Otherwise, you'll have to intelligently fix the conflicts (just as you do with merge conflicts), add the fixes, then run git rebase --continue
to move on. These instructions are also provided by the error message printed when the conflict occurs.