Open5

Reposoup: Bareリポジトリ上でSubmoduleを直接操作する

okuokuokuoku

手元のプロジェクトではyunibase https://github.com/okuoku/yunibase や WasmLinux https://github.com/okuoku/wasmlinux-project のようにインテグレーションにsubmoduleを使っているものが多い。

これらを分析に廻すときにbareリポジトリ上で各種操作を直接行えると嬉しい。つまり、

  • Submodule参照/更新をrefに変換する
  • Submoduleが指しているrefを更新する
  • Submoduleが指しているrefをtreeに変換する
okuokuokuoku

参照をrefにする

これは単に ls-tree で良い。つまり、例えば yunibase https://github.com/okuoku/yunibase において、

$ git ls-tree --no-abbrev HEAD yuni
160000 commit 1c35e56c528b9d2152cd40315b448acdad7b838d  yuni

として得られる commit はrefになっている。

ただし、この ref を直接元リポジトリ上で使うことはできない。

$ git show 1c35e56c528b9d2152cd40315b448acdad7b838d
fatal: bad object 1c35e56c528b9d2152cd40315b448acdad7b838d

... remoteに足してしまうのが一番簡単だと思う。

$ git remote add yuni git@github.com:okuoku/yuni
$ git fetch yuni
$ git show --pretty=oneline 1c35e56c528b9d2152cd40315b448acdad7b838d
1c35e56c528b9d2152cd40315b448acdad7b838d (yuni/master) r7c: Fix `unquote`

実際には .git/modules 以下にGitリポジトリができているので、そちらを GIT_DIR に指定すればアクセスはできる。(が、今回はrefをtreeに変換したいのでこの方法は使えない。)

okuokuokuoku

更新をrefにする

diff でできる。

$ git diff --no-abbrev --raw HEAD HEAD^
:160000 160000 6b556d3a7a609cced3c48779a2028656fff132f5 37b39693edac94964070814255794a0d802c9829 M      impl-current/cyclone
:160000 160000 d31d82960231a4fde04b4765ea911ff4b5dacf79 e0109790d0128e93808d7a9472cd531778ab00c8 M      impl-stable

Submodule 用には専用の mode 160000 がアサインされているので、これを確認すれば良い。

https://github.com/git/git/blob/d4cc1ec35f3bcce816b69986ca41943f6ce21377/object.h#L110-L116

okuokuokuoku

refを更新する

今回はsubmodule yuni/ を1つ前のコミットに戻してみる f3e41162fc8f25571c11bea34bdfe5d28b56c44a

$ git read-tree HEAD
$ git update-index --cacheinfo 160000,f3e41162fc8f25571c11bea34bdfe5d28b56c44a,yuni
$ git commit -m TEST
[master 5b306f0] TEST
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git show
commit 5b306f09a9b711362fad9ff56f8ed7e3ad3d5563 (HEAD -> master)
Author: okuoku <mjt@cltn.org>
Date:   Sat May 4 14:08:55 2024 +0900

    TEST

diff --git a/yuni b/yuni
index 1c35e56..f3e4116 160000
--- a/yuni
+++ b/yuni
@@ -1 +1 @@
-Subproject commit 1c35e56c528b9d2152cd40315b448acdad7b838d
+Subproject commit f3e41162fc8f25571c11bea34bdfe5d28b56c44a

普通に update-indexcache-info に mode 160000 としてオブジェクトを渡せば良い。

okuokuokuoku

Submoduleを普通のディレクトリに変換する

(逆方向、つまり普通のディレクトリをSubmoduleに変換するのは↑のように単に 160000 で直接突っ込めば良い)

この処理は read-tree コマンドで行えるが、 事前にsubmoduleだったリポジトリをfetchしている必要がある

$ git remote add yuni git@github.com:okuoku/yuni
$ git fetch yuni

いわゆるsubtree mergeを行うときと同様に、 --prefix= で元々のSubtreeの場所にコミットを読み込むことができる。(read-treeは treeish を取るためcommitを直接指定して良い)

$ git read-tree --prefix=yuni 1c35e56c528b9d2152cd40315b448acdad7b838d

$ git commit -m TEST
[master 9d8f9a5] TEST
 737 files changed, 51875 insertions(+), 1 deletion(-)
 delete mode 160000 yuni
 create mode 100644 yuni/.gitignore
 create mode 100644 yuni/CMakeLists.txt
   :

履歴は一切受けつがれない。履歴を保持したい場合はSubtree mergeとするしかない。