使用git-filter-branch重命名提交作者的便捷脚本

时间:2014-09-10 19:22:42

标签: git bash shell

为方便起见,本文中的所有内容都可以作为Bash命令运行。

我的following script基于GitHub's own

cat <<EOF > git-unite
#!/usr/bin/env bash

# Usage:
#
# git-unite "User Name" "new@email.com" \
#           "old1@email.com" \
#           "old2@email.com" \
#           ...

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

然而,当我在test repository上运行脚本时,我已经设置了:

git clone https://gist.github.com/dc896ccd9a272a126436.git
cd dc896ccd9a272a126436
git-unite "Test Author" "new@email.com" "hehe2" "hehe"

没有任何改变。有什么麻烦?

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,问题可以解决。有没有办法在不导出这些变量的情况下做到这一点?

1 个答案:

答案 0 :(得分:3)

如果你需要&#34;注射&#34; $name$email,您可以在 git filter-branch之前将其导出,或者您可以使用printf生成bash脚本。

注意: $(echo $old_email)与使用"$old_email"的结果相同,您可以无需分叉进程(如果使用内置函数,则使用bash fork)。< / p>

您还可以使用printf:我的想法是使用%q以引用形式转储不同的变量(适合在bash脚本或eval中使用)。您还可以利用分割脚本来有效地更改运行git命令的用户名/电子邮件。

#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

在你的剧本中:

git filter-branch --force --env-filter $(printf "$(<action.bash)" \
   "$name" "$email" "$old_email") --tag-name-filter cat -- --branches --tags

<强>注意:

  1. $(<foobar)执行与cat foobar相同的工作。当您使用cat时,除了bash之外,您可以创建一个子流程,而不只是阅读文件foobar
  2. 我截断了git filter-branch\)行以避免水平滚动条。
  3. 这个例子很好,但在考虑了一下之后,我认为你不应该这样做,而是以更猛烈的方式:

    #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
    

    而是:

    git filter-branch --force --env-filter "$(printf 'action.bash "%q" "%q" "%q"' \
       "$name" "$email" "$old_email")" --tag-name-filter cat -- --branches --tags
    

    这样,即使脚本什么都不做,如果您以静态方式需要它,它仍然可以工作:

    git filter-branch --force --env-filter 'action.bash name email old_email' \
      --tag-name-filter cat -- --branches --tags