How to squash all git commits into one?

331,905

Solution 1

Perhaps the easiest way is to just create a new repository with current state of the working copy. If you want to keep all the commit messages you could first do git log > original.log and then edit that for your initial commit message in the new repository:

rm -rf .git
git init
git add .
git commit

or

git log > original.log
# edit original.log as desired
rm -rf .git
git init
git add .
git commit -F original.log

Solution 2

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

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

Solution 3

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.

Solution 4

echo "message" | git commit-tree HEAD^{tree}

This will create an orphaned commit with the tree of HEAD, and output its name (SHA-1) on stdout. Then just reset your branch there.

git reset SHA-1

To do the above in a single step:

git reset $(git commit-tree HEAD^{tree} -m "Initial commit.")

Solution 5

Here's how I ended up doing this, just in case it works for someone else:

Remember that there's always risk in doing things like this, and its never a bad idea to create a save branch before starting.

Start by logging

git log --oneline

Scroll to first commit, copy SHA

git reset --soft <#sha#>

Replace <#sha#> w/ the SHA copied from the log

git status

Make sure everything's green, otherwise run git add -A

git commit --amend

Amend all current changes to current first commit

Now force push this branch and it will overwrite what's there.

Share:
331,905
Verhogen
Author by

Verhogen

Updated on July 08, 2022

