Pruning a Git Repository

August 03, 2024

Removing Entire Files

Did you ever want to delete files entirely from the history of a Git repo, while preserving the commit attributes? Such as time stamps and committer info? Of course, you did!

Run the following command to delete files entirely and to prune empty commits:

# Delete *.term files:
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --prune-empty \
  --index-filter 'git rm -rf --cached --ignore-unmatch \*\*/\*.term' \
  --env-filter 'GIT_COMMITTER_NAME="$GIT_COMMITTER_NAME"; GIT_COMMITTER_EMAIL="$GIT_COMMITTER_EMAIL";' HEAD
  • --index-filter: removes all files matching the pattern **/*.term.
  • --env-filter: preserves the commiter name and email.

Rebasing to Clean out Merge Commits

Never loved merge commits? Don't worry, we can dissolve them.

Save this file as amend_head_commit.sh:

#!/bin/bash

# https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables
GIT_AUTHOR_NAME=$(git show --no-patch --format=%an "$1") \
GIT_AUTHOR_EMAIL=$(git show --no-patch --format=%ae "$1") \
GIT_AUTHOR_DATE=$(git show --no-patch --format=%ad "$1") \
GIT_COMMITTER_NAME=$(git show --no-patch --format=%cn "$1") \
GIT_COMMITTER_EMAIL=$(git show --no-patch --format=%ce "$1") \
GIT_COMMITTER_DATE=$(git show --no-patch --format=%cd "$1") \
git commit --amend --no-edit

Run:

git rebase -i --exec ./amend_head_commit.sh "$UNTIL_COMMIT_HASH"

The todo file needs some adjusting. Run in a different shell:

gawk -i inplace '/^pick /{hash=$2} /^exec /{print $0 " " hash; next} {print}' .git/rebase-merge/git-rebase-todo

The script matches each pick line, remembers the hash...

pick 3874d2ac Something nice
exec ./amend_head_commit.sh

...and appends it to the next exec line:

pick 3874d2ac Something nice
exec ./amend_head_commit.sh 3874d2ac

Exit the editor called by git rebase and let it do its magic. 🪄✨

Yay! 🙌 No more knots (in your stomach)! 😌

Patching E-Mail

Made commits with the wrong email address? I got you covered!

FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch -f --env-filter '
CORRECT_EMAIL="aziz.koeksal@gmail.com"
if [ "$GIT_COMMITTER_EMAIL" = "aziz.koeksal@wrong.com" ]; then GIT_COMMITTER_EMAIL=$CORRECT_EMAIL; fi
if [ "$GIT_AUTHOR_EMAIL" = "aziz.koeksal@wrong.com" ]; then GIT_AUTHOR_EMAIL=$CORRECT_EMAIL; fi
export GIT_COMMITTER_EMAIL GIT_AUTHOR_EMAIL
' $FROM_COMMIT_HASH..HEAD