Reputation: 3658
For convenience, everything in this post is runnable as a Bash command.
I have the following script based off of GitHub's own:
cat <<EOF > git-unite
#!/usr/bin/env bash
# Usage:
#
# git-unite "User Name" "[email protected]" \
# "[email protected]" \
# "[email protected]" \
# ...
name="$1"
email="$2"
git config user.name "$name"
git config user.email "$email"
shift 2
for old_email in $*; do
echo "changing $old_email"
git filter-branch --force --env-filter '
if [ "$GIT_COMMITTER_EMAIL" = "$(echo $old_email)" ]
then
export GIT_COMMITTER_NAME="$(echo $name)"
export GIT_COMMITTER_EMAIL="$(echo $email)"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$(echo $old_email)" ]
then
export GIT_AUTHOR_NAME="$(echo $name)"
export GIT_AUTHOR_EMAIL="$(echo $email)"
fi
' --tag-name-filter cat -- --branches --tags
done
EOF
chmod u+x git-unite
Yet, when I run the script on a test repository I've set up:
git clone https://gist.github.com/dc896ccd9a272a126436.git
cd dc896ccd9a272a126436
git-unite "Test Author" "[email protected]" "hehe2" "hehe"
nothing is changed. What is the trouble?
changing hehe2 Rewrite be8d35aca918caaa86035ab8f8011d5ff6131939 (3/3) WARNING: Ref 'refs/heads/master' is unchanged changing hehe Rewrite be8d35aca918caaa86035ab8f8011d5ff6131939 (3/3) WARNING: Ref 'refs/heads/master' is unchanged
Using exports, the problem can be solved. Is there any way to do this without exporting these variables?
Upvotes: 1
Views: 820
Reputation: 11959
If you need to "inject" the $name
and $email
, you can export it before git filter-branch
, or you can generate a bash script using printf
.
Notice: the $(echo $old_email)
does the same result than using "$old_email"
and you could fork a process for nothing (well, if bash fork when you use builtins).
You can also use printf
: the idea is to use the %q
to dump the different variable in a quoted form (suitable for use in a bash script, or in eval). You also gain the advantage to split the script that effectively change the user name/email from the one that run the git command.
#action.bash:
declare -r _NEW_NAME="%q"
declare -r _NEW_EMAIL="%q"
declare -r _OLD_EMAIL="%q"
if [ "$GIT_COMMITTER_EMAIL" = %v ]
then
export GIT_COMMITTER_NAME="${_NEW_NAME}"
export GIT_COMMITTER_EMAIL="${_NEW_EMAIL}"
fi
if [ "$GIT_AUTHOR_EMAIL" = "${_OLD_EMAIL}" ]
then
export GIT_AUTHOR_NAME="${_NEW_NAME}"
export GIT_AUTHOR_EMAIL="${_NEW_EMAIL}"
fi
And in your script:
git filter-branch --force --env-filter $(printf "$(<action.bash)" \
"$name" "$email" "$old_email") --tag-name-filter cat -- --branches --tags
Notice:
$(<foobar)
does the same work that cat foobar
does. When you use cat
, you might however except bash to create a sub-process instead of just reading the file foobar
.git filter-branch
(the \
) line to avoid horizontal scrollbars. This example is fine, but after thinking about it a little, I think you should not do it like that, but in a more bash way:
#action.bash:
declare -r _NEW_NAME="$1"
declare -r _NEW_EMAIL="$2"
declare -r _OLD_EMAIL="$3"
if [ "$GIT_COMMITTER_EMAIL" = %v ]
then
export GIT_COMMITTER_NAME="${_NEW_NAME}"
export GIT_COMMITTER_EMAIL="${_NEW_EMAIL}"
fi
if [ "$GIT_AUTHOR_EMAIL" = "${_OLD_EMAIL}" ]
then
export GIT_AUTHOR_NAME="${_NEW_NAME}"
export GIT_AUTHOR_EMAIL="${_NEW_EMAIL}"
fi
And instead:
git filter-branch --force --env-filter "$(printf 'action.bash "%q" "%q" "%q"' \
"$name" "$email" "$old_email")" --tag-name-filter cat -- --branches --tags
That way, even if the script does nothing at all, it would still work if you needed it in a static way:
git filter-branch --force --env-filter 'action.bash name email old_email' \
--tag-name-filter cat -- --branches --tags
Upvotes: 3