72

How can I tell if a given directory is part of a git respository?

(The following is in python, but bash or something would be fine.)

os.path.isdir('.svn')

will tell you if the current directory is controlled by Subversion. Mercurial and Git just have a .hg/.git at the top of the repository, so for hg I can use

os.system('hg -q stat 2> /dev/null > /dev/null') == 0)

but git status returns a nonzero (error) exit status if nothing's changed.

Is iterating up the path looking for .git myself the best I can do?

nbro
  • 15,395
  • 32
  • 113
  • 196
Grumdrig
  • 16,588
  • 14
  • 58
  • 69
  • 4
    Doesnt `git status` complain about not being part of a repo if you call it on a path? i would think capturing that and comparing a line or two of text would be better than iterating back up the tree. – prodigitalson Jan 11 '10 at 20:18
  • 2
    it does yes, exit status 128 on my machine. – falstro Jan 11 '10 at 20:23
  • Speed's not a huge issue, except that I like to do things the right way when possible. Perhaps I will just iterate parent dirs. – Grumdrig Jan 11 '10 at 20:31
  • Sorry @roe. Corrected myself - I meant it returns a non-zero status. But maybe that's the best answer. The exit status seems to be 128 if there's no git repo, or 1 if there is and there's no changes. – Grumdrig Jan 11 '10 at 20:33
  • Hi @Trevoke. What are you doing here? :) – Grumdrig Jan 11 '10 at 20:34
  • @Grumdrig: apparently, staying true to the PQ tradition and going off-t*pic ! Also, helping out the Mighty Grumdrig :p – Trevoke Jan 11 '10 at 20:40
  • Oh, also @KennyTM, I'm usually in the root so I actually test `os.path.isdir` first so speed is that much less of an issue. – Grumdrig Jan 11 '10 at 20:41

10 Answers10

98

Just found this in git help rev-parse

git rev-parse --is-inside-work-tree

prints true if it is in the work tree, false if it's in the .git tree, and fatal error if it's neither. Both true and false are printed on stdout with an exit status of 0, the fatal error is printed on stderr with an exit status of 128.

alper
  • 2,919
  • 9
  • 53
  • 102
falstro
  • 34,597
  • 9
  • 72
  • 86
  • 1
    To check if a folder is under git, even if the folder doesn't exist I used: `\`cd "#{dir}" && git rev-parse --is-inside-work-tree\``. Returns "true" if under git control. If folder exists but not under git or if folder not exist it returns "". – peterept May 15 '14 at 01:23
  • 1
    Actually, `git rev-parse --is-inside-work-tree` allows to know where we are inside a Git directory. It will return true in working directy, false if in the .git folder or a bare repository. – Dereckson Nov 18 '14 at 18:16
  • 2
    Why did this answer get so many up votes. The answer is quite old, so maybe behaviour in git changed.I answer for git version 1.7.1. "git rev-parse --is-inside-work-tree" only shows true, if the directory is hierarchically under a directory that is git controlled. BUT, but one can not deduce that the directory we are checking for is under git control, meaning "git add" was executed on this directory. – user637338 Feb 04 '15 at 11:19
  • @user637338 that is an interesting corner case, that I assume not a lot of were interested in. *However*, the way git works, this is also a case that's very hard to quantify since git doesn't actually track directories at all, only files. Calling `git add` on a directory simply adds all files in that directory. You're essentially asking to check if there's at least one file in the directory (or any of it's subdirectories) being tracked by git. – falstro Feb 06 '15 at 08:08
  • Use `git rev-parse --show-toplevel` to find the parent folder of your git repo if inside. – andig Mar 28 '15 at 17:02
  • My output for a non git folder is not "false" - I'm using a mac `$ git rev-parse --is-inside-work-tree fatal: Not a git repository (or any of the parent directories): .git` – mrwaim Jun 09 '15 at 05:57
  • 1
    @mrwaim read the answer again "`true` if it is in the work tree, `false` if it's in the '.git' tree, and fatal error if it's neither." You're seeing the third option. – falstro Jun 09 '15 at 06:27
  • So this just shows whether there is a `.git` folder somehwere up your directory path, doesn't it? I was wondering if a folder was being tracked by git or not, and was surprised to see "true" when I ran this command in an ignored directory. – Buttle Butkus Dec 13 '15 at 07:16
  • 1
    @ButtleButkus see my comment to user637338 about this particular case, git doesn't track directories. Having something ignored is just a convenience feature to keep it from showing in `status` and being included in globs. You can stil add and commit files from your "ignored" directory. – falstro Dec 13 '15 at 08:22
50

In ruby, system('git rev-parse') will return true if the current directory is in a git repo, and false otherwise. I imagine the pythonic equivalent should work similarly.

EDIT: Sure enough:

# in a git repo, running ipython
>>> system('git rev-parse')
0

# not in a git repo
>>> system('git rev-parse')
32768

Note that there is some output on STDERR when you aren't in a repo, if that matters to you.

John Hyland
  • 6,855
  • 28
  • 32
  • 3
    Thanks! I'm going with `os.path.isdir(".git") or os.system('git rev-parse 2> /dev/null > /dev/null') == 0` – Grumdrig Jan 11 '10 at 20:51
  • 8
    @grumdrig os.path.isdir(".git") will not work, as .git can be a regular file that just contains a path to a git directory. Use git rev-parse. – William Pursell Sep 15 '11 at 13:14
  • But that would be okay, because then the `or` clause calling `git rev-parse` would kick in. (That `or` is in code - it doesn't describe two alternatives I'm considering.) – Grumdrig Sep 15 '11 at 18:09
8

With gitpython, you can make a function like this:

import git

...

def is_git_repo(path):
    try:
        _ = git.Repo(path).git_dir
        return True
    except git.exc.InvalidGitRepositoryError:
        return False
0 _
  • 10,524
  • 11
  • 77
  • 109
Pablo
  • 481
  • 5
  • 4
  • This does not work if the path is not the repository root. You are missing the `search_parent_directories` argument: `_ = git.Repo(path, search_parent_directories=True)` – Stefan Fabian Jan 06 '20 at 12:55
  • `AttributeError: module 'git' has no attribute 'Repo'` – alper Mar 21 '20 at 10:48
4

Well, the directory can also be ignored by the .gitignore file - so you need to check for a .git repository, and if there is one, parse the .gitignore to see whether that directory is indeed in the git repository.

What exactly do you want to do? There may be a simpler way to do this.

EDIT: Do you mean "Is this directory the root of a GIT repository" or, do you mean "Is this directory part of a GIT repository" ?

For the first one, then just check if there is a .git -- since that's at the root, and you're done. For the second one, once you've determined that you're inside a GIT repository, you need to check .gitignore for the subdirectory in question.

Trevoke
  • 4,115
  • 1
  • 27
  • 48
  • I've got a python tool that wraps various version control programs I use, so I can get status, e.g., independent of what VC is use. Git is a new addition for me. I don't need to be so robust as to consider .gitignore, really. – Grumdrig Jan 11 '10 at 20:29
  • The latter. [15 characters :] – Grumdrig Jan 11 '10 at 20:36
  • Staying consistent to your current method is probably the best (unless of course, your tool does something different for each VCS). It's probably, in the long run, simpler to stick to the directory traversal system to figure out what is what, and then go to custom actions, if necessary. – Trevoke Jan 11 '10 at 20:38
4

For the record, use git status or similar, this is just for completeness: :)

