git でパーミッションだけ変更されてしまったファイルが大量にあるときに元に戻す方法

何らかの事情により git リポジトリを clone しないで Samba サーバとかを介して Linux と Windows の間で普通にコピーしちゃったりすると、ファイルのパーミッションが狂って残念なことになることがある気がします。めったにないけど。

そんなときは慌てず、git のワーキングコピーのルートに cd してからおもむろに

  $ git diff --numstat | awk '{if ($1 == "0" && $2 == "0") print $3}' | xargs git checkout HEAD

と実行すると良いようです。
軽く解説すると、まず git diff --numstat は、変更された行数をスクリプトとかで処理しやすい形式で出力してくれます。
次に awk でその情報のうち追加された行数($1)と削除された行数($2)を見て、どちらも 0 だったら(つまり変更されたのがファイルの中身ではなく、属性だったら)、そのファイルの相対パス($3)を抜き出します。
最後に、git checkout HEAD でそのファイルの変更をリポジトリにコミットされている状態に戻しています。

もし、バイナリファイルも同様に扱いたい場合は、$1 と $2 が -(ハイフン)になりますので、

  $ git diff --numstat | awk '{if ($1 == "-" && $2 == "-") print $3}' | xargs git checkout HEAD

と実行すると同様のことができます。

git checkout HEAD にはもしかしたら念のため -- を最後に付けておいたほうがいいのかもしれません。ブランチ名とファイル名がかぶってると残念なことになるので。
(-- をつけては試してませんが)


プログラマが読むにはこの本がオススメです。

入門Git

入門Git


git の本はたくさんありますね。
Gitポケットリファレンス 入門git Gitによるバージョン管理 アリスとボブのGit入門レッスン 実用Git わかる Git