はじめに
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
を使っています。