Search code examples
gitgit-squash

squash multiple historical commits


I have an SVN history of some 20000 commits. I moved to Git and kept the history. Now i'm at 26000 commits, and I want to squash all commits from 1 to 20000.

  • Initial commit: a4f5d18
  • Migration to git: 5a42d81
  • HEAD: 933eff

I tried checking out 20000 and rebasing:

git checkout 5a42d81
git rebase squash a4f5d18

But i get:

fatal: Needed a single revision
invalid upstream squash

Solution

  • If I understand correctly, your main branch looks like:

    master: 1 -> 2 -> 3 -> .. -> 20000 -> A (First non migrated commit) -> B -> C -> ..

    And you'd like to get to:

    master: 1' (All your migrated commits) -> A' -> B' -> C' -> ..

    I think you could follow the approach of using git rebase HEAD~26000 (first commit hash probably easier) and changing pick to squash, but it might be fiddly/time consuming.

    One potentially viable solution would be to create a new commit with the contents of your first 20000. Probably worth testing on a backup branch.

    git checkout <last migrated commit hash> -b backup-master

    backup-master: 1 -> 2 -> 3 -> .. -> [20000] -> A (First non migrated commit) -> B -> C -> ..
                                           ^- you are here.
    

    git reset --soft <first migrated commit hash>

    backup-master: [1] -> 2 -> 3 -> .. -> 20000 -> A (First non migrated commit) -> B -> C -> ..
                    ^- you are here         ^- the working directory tree/index reflects this commit 
    

    Amend your initial commit contents/message (or create a new commit if you'd prefer).

    git commit --amend

    Now backup-master should contain your squashed migration commits, let's move the new commits.

    git checkout master

    git checkout -b master-rebase (just in case we mess something up).

    git rebase backup-master - I believe this will work because git is aware of the merges required to successfully rebase. If this works, you should be done, master-rebase will contain your desired result.

    If this fails, you might have better success with rebase --onto.

    git rebase --onto <destination commit> <parent of first desired commit> <last desired commit>

    I.e. git rebase --onto backup-master <A>~1 master`

    If this works it will place you on a commit that isn't currently on any branch, so you'll need to create one:

    git checkout -b rebase-success.

    A more thorough explanation of rebase --onto can be found here.