プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Git で過去のコミットのメールアドレスを修正する

GitHub の設定でprivateのメールアドレスでのコミットは受け付けないように設定しています。

が、既に過去に公開したリポジトリ(避難所検索@伊勢のリポジトリ)をよく見てみたら、どうもこの設定前にコミットしたらしく、メールアドレスとして private のメールアドレスがばっちりと載ってしまっていました。

本当はよくないのですが、forkとかはされていなかったので、過去のコミットのメールアドレスを変更することにしました。 clone してくれた方にはご迷惑をおかけしてしまいますが、平にご容赦ください。

ということで、その際の作業メモです。

変更方法

ちょっと調べると過去コミットのメールアドレスの変更方法は簡単に見つかりました。git filter-branch を使えという方法です。

version control - How to change the author and committer name and e-mail of multiple commits in Git? - Stack Overflow

なかでも、GitHub が変更用のスクリプトを公開してくれているので、それを利用することにします。

help.github.com

ここで紹介されているスクリプトの内容を rewrite というファイルに保存して実行権限を与えておきます。

変更作業

実際の作業は、上記リンク先の説明に従って進めます。

最初にローカル上に作業用リポジトリをクローンして、作成した rewrite スクリプトを実行して Author/Commiter の書き換えを行います。

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse$ ./rewrite
Rewrite 5127723affae8988878905cd4c88826f1ffc4614 (59/61) (17 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten
Ref 'refs/tags/1.0beta' was rewritten
1.0beta -> 1.0beta (5127723affae8988878905cd4c88826f1ffc4614 -> 03acac1d9dd4b51aaf193e740e7ca464a362f05a)
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse$

git log でログを表示させると

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse$ git log
commit 03acac1d9dd4b51aaf193e740e7ca464a362f05a (HEAD -> master, tag: 1.0beta)
Author: junichim 
Date:   Fri Jul 7 11:10:53 2017 +0900

    署名付きapk作成時にファイル名を自動で設定する処理を追加

commit 65016265bd4c026bdb20edf1bd50cf47a8921573
Author: junichim 
Date:   Fri Jul 7 10:51:52 2017 +0900

    現在位置の最寄りの避難所を任意のタイミングで検索できる機能を追加

commit d37b76b2a5a76061fd06a42c210e24bbe8f0ab28
Author: junichim 
Date:   Fri Jul 7 10:39:59 2017 +0900

    アプリバーがスワイプで消えてしまい、元に戻せないのを修正

確かに変更されています。

これで、問題ないかと思って、push すると

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse$ git push --force --tags origin 'refs/heads/*'
Username for 'https://github.com': junichim
Password for 'https://junichim@github.com':
Counting objects: 889, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (217/217), done.
Writing objects: 100% (889/889), 3.12 MiB | 1.20 MiB/s, done.
Total 889 (delta 427), reused 830 (delta 427)
remote: Resolving deltas: 100% (427/427), done.
remote: error: GH007: Your push would publish a private email address.
remote: You can make your email public or disable this protection by visiting:
remote: http://github.com/settings/emails
To https://github.com/junichim/ShelterAtIse.git
 + 5127723...03acac1 master -> master (forced update)
 ! [remote rejected] 1.0beta -> 1.0beta (push declined due to email privacy restrictions)
error: failed to push some refs to 'https://github.com/junichim/ShelterAtIse.git'
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse$

のようにpushが失敗しました。

実は作業用リポジトリをクローンする際に、--bare オプションを付けていませんでした。なので、それが原因かと考え、一度ローカルリポジトリを消して、--bare をつけて再度実行してみます。

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ ../rewrite
Rewrite 03acac1d9dd4b51aaf193e740e7ca464a362f05a (119/119) (32 seconds passed, remaining 0 predicted)
WARNING: Ref 'refs/heads/master' is unchanged
Ref 'refs/tags/1.0beta' was rewritten
1.0beta -> 1.0beta (5127723affae8988878905cd4c88826f1ffc4614 -> 03acac1d9dd4b51aaf193e740e7ca464a362f05a)
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$

で、pushします。

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ git push --force --tags origin 'refs/heads/*'
Username for 'https://github.com': junichim
Password for 'https://junichim@github.com':
Counting objects: 1, done.
Writing objects: 100% (1/1), 150 bytes | 75.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0)
remote: error: GH007: Your push would publish a private email address.
remote: You can make your email public or disable this protection by visiting:
remote: http://github.com/settings/emails
To https://github.com/junichim/ShelterAtIse.git
 ! [remote rejected] 1.0beta -> 1.0beta (push declined due to email privacy restrictions)
error: failed to push some refs to 'https://github.com/junichim/ShelterAtIse.git'
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$

相変わらず失敗します。何がまずいんだろうか?

しばらくあれこれ調べて考えていると、tag にも作成者があるんじゃないかな、と思い立ちました。

