git-annex によるファイル管理を試した
- Git
LWN.netの記事 で git-annex について知ったのでちょっと試してみた。
git-annex
- LWN.netの記事もそうであるように, Gitで巨大なファイルを管理する際に git-lfsと並んで名前があがることがある
- 大きなファイルに限らず, ファイルの実体とバージョン管理を分離することができる
- ユースケースについては git-annex の説明を
読むのがわかり易い
- 例1: バックアップ・アーカイブ用途
- 例2: キャッシュ的な用途(クラウドストレージにあるデータをリムーバブルメディアに持ってきて 利用した後, 書き戻す)
- 柔軟な制御は出来る反面面倒な部分が多い
- 実体の置き場を複数に分散させられるので, 分散ストレージもどきっぽくできる
- 置き場にはクラウドストレージ等も利用可能
- 保持しておくべき実体のコピーの数とかも指定できる(バックアップ用途に便利)
- 実体が手元にあるかないか気にする必要があるが, 裏を返せば実体は必要なものだけ持つこともできる
- バージョン管理はgitの流儀に従って都度自分で行うか自動でやる仕組みを整える必要がある
- cronジョブを仕込むとか
- 実体の置き場を複数に分散させられるので, 分散ストレージもどきっぽくできる
- ホスティングサービスの対応は薄い
- ちなみに haskell で書かれている模様
検証のモチベーション
- デスクトップ, ラップトップ, モバイルのデータ共有に使えないか
- 以下のような運用を実現したい
- デスクトップは常にアクセスできるNFS上のデータにアクセスするようにしておきたい
- NFS上のデータは適宜クラウドストレージ(Google Drive)にバックアップとしてデータをコピーしておきたい
- ラップトップは自宅ではNFSのデータを使いつつ, 外出先ではローカルのデータを使い,
もしローカルに無い場合はクラウドストレージからデータを持ってくるようにする
- ローカルの容量が小さいので, 必要なデータだけを持ってきたい
- モバイル(iOS)から参照が必要なものは都度クラウドストレージから読み出しつつ, 撮った写真とかはクラウドストレージにアップロードする
- gdriveなどでrsyncライクに同期を取ることはやっていたが,
気になる部分があってやめていた
- 同期忘れで並行して変更が発生してしまい不整合が出たら嫌
- NFSを使っている関係で書き換え発生時に自動的に同期することが出来ない
試してわかったこと
公式にwalkthroughがあるので, 参考にして色々試しつつ動きを確認した。
- git-annex version: 7.20181211
- インストール
- Gentooでは haskell リポジトリにある
- (余談) xmonad も同様のため自分の環境ではすんなり入れられた
- annexを使う準備として, 対象になるGitのworking tree内で
git annex init
-
.git/annex
ディレクトリが作られる``` $ ls -ln .git 合計 12 -rw-r--r-- 1 1000 1000 23 1月 6 05:45 HEAD drwxr-xr-x 4 1000 1000 180 1月 6 05:45 annex -rw-r--r-- 1 1000 1000 158 1月 6 05:45 config -rw-r--r-- 1 1000 1000 73 1月 6 05:39 description drwxr-xr-x 2 1000 1000 300 1月 6 05:45 hooks drwxr-xr-x 2 1000 1000 60 1月 6 05:39 info drwxr-xr-x 3 1000 1000 60 1月 6 05:45 logs drwxr-xr-x 9 1000 1000 180 1月 6 05:45 objects drwxr-xr-x 4 1000 1000 80 1月 6 05:39 refs ```
-
自分ではまだ何もcommitしていないが,
git annex init
により勝手に commit が作られている (masterブランチなどとは独立した木を作るgit-annex
ブランチ)$ git log --all --oneline --decorate --graph * c61da85 (git-annex) update * 75768be branch created
-
git-annex
ブランチ上ではuuid.log
にファイルの実体置き場としてのgit-annex repositoryの 情報が追記される``` $ git show c61da85 commit c61da85e7c2875610309b919b3c3d6a13f9cf0fc Author: ntoofu <ntoofu@users.noreply.github.com> Date: Sun Jan 6 06:12:37 2019 +0900 update diff --git a/uuid.log b/uuid.log new file mode 100644 index 0000000..280627a --- /dev/null +++ b/uuid.log @@ -0,0 +1 @@ +973e8872-e107-406e-89a1-14a375cbf631 qwert@isokaze:/tmp/annex timestamp=1546722757.613959057s ```
git annex add
でファイルをannexによる管理として追加できる
-
working tree及びgitとしてはsymlinkとして扱い, 実体は
.git/annex/objects
配下にhash値と共に置かれる``` $ ls -ln 合計 4 -rw-r--r-- 1 1000 1000 9 1月 6 06:23 hoge $ git annex add hoge add hoge ok (recording state in git...) $ ls -ln 合計 4 lrwxrwxrwx 1 1000 1000 178 1月 6 06:23 hoge -> .git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 ``` ``` $ git diff --cached | cat diff --git a/hoge b/hoge new file mode 120000 index 0000000..2b49184 --- /dev/null +++ b/hoge @@ -0,0 +1 @@ +.git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 \ No newline at end of file ```
- (最後の
git diff
では, file modeからsymlinkとわかる)
- (最後の
-
git-annex
ブランチにもcommitが作成される$ git log --all --oneline --decorate --graph * d60672a (git-annex) update * c61da85 update * 75768be branch created $ git show d60672a commit d60672a4ee4d1e36bc0dd3be90889a18f94f80ac Author: ntoofu <ntoofu@users.noreply.github.com> Date: Sun Jan 6 06:23:52 2019 +0900 update diff --git a/140/6ab/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1.log b/140/6ab/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1.log new file mode 100644 index 0000000..6ea53ba --- /dev/null +++ b/140/6ab/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1.log @@ -0,0 +1 @@ +1546723432.699459191s 1 973e8872-e107-406e-89a1-14a375cbf631
- ファイル実体を示すハッシュに紐づけて
.log
ファイルとして git-annex repository の情報を持つ
- ファイル実体を示すハッシュに紐づけて
- git commit や git mv は普通に実施してよい
- git的にはsymlinkの情報を管理するだけなので, ファイル実体に関わらない操作は通常通り
- repositoryの追加は git repository として追加すれば良い
-
git cloneなどでgit repositoryを複製できるが無効なsymlinkがあるのみ
``` $ git clone /tmp/annex/ annex-clone Cloning into 'annex-clone'... done. $ cd annex-clone $ ls -ln 合計 4 lrwxrwxrwx 1 1000 1000 178 1月 6 06:38 hoge -> .git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 $ cat hoge cat: hoge: そのようなファイルやディレクトリはありません ```
-
git annex init
で説明を変更できるが, やらなくてもgit-annex repositoryとしての追加もなされる``` $ git annex info repository mode: indirect trusted repositories: 0 semitrusted repositories: 4 00000000-0000-0000-0000-000000000001 -- web 00000000-0000-0000-0000-000000000002 -- bittorrent 37136c07-758b-467a-8a2e-ff6a0ab1a205 -- qwert@isokaze:/tmp/annex-clone [here] 973e8872-e107-406e-89a1-14a375cbf631 -- qwert@isokaze:/tmp/annex [origin] untrusted repositories: 0 transfers in progress: none available local disk space: 16.87 gigabytes (+1 megabyte reserved) local annex keys: 1 local annex size: 9 bytes annexed files in working tree: 1 size of annexed files in working tree: 9 bytes bloom filter size: 32 mebibytes (0% full) backend usage: SHA256E: 1 ```
-
git remote add
はやっておかないと, 後述のgit annex sync
で同期漏れの元になりかねない$ pwd /tmp/annex $ git annex sync commit On branch master nothing to commit, working tree clean ok $ git remote add cloned /tmp/annex-clone $ git annex sync commit On branch master nothing to commit, working tree clean ok pull cloned From /tmp/annex-clone * [new branch] git-annex -> cloned/git-annex * [new branch] master -> cloned/master * [new branch] synced/master -> cloned/synced/master ok
- git remoteで登録されているrepositoryとのみ同期を図っている
- metadataの同期は
git annex sync
を叩けば良い
-
あくまでsymlinkとファイル実体の在り処の情報が更新されるだけでデータ自体は同期されない
``` $ pwd /tmp/annex-clone $ git annex sync commit On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean ok pull origin ok $ cat hoge cat: hoge: そのようなファイルやディレクトリはありません ```
- データ実体の取得は
git annex get PATH
, 実体の削除はgit annex drop PATH
で行う
-
取得
``` $ git annex get hoge get hoge (from origin...) (checksum...) ok (recording state in git...) $ cat hoge hogehoge ```
-
削除
``` $ git annex drop hoge drop hoge ok (recording state in git...) $ cat hoge cat: hoge: そのようなファイルやディレクトリはありません ```
-
実体が全てのgit-annex repositoryからなくなるような削除は受け付けない
``` $ ../annex $ git annex drop hoge drop hoge (unsafe) Could only verify the existence of 0 out of 1 necessary copies Rather than dropping this file, try using: git annex move (Use --force to override this check, or adjust numcopies.) failed git-annex: drop: 1 failed ```
- annex管理下のデータの編集は
git annex unlock PATH
してから行い, それ以外は普通にcommitする
-
annexが管理しているデータはreadonlyにされている
``` $ ls -ln 合計 4 lrwxrwxrwx 1 1000 1000 178 1月 6 06:23 hoge -> .git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 $ ls -ln .git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/ 合計 4 -r--r--r-- 1 1000 1000 9 1月 6 06:23 SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 ```
-
unlockによりsymlinkだったworking treeの内容が実体のコピーになる
``` $ git annex unlock hoge unlock hoge (copying...) ok $ ls -ln 合計 4 -rw-r--r-- 1 1000 1000 9 1月 6 06:23 hoge ```
-
unlock中に別のrepositoryからunlockはエラーになる
``` $ cd ../annex-clone $ git annex unlock hoge unlock hoge content not present; cannot unlock failed git-annex: unlock: 1 failed ```
-
commitすると自動でsymlinkに戻してmetadata含め更新してくれる
``` $ echo "fuga" > hoge $ git add hoge $ git commit -m 'modify file' add hoge ok ok (recording state in git...) [master 5f7ed3c] modify file 1 file changed, 1 insertion(+), 1 deletion(-) ``` ``` $ ls -ln 合計 4 lrwxrwxrwx 1 1000 1000 178 1月 6 07:10 hoge -> .git/annex/objects/VX/m5/SHA256E-s5--e712aff3705ac314b9a890e0ec208faa20054eee514d86ab913d768f94e01279/SHA256E-s5--e712aff3705ac314b9a890e0ec208faa20054eee514d86ab913d768f94e01279 ```
-
変更されてもsymlinkが更新される一方で実体は別ファイルとなるため, 古いバージョンの情報は残り続ける
``` $ head .git/annex/objects/*/*/*/* ==> .git/annex/objects/VX/m5/SHA256E-s5--e712aff3705ac314b9a890e0ec208faa20054eee514d86ab913d768f94e01279/SHA256E-s5--e712aff3705ac314b9a890e0ec208faa20054eee514d86ab913d768f94e01279 <== fuga ==> .git/annex/objects/Xf/zv/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1/SHA256E-s9--678e851755589c2ed8905b10e0e8302a694af7e40ee86abacfa4bddfba859bb1 <== hogehoge ```
- git repositoryとセットになっていない特殊なgit-annex repositoryとして クラウドストレージ等様々なバックエンドを使える
- ここでは git-annex-remote-googledrive をインストールした後,
git annex initremote
でGoogle Driveを利用できるようにした - 追加後は
git annex copy PATH --to ANNEX_REMOTE
で実体の git-annex repo間のコピーを実施- hash値を名に持つデータの実体を持つファイルが作成される
仕組みに関する部分のまとめ
- annexで扱うことにしたファイルは, 実体を
.git/annex/objects
以下に移され working treeにはsymlinkが用いられ, gitとしてもこれを管理する - その他のメタデータは
git-annex
ブランチ上に置いてgit repository間で共有される- ファイルの実体の置き場であるgit-annex repositoryの情報
- ファイルごとにどのgit-annex repositoryに実体があるか
- git-annex repositoryは通常git repositoryと1対1対応する形で存在するが, 独立して様々なところに配置することも可能で, クラウドストレージも利用可能
結局のところ
- デスクトップやラップトップでやりたいことは概ねできそう
- モバイル(iOS)でgit-annexクライアントが無いため困る
- クラウドストレージのクライアントで直接見に行ってもhash名のファイルしかないので 読み書きに困るし目当てのファイルを見つけられない
- 明示的にgitの操作が必要であり, 自分の目的ではgdrive等で明示的にsyncするのとあまり変わりない
- 素直にrclone(gdriveと似たものでGoogle Drive以外も対応している)で 以前のような管理をすることにした