LibreOfficeのODF文書のGitでのバージョン管理方法

概要

LibreOfficeやOpenOfficeで作成したODF文書をGitでバージョン管理したい。しかし,ODF文書はXMLをzipで圧縮したバイナリーファイルのため,そのままでは差分を管理できない。

例えば以下のようにバイナリーファイルで内容が異なることしかわからない。

diff --git a/calc.ods b/calc.ods
index 77b5c96..4ca8761 100644
Binary files a/calc.ods and b/calc.ods differ

そこで,LibreOfficeのODF文書のGitでのバージョン管理方法を検討した。

まず,LibreOfficeでのバージョン管理の方法は3種類ある。

LibreOffice文書のバージョン管理方法
  1. LibreOffice標準の校閲機能
  2. Flat ODF
  3. gitattributes

1番目のLibreOffice標準の校閲機能は文章の推敲などを複数人でする場合には便利だ。しかし,文書の書式やメタデータを管理したり,差分を機械的に処理する場合には少々扱いにくい。

その他にテキストベースで管理するには,残りのFlat ODFとgitattributesを使う2種類の方法がある。Flat ODFはODFの仕様の内,テキスト形式のデータ形式となっている。テキストデータなため,このデータ形式を使えば,通常のテキストファイルと同じく特に意識しなくてもGitでバージョン管理できる。

ただし,欠点がいくつかあるので,今回はgitattributesを使ったバージョン管理方法を記す。

以下のサイトを参考にした。

情報源
  1. Versioning of OpenOffice/LibreOffice documents using git | ~rriemann
  2. libreoffice – How can I get useful git diff of files saved by Libre Office Writer, with output in the command line? – Stack Overflow

Flat ODFの欠点

LibreOfficeのODF文書のGitでのバージョン管理方法を調査していると,Flat ODFとgitattributesの2種類の方法をよく見かける。

この内,Flat ODFについては以下の理由から見送った。

Flat ODFの欠点
  1. Flat ODFはあまり一般的には使われていない。
  2. 全てテキストファイル形式になるため,通常のODFよりファイルサイズが3倍ほど大きくなる。

Gitで管理することを考えるならば,Flat ODFのほうがリポジトリーのサイズが小さいという調査結果がある (参考: openoffice – .odt vs .fodt: which is most space effective for git repositories? – Super User)。

しかし,データの配布を考えると元ファイルが約3倍になるのがいまいちだ。また,LibreOfficeの標準形式はODFが一般的であり,Flat ODFを使っている人はほとんどいない。

Flat ODFを使って嬉しいのは開発者だけで,普通の利用者にとっては馴染みがなくファイルサイズが大きくなるので利点がない。

こういう観点からFlat ODFではなく標準のODFを使ったほうがいいだろうと考えた。

gitattributesを使った管理方法

gitattributesはGitの属性と呼ばれる機能だ。Gitの属性とは,リポジトリー内の特定のパス (ディレクトリー・ファイル) に対する設定を意味する。

このgitattributesを活用することでバイナリーファイルもテキストの差分を比較することができる。

具体的には,odt2txtコマンドとxmllintコマンドでフィルターすることで,ODF文書のテキスト差分を把握できる。

管理の手順は以下の2ステップで行う。

  1. フィルターコマンドのインストール
  2. gitattributesの設定

次から順番に説明する。

1. フィルターコマンドのインストール

ODF文書からXMLテキストを抽出してテキスト差分をするためにodt2txtxmllintコマンドを使用する。odt2txtという名前だが,LibreOffice Writer (.odt) 以外の他のODF文書にも適用できる。

ODF文書からテキストを抽出するだけならば,odt2txtコマンドだけで十分だ。しかし,odt2txtコマンドは1行のXMLとしてテキストを抽出する。人が目視で確認する場合,改行やインデントで整形したXMLのほうが都合が良い。そのため,XML文書の整形のためにxmllintコマンドも併用する。

ただし,xmllintコマンドの代わりにsedコマンドで簡易的にXMLを整形できるので,xmllintコマンドはなくても問題ない。これについては後述する。

