Search code examples
gitgit-history-rewrite

Git squash/combine old commits in a bare repository


I'm looking for a way to remove certain old commits from a history for a bare git repo. Normally, I would do git squash and squash everything besides E and D commits. old history

That does not work for a bare repository

fatal: this operation must be run in a work tree

When using more complex tools such as git-filter-repo I can skip commits from the history, but I would lose all the changes made by them (e.g. adding or modifying the file).

How to achieve this result for a bare repository?

new history


Solution

  • You can clone the repository, perform your actions and then push the result. If you don't want to do that, you could use the replace mechanism with grafts and filter-branch to make the grafts permanent.

    DISCLAIMER: This is a destructive operation and you could lose data if you are not careful. Best to have a backup of your full repo.

    git replace --graft D # make D a root commit
    git filter-branch --tag-name-filter cat -- --all # make graft permanent
    

    This leverages the fact that each commit references a full snapshot of your repository at any time (the tree). Removing the parent(s) from a commit makes it a root commit, but keeps its tree. If file1 and file2 first appeared in commits A and C respectively, they are already part of D's tree. Once you have the new root commit D1, its tree will still contain the two files. Since it is now the first commit, it will look like the files first appeared as part of this commit.