簡易なファイルツリーのバージョン管理ツール(gitの真似事) 2020春

ファイルツリーの一部をちょこちょこ更新したり、 新らしいファイルを追加したり、古いファイル削除したり。 よくします。

更新するたびに、ファイルツリー丸ごとtarで固めてバックアップ。

ほとんど更新が無い同じ状態の大部分のファイルが重複しつつ、 tarファイルが溜まってくると、ストレージの容量を圧迫。

「まぁ最新版のバックアップだけを残しておけばOKかな?」 などと古いバックアップを削除した後に限って、

「そう言えばあの時消したあのファイルは...」

「あぁ、バックアップ消してもうたか〜orz」

古来からソースコードのバージョン管理ツールは存在します。 cvs, subversion そして git。

「もうgitでいいじゃん」って感じですが...

ということで、gitの真似事ツールを作ってみます。


2021/OCT/02

更新履歴
日付 変更内容
2020/APR 新規作成
2020/AUG 使用例 追加
2021/MAR/22 不具合修正2020/MAR/22
2021/APR/01 push時のpush_id()時間表示追加
2021/SEP/13 restore
2021/OCT/02 checkout改

目次


使用例

簡易なファイルツリーのバージョン管理ツール(gitの真似事) 2020春の使用例


構想

ツールの名前は「ファイルの同期」的な事で「fsyn」に。

対象のファイルツリーのあるディレクトリに、トップディレクトリと並んで管理用のディレクトリを作るようにします。

gitはツリーのトップディレクトリ xxx/ の中に xxx/.git/ を作りますが、 fsynでは、トップディレクトリの外に xxx.fsyn/ を作るようにしてみます。

バージョンの識別は日付時刻の文字列のみにします。

よって、1秒以内の重複は許されません ;-p)

管理する対象は、ファイルツリーの中の「ファイル」と「シンボリック・リンク」のみです。

「空のディレクトリ」は管理対象外とします。

仕組みの概要

ファイルツリーの中のファイルとシンボリック・リンクについて、 「情報」のスナップショットをとります。

「情報」は ls -l コマンド表示的な内容で。

lstatコマンドで次の「情報」を取得するようにします。

予めスナップショットをとっておいて、ファイルツリーを更新します。

更新後にスナップショットをとって、新旧のスナップショットを比較すると、

が、あぶり出されます。

例えば、今2つの環境に同じファイルツリーがあるとします。

双方でスナップショットを作成しておきます。

一方の側で、ファイルツリーを更新し、更新後のスナップショットをとります。

新旧のスナップショットを比較すると、追加、更新されたファイルをあぶり出せます。

それらの追加、更新されたファイルをtarで固めます。

更新後のスナップショットと固めたtarファイルだけを、もう一方の側に渡せば、次の手順で更新を反映できます。

もうちょっとがんばれば、リネームしただけのファイルや、 ツリー内でのサブツリーの移動なども検出できそうです。

ですがとりあえず基本、この方式のみで作ってみます。

バージョンの記録方式

古来からのツール通り、ファイルツリー変更後、保存したいところでcommit操作をする事にします。

管理用のディレクトリ xxx.fsyn/ に、 その時点の日付時刻を名前にした、バージョン・ディレクトリを作成します。

作成したバージョン・ディレクトリの中には、次のファイルを作成します。

リンク情報のファイルには、 親のバージョン、子(複数に分岐可)のバージョンを記録する事にします。

なのでcommitでは、親のバージョン・ディレクトリ側のリンク情報ファイルも、 自分のバージョンを「子」として追加して更新します。


はずはスナップショットを作るところから

ソースコードは、 pythonのユーティリティ・プログラム 2020冬snap_ut.py として追加してみました。

kon_pageのpythonモジュールのインストール からダウンロード、インストールできます。

kon_pgge/kon_ut/snap_ut.py


初版

ざっと書いてみました。

fsyn.py

最新版のダウンロードスクリプトは、 簡易なおれおれマークダウン 2019秋 からほぼ流用し、 dl.py です。

kon_pageのpythonモジュールのインストールinst.sh に設定を追加

エイリアス設定 kon_page.rc

alias fsyn='python -m fsyn'

を追加しました。

$ . kon_page.rc

などと設定してご使用ください。

差し障り無ければ

$ cat kon_page.rc >> ~/.bashrc

などとして、ログイン設定にご追加を。


とりあえず動かしてみつつコマンドの説明

kon_pageのpythonモジュールのインストール からインストールできてる状態で、さらに

$ alias | grep fsyn
alias fsyn='python -m fsyn'

のエイリアスが設定されているならば...

$ fsyn
Usage: .../kon_page/fsyn/fsyn.py cmd arg ...
  create
  commit
  log
  curr        # show current id
  ids         # show all id
  tails       # show tail ids
  checkout id
  check       # show dirty
  fixlink     # fix link info

などとヘルプ表示が出ます。

サンプルのファイルツリー作成

$ cd /tmp
$ rm -rf foo
$ mkdir -p foo/bar
$ echo hoge > foo/bar/hoge.txt
$ echo hello > foo/hello.txt

$ find foo
foo
foo/bar
foo/bar/hoge.txt
foo/hello.txt

などと/tmpの下に意味のないファイルツリーfooを作ります。

fsyn createコマンド

$ cd foo

ツリーのトップディレクトリの直下に入って

$ fsyn create
create /private/tmp/foo.fsyn

$ ls -lt ../ | head
total 8
drwxr-xr-x  4 kondoh  wheel  136  4  6 00:01 foo.fsyn
drwxr-xr-x  4 kondoh  wheel  136  4  5 23:59 foo
  :

foo/に並んで、foo.fsyn/が生成されました。

$ find ../foo.fsyn
../foo.fsyn
../foo.fsyn/curr
../foo.fsyn/org
../foo.fsyn/org/link
../foo.fsyn/org/snap
../foo.fsyn/org/update.tgz

foo.fsyn/ 以下のファイルの正体は

$ cat ../foo.fsyn/curr
org

現在のidが'org'(オリジナル)であり

$ cat ../foo.fsyn/org/snap
2020-04-06_00.01.22
0o100644 2020-04-05_23.59.03 5 bar/hoge.txt
0o100644 2020-04-05_23.59.14 6 hello.txt

id 'org' のスナップショットであり

tar tvzf ../foo.fsyn/org/update.tgz
drwxr-xr-x  0 kondoh wheel       0  4  5 23:59 bar/
-rw-r--r--  0 kondoh wheel       5  4  5 23:59 bar/hoge.txt
-rw-r--r--  0 kondoh wheel       6  4  5 23:59 hello.txt

オリジナル・ファイルを固めたtarファイルであり

$ cat ../foo.fsyn/org/link
from_: ''
to: []

id 'org' のリンク情報のYAML形式のファイルです。

fromがfrom_なのは大人の都合です。

オリジナルのidは親は無いので、from_は空文字列の''

生成したところでまだ子はないので、toは空リストの[]になります。

ファイルツリー更新してcommit

$ pwd
/tmp/foo

$ mkdir kon
$ echo kon > kon/kon.txt
$ echo bye >> hello.txt

$ find .
.
./bar
./bar/hoge.txt
./hello.txt
./kon
./kon/kon.txt
$ fsyn commit
commit 2020-04-06_00.24.29

問答無用でそのときの日付、時刻がcommit idになります。

commitメッセージもありません。

foo.fsyn/の様子は

$ find ../foo.fsyn
../foo.fsyn
../foo.fsyn/2020-04-06_00.24.29
../foo.fsyn/2020-04-06_00.24.29/link
../foo.fsyn/2020-04-06_00.24.29/snap
../foo.fsyn/2020-04-06_00.24.29/update.tgz
../foo.fsyn/curr
../foo.fsyn/org
../foo.fsyn/org/link
../foo.fsyn/org/snap
../foo.fsyn/org/update.tgz

現在のidは

$ cat ../foo.fsyn/curr
2020-04-06_00.24.29

に変わります。

このidは、ヘルプ表示にあるcurrコマンドで表示されます。

$ fsyn curr
curr 2020-04-06_00.24.29

commitしたidのsnapファイルは

$ cat ../foo.fsyn/2020-04-06_00.24.29/snap
2020-04-06_00.24.29
0o100644 2020-04-05_23.59.03 5 bar/hoge.txt
0o100644 2020-04-06_00.18.44 10 hello.txt
0o100644 2020-04-06_00.18.41 4 kon/kon.txt

commitしたidのupdate.tgzファイルは

$ tar tvzf ../foo.fsyn/2020-04-06_00.24.29/update.tgz
-rw-r--r--  0 kondoh wheel       4  4  6 00:18 kon/kon.txt
-rw-r--r--  0 kondoh wheel      10  4  6 00:18 hello.txt

追加、変更したファイルだけが固められてます。

commitしたidのlinkファイルは

$ cat ../foo.fsyn/2020-04-06_00.24.29/link
from_: org
to: []

親はorgを指して、子は空リスト[]です。

そして親であるorg側は

cat ../foo.fsyn/org/link
from_: ''
to:
- 2020-04-06_00.24.29

子のリストにcommitしたidが追加されます。

現在のidからオリジナルまでのcommi idの履歴は、 ヘルプ表示にあるlogコマンドで表示されます。

$ fsyn log
2020-04-06_00.24.29
org

もうちょっと更新してcommitしてみます。

$ rm hello.txt

$ fsyn commit
commit 2020-04-06_00.41.11

ファイルを消してcommitしてみましたが