なお,単純なテキストを抽出するだけならば,UNIXのstringsコマンドでも似たようなことができる。しかし,こちらはXMLの抽出ができず,メタデータなどの差分を管理できない。やはり,専用のodt2txtコマンドを使ったほうがよいだろう。

odt2txtxmllintのインストール方法は,パッケージマネージャーを使う方法とソースコードからビルドする方法がある。

パッケージマネージャーを使う場合,Debian系のAPTだとそれぞれodt2txtlibxml2-utilsパッケージを以下のコマンドでインストールする。

sudo apt install odt2txt libxml2-utils

ソースコードからインストールする場合,以下のページを参考にする。

これでフィルターコマンドのインストールは完了だ。

2. gitattributesの設定

コマンドを用意したのでgitattributesを設定する。なお,gitattributesについて詳しく知りたければ以下の公式文書を参照する。

以下の順番で設定する。

  1. gitattributesファイルに処理対象のファイルのパターンと処理方法を指定
  2. gitconfigファイルにテキスト変換時のコマンドを指定

まず以下のコマンドでgitattributesにODF文書のパターン (*.od[fgpst] binary diff=odf) を指定する。

PATTERN='*.od[fgpst] binary diff=odf'
ATTRIBUTES=~/.config/git/attributes
if ! grep -q "^$PATTERN$" "$ATTRIBUTES"; then
echo "$PATTERN" >>"$ATTRIBUTES"
fi

念のため同じ設定の有無を確認してからファイルに追記している。

今回はユーザーデフォルトのgitattributes (~/.config/git/attributes) に設定を指定した。万が一,リポジトリー単位で指定したい場合は.git/info/attributes,ディレクトリー単位で指定したい場合は.gitattributesに指定する。

パターンにはWriter (.odt), Calc (.ods), Impress (.odp), Draw (.odg), Math (.odf) を指定した。

続いて差分の適用時に実行するコマンドを指定する。

以下のコマンドでfile.odfをXMLで出力できる。

odt2txt --raw file.odf | xmllint --format -

odt2txt--rawオプションを指定することで,ファイル内容をXMLで出力する。このオプションを指定しないと,本文のテキストだけしか出力されず,メタデータの変化を管理できないので注意する。xmllint--formatオプションを指定することで,XMLを整形する。

上記相当の処理をGitに設定する。具体的には,以下のコマンドで~/.gitconfigに設定する。

git config --global diff.odf.textconv 'odf2xml() { odt2txt --raw $1 | xmllint --format -; }; odf2xml'

このコマンドで~/.gitconfigに以下の内容が追記される。

[diff "odf"]
	textconv = "odf2xml() { odt2txt --raw $1 | xmllint --format -; }; odf2xml"

変換コマンドを実行するシェルスクリプトを別途用意してもいいのだが,たった1行のコマンドのためだけにファイルを用意するのは大げさだ。そこで,関数odf2xmlを定義して,定義した関数を実行する形で登録した。これで余計なファイルを用意する手間が省ける。

なお,シェルの存在しないWindowsでどうなるのか気になって念の為cmd.exeからgitコマンドを実行して確認した。WindowsのGit for Windowsではbashなどが同梱されており,Gitコマンドの実行時にシェルを経由するため,特に問題なく実行できていた。

これでLibreOfficeの文書の差分を把握できる。例えば,LibreOffice Calc (.ods) でA1のセルにaを記入した場合の差分は以下のように確認できる。

diff --git a/calc.ods b/calc.ods
index 77b5c96..4ca8761 100644
--- a/calc.ods
+++ b/calc.ods
@@ -23,7 +23,9 @@
       <table:table table:name="Sheet1" table:style-name="ta1">
         <table:table-column table:style-name="co1" table:default-cell-style-name="Default"/>
         <table:table-row table:style-name="ro1">
-          <table:table-cell/>
+          <table:table-cell office:value-type="string" calcext:value-type="string">
+            <text:p>a</text:p>
+          </table:table-cell>
         </table:table-row>
       </table:table>
       <table:named-expressions/>

XML上でのデータ差分がきちんと表示されている。

コマンド不在時の対策

odt2txtまたはxmllintコマンドが存在しない場合,Gitの差分表示領域に以下のようなエラーメッセージが表示される。

