gitのfilterについて
概要
jupyter notebookをgitで管理する状況で、コミット時に出力セルを削除しつつgitの内部データとして保存するために、gitのclean filter機能というものを利用したところ便利だった。
このgit clean filterについて簡単に整理する。
詳細
git clean filterについて端的に説明すると、ステージングエリアにファイルを移動する際に、任意コマンドを対象ファイルに対して実行して変換する機能である。詳細は以下の通り。
リポジトリの内部状態
Gitは
.gitディレクトリ内に全てのデータを特殊な形式(主にオブジェクトデータベース)で保存しているこれが「リポジトリの内部状態」で、通常は直接編集することはない
コミットされたファイルやその履歴はすべてここに圧縮・最適化された形で格納されている
ワーキングディレクトリ
これは実際に見えて操作できるディレクトリで、コードを編集する場所
.gitディレクトリ以外のプロジェクトディレクトリ全体がこれに当たる
データフローとフィルター適用
コミット時 (ワーキングディレクトリ → リポジトリ): clean filterが適用される
git add→git commitの流れで、ファイルがステージングエリアを経由してリポジトリに格納されるこの過程でclean filterが適用され、Jupyter Notebookの出力などが削除される
チェックアウト時 (リポジトリ → ワーキングディレクトリ): smudge filterが適用される
git checkoutやgit cloneでファイルを取り出す際に適用されるこの例では
cat(何もしない) が指定されているため、クリーンアップされた状態のままファイルが展開される
ステージングエリア (インデックス)
実際には、ワーキングディレクトリとリポジトリの間にはステージングエリアという中間層がある
git addでファイルをステージングすると、この時点でclean filterが適用される
この流れを図式化すると
ワーキングディレクトリ → [clean filter] → ステージングエリア → リポジトリ内部
リポジトリ内部 → [smudge filter] → ワーキングディレクトリこのgit clean filterにjupyter notebookの出力セルを削除するコマンドを設定することで、コミット時に出力セルを削除しつつgitの内部データとして保存することができる。具体的には以下のように設定すれば良い。
git config filter.clean-notebook.clean "jq '.cells |= map(if .cell_type == \"code\" then (.outputs |= [] | .execution_count |= null) else . end | .metadata |= if has(\"tags\") then {tags: .tags} else {} end) | .metadata |= {kernelspec: .kernelspec}'"
git config filter.clean-notebook.smudge "cat"
git config filter.clean-notebook.required true
echo "*.ipynb filter=clean-notebook" >> .gitattributes