$ find ../foo.fsyn
../foo.fsyn
../foo.fsyn/2020-04-06_00.24.29
../foo.fsyn/2020-04-06_00.24.29/link
../foo.fsyn/2020-04-06_00.24.29/snap
../foo.fsyn/2020-04-06_00.24.29/update.tgz
../foo.fsyn/2020-04-06_00.41.11
../foo.fsyn/2020-04-06_00.41.11/link
../foo.fsyn/2020-04-06_00.41.11/snap
../foo.fsyn/curr
../foo.fsyn/org
../foo.fsyn/org/link
../foo.fsyn/org/snap
../foo.fsyn/org/update.tgz

id 2020-04-06_00.41.11 には id 2020-04-06_00.24.29 からの追加、変更は無いので update.tgz はありません。

$ fsyn log
2020-04-06_00.41.11
2020-04-06_00.24.29
org

checkout

ファイルツリーを指定のidの状態にしてみます。

$ fsyn log
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn curr
curr 2020-04-06_00.41.11

$ find .
.
./bar
./bar/hoge.txt
./kon
./kon/kon.txt

この状態から1つ前のid 2020-04-06_00.24.29 に戻してみます。

$ fsyn checkout 2020-04-06_00.24.29
checkout 2020-04-06_00.24.29

$ fsyn curr
curr 2020-04-06_00.24.29

$ find .
.
./bar
./bar/hoge.txt
./hello.txt
./kon
./kon/kon.txt

$ cat hello.txt
hello
bye

hello.txt 復活してます。

$ fsyn log
2020-04-06_00.24.29
org

logコマンドは、現在のidからorgまでを表示するので、 この場合、最新のidは表示されません。

idsコマンドで、全てのidが表示されます。

$ fsyn ids
2020-04-06_00.41.11
2020-04-06_00.24.29
org

tailsコマンドでは、linkのtoが空で子の無いid、 つまり枝の末端のidを表示します。

$ fsyn tails
2020-04-06_00.41.11

オリジナルの状態にまで戻してみます。

$ fsyn checkout org
checkout org

$ fsyn curr
curr org

$ fsyn log
org

$ find .
.
./bar
./bar/hoge.txt
./hello.txt

$ cat hello.txt
hello

hello.txt の内容はオリジナルの状態です。

最新の状態に戻してみます。

$ fsyn tails
2020-04-06_00.41.11

$ fsyn checkout 2020-04-06_00.41.11
checkout 2020-04-06_00.41.11

$ fsyn log
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ find .
.
./bar
./bar/hoge.txt
./kon
./kon/kon.txt

hello.txt は無事に消えました。OK


簡単な同期

随分時間が開きましたが、簡単なツリーの同期を試してみます。

手動で更新情報をコピーして反映します。

前回までの /tmp/foo /tmp/foo.fsyn を、 「他の人」を想定した環境にコピーしてみます。

$ cd /tmp
$ ls -ld foo foo.fsyn
drwxr-xr-x  4 kondoh  wheel  136  4  6 00:55 foo
drwxr-xr-x  6 kondoh  wheel  204  4  6 00:41 foo.fsyn

$ mkdir guest
$ tar cf - foo foo.fsyn | tar xf - -C guest

相対パスベースなので、コピー先でも同様に

$ cd guest/foo

$ fsyn curr
curr 2020-04-06_00.41.11

$ fsyn log
2020-04-06_00.41.11
2020-04-06_00.24.29
org

更新かけてみます

$ mkdir fuga
$ echo fuga > fuga/fuga.txt
$ echo fuga >> kon/kon.txt
$ fsyn check
curr 2020-04-06_00.41.11
dirty
add:
  fuga/fuga.txt
chg:
  kon/kon.txt

そしてcommit

$ fsyn commit
commit 2020-04-18_17.47.58

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

foo.fsyn/ は

$ ls -lt ../foo.fsyn/
total 8
drwxr-xr-x  5 kondoh  wheel  170  4 18 17:47 2020-04-18_17.47.58
-rw-r--r--  1 kondoh  wheel   20  4 18 17:47 curr
drwxr-xr-x  4 kondoh  wheel  136  4  6 00:41 2020-04-06_00.41.11
drwxr-xr-x  5 kondoh  wheel  170  4  6 00:24 2020-04-06_00.24.29
drwxr-xr-x  5 kondoh  wheel  170  4  6 00:01 org

本家の方に戻ってみると

$ pwd
/tmp/guest/foo

$ cd ../../foo

$ fsyn log
2020-04-06_00.41.11
2020-04-06_00.24.29
org

当然、更新前の状態です。

他の人の foo.fsyn/ から更新分だけもってきてみます。

$ pwd
/tmp/foo

$ tar cf - -C ../guest/foo.fsyn/ 2020-04-18_17.47.58 | tar xf - -C ../foo.fsyn/

$ ls -lt ../foo.fsyn
total 8
drwxr-xr-x  5 kondoh  wheel  170  4 18 17:47 2020-04-18_17.47.58
-rw-r--r--  1 kondoh  wheel   20  4  6 00:55 curr
drwxr-xr-x  4 kondoh  wheel  136  4  6 00:41 2020-04-06_00.41.11
drwxr-xr-x  5 kondoh  wheel  170  4  6 00:24 2020-04-06_00.24.29
drwxr-xr-x  5 kondoh  wheel  170  4  6 00:01 org

持ってきただけではリンクが繋がってないので

$ fsyn tails
2020-04-06_00.41.11

認識されません。

$ fsyn
Usage: .../kon_page/fsyn/fsyn.py cmd arg ...
  create
  commit
  log
  curr        # show current id
  ids         # show all id
  tails       # show tail ids
  checkout id
  check       # show dirty
  fixlink     # fix link info

fixlinkを使ってリンクを繋ぐ修復をかけます。

$ fsyn fixlink
2020-04-18_17.47.58 not in 2020-04-06_00.41.11.to
add 2020-04-18_17.47.58

これで他の人の変更分を取り込めました。

$ fsyn tails
2020-04-18_17.47.58

checkoutして他の人の更新した最新の状態にしてみます。

$ fsyn curr
curr 2020-04-06_00.41.11

$ fsyn checkout 2020-04-18_17.47.58
checkout 2020-04-18_17.47.58

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ find .
.
./bar
./bar/hoge.txt
./fuga
./fuga/fuga.txt
./kon
./kon/kon.txt

$ cat fuga/fuga.txt
fuga


push

xxx.fsyn/ の更新内容を別の場所にコピーする、pushコマンドを追加してみました。

逆に他の場所の xxx.fsyn/ から更新分を取り込むpullと対で、 目的の動作をしますが、まずはpush側だけです。

v2.patch

v2.patch

仕様

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr         # show current id
  ids          # show all id
  tails        # show tail ids
  checkout id
  check        # show dirty
  fixlink      # fix link info
  push to_path
$ fsyn push <出力するディレクトリのパス>

で指定します。

例えば、これまでの /tmp/foo

$ cd /tmp/foo

$ ls -ld ../foo ../foo.fsyn
drwxr-xr-x  5 kondoh  wheel  170  4 18 18:02 ../foo
drwxr-xr-x  7 kondoh  wheel  238  4 18 17:57 ../foo.fsyn

$ fsyn tails
2020-04-18_17.47.58

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn curr
curr 2020-04-18_17.47.58

カレントが最新のバージョンの状態です

ここから

$ mkdir /tmp/backup

$ fsyn push /tmp/backup
$ ls -l /tmp/backup
total 0
drwxr-xr-x  6 kondoh  wheel  204  4 19 15:52 foo.fsyn

$ find /tmp/backup
/tmp/backup
/tmp/backup/foo.fsyn
/tmp/backup/foo.fsyn/2020-04-06_00.24.29
/tmp/backup/foo.fsyn/2020-04-06_00.24.29/link
/tmp/backup/foo.fsyn/2020-04-06_00.24.29/snap
/tmp/backup/foo.fsyn/2020-04-06_00.24.29/update.tgz
/tmp/backup/foo.fsyn/2020-04-06_00.41.11
/tmp/backup/foo.fsyn/2020-04-06_00.41.11/link
/tmp/backup/foo.fsyn/2020-04-06_00.41.11/snap
/tmp/backup/foo.fsyn/2020-04-18_17.47.58
/tmp/backup/foo.fsyn/2020-04-18_17.47.58/link
/tmp/backup/foo.fsyn/2020-04-18_17.47.58/snap
/tmp/backup/foo.fsyn/2020-04-18_17.47.58/update.tgz
/tmp/backup/foo.fsyn/org
/tmp/backup/foo.fsyn/org/link
/tmp/backup/foo.fsyn/org/snap
/tmp/backup/foo.fsyn/org/update.tgz

$ diff -ur ../foo.fsyn /tmp/backup/foo.fsyn
Only in ../foo.fsyn: curr

currをコピーしてない以外は一致です。

バージョンの「分岐」はまだ試していませんが、 次のバージョンidについて、コピー先の方にコピーして、追加したリンクだけを追加します。

カレントバージョンをorgに移動してからpushすると、 存在する全てのidがpushする対象になるはずです。

ある枝の最新のバージョンに移動してからpushすると、 そのバージョンからorgにさかのぼるまでの1本道のid群がpush対象になります。

コピー先に対して、基本的に追加のみです。

コピー先の何かを削除するような動作はしません。(そのはずです)


pull

pullを追加してみました。

v3.patch

v3.patch

仕様

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr         # show current id
  ids          # show all id
  tails        # show tail ids
  checkout id
  check        # show dirty
  fixlink      # fix link info
  push to_path
  pull from_path
$ fsyn pull <取り込むディレクトリのパス>

で指定します。

例えば

/tmp/abc/xxx.fsyn/

の内容を

/tmp/xyz/xxx.fsyn/

に取り込む場合は

$ cd /tmp/xyz/xxx
$ fsyn pull /tmp/abc

相対パスで指定するなら

