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.