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?
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?
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.
git config --global alias.exec '!exec '
so I can do things like git exec make
. This works because shell aliases are always executed in the top-level directory. –
Met hg root
does. It prints out the top-level directory of your checked out repository. It doesn't switch you to it (and it couldn't, in fact, do so because of how the whole concept of current directory and your shell interact). –
Tactful ~/my.proj/foo/bar
, and ~/my.proj
is symlinked to ~/src/my.proj
, the above command will move you to ~/src/my.proj
. Could be a problem, if whatever you want to do after that is not tree agnostic. –
Subulate git-rev-parse
-- because of its name suggesting it's about processing revision specifications. BTW, I'd be glad to see git --work-tree
work similar to git --exec-path[=<path>]
: "If no path is given, git will print the current setting"; at least, IMO, it'd be a logical place to look for such a feature. –
Implacental git exec
alias can be combined with pwd
to print the root directory: git exec pwd
.Or sometimes, it's useful to have a subshell at the root temporarily, and then just exit to get back to the original task. –
Elston git config --global alias.top '!f() { GIT_TOP="${GIT_DIR%%/.git/modules/*}"; [ ".$GIT_TOP" != ".$GIT_DIR" ] && cd "$GIT_TOP"; exec "$@"; }; f'
and then git top pwd
–
Retinoscope .git
directory. –
Dissimilate git rev-parse && cd "$(git rev-parse --show-cdup)
doesn't work in the general case since git rev-parse --show-cdup
returns the empty string in case you are allready in the git root dir. Concequence is you go to your home. –
Forthright git rev-parse --show-toplevel > %TEMP%\GITROOT.TXT & SET /p BranchRoot=<%TEMP%\GITROOT.TXT & SET BranchRoot=%BranchRoot:/=\%
–
Protolithic .git
directory (or an external $GIT_DIR
of a repository. See my answer for one that works anywhere in a repository, and fails gracefully in an external $GIT_DIR
–
Footer git-rev-parse
: "Many Git porcelainish commands take mixture of flags and parameters meant for the underlying git rev-list
command they use internally and flags and parameters for the other commands they use downstream of git rev-list
. This command is used to distinguish between them." –
Leporide cd ../../;git rev-parse --show-top level
–
Feaze .git
directory. –
Culex ROOT=${ROOT:-$(git rev-parse --show-toplevel)}
–
Yelenayelich gcd
to go to project home as analogous to cd
which takes to user home dir. in my .bashrc
alias gcd='cd $(git rev-parse --show-toplevel)'
–
Moderate 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.
git-rev-parse
-- because of its name suggesting it's about processing revision specifications. BTW, I'd be glad to see git --work-tree
work similar to git --exec-path[=<path>]
: "If no path is given, git will print the current setting"; at least, IMO, it'd be a logical place to look for such a feature. –
Implacental root = rev-parse --show-toplevel
in your gitconfig. –
Supercargo git config --global alias.root "rev-parse --show-toplevel"
and then git root
will be able to do the job –
Tumulus git rev-parse --show-toplevel
works when I tried it in a submodule. It prints the root dir of the git submodule. What does it print for you? –
Whereabouts --show-cdup
to --show-top-level
in Feb 2011 (after this answer was submitted). –
Whereabouts 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'
.zshrc
, and I define `alias cg="cd $(git root)", the $() part gets evaluated at source-time, and always points to ~/dotfiles, as that is where my zshrc is. –
Adjacency .git/
directory. Then it returns your root directory as well as wherever you are in .git/
–
Dapplegray command.com
(cmd
) to make commits, but on Windows I guess you'd want git config --add alias.root '!cd'
or alternatively '!echo %CD%'
. Accepted answer, however, is best if you need cross-platform compatibility. –
Gassaway git -c alias.root=!pwd root
–
Ventage 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.
It does work even in a bare repository, while git rev-parse --show-toplevel
would trigger (in said bare repository) a "fatal: this operation must be run in a work tree
".
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).
.git
if you're already in the root directory. (At least, it does on msysgit.) –
Spinks (root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd)
but this doesn't cover external $GIT_DIR
s which are named other than .git
For a full solution, see my answer below. –
Footer .git/
folder below their toplevel folder. –
Slush --path-format=(absolute|relative)
to override the relative behavior. I was not able to answer the question of whether or not this was an option in 2010. –
Urdu 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.
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.
cd "$(git rev-parse --show-cdup)"
. This works because cd ""
takes you nowhere, rather than back $HOME
. And it's best practice to quote $() invocations anyway, in case they output something with spaces (not that this command will in this case, though). –
Eurypterid $PWD
. This example will resolve git's root relative to $PWD
instead of realpath $PWD
. –
Lenitalenitive 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
)
(root=$(git rev-parse --git-dir)/ && cd ${root%%/.git/*} && git rev-parse && pwd)
but this doesn't cover external $GIT_DIR
s which are named other than .git
–
Footer deleted by Community♦ Mar 2 at 0:00 (RemoveDeadQuestions)
–
Footer shellcheck
clean. –
Footer 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:
Most answers suggest prepending the output with `./` so that an empty output becomes `"./"` before it is fed to `cd`.
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).
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:-.}"
git root
' that the answer above you uses? –
Upcast 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.
"$(git rev-parse ...)"
instead of hacks like ./$(git rev-parse ...)
. –
Prem readlink -f
doesn't work the same on BSD. See this SO for workarounds. The Python answer will probably work without installing anything: python -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$(git rev-parse --show-cdup)"
. –
Wavelet 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)
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.
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
Unfortunately for us, it prints nothing if we're not in a submodule. In that case, --show-toplevel
still works. This Bash code will do the job regardless of whether we're in a submodule:
super=$(git rev-parse --show-superproject-working-tree)
case "$super" in
"")
git rev-parse --show-toplevel
;;
*)
echo "$super"
esac
--show-toplevel
to fall back to git root behaviour. By piping to tail -n1
or head -n1
you can grab one or the other depending on what is available. –
Kansas --show-toplevel
in my own answer. –
Performing --show-toplevel
when necessary. –
Thayne 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)"'
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
$ brew install git-extras
$ apt-get install git-extras
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)'
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.
$GIT_DIR
is detached from the workingtree using .git
-Files and gitdir: SOMEPATH
. Consequently this fails for submodules, too, where $GIT_DIR
contains .git/modules/SUBMODULEPATH
. –
Retinoscope $ git config alias.root '!pwd'
# then you have:
$ git root
[alias] findroot = "!f () { [[ -d ".git" ]] && echo "Found git in [
pwd]" && exit 0; cd .. && echo "IN
pwd" && f;}; f"
–
Interstate git config --global alias.root '!pwd'
works. I was unable to spot any case where it acts differently to the non-global variant. (Unix, git 1.7.10.4) BTW: Your findroot
requires a /.git
to avoid an endless recursion. –
Retinoscope 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.
git exec
idea is more helpful in non-bare repositories. However, this script in my answer handles the bare vs. non-bare case correctly, which could be of use to someone, so I'm leaving this answer here. –
Elston git submodule
s where $GIT_DIR
contains something like /.git/modules/SUBMODULE
. Also you assume, that the .git
directory is part of the worktree in the non-bare case. –
Retinoscope 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.)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
In case anyone needs a POSIX compliant way of doing this, without needing git
executable:
#$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)
}
if
statement was supposed to check whether you've changed directory in the recursion. If it remains in the same directory, it assumes you're stuck somewhere (e.g. /
), and brakes recursion. The bug is fixed, and should now work as expected. Thanks for pointing it out. –
Kansas When in a Git worktree, --git-dir
gets the path of the worktree metadata inside the .git
folder. To get the path of the actual .git
folder where hooks can be installed, use --git-common-dir
instead:
/Projects/mywork$ git rev-parse --git-dir
/Projects/app/.git/worktrees/mywork
/Projects/mywork$ git rev-parse --git-common-dir
/Projects/app/.git
EDIT: For hooks, there is a better solution:
/Projects/mywork$ git rev-parse --git-path hooks
/Projects/app/.git/hooks
git rev-parse --show-prefix
will return path from current folder to root folder of git repo.
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;
}
.git
directory are in separate paths (see the other answers/comments regarding GIT_WORK_TREE and GIT_DIR). –
Trombone © 2022 - 2024 — McMap. All rights reserved.
bzr root
was used a lot in Bazaar – Iyargit rev-parse --git-dir
, as explained in this comment – Performing