1つのコマンドでgitルートディレクトリを取得する方法はありますか?


669

Mercurialには(.hgを含む)ルートディレクトリを印刷する方法があります。

hg root

gitに.gitディレクトリを含むディレクトリを取得する同等のものはありますか?


良いスクリプトエミル。スクリプトをオンラインで利用できるようにし、ファイル/ディレクトリを引数として追加できるようにしました。github.com/Dieterbe/git-scripts/commit/...
Dieter_be

また、好奇心が強い人や検索bzr rootがバザーで
頻繁

「gcd」に注意してくださいjeetworks.org/node/52 でのオートコンプリート使用したリポジトリルートに対するGit対応の「cd」
Micha Wiedenmann 2013


1
@MichaWiedenmann:リンクが切れています。
d33tah 2015

回答:


1111

はい:

git rev-parse --show-toplevel

Gitコマンドをより直接的に複製する場合は、エイリアスを作成できます。

git config --global alias.root 'rev-parse --show-toplevel'

そして今git rootはちょうど同じように機能しhg rootます。


サブモジュール、親リポジトリではなくサブモジュールのルートディレクトリが表示されます。Git> = 2.13以上を使用している場合、サブモジュールがスーパープロジェクトのルートディレクトリを表示できる方法があります。あなたのgitがそれより古い場合は、この別の答えを見てください。


147
git config --global alias.exec '!exec 'ようなことをできるように、私は常に定義していますgit exec make。シェルエイリアスは常に最上位ディレクトリで実行されるため、これは機能します。
Daniel Brockman

8
これが何をするかhg rootです。チェックアウトしたリポジトリの最上位ディレクトリを出力します。それはあなたをそれに切り替えません(そして、実際には、現在のディレクトリとシェルの全体の概念がどのように相互作用するかのために、そうすることはできませんでした)。
Omnifarious

14
このソリューションの小さな警告-シンボリックリンクを追跡します。したがって、あなたがにいて~/my.proj/foo/bar、に~/my.projシンボリックリンクされている~/src/my.proj場合、上記のコマンドでに移動します~/src/my.proj。その後に何をしたいかがツリーにとらわれない場合、問題になる可能性があります。
Franci Penov

3
これをgitフック内からどのように使用できますか?具体的には、マージ後フックを実行しており、ローカルgitリポジトリの実際のルートディレクトリを取得する必要があります。
デレク

12
このソリューション(およびこのページで私が試した他の多くのソリューション)は、gitフック内では機能しません。より正確には、プロジェクトの.gitディレクトリ内では機能しません。
Dennis、

97

エイリアスの下の)のmanページには次のように書かれています:git-config

エイリアス展開の前に感嘆符が付いている場合は、シェルコマンドとして扱われます。[...]シェルコマンドは、リポジトリの最上位ディレクトリから実行されることに注意してください。これは、必ずしも現在のディレクトリであるとは限りません。

したがって、UNIXでは次のことができます。

git config --global --add alias.root '!pwd'