で、tagの情報を表示してみると

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ git show 1.0beta
tag 1.0beta
Tagger: mor 
Date:   Fri Jul 7 15:30:58 2017 +0900


commit 03acac1d9dd4b51aaf193e740e7ca464a362f05a (HEAD -> master, tag: 1.0beta)
Author: junichim 
Date:   Fri Jul 7 11:10:53 2017 +0900

    署名付きapk作成時にファイル名を自動で設定する処理を追加

(以下略)

当たりのようですね。tagのTaggerのところに、ばっちりprivateのメールアドレスが載ってました。

どうもこれが原因でpushに失敗していたようです。

tag の削除

tag も作成者(Tagger)が変更できるかと思って調べてみると、どうもtagについてはできないようです。 ということで、諦めて、tagについてはローカルおよびリモートで削除します。

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ git tag -d 1.0beta
Deleted tag '1.0beta' (was dbd1930)
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ git push --delete origin 1.0beta
Username for 'https://github.com': junichim
Password for 'https://junichim@github.com':
To https://github.com/junichim/ShelterAtIse.git
 - [deleted]         1.0beta
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$

そのあとに再度、pushします。

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$ git push --force --tags origin 'refs/heads/*'
Username for 'https://github.com': junichim
Password for 'https://junichim@github.com':
Everything up-to-date
mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/test4/ShelterAtIse.git$

問題なくpushできたようです。

最後に改めてタグを振って完了です。

振り返り

さて、上記に書いたように試行錯誤しながらいろいろとやりましたが、あとから表示されたログをよく見るとやっと状況がわかりました。

どうも最初の git push --force の段階で、masterブランチのpushは完了しており、各コミットの Author/Commiter は書き換えられて、tag の push(のみ)失敗したということのようです。 というのも、上記の途中でベアリポジトリをクローンして作業していると思いますが、rewrite しても master ブランチは書き換えが起こっていません。

つまり、この作業時点では、 Author/Commiter の書き換えのpushが失敗したと思っていたのですが、(リモートリポジトリの)状態としては、各コミットの Author/Commiter は書き換えられ、古いコミットに紐づいている tag が残っているという中途半端な状況になっていたようです。

たまたま確認用にこのタイミングでリモートリポジトリをクローンして、ログを見ていたのですが、それを見ると

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/check/ShelterAtIse$ git log
commit 03acac1d9dd4b51aaf193e740e7ca464a362f05a (HEAD -> master, origin/master, origin/HEAD)
Author: junichim 
Date:   Fri Jul 7 11:10:53 2017 +0900

    署名付きapk作成時にファイル名を自動で設定する処理を追加

commit 65016265bd4c026bdb20edf1bd50cf47a8921573
Author: junichim 
Date:   Fri Jul 7 10:51:52 2017 +0900

    現在位置の最寄りの避難所を任意のタイミングで検索できる機能を追加

commit d37b76b2a5a76061fd06a42c210e24bbe8f0ab28
Author: junichim 
Date:   Fri Jul 7 10:39:59 2017 +0900

    アプリバーがスワイプで消えてしまい、元に戻せないのを修正

のようにAuthorが変更されていますが、タグについては、

mor@DESKTOP-H6IEJF9:/mnt/c/Users/mor/home/work/own_app/tmp/check/ShelterAtIse$ git show 1.0beta
tag 1.0beta
Tagger: mor 
Date:   Fri Jul 7 15:30:58 2017 +0900


commit 5127723affae8988878905cd4c88826f1ffc4614 (tag: 1.0beta)
Author: mor 
Date:   Fri Jul 7 11:10:53 2017 +0900

    署名付きapk作成時にファイル名を自動で設定する処理を追加

(以下略)

と private のメールアドレスを持ち、Author変更前のコミット(ハッシュ値 5127723)を指していました。

Author変更スクリプトに書かれている git filter-branch のオプションを調べると --tag-name-filter cat は tag を新しいコミットに付け替えてくれるというオプションのようです。実際、作業途中で git show 1.0beta とタグの情報を見たときには、tagのメールアドレスはprivateのものですが、指しているコミットはAuthor更新後のもの(ハッシュ値 03acac1)となっていました。

で、最後にリモートからタグを消したことでこの状態が解消したんだと思います。

GitHub の設定で private メールアドレスのコミットを禁止しているので、ローカルでは tag 自体は新しいコミットを指すように付け替えられたけど、pushができなかったので、リモート上の tag は古いコミットを指しているままという、こんなややこしい状況になったんでしょうね。

まとめ

というわけで、

  • 過去コミットのメールアドレスを変更する際に、
  • GitHub の private アドレスのコミット禁止設定をしている場合は、
  • 最初から tag を消しておいて、
  • Author/Commiter の変更作業をする

というのが無難なようです。

表示されるメッセージはちゃんと読め、ということですね。あー、めんどくさかった。