ククログ

株式会社クリアコード > ククログ > LibreOffice Calcのスプレッドシートの変更点をgit diffで見られるようにする

LibreOffice Calcのスプレッドシートの変更点をgit diffで見られるようにする

プログラマーは基本的にプレーンテキスト形式が好きな生き物で、ドキュメントなら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環境では

  1. コマンドをフィルタとして登録するため、~/.gitconfigに以下の行を追加する。

    # odfspreadheetというフィルタの実体として、ods2csvを登録する。
    [diff "spreadsheet"]
            textconv = ~/local/bin/ods2csv
    
  2. 拡張子とフィルタを対応付けるため、~/.gitattributesに以下の行を追加する。

    *.ods diff=spreadsheet
    
  3. 以下のコマンド列を実行し、~/.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 loggit showに対しては機能しません。あしからずご了承ください。)

(なお、ここではgit diffについてのみ触れましたが、実際にはこの設定がなされていれば、git log -pgit showでもodsがCSVに変換された結果の差分が表示されるようになります。)