Reputation: 549
It must be very obvious, but I found no way to do it. Every manual describes rebasing onto a top of existing branch or simple interactive rebase and that's all. Suppose, I have a diamond-shaped git history like that:
* 949430f Merge commit (D) (HEAD, mybranch)
|\
| * e6a2e8b (C)
* | 3e653ff (B)
|/
* 9c3641f Base commit (A)
and i want to archieve history like:
* 949430f Combined commit (BCD)
|
* 9c3641f Base commit (A)
Commits B and C may be melted or discarded at all, doesn't matter, I want to preserve only the result. Also I DO NOT want to revert commits because of nasty conficts resolving.
Here things I've tried:
1) I can't archeve that by simple squashing B and C
git rebase -i HEAD~2
...
p 3e653ff (B)
f e6a2e8b (C)
...
Could not apply a91f3a4
Well, that's somewhat understandable, there're some conflicts.
2) I can't archeve that by squashing.
git rebase -i -p HEAD~3
...
pick 9c3641f Base commit (A)
f 3e653ff (B)
f e6a2e8b (C)
pick 949430f Merge commit (D)
...
error: unable to match a91f3a4...
3) I can't even discard B and C
git rebase -i -p -m HEAD~3
...
pick 9c3641f Base commit (A)
#pick 3e653ff (B)
#pick e6a2e8b (C)
pick 949430f Merge commit (D)
...
error: Commit 949430f is merged but option -m is not set.
fatal: cherry-pick failed
Could not pick 4f3e6231b5cecf57434613ca3afd2a21ba375eb9
Why? Here is option "-m"...
Does anyone know how to solve this problem?
Upvotes: 3
Views: 796
Reputation: 10892
git rebase -i 9c3641f^ # 1 before base commit
then in the interactive editor
pick 9c3641f Base commit (A)
pick 3e653ff (B)
fixup e6a2e8b (C)
fixup 949430f Merge commit (D) (HEAD, mybranch)
I would actually expect the last one to not be present because merge commits disappear during an interactive rebase in my experience.
That will squish B and C into one commit. D will disappear and you don't really want it anyway since it's not adding code.
That will give you the desired output. The conceptual thing you missed was that you need to keep the first commit (pick) but if you "fixup" the other two they will merge into it. You need to give them something to merge into that is not the base commit. So you pick the second, and then the third merges into it giving you two commits.
You could also do
git rebase -i 9c3641f
and then just wouldn't see the base commit in the interactive editor but the pick
and fixup
would be the same for the remaining lines.
Upvotes: 1
Reputation: 488053
What you want is git reset --soft
(plus a commit as noted by aragaer). Despite the different circumstances in the question, see this answer by VonC.
Upvotes: 6
Reputation: 3847
I'd try linearizing the commit graph first, and then squashing them together:
$ git checkout commitA -b newbranch
$ git cherry-pick commitB
$ git cherry-pick commitC
[resolve any conflicts]
$ git rebase -i commitA
[change the command for commitC to 'squash']
Upvotes: 1