How to squash all git commits into one?

As of git 1.6.2, you can use git rebase --root -i

For each commit except the first, change pick to squash.


Update

I've made an alias git squash-all.
Example usage: git squash-all "a brand new start".

[alias]
  squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f"

Note: the optional message is for commit message, if omitted, it will default to "A new start".

Or you can create the alias with the following command:

git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'

One Liner

git reset $(git commit-tree HEAD^{tree} -m "A new start")

Here, the commit message "A new start" is just an example, feel free to use your own language.

TL;DR

No need to squash, use git commit-tree to create an orphan commit and go with it.

Explain

  1. create a single commit via git commit-tree

    What git commit-tree HEAD^{tree} -m "A new start" does is:

Creates a new commit object based on the provided tree object and emits the new commit object id on stdout. The log message is read from the standard input, unless -m or -F options are given.

The expression HEAD^{tree} means the tree object corresponding to HEAD, namely the tip of your current branch. see Tree-Objects and Commit-Objects.

  1. reset the current branch to the new commit

Then git reset simply reset the current branch to the newly created commit object.

This way, nothing in the workspace is touched, nor there's need for rebase/squash, which makes it really fast. And the time needed is irrelevant to the repository size or history depth.

Variation: New Repo from a Project Template

This is useful to create the "initial commit" in a new project using another repository as the template/archetype/seed/skeleton. For example:

cd my-new-project
git init
git fetch --depth=1 -n https://github.com/toolbear/panda.git
git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit")

This avoids adding the template repo as a remote (origin or otherwise) and collapses the template repo's history into your initial commit.


If all you want to do is squash all of your commits down to the root commit, then while

git rebase --interactive --root

can work, it's impractical for a large number of commits (for example, hundreds of commits), because the rebase operation will probably run very slowly to generate the interactive rebase editor commit list, as well as run the rebase itself.

Here are two quicker and more efficient solutions when you're squashing a large number of commits:

Alternative solution #1: orphan branches

You can simply create a new orphan branch at the tip (i.e. the most recent commit) of your current branch. This orphan branch forms the initial root commit of an entirely new and separate commit history tree, which is effectively equivalent to squashing all of your commits:

git checkout --orphan new-master master
git commit -m "Enter commit message for your new initial commit"

# Overwrite the old master branch reference with the new one
git branch -M new-master master

Documentation:

  • git-checkout(1) Manual Page.

Alternative solution #2: soft reset

Another efficient solution is to simply use a mixed or soft reset to the root commit <root>:

git branch beforeReset

git reset --soft <root>
git commit --amend

# Verify that the new amended root is no different
# from the previous branch state
git diff beforeReset

Documentation:

  • git-reset(1) Manual Page.