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種類ある。
1番目のLibreOffice標準の校閲機能は文章の推敲などを複数人でする場合には便利だ。しかし,文書の書式やメタデータを管理したり,差分を機械的に処理する場合には少々扱いにくい。
その他にテキストベースで管理するには,残りのFlat ODFとgitattributesを使う2種類の方法がある。Flat ODFはODFの仕様の内,テキスト形式のデータ形式となっている。テキストデータなため,このデータ形式を使えば,通常のテキストファイルと同じく特に意識しなくてもGitでバージョン管理できる。
ただし,欠点がいくつかあるので,今回はgitattributesを使ったバージョン管理方法を記す。
以下のサイトを参考にした。
Flat ODFの欠点
LibreOfficeのODF文書のGitでのバージョン管理方法を調査していると,Flat ODFとgitattributesの2種類の方法をよく見かける。
この内,Flat ODFについては以下の理由から見送った。
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ステップで行う。
- フィルターコマンドのインストール
- gitattributesの設定
次から順番に説明する。
1. フィルターコマンドのインストール
ODF文書からXMLテキストを抽出してテキスト差分をするためにodt2txt
とxmllint
コマンドを使用する。odt2txt
という名前だが,LibreOffice Writer (.odt
) 以外の他のODF文書にも適用できる。
ODF文書からテキストを抽出するだけならば,odt2txt
コマンドだけで十分だ。しかし,odt2txt
コマンドは1行のXMLとしてテキストを抽出する。人が目視で確認する場合,改行やインデントで整形したXMLのほうが都合が良い。そのため,XML文書の整形のためにxmllint
コマンドも併用する。
ただし,xmllint
コマンドの代わりにsed
コマンドで簡易的にXMLを整形できるので,xmllint
コマンドはなくても問題ない。これについては後述する。
なお,単純なテキストを抽出するだけならば,UNIXのstrings
コマンドでも似たようなことができる。しかし,こちらはXMLの抽出ができず,メタデータなどの差分を管理できない。やはり,専用のodt2txt
コマンドを使ったほうがよいだろう。
odt2txt
とxmllint
のインストール方法は,パッケージマネージャーを使う方法とソースコードからビルドする方法がある。
パッケージマネージャーを使う場合,Debian系のAPTだとそれぞれodt2txt
とlibxml2-utils
パッケージを以下のコマンドでインストールする。
sudo apt install odt2txt libxml2-utils
ソースコードからインストールする場合,以下のページを参考にする。
これでフィルターコマンドのインストールは完了だ。
2. gitattributesの設定
コマンドを用意したのでgitattributesを設定する。なお,gitattributesについて詳しく知りたければ以下の公式文書を参照する。
以下の順番で設定する。
- gitattributesファイルに処理対象のファイルのパターンと処理方法を指定
- 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を用いる方法は有効に思う。
この方法で重要文書のバージョン管理をきっちり行いたい。