Make a comit the new root of its branch

Sat, 2008-10-11

This is a quick note on how to make a commit the new root commit of its branch, i.e. chop a branch off of it’s previous history at a specific point.

Imagine we have the following commits:

root -- o -- o -- o -- o -- newroot -- A -- B -- C -- D <-- branch

And what we want to do is drop everything before newroot so that we get the following:

                            newroot' -- A' -- B' -- C' -- D' <-- branch

You can accomplish this doing the following:

Create the file .git/info/grafts [*] and insert just one line with the SHA1 of the commit you want to be the new root of your branch. In the example we would put the SHA1 of newroot into .git/info/grafts.

If you now do a git log or gitk you will see that those commands will display newroot as the root of your branch. But nothing will have actually changed in your repository. You can delete .git/info/grafts and the output of git log or gitk will be as before. To actually create a new root you will have to run git-filter-branch, with no arguments:

$ git filter-branch

git-filter-branch will use the .git/info/grafts file to actually apply the change to the repository.

What git-filter-branch will do is create a new commit object with no parents and whose contents will be the same as the commit whose SHA1 you put into .git/info/grafts. As the root commit will have changed, in consequence, new commits will also be created for all commits following the commit the new root commit was created from. Using the example, git filter-branch would create newroot’ from newroot and it will also create A’, B’, C’ and D’ from A, B, C and D.

WARNING: Running git-filter-branch will rewrite history, which can be a problem.

[*] From the gitrepository-layout manual page:


This file records fake commit ancestry information, to pretend the set of parents a commit has is different from how the commit was actually created. One record per line describes a commit and its fake parents by listing their 40-byte hexadecimal object names separated by a space and terminated by a newline.