So, you have a pet project in a private repository. It turns out that the project could work as an open source project. When you looked into its history, you find some stuff that must not go public. You want to delete those early commits and keep recent commits.
Note that this article uses
master
as the default branch name. Recent repositories might usemain
for it.
Note that the instructions provided in this document might destroy your working repository. Be warned.
Let’s imagine our repository state like this:
master: O-----X--------H
O
is original first commitX
is the commit you want to be as the new first commitH
is the head, i.e., latest commit
Note: I recommend to delete all but
master
branch before we start the operation.
We will need to get the commits
ORev
is the commit ofO
– use the commandgit rev-list --max-parents=0 HEAD
XRev
is the commit ofX
. You will need to usegit log
or a Git GUI to find the commit.
$ git checkout -b reroot <XRev>
$ git reset --soft <ORev>
$ git commit --amend -m "My new initial commit"
The above commands do the following:
- First, it checks out
X
into a new branch namedreroot
- Resets the original commit without reseting the files and index
- Creates a new commit with files from commit
X
After running above commands, we are not at the following state.
master: O-----X--------H
reroot: R--------I
We are now at branch reroot
and at commit R
.
Next step is to get RRev
, which is the commit R
– use the command git rev-list --max-parents=0 HEAD
. Yes, the same command we used to obtain ORev
. We use the same command because we are now at commit R
, so, the first commit will be R
.
$ git checkout master
$ git replace <XRev> <RRev>
$ git filter-branch master
$ rm -rf .git/refs/original .git/refs/replace
$ git branch -d reroot
They do the following:
- First, it checks out the default branch (
master
in this case) - Replaces the reference to
XRev
withRRev
- Rewrites the history
- Removes temporary references
- Deletes the now unused
reroot
branch
End result will look like this:
master: R--------I
Please note that at the end of the process, the history is practically rewritten. The new head commit will not be the same as the old head commit and, no, you can’t have the same head commit after re-root as it is logically impossible.