2
それで、もしあなたが「git root」コマンドを持っているなら、どうやってそれをエイリアスに入れることができますか?それをmyに入れ.zshrc、 `alias cg =" cd $(git root) "を定義すると、$()部分はソース時に評価され、常に〜/ dotfilesを指します。 。
ゼルク

2
@cormacrelfシェルエイリアスに入れません。シェル関数またはスクリプトに入れることができます。
Conrad Meyer

4
@cormacrelf二重引用符ではなく一重引用符で囲みます。定義すると展開時ではなく、実行時に展開されます。
2013

3
@Mechanicalsnailほんと?では、外部レポで何ができると思いますか?
Alois Mahdal 2013

1
@AloisMahdalは実際には私がリポジトリ外で失敗するのではなく、単にcwdを報告します。
justinpitts 2014年

90

--show-toplevel、ごく最近に追加されgit rev-parseか、なぜ誰もそれを言及されていませんか?

git rev-parsemanページから:

   --show-toplevel
       Show the absolute path of the top-level directory.

1
これを指摘してくれてありがとう。あなたの助けがなければ、私が調査することを推測するのは難しいでしょうgit-rev-parse-その名前がリビジョン仕様の処理に関するものであることを示唆しているからです。ところで、「パスが指定されていない場合、gitは現在の設定を出力します」のgit --work-treeような動作を見てうれしいgit --exec-path[=<path>]です。少なくとも、IMO、それはそのような機能を探すための論理的な場所になるでしょう。
imz-Ivan Zakharyaschev

4
特に、root = rev-parse --show-toplevelgitconfigでエイリアスを設定できます。
機械式カタツムリ2013年

2
言い換えれば、あなたはできるしgit config --global alias.root "rev-parse --show-toplevel"、それからgit rootその仕事をすることができるでしょう
非極性

@RyanTheLeach git rev-parse --show-toplevelは、サブモジュールで試したときに機能します。gitサブモジュールのルートディレクトリを出力します。それはあなたのために何を印刷しますか?
wisbucky

2
この回答がトップの回答と重複する理由を私は混乱させました。トップの答えはから編集されたことをターンアウト--show-cdup--show-top-level2011年2月で(この答えは、提出された後)。
wisbucky

54

git rev-parse --git-dir」は?

F:\prog\git\test\copyMerge\dirWithConflicts>git rev-parse --git-dir
F:/prog/git/test/copyMerge/.git

--git-dirオプションが動作しているようです。

以下からのgit REV-解析マニュアルページ

--git-dir

    Show $GIT_DIR if defined else show the path to the .git directory.

このスクリプトで動作を確認できgit setup-shます

Git> = 2.13のサブモジュールフォルダーにいる場合は、次を使用します

git rev-parse --show-superproject-working-tree

を使用git rev-parse --show-toplevelている場合は、Git 2.25+(2020年第1四半期)を使用していることを確認してください。


3
ちょっと待ってください、これは近かったですが、それは実際の.gitディレクトリを取得し、gitリポジトリのベースを取得しません。また、.gitディレクトリが他の場所にある可能性があるため、これは私が正確に探していたものではありません。
wojo 2009年

2
そう、今あなたが実際に探していたものが見えます。--show-cdupの方が適しています。2つのオプションの違いを説明するために私の答えを残します。
VonC、2009年

2
また、このコマンドは、.gitすでにルートディレクトリにいる場合の相対パスを提供するようです。(少なくとも、msysgitでは実行されます。)
Blair Holloway

2
+1、これは、元の質問「.gitディレクトリを含むディレクトリを取得しますか?」に答える唯一の回答であり、OP自体が「.gitディレクトリが別の場所にある可能性がある」と言及しているのを見て面白い。
FabienAndre 2013

1
これはWindowsで動作する唯一のソリューションであり、gitサブモジュール(例:c:\ repositories \ myrepo \ .git \ modules \ mysubmodule)を使用すると解析可能な結果が得られます。これにより、特に再利用可能なスクリプトで最も堅牢なソリューションになります。
クリスケリー2014

36

ここに簡単な答えを書いて、

git root

仕事をするには、単に使用してgitを設定します

git config --global alias.root "rev-parse --show-toplevel"

そして、あなたはあなたに以下を追加したいかもしれません~/.bashrc

alias cdroot='cd $(git root)'

を使用cdrootして、リポジトリの先頭に移動できます。


非常に素晴らしい!究極に便利!
クレイジー

すばらしい回答、ありがとうございます!
AVarf

26

すでにトップレベルにいるか、gitリポジトリにいない場合は、cd $(git rev-parse --show-cdup)ホームに戻ります(cdのみ)。 cd ./$(git rev-parse --show-cdup)それを修正する1つの方法です。


7
別のオプションはそれを引用することです:cd "$(git rev-parse --show-cdup)"。これcd ""は、戻るのではなく、どこにも行かないため機能します$HOME。そして、スペースがあるものを出力する場合に備えて、とにかく$()呼び出しを引用符で囲むことをお勧めします(ただし、このコマンドはこの場合はそうではありません)。
ctrueden 2015

この回答は、シンボリックリンクを介してディレクトリをgitリポジトリに変更した場合でもうまく機能します。他のいくつかの例は、gitリポジトリがシンボリックリンクの下にある場合、bashに対するgitのルートディレクトリを解決しないため、期待どおりに機能しません$PWD。この例では、gitのルートをの$PWD代わりに解決しrealpath $PWDます。
DamienÓCeallaigh

16

現在のgitルートディレクトリの絶対パスを計算するには、たとえばシェルスクリプトで使用するには、次のreadlinkとgit rev-parseの組み合わせを使用します。

gitroot=$(readlink -f ./$(git rev-parse --show-cdup))

git-rev-parse --show-cdupcwdからルートに到達するための正しい「..」の数、またはルートにいる場合は空の文字列を提供します。次に、「./」を先頭に追加して、空の文字列のケースを処理し、 readlink -fして完全パスに変換します。

git-rootこのテクニックを適用するシェルスクリプトとして、PATHにコマンドを作成することもできます。

cat > ~/bin/git-root << EOF
#!/bin/sh -e
cdup=$(git rev-parse --show-cdup)
exec readlink -f ./$cdup
EOF
chmod 755 ~/bin/git-root

(上記をターミナルに貼り付けてgit-rootを作成し、実行ビットを設定できます。実際のスクリプトは2、3、4行目にあります。)

そして、実行git rootして現在のツリーのルートを取得することができます。シェルスクリプトでは、「-e」を使用してrev-parseが失敗した場合にシェルを終了させ、gitディレクトリにいない場合に終了ステータスとエラーメッセージを適切に取得できることに注意してください。


2
gitの「ルート」ディレクトリパスにスペースが含まれていると、サンプルが壊れます。の"$(git rev-parse ...)"ようなハックの代わりに常に使用します./$(git rev-parse ...)
Mikko Rantalainen 2017年

readlink -fBSDでは同じように機能しません。回避策については、このSOを参照してください。Pythonの答えはおそらく何もインストールしなくても機能しますpython -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$(git rev-parse --show-cdup)"
Bluu

16

他の人が指摘したように、ソリューションの中核はを使用することgit rev-parse --show-cdupです。ただし、対処すべきエッジケースがいくつかあります。

  1. cwdがすでに作業ツリーのルートである場合、コマンドは空の文字列を生成します。
    実際には空の行が生成されますが、コマンド置換は末尾の改行を削除します。最終結果は空の文字列です。

    ほとんどの回答では、./出力が"./"前に追加されるため、空の出力がに送られる前に出力が行われcdます。

  2. GIT_WORK_TREEがcwdの親ではない場所に設定されている場合、出力は絶対パス名になる可能性があります。

    ./この状況では、先頭に追加するのは間違っています。a ./が絶対パスの前に付加されている場合、それは相対パスになります(cwdがシステムのルートディレクトリの場合、それらは同じ場所のみを参照します)。

  3. 出力には空白が含まれる場合があります。

    これは実際には2番目のケースにのみ適用されますが、簡単な修正があります。コマンド置換(およびその後の値の使用)を二重引用符で囲みます。

他の回答が指摘しているように、私たちはできる cd "./$(git rev-parse --show-cdup)"が、2番目のエッジケース(および二重引用符を省略した場合は3番目のエッジケース)で壊れます。

多くのシェルcd ""は何もしないものとして扱うので、それらのシェルに対して実行できますcd "$(git rev-parse --show-cdup)"(二重引用符は、最初のエッジの場合は空の文字列を引数として保護し、3番目のエッジの場合は空白を保持します)。POSIXはの結果を言いますcd ""は指定されていないので、この仮定を行わないことをお勧めします。

上記のすべてのケースで機能するソリューションには、何らかのテストが必要です。明示的に行うと、次のようになります。

cdup="$(git rev-parse --show-cdup)" && test -n "$cdup" && cd "$cdup"

cd最初のエッジケースでは行われません。

cd .最初のエッジケースの実行が許容できる場合は、パラメーターの展開で条件を実行できます。

cdup="$(git rev-parse --show-cdup)" && cd "${cdup:-.}"

git root上記の答えが使用する "git config --global --add alias.root '!pwd'"とシェルエイリアスgitroot = 'cd 'を使用しないのはなぜですか?
Jason Axelson、2010

1
エイリアスを使用することができない場合があります。たとえば、スクリプトを作成したいが、カスタムエイリアスに依存できない場合などです。
2013年

1
サブモジュールは別のコーナーケースです。
ライアンザリーチ

14

このパスをGit自体にフィードしている場合に備えて、 :/

# this adds the whole working tree from any directory in the repo
git add :/

# and is equal to
git add $(git rev-parse --show-toplevel)

12

サブモジュール、フック、および.gitディレクトリ内で機能する短いソリューション

以下は、ほとんどの人が望む短い答えです。

r=$(git rev-parse --git-dir) && r=$(cd "$r" && pwd)/ && echo "${r%%/.git/*}"

これはgit作業ツリー(.gitディレクトリ内を含む)のどこでも機能しますが、リポジトリディレクトリが呼び出されていることを前提としています.git(これがデフォルトです)。サブモジュールを使用すると、これは最も外側の包含リポジトリのルートに移動します。

現在のサブモジュールのルートに到達したい場合は、以下を使用します。

echo $(r=$(git rev-parse --show-toplevel) && ([[ -n $r ]] && echo "$r" || (cd $(git rev-parse --git-dir)/.. && pwd) ))

サブモジュールルートのコマンドを簡単に実行するには、の下に以下[alias].gitconfig追加します。

sh = "!f() { root=$(pwd)/ && cd ${root%%/.git/*} && git rev-parse && exec \"$@\"; }; f"

これにより、次のようなことを簡単に行うことができます git sh ag <string>

異なる名前の、外部.gitまたは$GIT_DIRディレクトリをサポートする堅牢なソリューション。

$GIT_DIR外部のどこかを指す可能性があることに注意してください(そして呼び出されません).git)。したがって、さらにチェックする必要があります。

これをあなたの中に入れてください.bashrc

# Print the name of the git working tree's root directory
function git_root() {
  local root first_commit
  # git displays its own error if not in a repository
  root=$(git rev-parse --show-toplevel) || return
  if [[ -n $root ]]; then
    echo $root
    return
  elif [[ $(git rev-parse --is-inside-git-dir) = true ]]; then
    # We're inside the .git directory
    # Store the commit id of the first commit to compare later
    # It's possible that $GIT_DIR points somewhere not inside the repo
    first_commit=$(git rev-list --parents HEAD | tail -1) ||
      echo "$0: Can't get initial commit" 2>&1 && false && return
    root=$(git rev-parse --git-dir)/.. &&
      # subshell so we don't change the user's working directory
    ( cd "$root" &&
      if [[ $(git rev-list --parents HEAD | tail -1) = $first_commit ]]; then
        pwd
      else
        echo "$FUNCNAME: git directory is not inside its repository" 2>&1
        false
      fi
    )
  else
    echo "$FUNCNAME: Can't determine repository root" 2>&1
    false
  fi
}

# Change working directory to git repository root
function cd_git_root() {
  local root
  root=$(git_root) || return 1  # git_root will print any errors
  cd "$root"
}

入力して、それを実行しgit_root(シェルを再起動した後:exec bash


このコードは、gitリポジトリのルートを見つけるためにRobust bash関数でコードレビューされています。アップデートを探してください。
トム・ヘイル

いいね。7年前に提案したもの(stackoverflow.com/a/958125/6309)よりも複雑ですが、まだ+1
VonC、

1
これらの線に沿った短い解決策は次のとおりです。 (root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd)ただし、これは$GIT_DIR、以外の名前の外部sをカバーしません.git
Tom Hale

gitの中で可能になりましたアカウントに、このテイクは、複数のworktrees 2.5+(かしらstackoverflow.com/a/30185564/6309
VonC

「ルートを見つけるために堅牢なbashの機能...」とコメントリンクは現在、404を与える
ErikE

8

「git config」の回答を少し修正するには:

git config --global --add alias.root '!pwd -P'

パスをクリーンアップします。非常に素晴らしい。


7

これを行うための適切なエイリアスを探しているcd場合は、gitディレクトリにいない場合でも爆発しません:

alias ..g='git rev-parse && cd "$(git rev-parse --show-cdup)"'


6

このシェルエイリアスは、gitサブディレクトリでもトップレベルでも機能します。

alias gr='[ ! -z `git rev-parse --show-toplevel` ] && cd `git rev-parse --show-toplevel || pwd`'

バッククォートの代わりに最新の構文を使用するように更新:

alias gr='[ ! -z $(git rev-parse --show-toplevel) ] && cd $(git rev-parse --show-toplevel || pwd)'

5
alias git-root='cd \`git rev-parse --git-dir\`; cd ..'

その他のすべては、ホームディレクトリに移動するか、惨めに失敗するかのいずれかの時点で失敗します。これは、GIT_DIRに戻るための最速かつ最短の方法です。


「git rev-parse --git-dir」が最もクリーンなソリューションのようです。
ステーブルドッグ2013

3
が-Filesと$GIT_DIRを使用して.gitworkingtree から切り離されている場合、これは失敗しますgitdir: SOMEPATH。その結果、これはサブモジュールの場合も失敗し$GIT_DIRます.git/modules/SUBMODULEPATH
Tino

5

以下は、両方のケースを処理するために私が作成したスクリプトです。1)ワークスペースのあるリポジトリ、2)ベアリポジトリ。

https://gist.github.com/jdsumsion/6282953

git-root (パス内の実行可能ファイル):

#!/bin/bash
GIT_DIR=`git rev-parse --git-dir` &&
(
  if [ `basename $GIT_DIR` = ".git" ]; then
    # handle normal git repos (with a .git dir)
    cd $GIT_DIR/..
  else
    # handle bare git repos (the repo IS a xxx.git dir)
    cd $GIT_DIR
  fi
  pwd
)

うまくいけば、これは役に立ちます。


1
キース、提案ありがとうございます。私はスクリプトを含めました。
jdsumsion 2013

私はgit execアイデアが裸のリポジトリでより役立つことに気付いたので、私は実際にトップの回答に賛成しました。ただし、この回答のこのスクリプトは、ベアケースと非ベアケースを正しく処理するため、誰かに役立つ可能性があるため、この回答はここに残します。
jdsumsion 2013

1
ただし、これはのようなものgit submodule$GIT_DIR含まれるs 内では失敗します/.git/modules/SUBMODULE。また、あなたは、.gitディレクトリが非ベアケースのワークツリーの一部であると仮定します。
Tino、

4
$ git config alias.root '!pwd'
# then you have:
$ git root

このエイリアスはグローバルとして失敗します。代わりにこれを使用してください(〜/ .gitconfigで):[alias] findroot = "!f () { [[ -d ".git" ]] && echo "Found git in [pwd ]" && exit 0; cd .. && echo "IN pwd" && f;}; f"
FractalSpace

なぜ反対票を投じるのですか?エラーを強調表示するか、代わりに改善を提案してください。
FractalSpace 2013年

1
私にとってはgit config --global alias.root '!pwd'動作します。非グローバルバリアントとは異なる動作をするケースを見つけることができませんでした。(Unix、git 1.7.10.4)ところで:無限の再帰を避けるためにfindrootが必要/.gitです。
Tino

4

Git 2.13.0以降、ルートプロジェクトのパスを表示する新しいオプションがサポートされています。これは、サブモジュール内から使用されている場合でも機能します。

git rev-parse --show-superproject-working-tree

git-scm.com/docs/…。面白い。見逃したに違いない。+1
VonC、2018年

3
警告:「現在のリポジトリがプロジェクトでサブモジュールとして使用されていない場合は何も出力されません。」
VonC、

コメントをありがとう!気づかなかった。
ジョルディビラルタプラット

2

シェルフレームワークの事前設定されたシェルエイリアス

シェルフレームワークを使用している場合、シェルエイリアスがすでに使用可能になっている可能性があります。

  • $ grtOH-MY-zshの(68K) (cd $(git rev-parse --show-toplevel || echo ".")
  • $ git-rootprezto(8.8k) (ディスプレイ作業ツリーのルートへのパス)
  • $ g.. zimfw(1k)(現在のディレクトリを作業ツリーの最上位に変更します。)

1

Daniel Brockmanの優れたコメントをさらに詳しく説明したいと思います。

定義git config --global alias.exec '!exec 'すると、次のようなgit exec make理由により、次のことが可能になりますman git-config

エイリアス展開の前に感嘆符が付いている場合は、シェルコマンドとして扱われます。[...]シェルコマンドは、リポジトリの最上位ディレクトリから実行されることに注意してください。これは、必ずしも現在のディレクトリであるとは限りません。

また、それ$GIT_PREFIXがリポジトリのトップレベルのディレクトリを基準にした現在のディレクトリへのパスになることも知っておくと便利です。しかし、それが戦闘の半分に過ぎないことを知っている™。シェル変数展開では、使用がかなり難しくなります。だから私はそのように使うことを勧めbash -cます:

git exec bash -c 'ls -l $GIT_PREFIX'

他のコマンドは次のとおりです。

git exec pwd
git exec make

1

git実行可能ファイルを必要とせずに誰かがこれを行うためのPOSIX準拠の方法を必要とする場合:

git-root

#$1: Path to child directory
git_root_recurse_parent() {
    # Check if cwd is a git root directory
    if [ -d .git/objects -a -d .git/refs -a -f .git/HEAD ] ; then
        pwd
        return 0
    fi

    # Check if recursion should end (typically if cwd is /)
    if [ "${1}" = "$(pwd)" ] ; then
        return 1
    fi

    # Check parent directory in the same way
    local cwd=$(pwd)
    cd ..
    git_root_recurse_parent "${cwd}"
}

git_root_recurse_parent

スクリプトの一部として機能が必要な場合は、シバンを削除し、最後のgit_root_recurse_parent行を次のように置き換えます。

git_root() {
    (git_root_recurse_parent)
}

警告:これがgitリポジトリで呼び出されない場合、再帰はまったく終了しません。
AH

@AH 2番目のifステートメントは、再帰でディレクトリを変更したかどうかをチェックすることになっています。それが同じディレクトリに残っている場合は、どこかにスタックしていると想定し(例/:)、再帰を停止します。バグが修正され、期待どおりに動作するようになりました。指摘してくれてありがとう。
swalog

0

今日、これを自分で解決しなければなりませんでした。プログラムに必要なため、C#で解決しましたが、簡単に書き換えることができると思います。このパブリックドメインを検討してください。

public static string GetGitRoot (string file_path) {

    file_path = System.IO.Path.GetDirectoryName (file_path);

    while (file_path != null) {

        if (Directory.Exists (System.IO.Path.Combine (file_path, ".git")))
            return file_path;

        file_path = Directory.GetParent (file_path).FullName;

    }

    return null;

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.