27

I want to display the Git version on my site.

How can I display a semantic version number from Git, that non-technical users of a site can easily reference when raising issues?

lukeocodes
  • 1,192
  • 1
  • 16
  • 31
  • 5
    So you now want to share it with the world? Then you should split this: Formulate your question and then leave the resolution as an answer. See http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ – hakre May 02 '13 at 09:16
  • [`git describe`](https://www.kernel.org/pub/software/scm/git/docs/git-describe.html) makes a lot more sense when used with [tagging](http://git-scm.com/book/en/Git-Basics-Tagging). – Joe May 02 '13 at 13:59
  • Instead of rev-list HEAD piped to head, just use `git rev-parse HEAD` – Uwe Geuder May 02 '13 at 16:18
  • @Uwe Geuder - The output of `git rev-list HEAD | wc -l` and `git rev-parse HEAD` are totally different? – lukeocodes May 03 '13 at 10:24
  • @LukeOliff Oh, sorry, you are correct of course. I somehow read `git rev-list HEAD | head -1` but the head and the minus one appeared somewhere else... – Uwe Geuder May 03 '13 at 13:55

7 Answers7

61

Firstly, some git commands to fetch version information:

  • commit hash long
    • git log --pretty="%H" -n1 HEAD
  • commit hash short
    • git log --pretty="%h" -n1 HEAD
  • commit date
    • git log --pretty="%ci" -n1 HEAD
  • tag
    • git describe --tags --abbrev=0
  • tag long with hash
    • git describe --tags

Secondly, use exec() combined with the git commands of your choice from above to build the version identifier:

class ApplicationVersion
{
    const MAJOR = 1;
    const MINOR = 2;
    const PATCH = 3;

    public static function get()
    {
        $commitHash = trim(exec('git log --pretty="%h" -n1 HEAD'));

        $commitDate = new \DateTime(trim(exec('git log -n1 --pretty=%ci HEAD')));
        $commitDate->setTimezone(new \DateTimeZone('UTC'));

        return sprintf('v%s.%s.%s-dev.%s (%s)', self::MAJOR, self::MINOR, self::PATCH, $commitHash, $commitDate->format('Y-m-d H:i:s'));
    }
}

// Usage: echo 'MyApplication ' . ApplicationVersion::get();

// MyApplication v1.2.3-dev.b576fd7 (2016-11-02 14:11:22)
Harry B
  • 2,864
  • 1
  • 24
  • 44
Jens A. Koch
  • 39,862
  • 13
  • 113
  • 141
17

If you'd like to do it without exec() and you're using git lightweight (see comments below) tagging:

You can get the current HEAD commit hash from .git/HEAD or .git/refs/heads/master. We then loop to find matching. Reversing the array first for speed because you're more likely to at a higher recent tag.

So if the current php file sits in a public_html or www folder one level down from the .git folder...

<?php

$HEAD_hash = trim(file_get_contents('../.git/refs/heads/master')); // or branch x

$files = glob('../.git/refs/tags/*');
foreach(array_reverse($files) as $file) {
    $contents = trim(file_get_contents($file));

    if($HEAD_hash === $contents)
    {
        print 'Current tag is ' . basename($file);
        exit;
    }
}

print 'No matching tag';
Harry B
  • 2,864
  • 1
  • 24
  • 44
  • 1
    i prefer this approach as you won't need to have to do exec – Christian Noel Apr 25 '18 at 08:02
  • 1
    Me too, but it didn't work me. reading `.git/HEAD` gave me `ref: .git/refs/heads/master`. When I read in `.git/refs/heads/master` I got the commit hash. So I edited your answer! (and gave you an upvote and everyone else a downvote for using exec) – delboy1978uk May 23 '18 at 17:32
  • Your method of getting the current tag will not work if you are several commits past a tag, though. Also, if you go from (say) 1.0.9 to 1.0.10, reversing the array may not have the impact you expect. Furthermore, you can't really assume that one is talking about master - the repo could be reading off a separate branch. You would need to read HEAD to find out which branch is in use. I have sent an edit that does this. – Aaron Mason Oct 19 '18 at 05:11
  • Always great to have people improve on a solution or explaination. – Harry B Oct 20 '18 at 10:39
  • The edit got rejected. Apparently demonstrating the change in your comment isn't OK. – Aaron Mason Oct 21 '18 at 21:24
  • This will only work with lightweight tags though. Annotated tags are stored as objects distinct from commits they refer to, so `refs/tags` contents won't match head ref. – fbastien Jan 23 '20 at 15:16
  • Updated post to clarify – Harry B Jan 24 '20 at 11:19
  • 1
    A trim seems necessary on the $HEAD_hash as well to make this work on the latest version. – Eiji Oct 21 '22 at 01:33
13

Gist: https://gist.github.com/lukeoliff/5501074

<?php

class QuickGit {

  public static function version() {
    exec('git describe --always',$version_mini_hash);
    exec('git rev-list HEAD | wc -l',$version_number);
    exec('git log -1',$line);
    $version['short'] = "v1.".trim($version_number[0]).".".$version_mini_hash[0];
    $version['full'] = "v1.".trim($version_number[0]).".$version_mini_hash[0] (".str_replace('commit ','',$line[0]).")";
    return $version;
  }

}
lukeocodes
  • 1,192
  • 1
  • 16
  • 31
  • 5
    So your version number is just counting the commits. This will work fine if you never ever use any branching and merging. If there are any branches the number will not necessarily uniquely identify a single commit. One could try to solve the issue with a dot notation. E.g. 4.1.5 means after 4 commits from the root, take the first branch and advance 5 commits. This would require that branches are ordered e.g. by commit time. Git does not guarantee that newer commits have a newer commit time, you would need to guarantee that by your process. Coding such a numbering scheme takes more work. – Uwe Geuder May 03 '13 at 14:19
  • Actually the first branch is the default. (Some VCS would call it trunk, but in git everything is a branch.) So the after commit 4 the commit in the first branch would be number 5. If a branch is made from commit 4 it would be the second branch. The first commit in the second branch would naturally be 4.1.1. So the first branch is not reflected in the numbering, it is the default. The n'th branch is shown as n-1 in the numbering. – Uwe Geuder May 03 '13 at 14:29
  • And actually a git tree can have more than one root. Not a very common case maybe, but for a general solution one would need to indicate from which root to start. In this case the first root cannot be hidden as elegantly as the first branch or you need to to designate the second root with a letter. – Uwe Geuder May 03 '13 at 15:26
  • The version number consists of a commit count and mini-hash. i.e. `v1.48.9aec9f2` – lukeocodes May 07 '13 at 10:02
  • Yes, but you said your goal is help humans (especially inexperienced ones) to understand the version number. If you have branches, v1.48.0aa1234 could be higher than your example. Actually v1.47.123abcd could be even higher. I'd fear that such a system could confuse more than it helps. – Uwe Geuder May 07 '13 at 20:34
  • It doesn't really help me, you are right. But I have a staging environment that directors and managers can access for signing off changes and a live environment on the go. Whenever anyone emails me, they give me the version number that they encountered whatever it is this are contacting me for. I look at the mini-hash which translates to a commit. I can try to reproduce in latest code or a branch I think where it might have already been fixed. Then I can tell them what version it will be fixed after. As the commit number is incremental it will always go up, even if the branch is from earlier. – lukeocodes May 08 '13 at 10:00
4

Simple way:

  • Short hash: $rev = exec('git rev-parse --short HEAD');
  • Full hash: $rev = exec('git rev-parse HEAD');
Sergio Rodrigues
  • 964
  • 9
  • 12
3

I did it just as:

substr(file_get_contents(GIT_DIR.'/refs/heads/master'),0,7)

resource friendly and the same as i have under eclipse shown

cyroxis
  • 3,661
  • 22
  • 37
Virtimus
  • 96
  • 1
  • 3
0

Run git tag in terminal to preview your tags and say you got i.e:

v1.0.0
v1.1.0
v1.2.4

here's how to get the latest version v1.2.4

function getVersion() {
  $hash = exec("git rev-list --tags --max-count=1");
  return exec("git describe --tags $hash"); 
}
echo getVersion(); // "v1.2.4"

Coincidentally (if your tags are ordered), since exec returns only the last row we could just do:

function getVersion() {
  return exec("git tag");
}
echo getVersion(); // "v1.2.4"

To get all the rows string use shell_exec:

function getVersions() {
  return shell_exec("git tag");
}
echo getVersions(); // "v1.0.0
                    // v1.1.0
                    // v1.2.4"

To get an Array:

$tagsArray = explode(PHP_EOL, shell_exec("git tag"));

To sort tags by date:

git tag --sort=committerdate

Docs: git-for-each-ref#_field_names

For sorting purposes, fields with numeric values sort in numeric order (objectsize, authordate, committerdate, creatordate, taggerdate). All other fields are used to sort in their byte-value order.

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
0
  public function getTagVersion()
  {
        $files = glob($this->projectDir.'/.git/refs/tags/*');
       
        return basename(end($files));
  }
jim smith
  • 2,394
  • 5
  • 29
  • 35