Is there a way to get the git root directory in one command?

GitVersion Control

Git Problem Overview


Mercurial has a way of printing the root directory (that contains .hg) via

hg root

Is there something equivalent in git to get the directory that contains the .git directory?

Git Solutions


Solution 1 - Git

Yes:

git rev-parse --show-toplevel

If you want to replicate the Mercurial command more directly, you can create an alias:

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

and now git root will function just as hg root.


Note: In a submodule this will display the root directory of the submodule and not the parent repository. If you are using Git >=2.13 or above, there is a way that submodules can show the superproject's root directory. If your git is older than that, see this other answer.

Solution 2 - Git

The man page for git-config (under Alias) says:

> If the alias expansion is prefixed with an exclamation point, it will be treated as a shell > command. [...] Note that shell > commands will be executed from the top-level directory of a repository, which may not > necessarily be the current directory.

So, on UNIX you can do:

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

Solution 3 - Git

Has --show-toplevel only recently been added to git rev-parse or why is nobody mentioning it?

From the git rev-parse man page:

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

Solution 4 - Git

How about "git rev-parse --git-dir" ?

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

The --git-dir option seems to work.

From git rev-parse manual page:

--git-dir

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

You can see it in action in this git setup-sh script.

If you are in a submodule folder, with Git >=2.13, use:

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

If you are using git rev-parse --show-toplevel, make sure it is with Git 2.25+ (Q1 2020).

Solution 5 - Git

To write a simple answer here, so that we can use

git root

to do the job, simply configure your git by using

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

and then you might want to add the following to your ~/.bashrc:

alias cdroot='cd $(git root)'

so that you can just use cdroot to go to the top of your repo.

Solution 6 - Git

If you're already in the top-level or not in a git repository cd $(git rev-parse --show-cdup) will take you home (just cd). cd ./$(git rev-parse --show-cdup) is one way of fixing that.

Solution 7 - Git

As others have noted, the core of the solution is to use git rev-parse --show-cdup. However, there are a few of edge cases to address:

  1. When the cwd already is the root of the working tree, the command yields an empty string.
    Actually it produces an empty line, but command substitution strip off the trailing line break. The final result is an empty string.
Most answers suggest prepending the output with `./` so that an empty output becomes `"./"` before it is fed to `cd`.
  1. When GIT_WORK_TREE is set to a location that is not the parent of the cwd, the output may be an absolute pathname.
Prepending `./` is wrong in this situation. If a `./` is prepended to an absolute path, it becomes a relative path (and they only refer to the same location if the cwd is the root directory of the system).
  1. The output may contain whitespace.
This really only applies in the second case, but it has an easy fix: use double quotes around the command substitution (and any subsequent uses of the value).

As other answers have noted, we can do cd "./$(git rev-parse --show-cdup)", but this breaks in the second edge case (and the third edge case if we leave off the double quotes).

Many shells treat cd "" as a no-op, so for those shells we could do cd "$(git rev-parse --show-cdup)" (the double quotes protect the empty string as an argument in the first edge case, and preserve whitespace in the third edge case). POSIX says the result of cd "" is unspecified, so it may be best to avoid making this assumption.

A solution that works in all of the above cases requires a test of some sort. Done explicitly, it might look like this:

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

No cd is done for the first edge case.

If it is acceptable to run cd . for the first edge case, then the conditional can be done in the expansion of the parameter:

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

Solution 8 - Git

Short solutions that work with submodules, in hooks, and inside the .git directory

Here's the short answer that most will want:

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

This will work anywhere in a git working tree (including inside the .git directory), but assumes that repository directory(s) are called .git (which is the default). With submodules, this will go to the root of the outermost containing repository.

If you want to get to the root of the current submodule use:

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

To easily execute a command in your submodule root, under [alias] in your .gitconfig, add:

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

This allows you to easily do things like git sh ag <string>

Robust solution that supports differently named or external .git or $GIT_DIR directories.

Note that $GIT_DIR may point somewhere external (and not be called .git), hence the need for further checking.

Put this in your .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"
}

Execute it by typing git_root (after restarting your shell: exec bash)

Solution 9 - Git

To calculate the absolute path of the current git root directory, say for use in a shell script, use this combination of readlink and git rev-parse:

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

git-rev-parse --show-cdup gives you the right number of ".."s to get to the root from your cwd, or the empty string if you are at the root. Then prepend "./" to deal with the empty string case and use readlink -f to translate to a full path.

You could also create a git-root command in your PATH as a shell script to apply this technique:

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

