プログラマーは基本的にプレーンテキスト形式が好きな生き物で、ドキュメントならMarkdown、表形式のデータならCSVが定番です。プレーンテキスト形式だとシェルのコマンドや簡単なスクリプトで容易に加工できますし、Gitリポジトリなどに格納した状態でも変更点を追いやすいです。
しかし、たまにどうしても、もうちょっとリッチな形式のバイナリファイルをマスターデータとして持っておかないといけないことがあります。Microsoft ExcelやLibreOffice(OpenOffice.org)Calcなのスプレッドシートもそのひとつです。
Microsoft Excelの場合、悩みを抱える人が多いためか、すでに色々な方が解決策を公開されています。例えばGitで管理しているExcelファイルの差分を見るという記事では、Go言語製のツールを併用する手順が紹介されています。
一方、ODF(OpenDocument Format)のスプレッドシート形式(ods)についてはあまりそのような情報が出回っていないようです。odt2txtというツールを使ってドキュメント形式(odt)の差分を表示する方法の解説はあり、その一環でodsも差分を表示できるようになるのですが、元がodt用なのでodsについてはいまいち微妙な結果になってしまいます。
そこでこの記事では、「odsの変更点を差分表示する」という事に焦点を当てて解説することにします。
必要なのはファイル形式の変換
バイナリファイルの変更点の差分をgit diff
で見られるようにするために必要なのは、要するに、diff
で比較できる形式にファイルを変換するツールです。前述のExcel形式やodtの差分を見る方法も、それらのファイルに対応した*「引数で指定されたファイルをプレーンテキスト形式に変換して標準出力に出力する」というツールをいかに用意するか*がキモになっています。
odsをプレーンテキスト(CSV)に変換して標準出力に出力するコマンドが~/local/bin/ods2csv
の位置に置かれていたとすると、Linux環境では
-
コマンドをフィルタとして登録するため、
~/.gitconfig
に以下の行を追加する。# odfspreadheetというフィルタの実体として、ods2csvを登録する。 [diff "spreadsheet"] textconv = ~/local/bin/ods2csv
-
拡張子とフィルタを対応付けるため、
~/.gitattributes
に以下の行を追加する。*.ods diff=spreadsheet
-
以下のコマンド列を実行し、
~/.gitattributes
をグローバルな設定ファイルとして登録する。$ git config --global core.attributesFile ~/.gitattributes
これで、手元のGitリポジトリ内で拡張子が.ods
であるファイルに変更が加わっていた場合に、git diff
を実行すると自動的にods2csv
が実行され、プレーンテキストに変換した後の内容の差分が表示されるようになります。
ということで、あとはこのような振る舞いをするods2csv
をどのように用意するかという話になります。
LibreOffice(OpenOffice.org)自体を使う
実は、LibreOffice(OpenOffice.org)はコマンドライン引数を使ってある程度の自動操作ができ、--convert-to csv
と指定すれば、odsの中でアクティブなワークシートをCSVとしてエクスポートさせられます。以下は、この機能を使って~/local/bin/ods2csv
をシェルスクリプトとして記述した例です。
#!/bin/bash
tempdir="$(mktemp -d)"
csvfile="$(basename "$1" .ods).csv"
# $HOMEを上書きしておかないと、すでにLibreOfficeのプロセスが起動している場合に
# ここでのsofficeの実行に失敗してしまう。
export HOME="$tempdir"
soffice --nofirststartwizard --headless --convert-to csv --infilter=CSV:44,34,76,1 --outdir "$tempdir" "$1"
cat "$tempdir/$csvfile"
rm -rf "$tempdir"
ただ、これだとあくまでアクティブなワークシート1つだけが変換されて他のワークシートは無視されてしまいます。複数ワークシートがあるファイルだと期待したような結果を得られません。
ssconvert
を使う
より実用的な方法として、Gnumeric(Ubuntuであればsudo apt install gnumeric
でインストール可能)の一部として提供されているコマンドラインツールのssconvert
を使う方法があります。
ssconvert
はGnumericで取り扱える形式のファイルを変換する機能を提供していますが、出力形式をCSVにして、-S
(--export-file-per-sheet
)オプションを指定すれば、すべてのワークシートを別々のCSVファイルに分割して出力させる事もできます。以下は、それを応用して~/local/bin/ods2csv
をシェルスクリプトとして記述した例です。
#!/bin/bash
sscat() {
ssconvert -S "$(basename "$1")" "$(basename "$1").%s.csv" 1>/dev/null 2>&1
for csv in *.csv
do
echo "$csv:"
cat "$csv"
done
}
tempdir="$(mktemp -d)"
cp "$1" "$tempdir/"
(cd "$tempdir" &&
sscat "$(basename "$1")")
rm -rf "$tempdir"
実際のところは、ssconvert
は入出力ファイルの形式をファイル名から自動判別するようになっているため、このスクリプトは.xlsでも.xlsxでもGnumericが取り扱える形式なら何にでも使えます。よって、~/.gitattributes
は
*.ods diff=spreadsheet
*.xls diff=spreadsheet
*.xlsx diff=spreadsheet
と書いてしまって問題ありません。
まとめ
Gitリポジトリに格納された.odsの変更点を差分表示する方法として、ssconvert
をベースにしたシェルスクリプトで.ods(およびその他のスプレッドシート形式のファイル)の全内容をCSVに変換して出力する方法をご紹介しました。
Gitでのバイナリファイルの取り扱いは何かと面倒ですが、差分が見やすくなるだけでも使い勝手はずいぶん向上するはずです。また、直接はGitのテキスト化フィルタに使えないコマンドでも、これらの例のようにシェルスクリプトを作成するだけでテキスト化フィルタにすることができます。お手元のリポジトリでスプレッドシート形式のファイルの取り扱いにお悩みの方は、ぜひ一度挑戦してみて下さい。
(なお、この方法はあくまでgit diff
に対してのみ有効で、git log
やgit show
に対しては機能しません。あしからずご了承ください。)
(なお、ここではgit diff
についてのみ触れましたが、実際にはこの設定がなされていれば、git log -p
やgit show
でもodsがCSVに変換された結果の差分が表示されるようになります。)