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)