49

I wanted a simple git command to go up to the "root" of the repository.

I started with a script, but figured that I cannot change active directory of the shell, I had to do a function. Unfortunately, I cannot call it directly with the non-dash form "git root", for instance.

function git-root() {
 if [ -d .git ]; then
  return 0
 fi

 A=..
 while ! [ -d $A/.git ]; do 
  A="$A/.."
 done
 cd $A
}

Do you have a better solution? (the function has been written quickly, suggestions are welcome)

elmarco
  • 31,633
  • 21
  • 64
  • 68
  • what about when you have nested git repos? eg, my whole home directory is a git repo for the purposes of configuration files. – Peter Oct 15 '09 at 10:18
  • 2
    Do you mean the root of the working tree, rather than of the repository? – Ben James Oct 15 '09 at 10:21
  • 1
    better solution for config files is to create separate directory with symlinks from home folder like ~/.profile => ~/dot-files/profile – tig Oct 15 '09 at 10:24
  • exact dup: http://stackoverflow.com/questions/957928/is-there-a-way-to-get-to-the-git-root-directory-in-one-command – Peter Oct 15 '09 at 10:29
  • Thanks for the question, it helped me with a similar task except not for git :) – nafg Feb 05 '13 at 02:59
  • Possible duplicate of [Is there a way to get the git root directory in one command?](https://stackoverflow.com/questions/957928/is-there-a-way-to-get-the-git-root-directory-in-one-command) – Trevor Boyd Smith Aug 03 '17 at 13:06

9 Answers9

63

Simpler still, steal from Is there a way to get the git root directory in one command? , and make an alias (as suggested by Peter) from

cd "$(git rev-parse --show-toplevel)"

This works whether you're in the root directory or not.

wisbucky
  • 33,218
  • 10
  • 150
  • 101
Chris Westin
  • 1,160
  • 1
  • 12
  • 12
63

This has been asked before, Is there a way to get the git root directory in one command? Copying @docgnome's answer, he writes

cd $(git rev-parse --show-cdup)

Make an alias if you like:

alias git-root='cd $(git rev-parse --show-cdup)'
wisbucky
  • 33,218
  • 10
  • 150
  • 101
Peter
  • 127,331
  • 53
  • 180
  • 211
15

Peter's answer above works great if you're in a subdirectory of the git root. If you're already in the git root, it'll throw you back to $HOME. To prevent this, we can use some bash conditionals.

if [ "`git rev-parse --show-cdup`" != "" ]; then cd `git rev-parse --show-cdup`; fi

so the alias becomes:

alias git-root='if [ "`git rev-parse --show-cdup`" != "" ]; then cd `git rev-parse --show-cdup`; fi'
  • Makes a nice function too function cgr() { if [ "`git rev-parse --show-cdup`" != "" ]; then cd `git rev-parse --show-cdup`; fi } – Christian Feb 29 '16 at 07:06
  • 1
    @Christian The backticks in what you wrote got interpreted as slackoverflow formatting. This should look right, substituting $() for backticks: `function cgr() { if [ "$(git rev-parse --show-cdup)" != "" ]; then cd $(git rev-parse --show-cdup); fi }` – nealmcb Aug 14 '17 at 01:32
  • `--show-toplevel` will handle when you are in git root. See https://stackoverflow.com/a/14127035 – wisbucky Nov 07 '18 at 00:04
7
$ git config alias.root '!pwd'
$ git root
FractalSpace
  • 5,577
  • 3
  • 42
  • 47
  • 2
    Interesting... It appears `git` sets the working directory of shells to the root of the repository. It's probably best not to rely on this behaviour, however. – Tullo_x86 Nov 30 '15 at 19:37
3

Unfortunately, changing your current directory can only be done by the shell, not by any subprocess. By the time git gets around to parsing your command, it's already too late -- git has already been spawned in a separate process.

Here's a really gross, untested shell function that just might do what you want:

function git() {
    if [ "$1" == "root" ]; then
        git-root
    else
        git "$@"
    fi
}
Josh Lee
  • 171,072
  • 38
  • 269
  • 275
3

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)/ && cd "${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:

cd ''$(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 cd_git_root (after restarting your shell: exec bash)

Tom Hale
  • 40,825
  • 36
  • 187
  • 242
  • This code is being code-reviewed at [Robust bash function to find the root of a git repository](http://codereview.stackexchange.com/questions/138255/robust-bash-function-to-find-the-root-of-a-git-repository). Look there for updates. – Tom Hale Aug 09 '16 at 13:33
  • "This question was removed from Code Review Stack Exchange for reasons of moderation." – kelvin Sep 16 '19 at 02:06
  • 1
    @kelvin It got no replies (other than a suggestion to use `shellcheck`) and was `deleted by Community♦ Mar 2 at 0:00 (RemoveDeadQuestions)` – Tom Hale Sep 16 '19 at 03:34
2

A better alias working in bash and zsh is:

alias git-root='cd "$(git rev-parse --show-cdup)"'
tasmo
  • 31
  • 1
  • 3
0

Peter's answer is the master reply. Many others observed that it doesn't work if you're already in the git root directory, unfortunately their solutions are overwhelming complex.

Here my one-liner:

alias cd-gitroot='cd $(git rev-parse --show-cdup)/.'
Lord of the Goo
  • 1,214
  • 15
  • 31
0

If you are using oh-my-zsh and you added the git plugin in your

plugins+=(git)

.zshrc configuration file, the follwing command (alias) works:

grt

This alias is defined the following way:

grt='cd "$(git rev-parse --show-toplevel \|\| echo .)"'
abu_bua
  • 1,361
  • 17
  • 25