25

I am writing a custom git command that should only run in a completely new repository with no commits (see this question). How can I perform a simple check within my script to see if an existing repo has zero commits?

Essentially, what goes in the blank below?

if ___________ ; then
    echo "Git repo already has commits. Aborting.
else
    echo "Git repo has no commits. Doing whatever my script does."
fi
Community
  • 1
  • 1
Ryan C. Thompson
  • 40,856
  • 28
  • 97
  • 159

2 Answers2

39

Summary:

  • A commit is checked out: test git rev-parse HEAD 1>/dev/null 2>&1
  • A ref pointing to a commit exists: test git rev-list -n 1 --all 1>/dev/null 2>&1
  • Objects exist in the repo: test output of git fsck, git count-objects, or the examine the contents of .git/objects

And now for the discussion!

If you want to know whether a commit is checked out, you can use git rev-parse HEAD. There will be output, so you probably want to redirect to /dev/null and just use the exit code. For all practical purposes, this will be good enough - doing normal things, it's pretty much impossible to end up without HEAD pointing at anything. But it is possible, for example by deleting files in the .git directory. Depending on your script, this might be important - if you're about to blow away the .git directory, you want to be paranoid indeed.

If you want to see whether there are any refs at all with commits on them, you can use git rev-list -n 1 --all. Again, there will be output (the SHA1 of the first commit encountered), so redirect to /dev/null and check the exit code.

Finally, if you want to check if there are any commits - even if they aren't on any refs (you have to try really hard to get into this state) I'd probably just check for the presence of objects with git fsck or git count-objects - or failing that, list .git/objects and check for anything besides info and pack (commands tend to fail if there is no file .git/HEAD). And yes, you could actually have a repo with blobs and trees but no commits, but you'd have to try even harder to get there. These are the absolute safest methods, if your script is scary.

mtraceur
  • 3,254
  • 24
  • 33
Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 1
    Since we're dealing with this currently, I thought I might note that the ``git rev-parse HEAD`` is sensitive to the case where there's no "master" branch. ``git init --bare`` at least on my end creates a repo with HEAD pointing at master; but someone may come along and push commits to some other branch name and the script will not detect them (unless a hook updates HEAD or something). This seems like a pretty typical case worth noting in the answer. – OEP Dec 21 '13 at 15:55
  • beware that `git fsck` is not readonly or could take a long time, and that `git count-objects` will report ZERO if there is only a pack. You can try `git count-objects -v` to also see how many objects are in the packs. – robbat2 Nov 04 '15 at 18:26
  • I'm not sure how determining if a commit is checked out (e.g. detached HEAD state) is relevant to detecting an empty or new repo. – ingyhere Jul 18 '17 at 19:04
  • 1
    @ingyhere When the answer says "a commit is checked out", it does not mean "a branch is not checked out". If you have a branch checked out, that branch is pointing to a commit, and `git rev-parse HEAD` will succeed. If you have nothing whatsoever checked out, as when you've run git init but not yet committed, then it will fail. – Cascabel Jul 18 '17 at 19:20
  • 3
    `git rev-list -n1 --all` always exits with exit code 0, no matter whether there are any refs or not. Using `test -n "$(git rev-list -n1 --all)"` works fine for me though. – jlh May 11 '18 at 14:10
  • 1
    nit: These work in bash (and probably some other shells). However, in some shells (notably dash) 'git rev-parse HEAD &> /dev/null' will run git rev-parse in the background and then perform a no-op truncating /dev/null. IOW, `&>` is a non-portable shell construct (aka, a bashism) that should not be provided in an answer that is not specific to a particular shell. – William Pursell Nov 23 '18 at 13:38
  • @Cascabel someone brought to my attention that `git count-objects` will not do what the OP wants for a **freshly cloned repo**. I just cloned a test repo from GitHub locally containing 1 commit and found that that commands returns 0 objects even though there is 1 commit in there. Will test the other commands which may be better. – cardamom Jun 03 '19 at 13:02
  • Building on @WilliamPursell's comment: the portable replacement for `&>/dev/null` is `>/dev/null 2>&1`. (Since this question+answer are most likely to be used for scripting purposes, I've gone ahead and just edited that change into the answer, but no hard feelings if it's reverted. I could see some good arguments either way.) – mtraceur Aug 03 '23 at 20:48
3

You want to use git log

If you are using cygwin or are on a linux machine gitk is a useful program to have as well.

RDL
  • 7,865
  • 3
  • 29
  • 32
  • git log returns an error if there are no commits. Are you saying I should interpret a non-zero return value as a indication that the repo has no commits? – Ryan C. Thompson Mar 30 '11 at 20:41
  • 3
    correct, git log will give you an error saying there is no 'HEAD' which means nothing exists. Also if you use `git branch -l` you will get no result (and no error) as well. If a commit existed a branch would exist as well (can't have one without the other). – RDL Mar 30 '11 at 20:47
  • 2
    Though that's true in normal usage, you could certainly manage to blow away all your branches (and still have commits) if you wanted to - or if you made some particularly unfortunate shell mistake. – Cascabel Mar 31 '11 at 01:11
  • 2
    [[ "$( git log --oneline -5 2>/dev/null | wc -l )" -eq 0 ]] && echo -n "no commits on this branch" – ingyhere Jul 18 '17 at 18:55