$ cd /tmp/xyz/xxx
$ fsyn pull ../../abc

pullでは、カレントバージョンからの新しい子孫のバージョンを全て取り込みます。

実行例

前回の続きで、

/tmp/foo
/tmp/foo.fsyn
/tmp/guest/foo
/tmp/guest/foo.fsyn

が同じ内容の状態とします。

まず /tmp/gust/foo 側を更新してみます。

$ cd /tmp/guest/foo

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ ls
bar	fuga	kon

$ echo hello_guest > guest.txt

$ fsyn check
curr 2020-04-18_17.47.58
dirty
add:
  guest.txt

$ fsyn commit
commit 2020-04-22_22.22.54

/tmp/backup に push してみます。

$ fsyn push ../../backup

/tmp/foo で /tmp/backup から pull してみます。

$ cd /tmp/foo

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn pull ../backup

$ fsyn tails
2020-04-22_22.22.54

取り込みはしましたが、カレントのバージョンはそのままです。

$ fsyn curr
2020-04-18_17.47.58

pull時に、自動的に最新バージョンにチェックアウトで移動したいところですが、そうなると

が問題になります。

commitするか問い合わせて、commitするかキャンセルか。

分岐があるか判定して、どの枝の最新版に移動するのか問い合わせるか。

このあたりどうするか保留で、一旦カレントバージョンをキープとしてます。

2020/MAY/02

cloneコマンド追加時に、pullコマンンドでカレントが移動するように変更しました。

pullで他の xxx.fsyn/ の情報を引っ張ってきたあと、 checkoutして、カレントidの子孫のidの中の最新のid の状態 (clatest) にします。

$ fsyn log
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn tails
2020-04-22_22.22.54

$ fsyn checkout 2020-04-22_22.22.54
checkout 2020-04-22_22.22.54

$ fsyn log
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn curr
curr 2020-04-22_22.22.54

$ ls
bar		fuga		guest.txt	kon

$ cat guest.txt
hello_guest


コマンドライン引数処理の整理

get_name_args()関数を #arg.py に「のれん分け」で移動しました。

動作は変わらないはずです。

v4.patch

v4.patch


サイトへのpushとサイトからのpull

FTP便利ツール で追加したsite_ut.pyを使って、FTPサイトにpushしたり、 そのサイトのURLからpullできるようにしてみました。

push先、pull元のパスに'site_ut'を指定すると、特別な動作をする事にします。

site_ut.pyで使う設定ファイルftp_ut.yamlは、 作業ディレクトリ xxx/ や、xxx.fsyn/ ディレクトリと同じディレクトリに配置します。

例えば

cd /tmp
$ ls -l
  :
-rw-r--r--  1 kondoh  wheel  115  4 29 16:16 ftp_ut.yaml
drwxr-xr-x  6 kondoh  wheel  204  4 22 22:24 foo
drwxr-xr-x  8 kondoh  wheel  272  4 22 22:24 foo.fsyn
  :
$ cat ftp_ut.yaml
tmp_sample: xdomain/tmp_sample

url:
  tmp_sample: http://kondoh.html.xdomain.jp/tmp_sample

default:
  tmp_sample

本家ftp_ut.pyのあるディレクトリの方のftp_ut.yamlでは、 xdomainについての、ホスト、ユーザーなどの情報が記述されているものとします。

予めサイトに作業ディレクトリを作っておきます。

$ ftp_ut -s xdomain mkdir tmp_sample
login
mkdir tmp_sample

v5.patch

v5.patch

実行例

$ cd /tmp/foo

いつもの山坂道... じゃなくて /tmp/foo にやってまいりました。

$ fsyn curr
curr 2020-04-22_22.22.54

$ fsyn tails
2020-04-22_22.22.54

$ fsyn log
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ cat ../ftp_ut.yaml
tmp_sample: xdomain/tmp_sample

url:
  tmp_sample: http://kondoh.html.xdomain.jp/tmp_sample

default:
  tmp_sample
$ fsyn push site_ut
upload cmd=( cd /private/tmp ; python -m site_ut put -C /tmp/fsyn_20200429194356 foo.fsyn ) ...
  :

今自分が使ってる環境では、ささいな量のアップロードなのに、かなり時間がかかりました。

upload cmd=( cd /private/tmp ; python -m site_ut put -C /tmp/fsyn_20200429194356 foo.fsyn ) ... ok

site_ut.py から ftp_ut.py の動作が遅い...

$ ( cd .. ; ftp_ut find )
foo.fsyn/2020-04-06_00.24.29/link
foo.fsyn/2020-04-06_00.24.29/snap
foo.fsyn/2020-04-06_00.24.29/update.tgz
foo.fsyn/2020-04-06_00.41.11/link
foo.fsyn/2020-04-06_00.41.11/snap
foo.fsyn/2020-04-18_17.47.58/link
foo.fsyn/2020-04-18_17.47.58/snap
foo.fsyn/2020-04-18_17.47.58/update.tgz
foo.fsyn/2020-04-22_22.22.54/link
foo.fsyn/2020-04-22_22.22.54/snap
foo.fsyn/2020-04-22_22.22.54/update.tgz
foo.fsyn/org/link
foo.fsyn/org/snap
foo.fsyn/org/update.tgz

小さいファイルばかりではありますが、無事アップロードされた様子です。

ローカル側のこの状態を別の場所にコピーして、何か更新を加えてみます。

$ cd /tmp

$ mkdir other

$ tar cf - foo foo.fsyn ftp_ut.yaml | tar xf - -C other

同じサイトにアクセスするためftp_ut.yamlのコピーも忘れずに。

$ cd other/foo

$ fsyn log
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ echo other >> guest.txt
$ echo other > kon/other.txt

$ fsyn check
curr 2020-04-22_22.22.54
dirty
add:
  kon/other.txt
chg:
  guest.txt

$ fsyn commit
commit 2020-04-29_20.05.45

そしてサイトにpush

$ fsyn push site_ut
upload cmd=( cd /private/tmp/other ; python -m site_ut put -C /tmp/fsyn_20200429203700 foo.fsyn ) ... ok
$ cd ..
$ ftp_ut find

foo.fsyn/2020-04-06_00.24.29/link
foo.fsyn/2020-04-06_00.24.29/snap
foo.fsyn/2020-04-06_00.24.29/update.tgz
foo.fsyn/2020-04-06_00.41.11/link
foo.fsyn/2020-04-06_00.41.11/snap
foo.fsyn/2020-04-18_17.47.58/link
foo.fsyn/2020-04-18_17.47.58/snap
foo.fsyn/2020-04-18_17.47.58/update.tgz
foo.fsyn/2020-04-22_22.22.54/link
foo.fsyn/2020-04-22_22.22.54/snap
foo.fsyn/2020-04-22_22.22.54/update.tgz
foo.fsyn/2020-04-29_20.05.45/link
foo.fsyn/2020-04-29_20.05.45/snap
foo.fsyn/2020-04-29_20.05.45/update.tgz
foo.fsyn/org/link
foo.fsyn/org/snap
foo.fsyn/org/update.tgz

追加されてます。

では、元の/tmp/fooに戻ってpull

$ cd /tmp/foo

$ fsyn pull site_ut

$ fsyn tails
2020-04-29_20.05.45

$ fsyn curr
curr 2020-04-22_22.22.54

最新版を取り込んでもカレントは移動しないので、手動でチェックアウトします。

2020/MAY/02

cloneコマンド追加時に、pullコマンンドでカレントが移動するように変更しました。

pullで他の xxx.fsyn/ の情報を引っ張ってきたあと、 checkoutして、カレントidの子孫のidの中の最新のid の状態 (clatest) にします。

$ fsyn checkout 2020-04-29_20.05.45
checkout 2020-04-29_20.05.45

$ fsyn log
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org


バージョンidを表示するいくつかのコマンドを追加

ids, tails 以外にいくつか追加してみました。

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr           # show current id
  ids            # show all ids
  tails          # show tail ids
  ctails         # show tail ids in current children
  latest         # show latest id
  clatest        # show latest id in current chidren
  prev           # show prev id
  next           # show next ids
  checkout id
  check          # show dirty
  fixlink        # fix link info
  push to_path
  pull from_path
コマンド 説明
ids 全てのidを表示します
tails 全ての枝の最新(末端)のidを表示します
ctails カレントidの子孫の全ての枝の最新(末端)のidを表示します
latest 全てのidの中の最新のidを表示します
clatest カレントidの子孫のidの中の最新のidを表示します
prev カレントidの1つ古い(親)のidを表示します
next カレントidの1つ新しい(子)のidを表示します

tails, ctail, next については、次の形式で表示します。

v6.patch

v6.patch

使用例

未だ試せてなく、未デバッグ状態...


clone

cloneコマンドを追加してみました。

何もない状態から、既存の xxx.fsyn/ のパスを与えると、 カレントディレクトリに、xxx.fsyn/ xxx/ を作成して、 checkoutして、最新のid の状態 (latest) にします。

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr                # show current id
  ids                 # show all ids
  tails               # show tail ids
  ctails              # show tail ids in current children
  latest              # show latest id
  clatest             # show latest id in current chidren
  prev                # show prev id
  next                # show next ids
  checkout id
  check               # show dirty
  fixlink             # fix link info
  push to_path
  pull from_path
  clone dot_fsyn_path # ex. site/foo.fsyn

保留してたpullコマンドの動作について

pullで他の xxx.fsyn/ の情報を引っ張ってきたあと、 checkoutして、カレントidの子孫のidの中の最新のid の状態 (clatest) にします。

バージョンidを表示するいくつかのコマンド

依然として、後回しで未デバッグです。m(__)m

サイトへのアップロード時

