Script de conveniencia para renombrar autores de commits con git-filter-branch

Para su comodidad, todo en esta publicación se puede ejecutar como un command Bash.

Tengo el siguiente script basado en el propio de GitHub :

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 

Sin embargo, cuando ejecuto el script en un repository de testing que configuré:

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

nada ha cambiado ¿Cuál es el problema?

 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 

Usando exportaciones , el problema puede ser resuelto. ¿Hay alguna manera de hacer esto sin exportar estas variables?

Si necesita "inyectar" el $name y $email , puede exportarlo antes de git filter-branch , o puede generar un script bash utilizando printf .

Aviso: el $(echo $old_email) tiene el mismo resultado que el uso de "$old_email" y se puede bifurcar un process por nada (bueno, si bash tenedor cuando usas builtins).

También puede usar printf : la idea es usar %q para volcar la variable diferente en una forma citada (adecuada para uso en un script bash, o en eval). También obtiene la ventaja de dividir el script que efectivamente cambia el nombre de usuario / correo electrónico de aquel que ejecuta el command 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 

Y en tu script:

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

Darse count:

  1. $(<foobar) hace el mismo trabajo que cat foobar . Cuando usas cat , puedes sin embargo bash para crear un subprocess en lugar de solo leer el file foobar .
  2. Trunqué la línea git filter-branch (the \ ) para evitar barras de desplazamiento horizontales.

Este ejemplo está bien, pero después de pensarlo un poco, creo que no deberías hacerlo así, sino de una manera más contundente:

 #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 

Y en cambio:

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

De esa forma, incluso si el script no hace nada, aún funcionaría si lo necesitara de manera estática:

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