views:

3638

answers:

5

Suppose you have a history containing the three commits A, B and C:

A-B-C

I would like to combine the two commits A and B to one commit AB:

AB-C

I tried

git rebase -i A

which opens up my editor with the following contents:

pick e97a17b B
pick asd314f C

I change this to

squash e97a17b B
pick asd314f C

Then Git 1.6.0.4 says:

Cannot 'squash' without a previous commit

Is there a way or is this just impossible?

+7  A: 

In the case of interactive rebase, you have to do it before A so that the list will be:

pick A
pick B
pick C

to become:

pick A
squash B
pick C

If A is the initial commit, you have to have a different initial commit before A. Git thinks in differences, it will work on the difference between (A and B) and (B and C). Hence the squash not working in your example.

Loki
A: 

You have to perform a bit of command-line magic.

# git checkout -b a A
# git checkout B <files>
# git commit --amend
# git checkout master
# git rebase a

That should leave you with a branch that has AB and C as commits.

Bombe
Because the old and new initial commits have no common ancestor you may get some unnecessary conflicts as git tries to apply the whole history of master onto a, even though they have a tree in common. By using the --onto option to git rebase you can tell git the correct place to start applying.
Charles Bailey
+12  A: 

'A' was the initial commit, but now you want 'B' to be the initial commit. git commits are whole trees, not diffs even if they are normally described and viewed in terms of the diff that they introduce.

This recipe works even if there are multiple commits between A and B, and B and C.

# Go back to the last commit that we want to form the initial commit (detach HEAD)
git checkout <sha1_for_B>

# reset the branch pointer to the initial commit,
# but leaving the index and working tree intact.
git reset --soft <sha1_for_A>

# amend the initial tree using the tree from 'B'
git commit --amend

# temporarily tag this new initial commit
# (or you could remember the new commit sha1 manually)
git tag tmp

# go back to the original branch (assume master for this example)
git checkout master

# Replay all the commits after B onto the new initial commit
git rebase --onto tmp <sha1_for_B>

# remove the temporary tag
git tag -d tmp
Charles Bailey
Thank you, worked for me! The second but last command should be: git rebase --onto tmp <sha1_for_B>
Christian
Yes, thanks for the correction. I was origin using raw A and B but decided that <sha1_for_X> was clearer but missed changing the last one. Editing now...
Charles Bailey
Charles, you should create a git GUI so we never need to know this commands :)
balexandre
+11  A: 

You tried:

git rebase -i A

It is possible to start like that if you continue with edit rather than squash:

edit e97a17b B
pick asd314f C

then run

git reset --soft HEAD^
git commit --amend
git rebase --continue

Done.

David Lichteblau
I had a similar problem. I had A-B and wanted to turn it into AB. This answer worked for that as well. Thanks!
Apreche
Thanks this was really helpful!
thomasfedb
A: 

In a related question, I managed to come up with a different approach to the need of squashing against the first commit, which is, well, to make it the second one.

If you're interested: git: how to insert a commit as the first, shifting all the others?

kch