あまりにも「だんまり」期間が長いので、

FTP便利ツールVersion 24 で、site_ut.py に -verb オプションを追加しました。

fsyn.py からの site_ut.py 呼び出しで -verb オプション指定を追加して、 表示するようにしてみました。

fixlinkコマンド

バグがありました。

link情報を修正した後、正しくput_linkされない箇所がありました。 修正しておきます。

v7.patch

v7.patch

cloneの実行例

いつもの山坂道、じゃなくて/tmp/foo

$ cd /tmp/foo

$ fsyn curr
curr 2020-04-29_20.05.45

$ fsyn latest
2020-04-29_20.05.45

何か追加して更新してみます

$ echo hoge >> kon/kon.txt

$ fsyn check
curr 2020-04-29_20.05.45
dirty
chg:
  kon/kon.txt

$ fsyn commit
commit 2020-05-03_01.00.18

例えば次のようにsite_ut.pyでアクセスするサイトが設定されているものとします。

$ cat ../ftp_ut.yaml
tmp_sample: xdomain/tmp_sample

url:
  tmp_sample: http://kondoh.html.xdomain.jp/tmp_sample

default:
  tmp_sample

おもむろにサイトにpush

$ fsyn push site_ut
  :

そして使用するサイトの情報を、新たな地へとコピー

$ mkdir -p /tmp/new_dir

$ cp ../ftp_ut.yaml /tmp/new_dir/

その新たな地でclone

$ cd /tmp/new_dir

$ fsyn clone site_ut/foo.fsyn
checkout 2020-05-03_01.00.18

$ ls -l
total 8
drwxr-xr-x   6 kondoh  wheel  204  5  3 01:19 foo
drwxr-xr-x  10 kondoh  wheel  340  5  3 01:19 foo.fsyn
-rw-r--r--   1 kondoh  wheel  115  5  3 01:17 ftp_ut.yaml

中に入って確認

$ cd foo

$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ cat kon/kon.txt
kon
fuga
hoge

一応いつもの/tmp/foo.fsynとも比較

$ diff -ur ../foo.fsyn /tmp/foo.fsyn
$

一致OK

サイトでなくとも既存のxxx.fsyn/ ディレクトリからcloneできます。

例えば、いつぞやpushした/tmp/backup/foo.fsyn

$ mkdir /tmp/new_dir2

$ cd /tmp/new_dir2

$ fsyn clone /tmp/backup/foo.fsyn
checkout 2020-04-22_22.22.54

先ほど本家/tmp/fooでcommitしたバージョンは当然存在せず、古いです。

$ cd foo

$ fsyn log
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

本家からpullすると

$ fsyn pull /tmp
checkout 2020-05-03_01.00.18

今回からpullするとcurrの子孫の中の最新版にcheckoutされます。

$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

これで本家と同様に最新に。


checkoutでidをエイリアスで指定

checkoutで指定する日付時刻のバージョン文字列が、 あまりに入力が煩雑なので、 エイリアスが使えるようにしてみました。

例えば、日付が一番新しいidを指定するなら

$ fsyn checkout latest

のように。

エイリアス 説明
latest 全てのidの中の最新のid
clatest カレントidの子孫のidの中の最新のid
prev カレントidの1つ古い(親)のid
next カレントidの1つ新しい(子)のid (複数候補あり)
tail 全ての枝の最新(末端)のid (複数候補あり)
ctail カレントidの子孫の全ての枝の最新(末端)のid (複数候補あり)

「複数候補あり」では、 実際には1つしか候補がなかった場合、 その1つの候補を指定した事になります。

複数候補があった場合は、 候補一覧を表示するだけで終了します。

v8.patch

v8.patch

エイリアス使用例

いつもの山坂道へ

$ cd /tmp/foo

fsyn latest
2020-05-03_01.00.18

$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org
$

$ fsyn checkout prev
checkout 2020-04-29_20.05.45

$ fsyn checkout org
checkout org

$ fsyn checkout next
checkout 2020-04-06_00.24.29

$ fsyn checkout clatest
checkout 2020-05-03_01.00.18

分岐お試し

$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

中ほどのバージョンから、適当に分岐を試してみます。

$ fsyn checkout 2020-04-18_17.47.58
checkout 2020-04-18_17.47.58

$ ls
bar	fuga	kon

$ echo abc > branch

$ ls
bar	branch	fuga	kon

$ fsyn commit
commit 2020-05-03_13.03.02

$ fsyn log
2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

logではカレントからorgまでの枝だけの表示です。

全てのバージョンを表示すると

$ fsyn ids
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

全ての枝の末端を表示すると

$ fsyn tails
tail1: 2020-05-03_13.03.02
tail0: 2020-05-03_01.00.18

これまでの最新は tail0 として、 先ほどcommitした枝の最新は tail1 として表示されています。

カレントは先ほどcommitしたてのバージョンに居るので、 カレントの枝からの末端としては

$ fsyn ctails
2020-05-03_13.03.02

という事です。

もうちょい、今追加した枝を伸ばしてみます。

$ echo xyz >> branch

$ fsyn commit
commit 2020-05-03_13.10.21

$ fsyn log
2020-05-03_13.10.21
2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

例えば分岐点のバージョン 2020-04-18_17.47.58 に移動してみて

$ fsyn checkout prev
checkout 2020-05-03_13.03.02

$ fsyn checkout prev
checkout 2020-04-18_17.47.58

そこからのnext候補は

$ fsyn next
next1: 2020-05-03_13.03.02
next0: 2020-04-22_22.22.54

2つに分岐してます。

nextをcheckoutしようとすると

$ fsyn checkout next
next1: 2020-05-03_13.03.02
next0: 2020-04-22_22.22.54
select

どちらか指定せよと。

先ほど作った方の枝を選ぶならnext1の方なので

$ fsyn checkout next1
checkout 2020-05-03_13.03.02

このカレントバージョンの新しい枝の最新に移動するなら

$ fsyn checkout clatest
checkout 2020-05-03_13.10.21

以前からある枝の方の最新に移動するなら

$ fsyn tails
tail1: 2020-05-03_13.10.21
tail0: 2020-05-03_01.00.18

$ fsyn checkout tail0
checkout 2020-05-03_01.00.18

$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

新しい方の枝の最新に移動しておいて

$ fsyn checkout tail1
checkout 2020-05-03_13.10.21

$ fsyn log
2020-05-03_13.10.21
2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

そこからpushすると、新しい枝の末端からorgまでの、 このlog表示のバージョンについて、push先に無いものがpushされます。

$ fsyn push /tmp/backup

例えば、ちょっと古い状態のツリーに移動

$ cd /tmp/other/foo

$ fsyn ids
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn curr
2020-04-22_22.22.54

この古い方の枝から、先ほど新しい枝をpushした/tmp/backupからpullしても

$ fsyn pull /tmp/backup
checkout 2020-05-03_01.00.18
$ fsyn log
2020-05-03_01.00.18
2020-04-29_20.05.45
2020-04-22_22.22.54
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

古い方の枝の更新分だけが反映されて、 古い枝だけが最新になります。

子孫に新しい枝が含まれる位置までカレントを戻して、 pullすれば、新しい枝が取り込まれます。

とりあえずorgにまで戻ってpullしておけば、 全部の枝が最新に更新されて、 最新のidを含む枝の末端に移動します。

$ fsyn checkout org
checkout org

$ fsyn pull /tmp/backup
checkout 2020-05-03_13.10.21

$ fsyn log
2020-05-03_13.10.21
2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

新しい方の枝の末端が、今一番新日付時刻idなので、 新しい方の枝の最新に移動します。

こうなると、 全てのidの枝の全貌を表示するtree表示の機能が欲しいところです...


tree表示

treeを名のるのは申し訳ないような単純な表示ですが、とりあえず追加してみました。

v9.patch

v9.patch

実行例

$ cd /tmp/foo

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr                # show current id
  ids                 # show all ids
  tails               # show tail ids
  ctails              # show tail ids in current children
  latest              # show latest id
  clatest             # show latest id in current chidren
  prev                # show prev id
  next                # show next ids
  checkout id         # alias is used instead of id
  check               # show dirty
  fixlink             # fix link info
  push to_path
  pull from_path
  clone dot_fsyn_path # ex. site/foo.fsyn
  tree                # show id tree

$ fsyn tree
2020-05-03_01.00.18@
2020-04-29_20.05.45
  2020-05-03_13.10.21*
2020-04-22_22.22.54
  2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

枝の末端のidには'*'を、 カレントのidには'@'を追加してます。

$ fsyn checkout prev
checkout 2020-04-29_20.05.45

$ fsyn tree
2020-05-03_01.00.18*
2020-04-29_20.05.45@
  2020-05-03_13.10.21*
2020-04-22_22.22.54
  2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn checkout org
checkout org
$ fsyn tree
2020-05-03_01.00.18*
2020-04-29_20.05.45
  2020-05-03_13.10.21*
2020-04-22_22.22.54
  2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org@

$ fsyn tails
tail1: 2020-05-03_13.10.21
tail0: 2020-05-03_01.00.18

$ fsyn checkout tail0
checkout 2020-05-03_01.00.18

$ fsyn tree
2020-05-03_01.00.18@
2020-04-29_20.05.45
  2020-05-03_13.10.21*
2020-04-22_22.22.54
  2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org


ssh接続の場合

ssh接続できるなら、scpでファイルコピーができる事でしょう。

しかしながら、ファイルコピーのたびにパスワード入力を求められると、ちょっとうんざりですね。

ssh接続できるなら、sshfsでマウントできるであろうという事で、sshfsに頼る事にします。

sshfsをインストールして、使用できる状態を前提とします。

単純にsshfsでマウントする場合

