git rebase provides a simple way of combining multiple commits into a single one. However using rebase to squash an entire branch down to a single commit is not completely straightforward.

Squashing normal commits

Using the following repository:

$ git log --oneline  
c172641 Fix second file  
24f5ad2 Another file  
97c9d7d Add first file

we can combine the last two commits (c172641 and 24f5ad2) by rebasing up to the first commit:

$ git rebase -i 97c9d7d

and specify the following commands in the interactive rebase screen:

pick 24f5ad2 Another file  
squash c172641 Fix second file

which will rewrite the history into this:

$ git log --oneline  
1a9d5e4 Another file  
97c9d7d Add first file

Rebasing the initial commit

Trying to include the initial commit in the interactive rebase screen will return this error:

$ git rebase -i 97c9d7d^  
fatal: Needed a single revision  
Invalid base

and squashing the top commit in the interactive rebase screen:

$ git rebase -i 97c9d7d  

squash 24f5ad2 Another file  
squash c172641 Fix second file

will return this error:

Cannot 'squash' without a previous commit

So we need to use a different approach to deal with the initial commit.

Amending the initial commit

Here is an alternative to rebase which will work on commits that don't have a parent.

Taking the previously rebased branch:

$ git log --oneline  
1a9d5e4 Another file  
97c9d7d Add first file

we can rewind the branch to the initial commit:

$ git reset 97c9d7d  
$ git log --oneline  
97c9d7d Add first file

without losing any of the changes introduced in 1a9d5e4 (shown here as uncommitted changes):

$ git status  
# On branch master  
# Changed but not updated:  
# (use "git add ..." to update what will be committed)  
# (use "git checkout -- ..." to discard changes in working directory)  
#  
# modified: file1  
#  
# Untracked files:  
# (use "git add ..." to include in what will be committed)  
#  
# file2  
no changes added to commit (use "git add" and/or "git commit -a")

Then we can reopen commit 97c9d7d and add the changes present in the working directory:

$ git add .  
$ git commit -a --amend -m "Initial version"

which will finally give us a fully squashed branch:

$ git log --oneline  
fcb85fb Initial version  

$ git status  
# On branch master  
nothing to commit (working directory clean)

this works, but there's a better way. If you use git merge, you can specify the --squash option to tell git to merge everything as one commit.

Of course, that only works if you're merging, not while you're still working on the same branch, like you can with rebase.

Comment by Wouter Verhelst
git checkout -b thirdbranch firstbranch; git diff firstbranch secondbranch | patch -p1; git add -A; git commit
Comment by Anonymous

You could also use the much simpler 'git merge --squash && git commit'

Yes, the mechanism is different: it uses the "merge" machinery, not the "rebase" machinery. But, if it ever makes a difference, you have bigger headaches, and shouldn't be making a "squash commit" in the first place.

Comment by Raja R Harinath