(The above can be pasted into a terminal to create git-root and set execute bits; the actual script is in lines 2, 3 and 4.)

And then you'd be able to run git root to get the root of your current tree. Note that in the shell script, use "-e" to cause the shell to exit if the rev-parse fails so that you can properly get the exit status and error message if you are not in a git directory.

Solution 10 - Git

Just in case if you're feeding this path to the Git itself, use :/

# 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)

Solution 11 - Git

To amend the "git config" answer just a bit:

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

and get the path cleaned up. Very nice.

Solution 12 - Git

If you're looking for a good alias to do this plus not blow up cd if you aren't in a git dir:

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

Solution 13 - Git

git-extras

adds $ git root
see https://github.com/tj/git-extras/blob/master/Commands.md#git-root

$ pwd
.../very-deep-from-root-directory
$ cd `git root`
$ git add . && git commit
Availability of git-extras

Solution 14 - Git

This shell alias works whether you are in a git subdir, or at the top level:

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

updated to use modern syntax instead of backticks:

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

Solution 15 - Git

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

Everything else fails at some point either going to the home directory or just miserably failing. This is the quickest and shortest way to get back to the GIT_DIR.

Solution 16 - Git

Here is a script that I've written that handles both cases: 1) repository with a workspace, 2) bare repository.

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

git-root (executable file in your path):

#!/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
)

Hopefully this is helpful.

Solution 17 - Git

Since Git 2.13.0, it supports a new option to show the path of the root project, which works even when being used from inside a submodule:

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

Solution 18 - Git

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

Solution 19 - Git

Pre-Configured Shell Aliases in Shell Frameworks

If you use a shell framework, there might already be a shell alias available:

  • $ grt in oh-my-zsh (68k) (cd $(git rev-parse --show-toplevel || echo "."))
  • $ git-root in prezto (8.8k) (displays the path to the working tree root)
  • $ g.. zimfw (1k) (changes the current directory to the top level of the working tree.)

Solution 20 - Git

I wanted to expand upon Daniel Brockman's excellent comment.

Defining git config --global alias.exec '!exec ' allows you to do things like git exec make because, as man git-config states:

> If the alias expansion is prefixed with an exclamation point, it will be treated as a shell command. [...] Note that shell commands will be executed from the top-level directory of a repository, which may not necessarily be the current directory.

It's also handy to know that $GIT_PREFIX will be the path to the current directory relative to the top-level directory of a repository. But, knowing it is only half the battle™. Shell variable expansion makes it rather hard to use. So I suggest using bash -c like so:

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

other commands include:

git exec pwd
git exec make

Solution 21 - Git

In case anyone needs a POSIX compliant way of doing this, without needing git executable:

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

If you just want the functionality as part of a script, remove the shebang, and replace the last git_root_recurse_parent line with:

git_root() {
    (git_root_recurse_parent)
}

Solution 22 - Git

Had to solve this myself today. Solved it in C# as I needed it for a program, but I guess it can be esily rewritten. Consider this Public Domain.

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;
	
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionwojoView Question on Stackoverflow
Solution 1 - GitbaudtackView Answer on Stackoverflow
Solution 2 - GitScott LindsayView Answer on Stackoverflow
Solution 3 - Gitmarc.guentherView Answer on Stackoverflow
Solution 4 - GitVonCView Answer on Stackoverflow
Solution 5 - GitnonopolarityView Answer on Stackoverflow
Solution 6 - GitKarlView Answer on Stackoverflow
Solution 7 - GitChris JohnsenView Answer on Stackoverflow
Solution 8 - GitTom HaleView Answer on Stackoverflow
Solution 9 - GitEmil SitView Answer on Stackoverflow
Solution 10 - GitNick VolynkinView Answer on Stackoverflow
Solution 11 - GitBruce KView Answer on Stackoverflow
Solution 12 - GitDan HeberdenView Answer on Stackoverflow
Solution 13 - GitHotschkeView Answer on Stackoverflow
Solution 14 - GitMERMView Answer on Stackoverflow
Solution 15 - GittocororoView Answer on Stackoverflow
Solution 16 - GitjdsumsionView Answer on Stackoverflow
Solution 17 - GitJordi Vilalta PratView Answer on Stackoverflow
Solution 18 - GitFractalSpaceView Answer on Stackoverflow
Solution 19 - GitHotschkeView Answer on Stackoverflow
Solution 20 - GitBruno BronoskyView Answer on Stackoverflow
Solution 21 - GitswalogView Answer on Stackoverflow
Solution 22 - GitHylke BonsView Answer on Stackoverflow