Posted at

gitリポジトリのルートディレクトリを得るスクリプト


はじめに

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