例えば、ホスト名 hoge のマシンに、ユーザ kondoh でログインできるとして、

既存のディレクトリ

/home/kondoh/remote_backup/

以下に foo.fsyn/ を push する場合

予め、ローカルマシンの適当なマウントポイントに remote_backup ディレクトリをsshfsでマウントします。

これが全てです。

$ mkdir /tmp/remote_backup

$ sshfs kondoh@hoge:/home/kondoh/remote_backup /tmp/remote_backup
passwd

$ ls /tmp/remote_backup
  :

パスワード入力が求められるとすれば、このマウント時だけです。

/tmp/remote_backup 以下に、マシン hoge の方の内容が見えてるはずです。

あとは、いつもの /tmp/foo から普通にpushするだけ

$ cd /tmp/foo

$ fsyn push /tmp/remote_backup

pullする場合も

$ cd /tmp/new_dir/foo

$ fsyn pull /tmp/remote_backup

clone する場合は

$ mkdir /tmp/from_hoge

$ cd /tmp/from_hoge

$ fsyn clone /tmp/remote_backup/foo.fsyn

使い終わったらアンマウントしておきます

$ umount /tmp/remote_backup

ファイアウォールの内側のマシンにアクセスしたい場合

sshのポートフォワーディングを使います。

ローカルマシンから直接見えない、ホスト名 fuga のマシンのディレクトリで、 push, pull, clone したい場合。

例えば、ホスト名 hoge のマシンに、ユーザ kondoh でログインできて、、、

マシン hoge からならば、sshでマシンfuga にユーザ masao でログインできるとします。

マシン fuga にユーザ masao でログインして、既存のディレクトリ

/home/masao/fuga_backup/

以下を使いたい場合

まず、ポートフォワーディングの設定でマシン hoge にsshでログインします。

$ ssh -L 8022:fuga:22 kondoh@hoge

例えば上記の-Lオプション指定の場合、 マシンhogeから見えてる、マシンfugaのポート22が、 ローカルマシンのポート8022として見える状態になります。

上記のsshによるログインを保ったままにしておいて、 別端末からsshfsコマンドでマウントします。

別端末から
$ mkdir /tmp/fuga_backup

$ sshfs -p 8022 masao@localhost:/home/masao/fuga_backup /tmp/fuga_backup
passwd

$ ls /tmp/fuga_backup
  :

ローカルマシンのポート8022は、マシンfugaのポート22が見えているので、 -p 8022 を指定してsshfsでマウントします。

マシンfugaにユーザmasaoでログインする時のパスワード入力を求められるので、 入力します。

ローカルマシンの/tmp/fuga_backup/以下に、 マシンfugaの/home/masao/fuga_backup/以下の内容が見えてるはずです。

これが全てです。

$ cd /tmp/foo

$ fsyn push /tmp/fuga_backup

$ cd /tmp/other/foo

$ fsyn pull /tmp/fuga_backup

$ mkdir /tmp/new_dir3

$ cd /tmp/new_dir3

$ fsyn clone /tmp/fuga_backup/foo.fsyn

使い終わったらアンマウントして、マシン hoge からもログアウトしておきます

$ umount /tmp/fuga_backup

hogeにログインを保っている端末で
$ exit


diff

diffコマンドを追加してみました。

端末の画面に入りきらない場合でもgitのようなless表示にはなりません。サボってます。

流れていくようであれば、手動で " | less" を追加して実行してください。

v10.patch

v10.patch

実行例

いつもの山坂道で確認してみます。

$ cd /tmp/foo

$ fsyn tree
2020-05-03_01.00.18@
2020-04-29_20.05.45
  2020-05-03_13.10.21*
2020-04-22_22.22.54
  2020-05-03_13.03.02
2020-04-18_17.47.58
2020-04-06_00.41.11
2020-04-06_00.24.29
org

$ fsyn curr
2020-05-03_01.00.18

の状態から、1つ前のバージョンとの差分は

$ fsyn prev
2020-04-29_20.05.45

$ fsyn diff prev
diff -urN a/kon/kon.txt b/kon/kon.txt
--- a/kon/kon.txt	2020-04-18 17:46:21.000000000 +0900
+++ b/kon/kon.txt	2020-05-03 01:00:05.000000000 +0900
@@ -1,2 +1,3 @@
 kon
 fuga
+hoge

orgとその次の2020-04-06_00.24.29との差分ならば

$ fsyn diff org 2020-04-06_00.24.29
diff -urN a/hello.txt b/hello.txt
--- a/hello.txt	2020-04-05 23:59:14.000000000 +0900
+++ b/hello.txt	2020-04-06 00:18:44.000000000 +0900
@@ -1 +1,2 @@
 hello
+bye
diff -urN a/kon/kon.txt b/kon/kon.txt
--- a/kon/kon.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/kon/kon.txt	2020-04-06 00:18:41.000000000 +0900
@@ -0,0 +1 @@
+kon

orgからprevまでの差分は

$ fsyn diff org 2020-04-06_00.24.29
diff -urN a/hello.txt b/hello.txt
--- a/hello.txt	2020-04-05 23:59:14.000000000 +0900
+++ b/hello.txt	2020-04-06 00:18:44.000000000 +0900
@@ -1 +1,2 @@
 hello
+bye
diff -urN a/kon/kon.txt b/kon/kon.txt
--- a/kon/kon.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/kon/kon.txt	2020-04-06 00:18:41.000000000 +0900
@@ -0,0 +1 @@
+kon
$ fsyn diff org prev
diff -urN a/fuga/fuga.txt b/fuga/fuga.txt
--- a/fuga/fuga.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/fuga/fuga.txt	2020-04-18 17:46:07.000000000 +0900
@@ -0,0 +1 @@
+fuga
diff -urN a/guest.txt b/guest.txt
--- a/guest.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/guest.txt	2020-04-29 20:04:53.000000000 +0900
@@ -0,0 +1,2 @@
+hello_guest
+other
diff -urN a/hello.txt b/hello.txt
--- a/hello.txt	2020-04-05 23:59:14.000000000 +0900
+++ b/hello.txt	1970-01-01 09:00:00.000000000 +0900
@@ -1 +0,0 @@
-hello
diff -urN a/kon/kon.txt b/kon/kon.txt
--- a/kon/kon.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/kon/kon.txt	2020-04-18 17:46:21.000000000 +0900
@@ -0,0 +1,2 @@
+kon
+fuga
diff -urN a/kon/other.txt b/kon/other.txt
--- a/kon/other.txt	1970-01-01 09:00:00.000000000 +0900
+++ b/kon/other.txt	2020-04-29 20:05:06.000000000 +0900
@@ -0,0 +1 @@
+other

カレントの状態から変更して汚してみます。

$ echo fuga_fuga_fuga >> fuga/fuga.txt

$ fsyn check
2020-05-03_01.00.18
dirty
chg:
  fuga/fuga.txt

commitせずに、バージョン指定なしで実行すると、 元のcommitしてるバージョンとの差分が表示されます。

$ fsyn diff
diff -urN a/fuga/fuga.txt b/fuga/fuga.txt
--- a/fuga/fuga.txt	2020-04-18 17:46:07.000000000 +0900
+++ b/fuga/fuga.txt	2020-05-06 16:19:17.000000000 +0900
@@ -1 +1,2 @@
 fuga
+fuga_fuga_fuga

せっかくなのでcommitしておきます

$ fsyn commit
commit 2020-05-06_16.21.55

動作確認

いつもの /tmp/foo の差分では、量が少なく頼りないので、もうちょっと動作確認しておきます。

テスト用の場所で、これまでの fsyn.py の変遷をcommitして、 diff結果を確認してみます。

test_dir.sh

$ cat test_dir.sh
#!/bin/bash

FSYN="python -m fsyn"
DIR="/tmp/test_dir"
WORK="/tmp/test_dir_work"
URL="http://kondoh.html.xdomain.jp/fsyn"

rm -rf ${DIR} ${WORK}
mkdir -p ${DIR}/src ${WORK}
cd ${DIR}

( cd src ; wget ${URL}/fsyn.py )

${FSYN} create

for i in $(seq 2 9); do
  sleep 2
  ( cd src ; wget -q -O- ${URL}/v${i}.patch | patch -p1 )
  ${FSYN} commit
done

${FSYN} log > log
n=$(cat log | wc -l)
tail +2 log > ${WORK}/a
head -$( expr ${n} - 1 ) log > ${WORK}/b

i=9
for l in $( ( cd ${WORK} ; paste -d '@' a b ) ); do
  targs=$( echo ${l} | sed -e 's/@/ /' )
  ${FSYN} diff ${targs} > ${WORK}/d
  wget -q -O- ${URL}/v${i}.patch | diff -u ${WORK}/d -
  i=$( expr ${i} - 1 )
done

rm -rf ${WORK}

# EOF

実行すると、最初のバージョンから順にパッチをあててはcommitしていきます。

1秒以内のcommitは、id重複の危険があるので、commit前に2秒の待機を入れてます ;-p)

各バージョンのfsyn diffをファイルに落としつつ、 元の各バージョンのパッチファイルと比較した結果が表示されます。

$ chmod +x test_dir.sh
$ ./test_dir.sh
  :
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 8021 (7.8K) [text/plain]
`fsyn.py' に保存中

fsyn.py             100%[===================>]   7.83K  --.-KB/s 時間 0.03s

2020-05-06 16:26:11 (305 KB/s) - `fsyn.py' へ保存完了 [8021/8021]

