0

I have a file that I think might be inside a git repo. I am currently in my home directory. How can I get the top level directory of the repo without changing my current working directory?

If I am inside the repo I can run to get the root directory.

(~/code/dir1) $ git rev-parse --show-toplevel

Effectively I want to be able to use a file to find the root of the git directory directly.

(~) $ find . -name "specific_file.py"

Where the important parts of the tree are:

~/code/dir1/.git
~/code/dir1/files/more_files/specific_file.py

Is there a git way of doing this, or is generic shell manipulations the best way of doing this?

I know I can do:

(~) $ cd $(dirname $(find . -name "specific_file.py"))
(~/code/dir1/files/more_files) $ git rev-parse --show-toplevel
~/code/dir1
(~/code/dir1/files/more_files) $ cd - 

If I try without being inside the repo I get the message:

Fatal '~/code/dir1/files/more_file/specific_file.py' is outside repository

If I try and set the git directory to being further down the tree:

git --git-dir=$(dirname $(find . -name "specific_file.py")) rev-parse --show-toplevel

It tells me that I'm not working in a git directory.

I have also tried playing with the working tree, but that doesn't seem to be what I'm after as its looking directly for a /.git directory directly on the path.

Luke Exton
  • 3,506
  • 2
  • 19
  • 33
  • I'm not sure I understand what the need is for doing this. Won't `find` show you the full path of the file, from which it should be obvious whether or not it is located inside a Git repo? – Tim Biegeleisen Sep 27 '16 at 04:35
  • What platform are you on? – pneumatics Sep 27 '16 at 04:36
  • @TimBiegeleisen yes, because I know the structure of the repo on my machine, but there doesn't seem to be an easy way to find the root path so that it could locate other helper scripts if being built from source. I could of course just take the path as a parameter, but I would like to avoid that if there is an easy solution built into git. – Luke Exton Sep 27 '16 at 04:39
  • @pneumatics, I am running bash3/4 on OSX 10.11 and Ubuntu 14.04, but a built-in solution to git would be ideal, if it exists. – Luke Exton Sep 27 '16 at 04:39
  • nuts, the lack of `readlink -f`, the canonicalize flag, is a wrinkle. You'll have to be sure to `brew install greadlink` on MacOS, then you can use the `topgit` function below with a modification. – pneumatics Sep 27 '16 at 04:44

3 Answers3

3

After reading the man page in detail, it was the -C flag I needed not the --git-dir or --work-dir flags.

git -C $(dirname $(find . -name "specific_file.py")) rev-parse --show-toplevel

My interpretation is that multiple -C flags might be useful if working with submodules/subtrees.

From the git man page:

Run as if git was started in instead of the current working directory. When multiple -C options are given, each subsequent non-absolute -C is interpreted relative to the preceding -C .

This option affects options that expect path name like --git-dir and --work-tree in that their interpretations of the path names would be made relative to the working directory caused by the -C option. For example the following invocations are equivalent:

Luke Exton
  • 3,506
  • 2
  • 19
  • 33
1

Try changing directories in a subshell, then call git rev-parse --show-toplevel. You'll be right back where you started, with the information you want at hand!

Note: the $OSTYPE gyrations are due to the absence of the -f|--canonicalize flag in the BSD version of readlink that ships with OSX. An alternative on osx would be to brew install greadlink and set a local variable like readlink=$(command -v greadlink readlink | head -n 1), then call ${readlink} -f.

abspath()
{
    case $OSTYPE in
       darwin*)  python -c 'import sys, os.path; print os.path.abspath(sys.argv[1])' $1;;
       linux*)   readlink -f $1;;
    esac
}

topgit()
{
    [ -e "$1" ] || { echo >&2 "$1 does not exist"; return 1; }
    abspath=`abspath $1`
    (
     [ -f "$abspath" ] && cd `dirname $abspath` || cd $abspath
     git rev-parse --show-toplevel
    )
}
pneumatics
  • 2,836
  • 1
  • 27
  • 27
  • And I don't think installing the `greadlink` is a fair dependency for something so minor. The subshell component means I'm less worried about the directory change. There isn't any git builtin/flag/env_var to do this directly? – Luke Exton Sep 27 '16 at 04:50
  • 1
    Yes, absence of `readlink -f` is quite a pincher on osx. You can force callers to give absolute paths, but that's no fun. I've used a Python or Perl one-liner as a replacement, as a portable solution that doesn't require installing `greadlink`, but in some way that's even more silly. But it would work, want that solution? – pneumatics Sep 27 '16 at 04:57
  • 1
    Nah its okay, I have often just used `python -c "import os.path as p; print(p.realpath($1))"` as an alternative. – Luke Exton Sep 27 '16 at 05:10
0

If you are using git rev-parse --show-toplevel (and you are not in a submodule), make sure to use Git 2.25 (Q1 2020)

"git rev-parse --show-toplevel" run outside of any working tree did not error out, which has been corrected.

See commit 2d92ab3 (19 Nov 2019) by Jeff King (peff).
(Merged by Junio C Hamano -- gitster -- in commit 36fd304, 05 Dec 2019)

rev-parse: make --show-toplevel without a worktree an error

Signed-off-by: Jeff King

Ever since it was introduced in 7cceca5ccc ("Add 'git rev-parse --show-toplevel' option.", 2010-01-12, Git v1.7.0-rc0 -- merge listed in batch #0), the --show-toplevel option has treated a missing working tree as a quiet success: it neither prints a toplevel path, but nor does it report any kind of error.

While a caller could distinguish this case by looking for an empty response, the behavior is rather confusing.
We're better off complaining that there is no working tree, as other internal commands would do in similar cases (e.g., "git status" or any builtin with NEED_WORK_TREE set would just die()).
So let's do the same here.

While we're at it, let's clarify the documentation and add some tests, both for the new behavior and for the more mundane case (which was not covered).

The error now (Git 2.25+) message is:

this operation must be run in a work tree
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250