Comments

  • Verhogen
    Verhogen almost 2 years

    How do you squash your entire repository down to the first commit?

    I can rebase to the first commit, but that would leave me with 2 commits. Is there a way to reference the commit before the first one?

  • Olivier Refalo
    Olivier Refalo about 12 years
    but you are loosing branches with this method
  • skalee
    skalee over 11 years
    @OlivierRefalo – There is no way to preserve branches while reducing whole history to a single commit (make new initial commit). If someone wants to preserve branches, he got to preserve the old initial commit. To do that, rebase against initial commit (like written in question) and live with as many as two commits.
  • skalee
    skalee over 11 years
    Actually, there is a way to preserve branches, see answer by @FrefichRaabe However concept of old branches with history squashed to single commit sounds odd.
  • ryenus
    ryenus about 11 years
    git reset $(git commit-tree HEAD^{tree} -m "commit message") would make it easier.
  • serbaut
    serbaut about 11 years
  • David J.
    David J. almost 11 years
    Git has evolved since this answer was given. No, there is a simpler and better way: git rebase -i --root. See: stackoverflow.com/a/9254257/109618
  • iwein
    iwein over 10 years
    This might work for some cases, but it is essentially not the answer to the question. With this recipe, you loose all your config and all other branches too.
  • chesterbr
    chesterbr about 10 years
    ^ THIS! - should be an answer. Not entirely sure if it was the author's intention, but was mine (needed a pristine repo with a single commit, and that gets the job done).
  • tldr
    tldr almost 10 years
    @ryenus, your solution did exactly what I was looking for. If you add your comment as an answer, I'll accept it.
  • kusma
    kusma almost 10 years
    The reason I didn't suggest the subshell-variant myself, is that it won't work on cmd.exe in Windows.
  • Admin
    Admin almost 10 years
    This answer is ok, but if you're interactively rebasing more than, say, 20 commits, the interactive rebase will probably be way too slow and unwieldy. You're probably going to have a hard time trying to squash hundreds or thousands of commits. I would go with a soft or mixed reset to the root commit, then recommit, in that case.
  • sanmai
    sanmai almost 10 years
    If you have any files forcedly added over .gitignore, this recipe wouldn't add them again.
  • Colin Bowern
    Colin Bowern almost 10 years
    The git revision syntax (HEAD^{tree}) syntax is explained here in case anyone else was wondering: jk.gs/gitrevisions.html
  • ryenus
    ryenus almost 10 years
    @ColinBowern, thank you for noting this, I've updated the answer with a bit information about the tree object syntax and a reference from the Git Book.
  • aleclarson
    aleclarson over 9 years
    Does this reset both the local and remote repository, or just one of them?
  • ryenus
    ryenus over 9 years
    @aleclarson, this only reset the current branch in the local repository, use git push -f for propagation.
  • toolbear
    toolbear about 9 years
    I found this answer while looking for a way to start a new project from a project template repository that didn't involve git clone. If you add --hard to git reset and switch HEAD with FETCH_HEAD in the git commit-tree you can create an initial commit after fetching the template repo. I've edited the answer with a section at the end demonstrating this.
  • ryenus
    ryenus almost 9 years
    Note about git checkout --orphan, this command can also be used to create a root commit, but it only does the preparation (things are only staged, not committed), and it must be followed by git commit -m "commit message", which is slower, but can be used interactively so one has a chance to make some change before committing.
  • thebugfinder
    thebugfinder over 8 years
    you are loosing all the repos information by doing this.
  • Daniel Kamil Kozar
    Daniel Kamil Kozar over 8 years
    This is a horrible solution that's needlessly destructive. Please don't use it.
  • Bernard
    Bernard about 8 years
    In Windows prompts, you might have to quote the last parameter: echo "message" | git commit-tree "HEAD^{tree}"
  • E Ciotti
    E Ciotti about 8 years
    git remote -v // take note of the URL git remote add origin <url above>
  • jishi
    jishi almost 8 years
    This was the only way I could introduce a master branch to my newly created github repo after I had pushed a feature branch that I wanted to create a pull request for.
  • Krum
    Krum almost 8 years
    also will break submodules. -1
  • catastrophic-failure
    catastrophic-failure almost 8 years
    Does this take a long time to run?
  • mcont
    mcont over 7 years
    It actually takes some time. About 1 second per commit here.
  • denns
    denns over 7 years
    @kusma in my opinion git bash with mintty should be used anyway in windows.
  • denns
    denns over 7 years
    thanks for this awesome one liner!!! i like to add that if you already pushed your project into your remote, this will only work on unprotected branches. to push this into your repo you will need to force push with push -f
  • Brad
    Brad over 7 years
    Note that this actually seems to leave the history around. You orphan it, but it's still there.
  • eilas
    eilas over 6 years
    If you have many commits it's hard to change 'pick' to 'squash' manually. Use :%s/pick/squash/g in VIM command line to do this faster.
  • Elliot Cameron
    Elliot Cameron over 6 years
    This is definitely not the best way. @ryenus has the best solutions.
  • Elliot Cameron
    Elliot Cameron over 6 years
    You could get rid of that "Caveat" but just using ${1?Please enter a message}
  • not2qubit
    not2qubit over 5 years
    Will this preserve the previous commit messages?
  • David
    David over 5 years
    @not2qubit no this will not preserve the previous commit messages, instead of commit #1, commit #2, commit #3, you'll get all of the changes in those commits packed into a single commit #1. Commit #1 will be the <root> commit you reset back to. git commit --amend --no-edit will commit all changes to the current commit which is <root> without needing to edit the commit message.
  • Erich Kuester
    Erich Kuester about 5 years
    Very useful answer ... but you should be aware that after the amend command you will find yourself in vim editor with its special syntax. ESC, ENTER, :x is your friend.
  • kyb
    kyb almost 5 years
    @AlexanderMills, you should read git help checkout about --orphan
  • Alexander Mills
    Alexander Mills almost 5 years
    can you link to the docs for that so everyone who reads this doesn't have to manually search for it?
  • kyb
    kyb almost 5 years
    easy. here it is git help checkout --orphan
  • dfsg76
    dfsg76 over 4 years
    one should mention that it could be wise to either place the log file outside the repo before git add . or adding all files called **/log, **/*.*log* to .gitignore (choose wildcards wisely) - otherwise that file or backup copies you created from it might end up in public
  • philshem
    philshem over 4 years
    this works find, but I had to do a forced push. be careful! git push -f
  • dark_ruby
    dark_ruby almost 4 years
    this should be accepted answer, because this keeps other branches
  • Mentor
    Mentor about 3 years
    For nano users, use `ctrl+` to trigger search/replace.
  • Bernardo Dal Corno
    Bernardo Dal Corno about 2 years
    will this work for getting multiple template repos? History like "initial commit", "template 1", "template 2", etc.