Searching upward a tree is no biggie really, in bash you can do this simple one-liner (if you put it on one line...) ;) Returns 0 if one is found, 1 otherwise.

d=`pwd`
while [ "$d" != "" ]; do
  [ -d "$d"/.git ] && exit 0
  d=${d%/*}
done
exit 1

will search upward looking for a .git folder.

falstro
  • 34,597
  • 9
  • 72
  • 86
4

It is hard to define what a .git/ repository is

I did a bit of experimenting to see what Git considers as a Git repository.

As of 1.9.1, the minimal directory structure that must be inside a .git directory for Git to consider it is:

mkdir objects refs
printf 'ref: refs/' > HEAD

as recognized by rev-parse.

It is also obviously a corrupt repository in which most useful commands will fail.

The morale is: like any other format detection, false positives are inevitable, specially here that the minimal repo is so simple.

If you want something robust, instead of detecting if it is a Git repo, try to do whatever you want to do, and raise errors and deal with them if it fails.

It's easier to ask forgiveness than it is to get permission.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    This is accurate. The relevant Git code can be found in [is_git_directory](https://github.com/git/git/blob/57dd3dd/setup.c#L297) and [validate_headref](https://github.com/git/git/blob/57dd3dd/path.c#L636). – Søren Løvborg Nov 14 '17 at 14:58
3

From git help rev-parse again, I think you can do something like :

git rev-parse --resolve-git-dir <directory> 

and check if the command returns the directory path itself. According to the manual git rev-parse returns the path to the directory if the argument contains a git repo or is a file which contains the path to a git repo.

eerpini
  • 85
  • 3
1

Add this to your .bash_profile, and your prompt will always show the active git branch and whether you have uncommitted changes.

function parse_git_dirty {
  [[ $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" ]] && echo "*"
}
function parse_git_branch {
  git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/* \(.*\)/[\1$(parse_git_dirty)]/"
}

export PS1=' \[\033[0;33m\]\w\[\033[00m\]\[\033[01;00m\]$(parse_git_branch): ' #PS1='\w> '

You'll see this:

 ~: 
 ~: cd code/scala-plugin/
 ~/code/scala-plugin[master*]: 
retronym
  • 54,768
  • 12
  • 155
  • 168
  • 1
    Don't use porcelain (user interface) commands in scripts: they output can change. Use `git rev-parse` and `git symbolic-ref` instead. See also `__git_ps1` function in ' contrib/completion/git-completion.bash' – Jakub Narębski Jan 12 '10 at 16:04
0

If you'd prefer to look for a .gitdirectory, here's a cute way of doing that in Ruby:

require 'pathname'

def gitcheck()
  Pathname.pwd.ascend {|p| return true if (p + ".git").directory? }
  false
end

I'm unable to find something similar to ascend in Python.

skagedal
  • 2,323
  • 23
  • 34
  • Looking for the git directory is not good enough, since it is possible that the repo is corrupted (missing key files). Really need for git to determine validity – Todd Aug 03 '18 at 12:52
0

Using git rev-parse --is-inside-work-tree along with subprocess.Popen, you can check if "true" is printed from the output indicating the directory does have a git repo:

import subprocess

repo_dir = "../path/to/check/" 

command = ['git', 'rev-parse', '--is-inside-work-tree']
process = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=repo_dir,
                           universal_newlines=True)
process_output = process.communicate()[0]

is_git_repo = str(process_output.strip())

if is_git_repo == "true":
    print("success! git repo found under {0}".format(repo_dir))
else:
    print("sorry. no git repo found under {0}".format(repo_dir))
Ray
  • 187,153
  • 97
  • 222
  • 204