.gitattributes で Git LFS の適用をディレクトリごとに変更する
こんにちは、kenzauros です。
Git LFS で巨大ファイルとして管理したいファイルは git lfs track
で管理対象として指定すればいいのですが、今回は同じリポジトリ内でフォルダによって LFS の適用を切り替えたくなったので、その方法をメモしておきます。
Git LFS の適用
バイナリーでなくとも、たとえば数 MB を超えるようなデータベースダンプファイルなどは差分を表示するだけでも時間がかかる上、特に差分管理が必要でない場合があります。
こんなときは git lfs track *.sql
と叩いて、拡張子が *.sql
のファイルを Git LFS で追跡させるようにします。
これを行うとルートディレクトリの .gitattributes
ファイルに下記のような設定が追加されます。
*.sql filter=lfs diff=lfs merge=lfs -text
.gitattributes
のリファレンスはあまりいいものがないのですが、見づらいながらも公式ページでオプションが確認できます。
これを見ながら *.sql
の設定を解釈すると filter
, diff
, merge
オプションがそれぞれ lfs
に設定され、 text
オプションの設定が解除されていることがわかります。
これで *.sql
ファイルは Git LFS が扱ってくれるようになり、 SourceTree などにも diff が表示されなくなります。
特定のディレクトリだけ設定を変える
さて、すべての *.sql
ファイルが巨大なダンプファイルなら問題ないのですが、小さな SQL を書いたファイルの場合、変更を差分で見たいことがあります。
たとえば下記のようなディレクトリ構造を考えます。
ルートの .gitattributes
に前項の設定がされており、 LARGE/LARGE.sql
が巨大なファイル、 small/small.sql
が比較的小さな SQL ファイルだと仮定します。
/
.gitattributes
LARGE/
LARGE.sql
small/
small.sql
この例だとルートの .gitattributes
のせいで small.sql
まで Git LFS で扱われてしまいます。
これを解消するには下層の small/
ディレクトリにも .gitattributes
ファイルを配置します。
/
.gitattributes
LARGE/
LARGE.sql
small/
.gitattributes <- 追加
small.sql
Git は .gitignore
も .gitattributes
も下層ディレクトリに配置することができ、下層のファイルにはその下層ディレクトリに存在する設定が上書きされて適用されます。
ここにルートディレクトリで指定された filter=lfs diff=lfs merge=lfs -text
を打ち消す設定を追記します。
*.sql !filter !diff !merge text
filter
, diff
, merge
オプションを未指定の状態に, text
オプションを設定状態に設定しています。
(リファレンスも不親切で明記はされていないのですが、 !
で未指定の状態に戻すことができます。)
これでこのディレクトリ以下は *.sql
を通常のソースコードのように差分管理できるようになっているはずです。
おまけ: .gitattributes の属性
参考までに .gitattributes
の属性について、公式ページの内容を要約しておきます。
意訳がずれているかもしれませんので、正確な情報は公式ページを参照してください。
text: 改行コードの正規化
text
属性は改行コードの正規化を有効にするかどうか+ファイルの中身を判断するかどうかを設定します。
text
: 強制的にテキストファイルだと認識され、中身にかかわらず改行コードの変換が行われます。-text
: 改行コードの正規化を全く行いません。バイナリーファイルで-text
が指定されるのはこのためです。text=auto
: 中身がテキストだと判断されるとチェックインの際に改行コードが LF に変換されます。ただし、CRLF のファイルは変換されません。
よくルートディレクトリの .gitattributes
で * text=auto
と指定されているのはこのためですね。
未指定の状態では core.autocrlf
の設定に依存して変換が行われます。
Git における改行コード関連は core.autocrlf
や core.eol
を含めてけっこうややこしいので私自身完全には把握できていません。
eol: 改行コードの指定
eol
属性は text
と組み合わせて、ファイルの改行コードを指定します。
eol=crlf
: チェックインの際に正規化され、チェックアウトの際に CRLF に変換されると記載されていますが、チェックインのときにどう正規化されるのか明記がありません。eol=lf
: チェックインの際に LF に正規化され、チェックアウトの際には CRLF に変換されることを防ぎます。つまり常に LF のファイルとして扱います。
リファレンスでもサンプルとして上がっている下記の設定が参考になります。
* text=auto
*.txt text
*.vcproj text eol=crlf
*.sh text eol=lf
*.jpg -text
*.txt
, *.vcproj
, *.sh
はテキストファイルとして改行コードを正規化するが、 *.vcproj
は CRLF 、 *.sh
は LF であることが作業ディレクトリ内で保証されるようになります。
この作業ディレクトリ内で保証というのが割とツボで、つまりリモートリポジトリに上がっているファイルがどういう状態かは保証されません。
filter: checkout/add 時のフィルター指定
Git における filter は checkout するときや add するときにファイルに対してなんらかの操作をかけるものです。
Git LFS はこの filter 機能を使って、 checkout/add のタイミングでポインターファイルと実際のコンテンツファイルとを相互に置換しています。
その他 filter を使うといろいろできるようですが、あまり活用されているところを見たことがありません。どなたかご存知でしたら教えてください。
diff: 差分生成の指定
diff
属性はどのように差分を生成するかを指定します。
- 未指定: テキストと判断できるファイルかつ
core.bigFileThreshold
より小さい場合に、テキストとして扱い、差分を生成します。 diff
: テキストファイルとして扱い、差分を生成します。-diff
: バイナリファイルとして扱い、Binary files differ
を生成します。diff=ドライバー名
: 指定したドライバーを使って差分を生成します。
css
や html
, php
, tex
などいくつかデフォルトのパターンが定義されているようですが、使用されている例を見たことがありません。
merge: マージ方法の指定
merge
属性はマージをどのように行うかを指定します。
- 未指定 もしくは
merge
: 標準のマージドライバー (3-way merge driver) を使ってマージします。 -merge
: 現在のブランチのバージョンを仮の結果として、競合があることを通知します。バイナリーファイルなど自動でマージできない場合に使用します。merge=ドライバー名
: 指定したドライバーを使ってマージを行います。text
を指定するとmerge
と同様に、binary
を指定すると-merge
を指定した場合と同様になります。
ちなみに 3-way merge driver というのはコンフリクト部分が <<<<<<<
, =======
, >>>>>>>
で表示される、いつものアレです。