普段使っているファイラーはファイルのアクセス日時でソートされるように設定しています。大きめのリポジトリをcloneしてコードを読む時に、意外とファイルの最終変更日時が参考になったりします。仕事で使うリポジトリや、定期的にpullしているなら、徐々に変更のないファイルはファイラーの下の方に移動していく (上の方からアクセス日時の降順として) のですが、cloneしたばかりだとこうは行きません。
要はgitリポジトリ内の各ファイルのアクセス日時を、そのファイルのgit履歴上での最終変更日時に戻したいという気持ちになるわけです。そうするとファイラー上でもいい感じにファイルがソートされるのです。
#!/usr/bin/env bash if command -v gdate >/dev/null 2>&1; then DATE=gdate else DATE=date fi while IFS= read -r -d '' file; do logtime=$(git log -1 --format=%ci "$file") mtime=$("$DATE" -d "$logtime" +%Y%m%d%H%M.%S) echo "$(printf "%-80s" "$file")"$'\t'"$logtime"$'\t'"$mtime" touch -t "$mtime" "$file" done < <(git ls-files -z)
実行してみるとこういう感じ。
$ git-touch .ctags 2015-08-28 10:47:55 +0900 201508281047.55 .tmux.conf 2016-02-05 08:43:46 +0900 201602050843.46 .vimrc 2017-05-01 18:38:35 +0900 201705011838.35 .vimshrc 2017-03-10 20:45:22 +0900 201703102045.22 .zshrc 2017-04-20 00:32:39 +0900 201704200032.39 $ stat -x .zshrc File: ".zshrc" Size: 10518 FileType: Regular File Mode: (0644/-rw-r--r--) Uid: ( 501/ itchyny) Gid: ( 20/ staff) Device: 1,4 Inode: 203408150 Links: 1 Access: Thu Apr 20 00:32:39 2017 Modify: Thu Apr 20 00:32:39 2017 Change: Sun May 7 01:03:05 2017
atimeとmtimeがgitでのファイル更新時刻になりました。やったー!
git logはタイムゾーン込みで保存しているようなので、touchする前にローカルタイムゾーンに変換しないといけない。最初はsedでタイムゾーン取り除いていたけれど、流石に乱暴だったのでdateコマンドを挟むようにしました。
できたーと思ってふと検索してみるといっぱいでてきた… まぁみんなやるよねぇ…
- Setting the timestamps of the files to the commit timestamp of the commit which last touched them
- コミット日付をタイムスタンプに復元したい - by shigemk2
- gitで取ってきたファイルのタイムスタンプをcommit日付に直す - モーグルとカバとパウダーの日記
macOSの (というかFreeBSDの) dateコマンドの-dは日付を指定するオプションじゃないのが罠っぽい。date -jf
であれこれしたほうが本当はいいけれど、coreutilsのgdateに逃げた。あとスペース含むファイル名の扱いにはご注意を。
dateコマンドのところはPythonでやろうかと思ったけれど、実装してみたら意外とオーバーヘッドが大きかった。まぁオーバーヘッドと言っても二倍時間がかかるくらいなんですが、リポジトリ内のファイルを全部なめて更新していくので、できれば速いほうが嬉しい。速度を取るかポータビリティを取るかはいつも難しい。PythonはPythonで2.7と3系両方で動くスクリプトを書ける自信はない。ところで jq fromdateiso8601
を使う手も考えたのですが (ポータビリティ的には良いアイディア)、タイムゾーン部分をパースできなくて (Z
しか対応してない)、なーにがISO 8601じゃと思いました。ああ、いい話だなぁ。それじゃーね!ばいばい!