67

In a similar topic Validate if commit exists they recommend:

git rev-list HEAD..$sha

If it exits without error code than the commit exists.

But is it efficient enough just for validation?

I was thinking about this option:

git cat-file commit $sha

Is it correct for my task and are there any other ideas?

Community
  • 1
  • 1
skovalyov
  • 2,069
  • 1
  • 16
  • 12
  • "*But is it efficient enough just for validation?*" I'm not sure I understand the question – Gabriele Petronella Aug 29 '13 at 15:50
  • @Gabriele Petronella To be more precise I am looking for the fastest approach. I want to choose the right git command to avoid time penalty. – skovalyov Aug 29 '13 at 16:01
  • 2
    My favorite is `git merge-base >/dev/null 2>&1`. Without the `2>&1`, it gives nice error messages, no further fiddling required. – Palec Sep 07 '15 at 22:14

5 Answers5

71

You can just run git cat-file -t $sha and check it returns "commit". You are right, you don't need to actually print the actual object for that...

I'm not 100% sure that what goes on behind the scene is more efficient, though.

test $(git cat-file -t $sha) == commit

remram
  • 4,805
  • 1
  • 29
  • 42
  • 11
    Eventually I decided to use `git cat-file commit $sha` as it just exits with error if `$sha` does not represent a commit, so I do not need to test stdout (in my case I also need to trim it as it adds line break at the end of "commit"). – skovalyov Aug 30 '13 at 09:29
  • 3
    Note this will also return "commit" for a branch name – Aram Kocharyan Mar 23 '16 at 01:50
  • 3
    Branches are not objects :) Checking whether you entered a ref or a SHA-1 can be done with rev-parse: `[[ $(git rev-parse --symbolic-full-name $sha) = refs/* ]]` – remram Mar 23 '16 at 02:30
51
git cat-file -e $sha^{commit}

From git cat-file docs:

   -e
      Suppress all output; instead exit with zero status if <object> exists
      and is a valid object.

This (1) shows that this is an intended use case for cat-file and (2) avoids the resources of actually outputting any commit contents.

Appending ^{commit} ensures that the object is a commit (i.e. not a tree or blob) or -- as remram points out -- resolves to a commit.

For example,

if git cat-file -e $sha^{commit}; then
  echo $sha exists
else
  echo $sha does not exist
fi
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • This does NOT ensure that the object is a commit, but that **it can resolve to a commit**; for instance it will work for something like a tag. `^{commit}` means to resolve to a commit, not to ensure the original object has that type. My answer makes sure it's a commit, though I'm not sure the OP cares about this distinction (his own version has the same issue as yours). – remram Jan 21 '16 at 21:48
  • @remram, I assume that `$sha` is a SHA-1 hash, full or abbreviated. If `$sha` is in fact *not* a SHA hash, but an arbitrary identifier, then things are are somewhat ambiguous, as the OP never makes clear whether he want to resolve the identifier (as in this answer) or not. – Paul Draper Jan 21 '16 at 22:12
  • Sorry if I was no clear, I mean that if it's the SHA of a tag object it will still work, not only for the SHA of a commit object. – remram Jan 22 '16 at 17:54
  • I would suggest editing to add `if git cat-file... >& /dev/null` to avoid the "fatal: Not a valid object name" output to `stderr` – Joshua Goldberg May 09 '19 at 18:19
11

The error code of this command will be 0 if the commit exists, or 1 if not:

git rev-parse -q --verify "$sha^{commit}" > /dev/null

If you want an easier read, append && echo "exists" || echo "doesn't exist".

From the git rev-parse docs:

   --verify
       Verify that exactly one parameter is provided, and that it can be turned into a raw 20-byte SHA-1 that can be used to access the object database. If so, emit it to the standard output; otherwise, error out.

       If you want to make sure that the output actually names an object in your object database and/or can be used as a specific type of object you require, you can add the ^{type} peeling operator to the parameter. For
       example, git rev-parse "$VAR^{commit}" will make sure $VAR names an existing object that is a commit-ish (i.e. a commit, or an annotated tag that points at a commit). To make sure that $VAR names an existing object of
       any type, git rev-parse "$VAR^{object}" can be used.

   -q, --quiet
       Only meaningful in --verify mode. Do not output an error message if the first argument is not a valid object name; instead exit with non-zero status silently. SHA-1s for valid object names are printed to stdout on
       success.

As a bonus, if you don't suppress the output, you can get the full sha.

Black Mantha
  • 1,147
  • 8
  • 11
  • This does not work for me, I get the following error: `fatal: Needed a single revision`. – caram Jun 16 '20 at 07:26
  • @caram: That error shows up when you don't use -q, right? Hard to tell what the problem is. It gives the same error when the commit isn't found, when $sha isn't long enough to be unique, and when you've made a syntax error in the parameter. – Black Mantha Jun 16 '20 at 09:01
  • @caram thats the error it gives if it can't find it. – run_the_race Mar 09 '22 at 20:16
  • Why not just `git rev--parse $committish` (it errors out if the hash it not found) – run_the_race Mar 09 '22 at 20:16
  • 1
    @BlackMantha not sure if I'm misunderstanding you, but `git rev-parse -q --verify HEAD^{commit}` doesn't seem to report an error? so I don't think this distinguishes between commit hashes and things that refer to them – Ben Millwood Aug 17 '22 at 10:30
  • also as pointed out in another comment, `git rev-parse --verify` seems happy to accept any full SHA hash, even if no commit exists with that hash – Ben Millwood Aug 17 '22 at 10:34
  • @BenMilwood This command communicates via the exit code. But that was only in the quoted doc, so I edited my answer to make this explicit. – Black Mantha Sep 27 '22 at 09:10
8

If you are sure that the sha are commits then cat-file -e can be used for example:

if git cat-file -e $sha 2> /dev/null 
then 
  echo exists 
else 
  echo missing 
fi

This is pretty efficient as this is a built-in and doesn't do anything other than checking the sha exists:

return !has_sha1_file(sha1);

Otherwise if it is uncertain that the sha are commit objects you would need to determine the type as with the other answer using git cat-file -t. This is only slightly less performant as git would have to look at the file information. This isn't as costly as unpacking the whole file.

andygavin
  • 2,784
  • 22
  • 32
  • 4
    `git rev-parse --verify` will only check if the identifier is a valid hash. `git rev-parse --verify 1234567890123456789012345678901234567890` will always return true – knittl Aug 29 '13 at 16:07
  • 1
    Exactly. I have used this method, i.e. `git rev-parse $sha`, initially, but it turns out it exits with success on any valid hash. – skovalyov Aug 29 '13 at 16:15
  • Quite right, really the top one one is translating the a ref into the sha. Thanks. – andygavin Aug 29 '13 at 16:25
0

git rev-list HEAD..$sha will check if the $sha relates to HEAD.

git cat-file commit $sha will check if the $sha exists in the repository.

So it will depend on your use-case.

One use case that needs the first option, for instance: checking if upstream has new commits

# FETCH_HEAD = 2d9dbaa

$ git rev-list main..2d9dbaa
2d9dbaad0aad8e63b99235448c5aa4f82e9b1837
...
fb44705de992b19efd77af6e2f05e4fc1a79b568
$ git rev-list FETCH_HEAD..2d9dbaa
$ git cat-file -e 2d9dbaa && echo exists
exists

Using rev-list I can validate that main does not have revision 2d9dbaa while cat-file would not help (as revision does exist in a different branch).

Filipe Pina
  • 2,201
  • 23
  • 35