create /private/tmp/test_dir.fsyn
patching file fsyn.py
commit 2020-05-06_16.26.13
patching file fsyn.py
commit 2020-05-06_16.26.15
patching file fsyn.py
commit 2020-05-06_16.26.17
patching file fsyn.py
commit 2020-05-06_16.26.20
patching file fsyn.py
commit 2020-05-06_16.26.22
patching file fsyn.py
commit 2020-05-06_16.26.24
patching file fsyn.py
commit 2020-05-06_16.26.26
patching file fsyn.py
commit 2020-05-06_16.26.28
--- /tmp/test_dir_work/d	2020-05-06 16:26:29.000000000 +0900
+++ -	2020-05-06 16:26:29.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:26.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:28.000000000 +0900
+diff -ur v8/fsyn.py v9/fsyn.py
+--- v8/fsyn.py	2020-05-03 13:13:09.000000000 +0900
++++ v9/fsyn.py	2020-05-05 12:24:53.000000000 +0900
 @@ -554,6 +554,9 @@
  		if do_checkout_clatest:
  			checkout( get_clatest() )
--- /tmp/test_dir_work/d	2020-05-06 16:26:30.000000000 +0900
+++ -	2020-05-06 16:26:30.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:24.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:26.000000000 +0900
+diff -ur v7/fsyn.py v8/fsyn.py
+--- v7/fsyn.py	2020-05-03 01:43:08.000000000 +0900
++++ v8/fsyn.py	2020-05-03 13:13:09.000000000 +0900
 @@ -162,7 +162,7 @@
  		msg_done( 'commit', id )

--- /tmp/test_dir_work/d	2020-05-06 16:26:31.000000000 +0900
+++ -	2020-05-06 16:26:31.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:22.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:24.000000000 +0900
+diff -ur v6/fsyn.py v7/fsyn.py
+--- v6/fsyn.py	2020-05-01 00:00:02.000000000 +0900
++++ v7/fsyn.py	2020-05-03 01:43:08.000000000 +0900
 @@ -25,6 +25,15 @@
  	s = sys.stdin.readline().strip()
  	return s == 'y'
--- /tmp/test_dir_work/d	2020-05-06 16:26:32.000000000 +0900
+++ -	2020-05-06 16:26:32.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:20.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:22.000000000 +0900
+diff -ur v5/fsyn.py v6/fsyn.py
+--- v5/fsyn.py	2020-04-29 20:50:21.000000000 +0900
++++ v6/fsyn.py	2020-05-01 00:00:02.000000000 +0900
 @@ -176,11 +176,61 @@
  		f = lambda s: s == 'org' or tm_ut.is_date_time_str( s, tm_ut.sample )
  		return list( filter( f, lst ) )
--- /tmp/test_dir_work/d	2020-05-06 16:26:32.000000000 +0900
+++ -	2020-05-06 16:26:32.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:17.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:20.000000000 +0900
+diff -ur v4/fsyn.py v5/fsyn.py
+--- v4/fsyn.py	2020-04-26 17:39:47.000000000 +0900
++++ v5/fsyn.py	2020-04-29 20:50:21.000000000 +0900
 @@ -17,7 +17,7 @@

  to_str = base.to_str
--- /tmp/test_dir_work/d	2020-05-06 16:26:34.000000000 +0900
+++ -	2020-05-06 16:26:34.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:15.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:17.000000000 +0900
+diff -ur v3/fsyn.py v4/fsyn.py
+--- v3/fsyn.py	2020-04-22 22:24:17.000000000 +0900
++++ v4/fsyn.py	2020-04-26 17:39:47.000000000 +0900
 @@ -389,59 +389,21 @@

  	return empty.new( locals() )
--- /tmp/test_dir_work/d	2020-05-06 16:26:34.000000000 +0900
+++ -	2020-05-06 16:26:34.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-05-06 16:26:13.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:15.000000000 +0900
+diff -ur v2/fsyn.py v3/fsyn.py
+--- v2/fsyn.py	2020-04-19 15:43:08.000000000 +0900
++++ v3/fsyn.py	2020-04-22 22:24:17.000000000 +0900
 @@ -353,6 +353,40 @@

  		push_to( get_curr() )
--- /tmp/test_dir_work/d	2020-05-06 16:26:35.000000000 +0900
+++ -	2020-05-06 16:26:35.000000000 +0900
@@ -1,6 +1,6 @@
-diff -urN a/src/fsyn.py b/src/fsyn.py
---- a/src/fsyn.py	2020-04-06 01:08:40.000000000 +0900
-+++ b/src/fsyn.py	2020-05-06 16:26:13.000000000 +0900
+diff -ur v1/fsyn.py v2/fsyn.py
+--- v1/fsyn.py	2020-04-06 00:24:24.000000000 +0900
++++ v2/fsyn.py	2020-04-19 15:43:08.000000000 +0900
 @@ -27,17 +27,23 @@


$

行頭に"+"や"-"がある行は、ヘッダの部分以外に特に見当たらず、問題なさそうです。

/tmp/test_dir/
/tmp/test_dir.fsyn/

が残っていますので

$ cd /tmp/test_dir

$ fsyn log
2020-05-06_16.26.28
2020-05-06_16.26.26
2020-05-06_16.26.24
2020-05-06_16.26.22
2020-05-06_16.26.20
2020-05-06_16.26.17
2020-05-06_16.26.15
2020-05-06_16.26.13
org

$ fsyn curr
2020-05-06_16.26.28

この場合の最新は、treeコマンドを追加したときのバージョン9になっているので、 最新とその1つ前との差分を表示してみると

$ fsyn diff prev
diff -urN a/src/fsyn.py b/src/fsyn.py
--- a/src/fsyn.py	2020-05-06 16:26:26.000000000 +0900
+++ b/src/fsyn.py	2020-05-06 16:26:28.000000000 +0900
@@ -554,6 +554,9 @@
 		if do_checkout_clatest:
 			checkout( get_clatest() )

+	def tree():
+		nodes_tree( get_link, get_curr() )
+

 	return empty.new( locals() )

@@ -587,6 +590,75 @@
 	fsyn.checkout( id )


+def nodes_tree(get_link, curr):
+	def link_to_nodes(id, from_node=None):
+		link = get_link( id )
+		node = empty.new( id=id, from_=from_node, to=[] )
+		if link.to:
+			node.to = list( map( lambda id: link_to_nodes( id, node ), link.to ) )
+		return node
+
+	def get_tails(node):
+		if not node.to:
+			return [ node ]
+		return sum( map( get_tails, node.to ), [] )
+
+	def max_tail(node):
+		def len_from(node, from_node):
+			if node == from_node:
+				return 0
+			return len_from( node.from_, from_node ) + 1
+
+		return max( get_tails( node ), key=lambda tail: len_from( tail, node ) )
+
+	def get_lst(from_node, tail):
+		lst = [ tail ]
+		while tail != from_node:
+			tail = tail.from_
+			lst.insert( 0, tail )
+		return lst
+
+	lsts = []
+
+	def add_lst(from_node, i=0):
+		tail = max_tail( from_node )
+		lst = get_lst( from_node, tail )
+		if lsts:
+			n = len( lsts[ 0 ] )
+			j = n - i - len( lst )
+			lst = [ None ] * i + lst + [ None ] * j
+		lsts.append( lst )
+
+		n = len( lst )
+		for i in reversed( range( n ) ):
+			node = lst[ i ]
+			if not node or len( node.to ) < 2:
+				continue
+			for to_node in node.to:
+				if to_node != lst[ i + 1 ]:
+					add_lst( to_node, i + 1 )
+
+	def show():
+		if not lsts:
+			return
+		zip_lsts = list( zip( *lsts ) )
+		n = len( zip_lsts )
+		for i in reversed( range( n ) ):
+			lst = zip_lsts[ i ]
+			for j in range( len( lst ) ):
+				node = lst[ j ]
+				if node:
+					s = '  ' * j + node.id
+					if node.id == curr:
+						s += '@' # curr
+					elif i == n - 1 or not lsts[ j ][ i + 1]:
+						s += '*' # tail
+					dbg.out( s )
+
+	org = link_to_nodes( 'org' )
+	add_lst( org )
+	show()
+
 if __name__ == "__main__":
 	cmds = [
 		arg.cmd_new( 'create' ),
@@ -606,6 +678,7 @@
 		arg.cmd_new( 'push', [ 'to_path' ] ),
 		arg.cmd_new( 'pull', [ 'from_path' ] ),
 		arg.cmd_new( 'clone', [ 'dot_fsyn_path' ], comment='ex. site/foo.fsyn' ),
+		arg.cmd_new( 'tree', comment='show id tree' ),
 	]
 	cmd = arg.get_name_args( cmds )

$

確かに、treeコマンドを追加する内容になってます。OK

orgからその次のバージョンへの差分は

$ fsyn diff org 2020-05-06_16.26.13
diff -urN a/src/fsyn.py b/src/fsyn.py
--- a/src/fsyn.py	2020-04-06 01:08:40.000000000 +0900
+++ b/src/fsyn.py	2020-05-06 16:26:13.000000000 +0900
@@ -27,17 +27,23 @@


 def fsyn_new(dir_path):
-	fsyn_path = dir_path + '.fsyn'

-	id_fn = lambda id, name: path_join( fsyn_path, id, name )
+	def fsyn_path(dir_=None):
+		if not dir_:
+			return dir_path + '.fsyn'
+
+		( DIR, BASE ) = os.path.split( dir_path )
+		return path_join( dir_, BASE ) + '.fsyn'
+
+	id_fn = lambda id, name, dir_=None: path_join( fsyn_path( dir_ ), id, name )

 	def set_curr(id):
-		fn = path_join( fsyn_path, 'curr' )
+		fn = path_join( fsyn_path(), 'curr' )
 		cmd = 'echo {} > {}'.format( id, fn )
 		cmd_call( cmd )

 	def get_curr():
