【问题标题】:Convenience script for renaming commit authors with git-filter-branch使用 git-filter-branch 重命名提交作者的便捷脚本
【发布时间】:2014-09-10 19:22:42
【问题描述】:

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

我有基于GitHub's ownfollowing script

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

【问题讨论】:

  • 如果您从该过滤器分支脚本中打印出$email,它是否有任何价值? (还有为什么$(echo $name)$(echo $email) 而不仅仅是$name$email?)
  • 我之前试过不带echo,但没用。我相信这是因为该命令在某种子外壳中运行(我并不真正了解这里的术语)并且这些变量丢失了。我宁愿不导出它们,因为它们对其他人没有意义。经过测试,$email 似乎无论如何都是空的。
  • 当我导出时,name 仍然是空的,我得到空标识错误。
  • 这个new version 的脚本现在可以正常工作了,但是有什么方法可以在不导出的情况下做到这一点?我正在编辑原始问题帖子以反映这个问题。
  • 它为当前 shell 运行的程序导出它(它把它放到进程环境中以便它被继承)。它不会影响任何当前正在运行的程序。

标签: git bash shell


【解决方案1】:

如果您需要“注入”$name$email,您可以将其导出之前 git filter-branch,或者您可以使用printf 生成一个bash 脚本。

注意:$(echo $old_email) 的结果与使用 "$old_email" 的结果相同,并且您可以免费分叉一个进程(好吧,如果在使用内置函数时 bash 分叉)。

您也可以使用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. $(&lt;foobar)cat foobar 所做的工作相同。但是,当您使用 cat 时,您可能会在 bash 之外创建一个子进程,而不仅仅是读取文件 foobar
  2. 我截断了git filter-branch\)行以避免水平滚动条。

这个例子很好,但稍微想了想,我觉得你不应该那样做,而是用更 bash 的方式:

#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

【讨论】:

  • 是的,这基本上是一个引用问题。我没有时间写一个完整的替代答案,但请注意--env-filter '...$x' 在单引号内有$x,这会阻止它被扩展。
  • 谢谢!问题:这是我今天第二次看到$(&lt;file) 构造——它实际上是做什么的?
  • 我再次更新了我的答案。基本上是一样的,但是有一个例子,你不编写临时 bash 文件,而是将 name/email/old_email 作为参数传递给它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多