Home > Software engineering >  How to return first commit in case of no existing tag?
How to return first commit in case of no existing tag?

Time:10-10

This is how I am searching for the commit of the latest tag:

tag=$(git describe --tags --abbrev=0)
base=$(git rev-parse $tag)

But if there is no tag existing, it should return the first commit of the repo.

first=$(git log --reverse --oneline)

How do I get only the first commit - not a list, like with the command above? And how do I do an or for the base? If tag existing, return its commit, if not, return first commit.

CodePudding user response:

You will need a test.

The git tag command does not document an exit status. Hand-testing it shows that it has one, but you may not wish to depend on this undocumented feature. To avoid depending on the exit status, we can check the output instead:

tag=$(git describe --tags --abbrev=0)
if [ "x$tag" = x ]; then
    # there is no tag
    # so here we'll do something different
fi

In modern shells you don't need the slightly silly "x$tag" trick:

tag=$(git describe --tags --abbrev=0)
if [ "$tag" = "" ]; then ...; fi

for instance.

To get the hash ID of the root commit reachable from the current commit, use --max-parents=0. Ideally there's only one such root, but it's possible, depending on the shape of the repository history, to have more than one, in which case you must decide what you want to do about this. One way to sidestep that possibility is to decide in advance that you want whichever root is found by traversing only first-parents.

Rather than git log, which requires adding --pretty=format or --format directives to extract just the hash ID, it's usually more sensible to use git rev-list here. The minor annoyance with the rev-list "plumbing" command is that you must supply a starting point, whereas git log will assume HEAD if you don't supply a starting point. This is minor because it's trivial to write in HEAD:

first=$(git rev-list --max-parents=0 --first-parent HEAD)

Putting these together we get:

tag=$(git describe --tags --abbrev=0)
if [ "$tag" = "" ]; then
    tag=$(git rev-list --max-parents=0 --first-parent HEAD)
fi

or the more compact form:

tag=$(git describe --tags --abbrev=0)
[ "$tag" != "" ] || tag=$(git rev-list --max-parents=0 --first-parent HEAD)

Actually trying this we find that git describe in a repository where the git describe step fails and outputs the empty string does this:

$ git describe --tags --abbrev=0
fatal: No tags can describe '2808ac68000c62c3db379d73e3b7df292e333a57'.
Try --always, or create some tags.
$ echo $?
128

So the "test" can just be: did git describe succeed? In this case we get:

tag=$(git describe --tags --abbrev=0 ||
    git rev-list --max-parents=0 --first-parent HEAD)

When run in my example repository I get:

$ tag=$(git describe --tags --abbrev=0 ||
>         git rev-list --max-parents=0 --first-parent HEAD)
fatal: No tags can describe '2808ac68000c62c3db379d73e3b7df292e333a57'.
Try --always, or create some tags.
$ echo $tag
460cce09ff42c43a6826af9deaab0f7a8d1f6f44

To make the error message vanish, we can redirect it to /dev/null:

tag=$(git describe --tags --abbrev=0 2> /dev/null ||
    git rev-list --max-parents=0 --first-parent HEAD)

Note that if you want to hide the error message, you have to do this redirection, no matter which of the testing methods you pick.

  •  Tags:  
  • git
  • Related