はじめに
Gitリポジトリのルートディレクトリを知りたいことがあって、調べるとだいたいgit rev-parse --show-toplevelが出てきます。しかしこのコマンドでは、submoduleになっているリポジトリにて親リポジトリのルートディレクトリを得ることはできません。
たとえばmainリポジトリがsubmoduleとしてsubリポジトリを含んだケースの場合、以下のようになります。
$ cd /work
$ git clone http://xxx main
$ tree main
main
`-- sub
$ cd main: git rev-parse --show-toplevel
/work/main
$ cd sub: git rev-parse --show-toplevel
/work/main/sub
ここではsubディレクトリにおいても親のリポジトリルート/work/mainを得るスクリプトを紹介します。
Git 2.13以降
Git 2.13から--show-superproject-working-treeなるオプションが追加されました。これはsubmodule内から親リポジトリのルートを表示するオプションです。しかしsubmodule外では何も出力されないので--show-toplevelと一緒に使って両方表示させた上でhead -1で1行目を抜いてきます。
git rev-parse --show-superproject-working-tree --show-toplevel | head -1
headしなかった場合の出力はこんな感じです。
$ cd main; git rev-parse --show-superproject-working-tree --show-toplevel
/work/main
$ cd sub; git rev-parse --show-superproject-working-tree --show-toplevel
/work/main
/work/main/sub
Git 2.13より前
例えばRHEL7/CentOS7ではGitはいまだに1.8.3.1です。ビルドスクリプトとして配布したい場合など、2.13以降を期待するのが難しい場合もありますが、このようにすればルートディレクトリが得られます。
readlink -f $(git rev-parse --git-dir) | sed 's/\/\.git.*//g'
--git-dirオプションは.gitディレクトリのパスを表示するオプションです。submodule内から見た場合も.gitディレクトリは親リポジトリの.git/modules/subになるので、sedで/.git以降を削ることで親リポジトリのルートが得られます。
sedしなかった場合の出力は以下です。
$ cd main: readlink -f $(git rev-parse --git-dir)
/work/main/.git
$ cd sub: readlink -f $(git rev-parse --git-dir)
/work/main/.git/modules/sub
ここでreadlink -fしているのは--git-dirの微妙な挙動を補正するためです。
--git-dirは通常.gitディレクトリの絶対パスを表示しますが、リポジトリルートで実行した場合に限り.gitだけを出力します。そのため絶対パスに揃えるためにreadlink -fを使っています。