1

Is there a way of squashing lots of commits in a Git branch's history into one? I know you can git rebase -i but the trouble is that the branch's history has maybe 10,000 commits, and interactive rebase will give me a huuuuge file with all commits listed. I just want to say "squash all commits from x to y into one". I want to squash all commits in the branch's history that are older than 6 months into one.

Jez
  • 27,951
  • 32
  • 136
  • 233
  • Possible duplicate of [Git: How to squash all commits on branch](https://stackoverflow.com/questions/25356810/git-how-to-squash-all-commits-on-branch) – phd May 29 '18 at 13:51

2 Answers2

2

Suppose the branch is master.

If x is the root commit,

git checkout --orphan tmp y
git commit
git rebase --onto tmp y master
git branch -D tmp

If x is not the root commit,

git checkout y
git reset x --soft
git commit
git rebase --onto HEAD y master
ElpieKay
  • 27,194
  • 6
  • 32
  • 53
1

If you mean that you only want 6 months of history, then you might want to consider using a shallow clone. See the --shallow-since option of git clone (https://git-scm.com/docs/git-clone).

This preserves the identities of your commits, so it's unlikely to cause disruption even when used with a shared origin repository. It also affords you the option of keeping the deeper history either on origin, or in an archive repository. Again because commit identity is preserved, you can easily graft any new work done on the shallow repo with the deep history if need arises for future trouble-shooting or whatever.


The other alternative is to do a history rewrite that creates a new commit in place of a series of old commits. If the repository is shared, this will "break" everyone else's repo. You'll need to coordinate with them to ensure they "fix" their repos in the right way, or else they are likely to undo your rewrite. See "Recovering from Upstream Rebase" in the git rebase docs (https://git-scm.com/docs/git-rebase) as this is the applicable situation no matter how you do your rewrite.

The procedure suggested by ElpieKay will work as long as your history is strictly linear, but it may be time-consuming. If there are multiple branches, or if the part of history you're preserving includes merges, this becomes much less straightforward.

A more generally applicable solution is to use git filter-branch with the --parent-filter. Given a history like

... x -- O -- O -- O -- y ...

and wanting to reduce it to

... xTHRUy ...

(where xTHRUy is a single commit) you could do something like this:

export NEW_PARENTS=$(git show --format='%P' --no-patch) x
export OLD_PARENTS=$(git show --format='%P' --no-patch) y

where x and y are expressions that resolve to the corresponding commits. This could be the commit ID, or something like master~137 if you happen to know a "path" to the correct commit. Then

git filter-branch --parent-filter "sed \"s/$OLD_PARENTS/$NEW_PARENTS/\"" -- --all
Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52