diff --git a/calc.ods b/calc.ods
new file mode 100644
index 0000000..77b5c96
odf2xml() { odt2txt --raw $@ | xmllint --format -; }; odf2xml: 1: odt2txt: not found
odf2xml() { odt2txt --raw $@ | xmllint --format -; }; odf2xml: 1: xmllint: not found
fatal: unable to read files to diff

コマンドが存在しないことが表示されている。

このエラーメッセージが鬱陶しい場合,素直にコマンドをインストールする他に,~/.gitconfigに設定したtextconvの行をコメントアウトしたり削除する。

[diff "odf"]
#	textconv = "odf2xml() { odt2txt --raw $1 | xmllint --format -; }; odf2xml"

コマンドで削除したい場合は以下のコマンドを実行する。

if (command -v odt2txt && command -v xmllint) >/dev/null; then
git config --global --unset diff.odf.textconv
fi

なお,エラーメッセージを別途表示させたい場合,以下のようなコマンドを登録することで実現できる。

git config --global diff.odf.textconv "odf2xml() { (command -v odt2txt && command -v xmllint) >/dev/null || { echo odt2txt or xmllint is not found. Please install them or run '`git config --global --unset diff.odf.textconv`'. >&2; exit; }; odt2txt --raw $1 | xmllint --format -; }; odf2xml"

元々のシェルスクリプトの最初にコマンドの有無をチェックし,存在しない場合にメッセージを出力している。

この場合,以下のメッセージが表示される。

diff --git a/calc.ods b/calc.ods
new file mode 100644
index 0000000..77b5c96
odt2txt or xmllint is not found. Please install them or run `git config --global --unset diff.odf.textconv`

ただ,情報に対して違いがなく,コマンドが複雑になるだけなので,特にメッセージはいらないと思う。

なお,フォールバックとしてコマンドが不在の場合にodt2txtと似たstringsコマンドで代用しようとしたが,意味のないテキスト差分が大量に出たため断念した。

コマンドの終了ステータスが0以外の場合に末尾に,fatal: unable to read files to diffが表示されるようだ。

xmllint不在対策

ODF文書の差分を管理する上で必須なのはodt2txtコマンドであり,xmllintはXMLを整形するためだけに使用している。

XMLの整形のためだけにxmllintを要するのは少々大げさなので,xmllintがなくてもODF文書の差分を管理できるようにしたい。

UNIXコマンドを用いて簡易的にXMLを整形して対応する。この場合,以下のコマンド相当で実現する。

odf2xml() {
	format=$(command -v xmllint && echo --format - || echo "eval grep ^ | sed 's@><@>\\\n<@g'")
	odt2txt --raw $1 | $format
}
odf2xml ${1+"$@"}

上記ではxmllintコマンドが存在しない場合だけ,sedコマンドでXMLのタグ毎に改行している。インデントはできないが,実務的には最低限改行されていれば視認できて問題ないだろう。

odt2txtの出力は行末に改行が存在しない。SolarisやHP-UXなど一部の古いsedは改行のない入力を受け付けるとエラーが出るため,念のためgrep ^で改行を追加する処理を挟んでいる。

このコマンドをgitattributesに登録する場合は以下のコマンドを実行する。

git config --global diff.odf.textconv \
'odf2xml() { format=$(command -v xmllint && echo --format - || echo "eval grep ^ | sed '"'s@><@>\\\\\n<@g'"'"); odt2txt --raw $1 | $format; }; odf2xml'

上記コマンドを実行すると以下の内容が~/.gitconfigに登録される。

[diff "odf"]
	textconv = "odf2xml() { format=$(command -v xmllint && echo --format - || echo \"eval grep ^ | sed 's@><@>\\\\\\n<@g'\"); odt2txt --raw $1 | $format; }; odf2xml"

エスケープの処理で若干複雑になってしまった。

これでodt2txtさえあれば,xmllintが不在でも問題なくODF文書の差分を管理できる。

結論

LibreOfficeのODF文書のGitでのバージョン管理方法を整理した。

LibreOfficeの他にもMicrosoft Officeなどバイナリーファイルだが差分を管理したい場合にgitattributesを用いる方法は有効に思う。

この方法で重要文書のバージョン管理をきっちり行いたい。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です