30

I'm looking for a way to squash all git commits into a single big commit in master branch. I fully understand the consequences of what I'm trying to do, no need to explain that this is dangerous or that it's not the right way to go - I want to lose all my history and turn this repository into a single big commit.

The main problem is: I have no other living branches, no local commits, and all of the previous commits have already been pushed to remote master.

Hacky scripts are also welcome.

1
  • First thing that comes to my mind: git rebase -i <first-commit-hash>. Then use your editor's query replace functionality to replace all the pick by squashand save. Then git push --force.
    – JB Nizet
    Commented Mar 24, 2019 at 16:33

2 Answers 2

71

I would use git reset --soft:

git reset --soft id-of-first-revision-of-master
git commit --amend -m "single commit for master"

Then you can git push --force wherever you need that new branch.

Update

I can think of another simple way to do it, with more git commands:

git checkout --orphan some-branch
git commit -m "First commit"
git branch -f master # move the local branch
git checkout master
git branch -d some-branch # delete the temp branch

It could also be done like this, in a more hackish fashion:

git commit-tree -m "First commit" HEAD^{tree}

That will write a revision.... if you check it out, you will have a single revision, content will be exactly like it is where you were standing before.... feel free to move the local branch (as explained in the previous recipe)

4
  • @eftshift0 the git command git checkout --orphan -b some-branch is causing an error fatal: 'some-branch' is not a commit and a branch '-b' cannot be created from it. Did you actualy mean git checkout --orphan some-branch?
    – Boris
    Commented Mar 25, 2021 at 17:46
  • might make sense without the -b. I mean, if you are checking out an --orphan, then it's a new branch, right? Then no need to specify -b. Perhaps that's the logic.
    – eftshift0
    Commented Mar 25, 2021 at 17:56
  • I did first five commands here, and then 'git push --force-with-lease' - which did the trick. Only 1 commit on my remote, and my commits with sensitive data were gone.
    – Vince Hill
    Commented Mar 8 at 5:08
  • 5 years to push? Going over a slow dial-up?
    – eftshift0
    Commented Mar 8 at 7:30
16

You can still modify the history on the upstream by using git push --force-with-lease. However you have to be aware of the consequences.

Using git push --force will create a parallel tree on your upstream so all the developers may find themselves lost in a legacy branch.

In order to squash your history, simply do:

git rebase -i HEAD~10

Where 10 is the number + 1 of commits you want to squash together. If you want to squash all the commits, then just refer your <first-commit-hash> instead of HEAD~10. Then on the editor you select squash for all the commits you want to group together. You can do search/replace: pick by squash

Once done, simply push your changes:

git push --force-with-lease

I would never recommend to do --force because if another developer has pushed a commit in the meantime you will erase its move. By using --force-with-lease Git will prevent you to push if somebody else has pushed on the top of your last change (see this question for more details).

4
  • 24
    I believe instead of using the <first-commit-hash>, you can use git rebase -i --root to rebase all the way to the initial commit Commented Mar 24, 2019 at 17:06
  • 3
    The --root parametr does the trick for me. The solution whit HEAD~number or first commit hash does not work for me - they finish with two commits - not the only one.
    – eNca
    Commented Oct 18, 2020 at 20:41
  • 1
    Perfect @RuChernChong Thank you, using --root also did the trick for me Commented Apr 12, 2023 at 5:37
  • To be clear, it should be git push origin HEAD:main --force-with-lease Commented Jun 30 at 7:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.