Skip to content

Instantly share code, notes, and snippets.

@major0
Last active May 21, 2024 05:02
Show Gist options
  • Save major0/ac08c371bf3bd010190d4391c5eea8f2 to your computer and use it in GitHub Desktop.
Save major0/ac08c371bf3bd010190d4391c5eea8f2 to your computer and use it in GitHub Desktop.
Write Author Name/Email in git's commit history.
#!/bin/sh
# git-rewrite-author.sh
#
# Rewrite git-commit author's Name/EmailAddress.
#
# Based on previous versions of this tool This version runs in POSIX compliant
# shell and .. IMHO .. cleans up a large portion of the filter logic and the
# core syntax such that is is readable and not full of back-slash escape hell.
#
# Other versions:
# - https://gist.github.com/Hackl0us/490f56dbc135b2fae47d8e2b97c9d8b3
# - https://gist.github.com/octocat/0831f3fbd83ac4d46451
# - https://gist.github.com/frz-dev/adf8c2c7275da1369e0cc340feda0ba0
# - https://gist.github.com/octocat/0831f3fbd83ac4d46451#gistcomment-2178506
error() { echo "error: $*"; }
die() { echo "error: $*"; exit 1; }
usage() {
if test "$#" -gt '0'; then
error "error: $*"
echo "try '${0} --help'" >&2
exit 1
fi
sed -e 's/^ //' << END_OF_USAGE
usage: ${0} [<options>] [--] [<git filter options>]
Options:
-n, --cur-name "First Last" Match commits with this name
-N, --new-name "First Last" New name to write to commits
-e, --cur-email EMAIL Match commits with this email address
-E, --new-email EMAIL New email address write to commits
-c, --current Use current git configured user/email when updating commits
--dry-run Do not actually make any changes
-x, --trace Enable execution tracing
-h, --help Display this help
Notes:
- For all matches, both author/committer name and email will be
replaced (if both are provided)
- You can pass the git-filter option --force to foricbly overwrite
backups if you need to run the command multiple times.
- To push the changes use the following command:
git push --force --tags origin 'refs/heads/*'"
END_OF_USAGE
# requests for help are not an error
exit 0
}
## gen_filter
# $1: cur_name
# $2: cur_email
# $3: new_name
# $4: new_email
gen_filter() {
cat<<END_OF_FILTER
if [ "\${GIT_AUTHOR_NAME}" = "${1}" ] || [ "\${GIT_AUTHOR_EMAIL}" = "${2}" ]; then
test -z "${3}" || export GIT_AUTHOR_NAME="${3}"
test -z "${4}" || export GIT_AUTHOR_EMAIL="${4}"
fi
END_OF_FILTER
}
##
# parse args
current='false'
dry_run='false'
while test "$#" -gt '0'; do
case "$1" in
(-h|-help|--help)
usage;;
(-n|--cur-name)
cur_name="$2"
shift;;
(-N|--new-name)
new_name="$2"
shift;;
(-e|--cur-email)
cur_email="$2"
shift;;
(-E|--new-email)
new_email="$2"
shift;;
(-c|--current)
current='true';;
(--dry-run)
dry_run='true';;
(-x|--trace)
set -x;;
# POSIX CLI handling
(--) shift; break;;
(-*) usage "unknown option '$1'";;
(*) break;;
esac
shift
done
##
# Validate our inputs
if ${current}; then
new_email="$(git config user.email)"
new_name="$(git config user.name)"
fi
if test -z "${cur_name}" && test -z "${cur_email}"; then
usage 'must specify a current name or email address to match'
fi
if test -z "${new_name}" && test -z "${new_email}"; then
usage 'must specify a new name or email address to write'
fi
##
# Just do it
if "${dry_run}"; then
echo git filter-branch \
--env-filter "$(gen_filter "${cur_name}" "${cur_email}" "${new_name}" "${new_email}")" \
"$@" --tag-name-filter cat -- --branches --tags
else
exec git filter-branch \
--env-filter "$(gen_filter "${cur_name}" "${cur_email}" "${new_name}" "${new_email}")" \
"$@" --tag-name-filter cat -- --branches --tags
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment