Skip to content

Instantly share code, notes, and snippets.

@liamnewmarch
Last active August 13, 2021 14:04
Show Gist options
  • Save liamnewmarch/69df7ddfa3662ab22196f54ba22bcf86 to your computer and use it in GitHub Desktop.
Save liamnewmarch/69df7ddfa3662ab22196f54ba22bcf86 to your computer and use it in GitHub Desktop.
Correct git author name and email for a branch by rewriting its history.
#!/bin/bash
# shellcheck disable=SC2016
# Rewrite git history.
readonly VERSION='2.0.5'
# Exit immediately if a command exits with a non-zero status.
set -e
exit_with_help_text() {
printf '
usage:
git fix-author (--set-name=<name> | --set-email=<email>) [<args>]
args:
-v, --version Print version number.
-h, --help Print this help text.
-n, --set-name Rewrite commits to have this author name.
-e, --set-email Rewrite commits to have this author email.
-N, --filter-name Only rewrite commits originally by this author name.
-E, --filter-email Only rewrite commits originally by this author email.
description:
This command corrects git author name and email for a branch by by rewriting
its history. By default it will update all commits; use the optional filter
flags to update commits from a specific author.
example:
git fix-author -e bob@example.com -E alice@example.com
'
exit 0
}
exit_with_version_text() {
printf '%s\n' "$VERSION"
exit 0
}
exit_with_unknown_param_error() {
printf 'Error: Unknown parameter %s.\n' "$1"
exit 1
}
clean_up() {
unset filter replace_email replace_name search_email search_name
}
get_params() {
for _; do
case "$1" in
# Version param.
-v|--version)
exit_with_version_text
;;
# Help param.
-h|--help)
exit_with_help_text
;;
# Replacement email.
-e|--set-email)
shift
replace_email=$1
;;
--set-email=*)
replace_email="${1#*=}"
;;
# Replacement name.
-n|--set-name)
shift
replace_name=$1
;;
--set-name=*)
replace_name="${1#*=}"
;;
# Search email.
--filter-email)
shift
search_email=$1
;;
--filter-email=*)
search_email="${1#*=}"
;;
# Search name.
--filter-name)
shift
search_name=$1
;;
--filter-name=*)
search_name="${1#*=}"
;;
# Unknown params.
*)
exit_with_unknown_param_error "$1"
;;
esac
shift
done
# Exit with help text if neither replace_name nor replace_email has been set.
if [[ -z "$replace_name" ]] && [[ -z "$replace_email" ]]; then
exit_with_help_text
fi
}
create_commit_filter() {
# We use double quoted strings to build our filter and these variables need
# to be passed verbatim to git filter-branch. It's convenient to store these
# as variables to avoid escaping later.
local git_author_email='"$GIT_AUTHOR_EMAIL"'
local git_author_name='"$GIT_AUTHOR_NAME"'
local default_filter='git commit-tree "$@"'
# Starting point for the commit filter.
filter="$default_filter;"
# If specified, add name replacing to filter command.
if [[ -n "$replace_name" ]]; then
filter="
GIT_COMMITTER_NAME='$replace_name';
GIT_AUTHOR_NAME='$replace_name';
$filter
"
fi
# If specified, add email replacing to filter command.
if [[ -n "$replace_email" ]]; then
filter="
GIT_COMMITTER_EMAIL='$replace_email';
GIT_AUTHOR_EMAIL='$replace_email';
$filter
"
fi
# If specified, add name search to filter command.
if [[ -n "$search_name" ]]; then
filter="
if [[ $git_author_name = '$search_name' ]]; then
$filter
else
$default_filter;
fi
"
fi
# If specified, add email search to filter command.
if [[ -n "$search_email" ]]; then
filter="
if [[ $git_author_email = '$search_email' ]]; then
$filter
else
$default_filter;
fi
"
fi
}
execute_commit_filter() {
git filter-branch --commit-filter "$filter" --force HEAD
}
main() {
get_params "$@"
create_commit_filter
execute_commit_filter
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment