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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With