I have git repository with many, many (2000+) commits, for example:
l-- m -- n
/
a -- b -- c -- d -- e -- f -- g -- h -- i --
In order not to lose some history; better first take a copy of your repository :). Here we go: (<f>
is the sha of the commit f that you want to be the new root commit)
git checkout --orphan temp <f> # checkout to the status of the git repo at commit f; creating a branch named "temp"
git commit -m "new root commit" # create a new commit that is to be the new root commit
git rebase --onto temp <f> master # now rebase the part of history from <f> to master onthe temp branch
git branch -D temp # we don't need the temp branch anymore
If you have a remote where you want to have the same truncated history; you can use git push -f
. Warning this is a dangerous command; don't use this lightly! If you want to be sure that your last version of the code is still the same; you can run git diff origin/master
. That should show no changes (since only the history changed; not the content of your files).
git push -f
The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos
For those who get alot of merge conflicts (and broken results) with rebase --onto
I'd like recommend this script which uses git filter-branch
:
#!/bin/sh
cut_sha="$1"
branch="$2"
git filter-branch \
--parent-filter "sed -e 's/-p $cut_sha[0-9a-f]*//'" \
--prune-empty \
-- $branch
git for-each-ref --format='%(refname)' refs/original | \
while read ref
do
git update-ref -d "$ref"
done
git reflog expire --expire=0 --all
git repack -ad
git prune
Source: https://github.com/adrienthebo/git-tools/blob/master/git-truncate
Instructions:
git-truncate.sh
).master
).2c75a32
) AND ensure the commit has no branches in parallel!$ ./git-truncate.sh 2c75a32 master
.IMPORTANT: The SHA must be "part" of the branch and it must be the first commit you want to delete. Don't pass the first commit you want to keep (the new "beginning of repository" commit)!
A possible solution for your problem is provided by git clone using the --shallow-since option. If there is only a small number of commits since f
and there is no trouble counting them then you can use the --depth option.
The second option (--depth
) clones only the specified branch. If you need additional branches you can then add the original repo as a remote and use git fetch and to retrieve them.
When you are pleased with the result, remove the old repository and rename the new one to replace it. If the old repository is remote then re-create it after removal and push from the new repo into it.
This approach has the advantage of size and speed. The new repo contains only the commits you want and there is no need to run git prune
or git gc
to remove the old objects (because they are not there).