Gitでチェックアウトしたファイルの更新日時をコミット日時にあわせたい
概要
Gitでチェックアウトしたファイルの更新日時が現在のシステム時刻に設定されてしまい困っている。Gitのコミット日時に設定して欲しいのだが。。
そんな方のためのTipsです。
環境
Windows11
Git for Windows 2.50.1
Git Bash (Git for Windows付属MinGW-w64環境)
背景
Gitコマンドはリポジトリをクローンしたり他のブランチへ切り替えを行う際、変更のあったファイルの更新日時を現在のシステム時刻に設定する仕様となっています。
「ファイルの更新日時なんて気にしないよ」であれば問題ないのですが、普段Everythingでファイル管理をしていて更新日の降順(新しい順)を既定にしている場合、なんら変更を行っていない(なんならgitを過去のタグに戻した)にもかかわらず過去のファイルが軒並みトップに並んでしまうことになります。この仕様のせいで EverythingではGitリポジトリを敢えて除外している方も多いのではないでしょうか。
対策
GitコマンドにはHooksという機能が用意されており、Gitコマンドの各アクションに対し前処理・後処理として任意のスクリプトを実行させることができます。
我々が問題としているのは「ローカルファイルの更新日時が現在のシステム時刻に設定されてしまう」シーンであり、Git操作としては下記のユースケースとなるでしょう。
- リモートリポジトリから新たにローカルリポジトリをcloneした(Clone)
- リモートリポジトリから同一ブランチの差分を取り込んだ(Pull)
- ローカルリポジトリ上でブランチ・タグを切り替えた(Branch/Checkout)
- ローカルリポジトリ上で別のブランチから変更を取り込んだ(Merge)
Gitの公式ドキュメントを読んでみると、「post-checkout」「post-merge」でだいたいのシーンをカバーすることができそうです。
8.3 Git のカスタマイズ - Git フック より一部抜粋
その他のクライアントフック
(中略)
git checkout が正常に終了すると、post-checkout フックが実行されます。これを使うと、作業ディレクトリを自分のプロジェクトの環境にあわせて設定できます。 たとえば、バージョン管理対象外の巨大なバイナリファイルを作業ディレクトリに取り込んだり、ドキュメントを自動生成したりといった処理が行えます。post-merge フックは、merge コマンドが正常に終了したときに実行されます。 これを使うと、Git では追跡できないパーミッション情報などを作業ツリーに復元できます。 作業ツリーに変更が加わったときに取り込みたい Git の管理対象外のファイルの存在確認などにも使えます。
(後略)
コミット日時修正スクリプト
コミット日時を修正するスクリプトを記述しましょう。
Git for WindowsではMinGW-w64環境によるGit Bashがシームレスに利用可能です。BashスクリプトのほかPerlを利用することもできます。
#!/bin/sh
echo "Fixes updated files commit timestamp:"
# 5分前のUNIXタイムスタンプ
five_minutes_ago=$(date -d "5 minutes ago" +%s)
# ファイルパスとUNIXタイムスタンプを1行ずつ取得
find . -type f -not -path "*/.git/*" -print0 | perl -0ne 'chomp; $t=(stat $_)[9]; print "$_ $t\n";' |
while IFS= read -r line; do
# 最後の空白以降をmtimeとみなしてfilename,filetimestampを分離
filename="${line% *}"
filetimestamp="${line##* }"
# 5分前より新しい(更新のあった)ファイルのみタイムスタンプをCommit日時に設定
if [ "$filetimestamp" -gt "$five_minutes_ago" ]; then
commitdate=$(git log -1 --format=%ci -- "$filename")
if [ -n "$commitdate" ]; then
touch -d "$commitdate" "$filename"
echo "- \"$commitdate\" \"$filename\""
fi
fi
done
Git Hooks 配備
各リポジトリ直下「.git」ディレクトリの「hooks」ディレクトリに下記2ファイルを配置します。ファイルの中身はコミット日時修正スクリプトで作成したものとしてください(2ファイルとも同じ内容でOK)。
- post-checkout
- post-merge
下記の3点に注意しましょう。
- 拡張子はつけない
- 改行コードはLF
- 文字エンコーディングはUTF-8
準備は完了です。
早速Gitを操作してみましょう!!
下記のようにコミットタイムスタンプ変更一覧が出力されたら成功です!
※ファイル一覧が不要であれば5分前のUNIXタイムスタンプで作成したスクリプトの「echo」に関する行を削除またはコメントアウトしてください
制限事項
- 本Hooksでは更新のあったファイルごとに
git log -1
コマンドを実行するためチェックアウト等が遅くなることがあります(差分の量次第) - ディレクトリの更新日時は更新されません
以上