4

Having all binary files added to my git history by mistake, I have a git repo of size about 50 MB. How can I remove all commits before a specific commit from history? For example having following history, I want to remove all commits before C2 as if C2 was first commit. (Actually C2 may be a merge commit)

I already tried git-filter-branch which simply doesn't do what I need, and this script which seems to be quiet popular, though it removes entire history; leaving a single commit.

History

Community
  • 1
  • 1
sorush-r
  • 10,490
  • 17
  • 89
  • 173

3 Answers3

2

One way to accomplish this would be to use git filter-branch --parent-filter, and drop C1 from the set of parents.

git filter-branch --parent-filter "sed -e 's/-p $(git rev-parse C1)//'" -- --all

$(git rev-parse C1) is used to get the full 40-character commit ID, and can be replaced if you already know what it is.

If some of C1's ancestors are being pulled in via merges or so on, you can also drop them in the same way, or use a more complex script like

git filter-branch --parent-filter '
    for i in $(cat); do
        if [[ $i = -* ]] || git merge-base --is-ancestor "$i" C1; then
            continue
        else
            echo -n " -p $i"
        fi
    done
' -- --all
ephemient
  • 198,619
  • 38
  • 280
  • 391
0

I would suggest a new orphan branch with cherry-picking afterwards.

That way you can check everything and maybe even leave the old state as a backup on your machine.


To start things off create a new branch at the point of C2

$ git checkout <Hash of C2>
$ git checkout --orphan new-master

git checkout --orphan will create a new branch with no parents.

At this point add or remove files as you like with

  • git add Add files for this new first commit
  • git rm --cached Remove files for the new first commit but leave them on the hard drive
  • git rm Delete files for the first commit as well as from the hard drive

Make sure that all files are present that are going to be changed by later commits, otherwise the cherry-picking might get very messy.

Now git commit to get your new initial commit ready.

Last but not least simply reapply all the other commits by cherry-picking the correct ranges.

$ git branch new-starting-point # for future reference
$ git cherry-pick ^C2 master

And repeat these steps for all the other branches that need a new history.

$ git checkout -b new-server new-starting-point
$ git cherry-pick ^C2 server

If you want to check for correctness you might want to do some git diff master new-master.

To clear space delete all old branches (so that the old commits are not reachable anymore). At the following step you might lose data. Be sure you still have everything you want: A git gc should be all you need to clear space.

AnimiVulpis
  • 2,670
  • 1
  • 19
  • 27
  • Cherry-picking (and rebasing) doesn't preserve anything other than a linear history - you'll have to manually deal with merges, if you want them in the new history. – ephemient Apr 19 '17 at 16:46
  • @ephemient you are completely right. If the history after your new start commit has merges `cherry-picking` is no real option. It would be cleaner to use `filter-branch`. If you expand your answer and explain the options I will happily upvote it – AnimiVulpis Apr 19 '17 at 16:57
-1

Using git filter-branch, rather than erasing the whole commit, you can remove the binary files from the commit :

git filter-branch --index-filter 'git rm --cached -rf bin/ && git rm --cached -f ./a.out' -- --all
LeGEC
  • 46,477
  • 5
  • 57
  • 104