Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I delete all commits before a given date in Git history?

Tags:

git

Given a repository, I want to delete all commits that were before a particular commit, or a date in history.

I have around 10000 commits in my repository, and I want to only keep the last 1000 or so, and delete the rest. Basically what I want to do is to say move the first commit forward to X.

At first I thought I could just rebase and squash all of those commits into one, but that causes a lot of merge conflicts during the rebase. If there was a way to squash commits such that the version after the squash is the last commit, that'd work too.

like image 921
Jakub Arnold Avatar asked Mar 13 '15 22:03

Jakub Arnold


People also ask

Can you erase git history?

If you commit sensitive data, such as a password or SSH key into a Git repository, you can remove it from the history. To entirely remove unwanted files from a repository's history you can use either the git filter-repo tool or the BFG Repo-Cleaner open source tool.

How do you remove commits from history?

To modify or remove a commit that is farther back in history, you must move to more complex tools. Git doesn't have a modify-history tool, but you can use the rebase tool to rebase a series of commits into the HEAD. With the interactive tool, you can remove a commit that you want.

Can I delete previous commits?

The easiest way to undo the last Git commit is to execute the “git reset” command with the “–soft” option that will preserve changes done to your files. You have to specify the commit to undo which is “HEAD~1” in this case. The last commit will be removed from your Git history.


1 Answers

Warning: the following is dangerous, as it rewrites history. Always make sure you have a backup of your repo before doing any kind of major history rewriting like this.

Replace the hash in the following with the hash of the parent of the commit you want to have as your new first commit.

git filter-branch --parent-filter '     read parent     if [ "$parent" = "-p 5bdd44e5919cb0a95a9924817529cd7c980f88b5" ]     then         echo     else         echo "$parent"     fi' 

This rewrites the parents of each commit; for most commits, it leaves them the same, but the one with the parent matching the given hash, it replaces with an empty parent, meaning it will now become a commit with no parent. This will detach all of your old history.

Note that if what you want to be your first commit is a merge commit, you'll need to match against something like -p parent1 -p parent2 -p parent3 for each of the parents of the merge commit, in the correct order.

If you want to apply this to all branches and tags instead of just the current branch, pass in --all at the end of the command (after the script).

After you have done this, and checked that it worked properly, you can delete the original branch and run a gc to clean out the now unreferenced commits:

git update-ref -d refs/original/refs/heads/master 

Note that since git tends to try to preserve data, in order to actually free up the space you will also have to remove the commits from your reflog, and then run the gc to clean it up.

git reflog expire --expire-unreachable=all --all git gc --prune=all 

If you are not doing this to save space or eradicate the old commits, you can keep the old history around in a branch, such as git branch old-master refs/original/refs/heads/master; you can even "virtually reattach" it using git replace, at which point you would have two unconnected histories (so when you push to a remote repo, you'll only push the truncated history) but when you look through history in your local repo you will see the full history.

like image 167
Brian Campbell Avatar answered Sep 26 '22 08:09

Brian Campbell