-		fn = path_join( fsyn_path, 'curr' )
+		fn = path_join( fsyn_path(), 'curr' )
 		cmd = 'cat ' + fn
 		return cmd_call( cmd ).strip()

@@ -56,24 +62,24 @@
 		f = lambda name: path_exists( id_fn( id, name ) )
 		return all( map( f, ( 'link', 'snap' ) ) )

-	def get_link(id):
-		fn = id_fn( id, 'link' )
+	def get_link(id, dir_=None):
+		fn = id_fn( id, 'link', dir_ )
 		if path_exists( fn ):
 			d = yaml_ut.load_fn( fn )
 			return empty.new( d )
 		return empty.new( from_='', to=[] ) # !

-	def put_link(id, link):
-		fn = id_fn( id, 'link' )
+	def put_link(id, link, dir_=None):
+		fn = id_fn( id, 'link', dir_ )
 		d = vars( link )
 		yaml_ut.save( d, fn )

 	def create():
-		if path_exists( fsyn_path ):
-			dbg.err_exit( 'exists {}'.format( fsyn_path ) )
+		if path_exists( fsyn_path() ):
+			dbg.err_exit( 'exists {}'.format( fsyn_path() ) )

 		id = 'org'
-		cmd = 'mkdir -p ' + path_join( fsyn_path, id )
+		cmd = 'mkdir -p ' + path_join( fsyn_path(), id )
 		cmd_call( cmd )

 		snap = get_snap( 'now' )
@@ -88,7 +94,7 @@

 		set_curr( id )

-		msg_done( 'create', fsyn_path )
+		msg_done( 'create', fsyn_path() )

 	def get_curr_cmp():
 		id_old = get_curr()
@@ -120,7 +126,7 @@
 		id_old = curr_cmp.id_old
 		cmp = curr_cmp.cmp

-		cmd = 'mkdir ' + path_join( fsyn_path, id )
+		cmd = 'mkdir ' + path_join( fsyn_path(), id )
 		cmd_call( cmd )

 		fn = id_fn( id, 'snap' )
@@ -161,7 +167,7 @@
 		dbg.out( msg )

 	def ids_in_dir():
-		cmd = 'ls ' + fsyn_path
+		cmd = 'ls ' + fsyn_path()
 		lst = cmd_lst( cmd )
 		f = lambda s: s == 'org' or tm_ut.is_date_time_str( s, tm_ut.sample )
 		return list( filter( f, lst ) )
@@ -202,7 +208,7 @@
 	def ckout(id):
 		(targ_id, id) = ( id, get_curr() )

-		if not path_exists( path_join( fsyn_path, targ_id ) ):
+		if not path_exists( path_join( fsyn_path(), targ_id ) ):
 			msg = 'not found ' + targ_id
 			dbg.err_exit( msg )

@@ -310,6 +316,43 @@
 		for id in id_lst:
 			fix_link( id )

+	def push(to_path):
+		(fsyn_DIR, fsyn_BASE) = os.path.split( fsyn_path() )
+
+		def copy(id, name, force=False):
+			fn = path_join( fsyn_BASE, id, name )
+			if not path_exists( path_join( fsyn_DIR, fn ) ):
+				return # no src
+			if not path_exists( path_join( to_path, fn ) ) or force:
+				cmd = 'tar cf - -C {} {} | tar xf - -C {}'.format( fsyn_DIR, fn, to_path )
+				cmd_call( cmd )
+
+		def push_id(id, link_lmt=''):
+			copy( id, 'snap' )
+			copy( id, 'update.tgz' )
+
+			link = get_link( id )
+			if link_lmt:
+				link.to = list( filter( lambda s: s == link_lmt, link.to ) )
+			link_d = get_link( id, to_path )
+			link_d.from_ = link.from_
+			for s in link.to:
+				if s not in link_d.to:
+					link_d.to.append( s )
+			put_link( id, link_d, to_path )
+
+		def push_to(id):
+			push_id( id )
+			link = get_link( id )
+			for s in link.to:
+				push_to( s )
+
+		lst = is_ancestor_lst( 'org', get_curr() ) # [ 'org', ... ]
+		for i in range( len( lst ) - 1 ):
+			push_id( lst[ i ], lst[ i + 1 ] )
+
+		push_to( get_curr() )
+
 	return empty.new( locals() )


@@ -326,6 +369,7 @@
 		cmd_new( 'checkout', [ 'id' ] ),
 		cmd_new( 'check', comment='show dirty' ),
 		cmd_new( 'fixlink', comment='fix link info' ),
+		cmd_new( 'push', [ 'to_path' ] ),
 	]

 	dic = dict( map( lambda cmd: ( cmd.name, cmd ), cmds ) )

pushコマンドを追加する内容になってます。OK


pushコマンドに-no_orgオプション追加

巨大なツリーからcreateで作成すると、xxx.fsyn/org/update.tgz が巨大になります。

予め、バージョンorgの状態は、コピーなりcloneなりで共有しておいて、 そこからの差分だけをpushしたい場合が、ままあるかと。

pushコマンドに-no_orgオプションを追加してみました。

-no_org 付きでpushすると、xxx.fsyn/org/update.tgz はpush対象から外されます。

その他修正

createした直後で、commitする前の"org"だけの状態で、 pushやpullしようとした時に、色々不具合がありました。

修正しておきます。

FTP便利ツール 側の site_ut.py も、不具合の修正をかけました。

Version 25 が「この時点」での最新になります。

v11.patch

v11.patch

実行例

$ cd /tmp

$ mkdir bar
$ cd bar

$ dd if=/dev/zero of=big bs=1m count=10
10+0 records in
10+0 records out
10485760 bytes transferred in 0.016847 secs (622406175 bytes/sec)

$ echo hello > txt

$ ls -l
total 20488
-rw-r--r--  1 kondoh  wheel  10485760  5  6 23:01 big
-rw-r--r--  1 kondoh  wheel         6  5  6 23:01 txt

$ fsyn create
create /private/tmp/bar.fsyn

を作成したとします。

このorgだけの状態を他の場所にもcloneしたとします。

$ mkdir -p /tmp/other

$ fsyn clone /tmp/bar.fsyn
checkout org

例えばclone先で更新してcommit

$ cd /tmp/other/bar

$ echo bye >> txt

$ fsyn commit
commit 2020-05-06_23.04.23

大きなbar.fsyn/org/update.tgzを除く更新情報だけを、別の場所にpushしたい場合は

$ fsyn
Usage: .../fsyn.py cmd arg ...
  create
  commit
  log
  curr                     # show current id
  ids                      # show all ids
  tails                    # show tail ids
  ctails                   # show tail ids in current children
  latest                   # show latest id
  clatest                  # show latest id in current chidren
  prev                     # show prev id
  next                     # show next ids
  checkout id              # alias is used instead of id
  check                    # show dirty
  diff
  fixlink                  # fix link info
  push [ -no_org ] to_path
  pull from_path
  clone dot_fsyn_path      # ex. site/foo.fsyn
  tree                     # show id tree

$ mkdir -p /tmp/backup

$ fsyn push -no_org /tmp/backup

$ find /tmp/backup/bar.fsyn -type f
/tmp/backup/bar.fsyn/2020-05-06_23.04.23/link
/tmp/backup/bar.fsyn/2020-05-06_23.04.23/snap
/tmp/backup/bar.fsyn/2020-05-06_23.04.23/update.tgz
/tmp/backup/bar.fsyn/org/link
/tmp/backup/bar.fsyn/org/snap

元の場所はorgの状態なので、/tmp/backupからpullして最新にできます

$ cd /tmp/bar

$ fsyn tree
org@

$ fsyn pull /tmp/backup
checkout 2020-05-06_23.04.23

$ fsyn tree
2020-05-06_23.04.23@
org

$ cat txt
hello
bye

ツリーが巨大で、しかもその大部分は初期状態のまま変化しない場合ならば、 この push -no_org が有効かと。


シンボリックリンクのモードの判定

リンク切れのシンボリックリンクを、別の環境にtarで展開した時に、 更新されたように判定されてしまう現象が出てました。

0120777 2018-10-13_00.36.06 13 argmnt/ido.sh
0o120755 2018-10-13_00.36.06 13 argmnt/ido.sh

シンボリックリンクの場合のモードの一致判定を、ゆるめて対応してみます。

グループとその他の書き込み権限のビットは、無視して判定するようにしました。

この箇所の処理は、 #snap_ut.py が担当しているので、fsyn.py 自体に変更はありません。


不具合修正2020/MAR/22

tarコマンドでupdate.tgzを固める時にエラーが出る場合があったので修正しました。

ファイルパス中に'('や')'が含まれていたときに、 commitするとエラー表示が出て、update.tgz が生成されない現象がありました。

(自分ではそんなファイル名は作らないので、気にしてなかったのですが、、、)

v12.patch
diff -ur v11/fsyn.py v12/fsyn.py
--- v11/fsyn.py	2020-05-06 23:18:08.000000000 +0900
+++ v12/fsyn.py	2021-03-22 11:58:30.000000000 +0900
@@ -150,9 +150,17 @@
 		cmp.snap_new.write( fn )

 		add_chg = cmp.add + cmp.chg
+
+		def to_str_paths( paths ):
+			def f( path ):
+				if any( map( lambda c: c in path, [ '(', ')', "'" ] ) ):
+					path = '"' + path + '"'
+				return path
+			return ' '.join( map( f, paths ) )
+
 		if add_chg:
 			fn = id_fn( id, 'update.tgz' )
-			cmd = 'tar czf {} -C {} {}'.format( fn, dir_path, to_str( add_chg, delim=' ' ) )
+			cmd = 'tar czf {} -C {} {}'.format( fn, dir_path, to_str_paths( add_chg ) )
 			cmd_call( cmd )

 		put_link( id, empty.new( from_=id_old, to=[] ) )


push時のpush_id()時間表示追加

変更履歴が多くなるとpush時に、表示が無いまま随分待たされた後、 いきなり処理が終わります。

ちゃんと実行できてるのか不安になるので、 進捗表示を pythonのユーティリティ・プログラム 2020冬 の cnt_ut.py を使って追加してみました。

v13.patch
diff -ur v12/fsyn.py v13/fsyn.py
--- v12/fsyn.py	2021-03-22 11:58:30.000000000 +0900
+++ v13/fsyn.py	2021-04-01 16:06:09.000000000 +0900
@@ -14,6 +14,7 @@
 import arg
 import base
 import snap_ut
+import cnt_ut

 to_str = base.to_str
 msg_done = lambda done, targ: dbg.out( '{} {}'.format( done, targ ) )
@@ -584,8 +585,9 @@
 				push_to( s )

 		lst = is_ancestor_lst( 'org', get_curr() ) # [ 'org', ... ]
-		for i in range( len( lst ) - 1 ):
+		def f_i( i ):
 			push_id( lst[ i ], lst[ i + 1 ] )
+		cnt_ut.cnt_loop( f_i, len( lst ) - 1, 'push_id {}' )

 		push_to( get_curr() )


restore

これまでcheckoutで古い方向へ移動する際は、 orgからの作り直す方法の実装でやってきました。

commitを多数重ねていると、1つ前のcommit idに戻るのに、 ものすごい時間がかかる上に、途中で失敗すると修復がかなり面倒です。

できれば古いcommit idに向けてのcheckoutを、 効率の良い実装で追加したいのですが、、、

かなり久しぶりにソースを読み返してみて、 ずいぶん忘れてしまってます (T_^;

とりあえず練習がてら、カレントのcommit idから、汚してしまったり、削除してしまったファイルを、 復活させる restore コマンドを追加してみます。

$ fsyn restore パス

でパスのファイルをカレントのcommit idの状態に戻します。

カレントのcommit idから古い方のcommit idに向かって探索して、 そのバージョンのファイルが格納されているcommit idのupdate.tgzを見つけて、 ファイルを展開します。

パスを指定せずに

$ fsyn restore

で実行すると、更新したり、削除した全てのファイルについて、 1つずつ繰り返し restore を実行します。

v14.patch
diff -ur v13/fsyn.py v14/fsyn.py
--- v13/fsyn.py	2021-04-01 16:06:09.000000000 +0900
+++ v14/fsyn.py	2021-09-13 21:11:19.000000000 +0900
@@ -639,6 +639,55 @@
 	def tree():
 		nodes_tree( get_link, get_curr() )

+	def get_id_inc_id_path( id, path ):
+		snap = get_snap( id )
+		if path not in snap.get_paths():
+			return None
+
+		while id != 'org':
+			dbg.out( 'search id={}'.format( id ) )
+			prev_id = get_prev( id )
+			prev_snap = get_snap( prev_id )
+			cmp = snap_ut.cmp_new( prev_snap, snap )
+			add_chg = cmp.add + cmp.chg
+			if path in add_chg:
+				#dbg.out( 'hit' )
+				break
+			id = prev_id
+			snap = prev_snap
+
+		return id
+
+	def restore( path='' ):
+		if not path:
+			curr_cmp = get_curr_cmp()
+			if curr_cmp.same:
+				dbg.out( 'clean id={}'.format( get_curr() ) )
+				return
+			rm_chg = cmp.rm + cmp.chg
+			dbg.out( 'rm_chg {}'.format( '\n'.join( rm_chg ) ) )
+			for path in rm_chg:
+				restore( path )
+			return
+
+		#dbg.out( 'restore {}'.format( path ) )
+
+		id = get_curr()
+		id = get_id_inc_id_path( id, path )
+		if not id:
+			msg = 'not exists {} in curr'.format( path )
+			dbg.err_exit( msg )
+
+		fn = id_fn( id, 'update.tgz' )
+		cmd = 'tar tzf {} | grep {}'.format( fn, path )
+		if not cmd_ut.call( cmd ):
+			msg = 'find id={}, but no target in update.tgz'.format( id )
+			dbg.err_exit( msg )
+
+		cmd = 'tar xzf {} -C {} {}'.format( fn, dir_path, path )
+		#dbg.out( 'cmd={}'.format( cmd ) )
+		cmd_call( cmd )
+

 	return empty.new( locals() )

@@ -762,6 +811,7 @@
 		arg.cmd_new( 'pull', [ 'from_path' ] ),
 		arg.cmd_new( 'clone', [ 'dot_fsyn_path' ], comment='ex. site/foo.fsyn' ),
 		arg.cmd_new( 'tree', comment='show id tree' ),
+		arg.cmd_new( 'restore', [ 'path' ] ),
 	]
 	cmd = arg.get_name_args( cmds )


checkout改

checkoutで古い方向へ戻す際も、 restore 方式の実装にしてみました。

パッチ

v15.patch
diff -ur v14/fsyn.py v15/fsyn.py
--- v14/fsyn.py	2021-09-13 21:11:19.000000000 +0900
+++ v15/fsyn.py	2021-10-02 15:08:45.000000000 +0900
@@ -289,7 +289,7 @@
 		cmp = get_cmp( id, targ_id )
 		if cmp.rm:
 			cmd = '( cd {} ; rm -f {} )'.format( dir_path,  to_str( cmp.rm, delim=' ' ) )
-		cmd_call( cmd )
+			cmd_call( cmd )

 		set_curr( targ_id )

@@ -323,6 +323,9 @@
 				return

 			# targ_id is not child
+			restore_id( targ_id )
+			set_curr( targ_id )
+			return

 		else: # dirty
 			msg = 'dirty from curr {}. checkout {} ?'.format( curr_cmp.id_old, id )
@@ -658,19 +661,8 @@

 		return id

-	def restore( path='' ):
-		if not path:
-			curr_cmp = get_curr_cmp()
-			if curr_cmp.same:
-				dbg.out( 'clean id={}'.format( get_curr() ) )
-				return
-			rm_chg = cmp.rm + cmp.chg
-			dbg.out( 'rm_chg {}'.format( '\n'.join( rm_chg ) ) )
-			for path in rm_chg:
-				restore( path )
-			return
-
-		#dbg.out( 'restore {}'.format( path ) )
+	def restore_path( path ):
+		dbg.out( 'restore {}'.format( path ) )

 		id = get_curr()
 		id = get_id_inc_id_path( id, path )
@@ -688,6 +680,28 @@
 		#dbg.out( 'cmd={}'.format( cmd ) )
 		cmd_call( cmd )

+	def restore_id( id ):
+		cmp = get_cmp( id, 'now' )
+		if cmp.is_same():
+			dbg.out( 'clean id={}'.format( id ) )
+			return
+
+		rm_chg = cmp.rm + cmp.chg
+		dbg.out( 'rm_chg {}'.format( '\n'.join( rm_chg ) ) )
+		for path in rm_chg:
+			restore_path( path )
+
+		dbg.out( 'add {}'.format( '\n'.join( cmp.add ) ) )
+		if cmp.add:
+			cmd = '( cd {} ; rm -f {} )'.format( dir_path,  to_str( cmp.add, delim=' ' ) )
+			cmd_call( cmd )
+
+	def restore( path='' ):
+		if path:
+			restore_path( path )
+		else:
+			id = get_curr()
+			restore_id( id )

 	return empty.new( locals() )

動作確認

debugしきれてない感がありますが、一応簡単な動作確認をしてみました。

まずは適当に /tmp/foo/ を作って

$ mkdir /tmp/foo
$ cd /tmp/foo

適当にcommitを重ねつつ

$ echo a > a
$ fsyn create
create /private/tmp/foo.fsyn
$ echo b > b
$ fsyn commit
commit 2021-10-02_15.02.15
$ echo b > b
$ fsyn commit
commit 2021-10-02_15.02.15
$ mkdir bar
$ echo c > bar/c
$ fsyn commit
commit 2021-10-02_15.02.53
$ echo d > bar/d
$ fsyn commit
commit 2021-10-02_15.03.26
$ fsyn log
2021-10-02_15.03.26
2021-10-02_15.02.53
2021-10-02_15.02.15
org

1つ前に戻してみます

$ fsyn checkout prev
rm_chg
add bar/d
checkout 2021-10-02_15.02.53

確かに bar/d 消えてて

$ find .
.
./a
./b
./bar
./bar/c

カレントは移動

$ fsyn tree
2021-10-02_15.03.26*
2021-10-02_15.02.53@
2021-10-02_15.02.15
org

最新に戻しなおしてみます

$ fsyn checkout next
checkout 2021-10-02_15.03.26

新しい方向への移動は従来通りの動作

bar/d 復活してます

$ find .
.
./a
./b
./bar
./bar/c
./bar/d

2つ前に戻してみます

$ fsyn checkout 2021-10-02_15.02.15
rm_chg
add bar/c
bar/d
checkout 2021-10-02_15.02.15

bar/d, bar/c 消えて

$ find .
.
./a
./b
./bar

空のディレクトリは管理外なのでのこりつつ

指定のcommit idに移動しました

$ fsyn tree
2021-10-02_15.03.26*
2021-10-02_15.02.53
2021-10-02_15.02.15@
org

また最新に戻して

$ fsyn checkout latest
checkout 2021-10-02_15.03.26

bar/c, bar/d 復活して

$ find .
.
./a
./b
./bar
./bar/c
./bar/d

最新のcommit idに移動してます

$ fsyn tree
2021-10-02_15.03.26@
2021-10-02_15.02.53
2021-10-02_15.02.15
org