Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get my C code to automatically print out its Git version hash?

People also ask

How does git generate hash?

Git relies on the collision-resistance of SHA-1. (In practice, if you encounter such a collision, which I believe has never occurred outside of intentional attempts to create collisions), you can just add a space to the end of the commit message, which will generate a new hash.

How do I get git hash from github?

To search for a hash, just enter at least the first 7 characters in the search box. Then on the results page, click the "Commits" tab to see matching commits (but only on the default branch, usually master ), or the "Issues" tab to see pull requests containing the commit.

What is git hash code?

Git uses hashes in two important ways. When you commit a file into your repository, Git calculates and remembers the hash of the contents of the file. When you later retrieve the file, Git can verify that the hash of the data being retrieved exactly matches the hash that was computed when it was stored.

Does git amend Change commit hash?

Amending a CommitIf you amend the commit message, or the files in a commit, this will change the git hash.


If you are using a make-based build, you can put this in the Makefile:

GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"

(See man git describe for what the switches do)

then add this to your CFLAGS:

-DVERSION=\"$(GIT_VERSION)\"

Then you can just reference the version directly in the program as though it was a #define:

printf("Version: %s\n", VERSION);

By default this just prints an abbreviated git commit id, but optionally you can tag particular releases with something like:

git tag -a v1.1 -m "Release v1.1"

then it will print out:

Version: v1.1-2-g766d

which means, 2 commits past v1.1, with a git commit id beginning with "766d".

If there are uncommitted changes in your tree, it will append "-dirty".

There is no dependency scanning so you have to do an explicit make clean to force the version to be updated. This can be solved however.

The advantages are that it is simple and doesn't require any extra build dependencies like perl or awk. I have used this approach with GNU automake and with Android NDK builds.


In my program, I hold the git version number and the date of the build in a separate file, called version.c, which looks like this:

#include "version.h"
const char * build_date = "2009-11-10 11:09";
const char * build_git_sha = "6b54ea36e92d4907aba8b3fade7f2d58a921b6cd";

There is also a header file, which looks like this:

#ifndef VERSION_H
#define VERSION_H
extern const char * build_date; /* 2009-11-10 11:09 */
extern const char * build_git_sha; /* 6b54ea36e92d4907aba8b3fade7f2d58a921b6cd */
#endif /* VERSION_H */

Both the header file and the C file are generated by a Perl script which looks like this:

my $git_sha = `git rev-parse HEAD`;
$git_sha =~ s/\s+//g;
# This contains all the build variables.
my %build;
$build{date} = make_date_time ();
$build{git_sha} = $git_sha;

hash_to_c_file ("version.c", \%build, "build_");

Here hash_to_c_file does all the work of creating version.c and version.h and make_date_time makes a string as shown.

In the main program, I have a routine

#include "version.h"

// The name of this program.
const char * program_name = "magikruiser";
// The version of this program.
const char * version = "0.010";

/* Print an ID stamp for the program. */

static void _program_id_stamp (FILE * output)
{
    fprintf (output, "%s / %s / %s / %s\n",
             program_name, version,
             build_date, build_git_sha);
}

I'm not that knowledgeable about git, so I'd welcome comments if there is a better way to do this.


I ended up using something very similar to @Kinopiko's answer, but I used awk instead of perl. This is useful if your stuck on windows machines which have awk installed by nature of mingw, but not perl. Here's how it works.

My makefile has a line in it that invokes git, date, and awk to create a c file:

$(MyLibs)/version.c: FORCE 
    $(GIT) rev-parse HEAD | awk ' BEGIN {print "#include \"version.h\""} {print "const char * build_git_sha = \"" $$0"\";"} END {}' > $(MyLibs)/version.c
    date | awk 'BEGIN {} {print "const char * build_git_time = \""$$0"\";"} END {} ' >> $(MyLibs)/version.c 

Everytime I compile my code, the awk command generates a version.c file that looks like this:

/* version.c */
#include "version.h"
const char * build_git_sha = "ac5bffc90f0034df9e091a7b3aa12d150df26a0e";
const char * build_git_time = "Thu Dec  3 18:03:58 EST 2009";

I have a static version.h file that looks like this:

/*version.h*/
#ifndef VERSION_H_
#define VERSION_H_

extern const char * build_git_time;
extern const char * build_git_sha;


#endif /* VERSION_H_ */

The rest of my code can now access the build-time and the git hash by simply including the version.h header. To wrap it all up, I tell git to ignore version.c by adding a line to my .gitignore file. This way git isn't constantly giving me merge conflicts. Hope this helps!


Your program can shell out to git describe, either at runtime or as part of the build process.


There are two things that you can do:

  • You can make Git to embed some version information in the file for you.

    The simpler way is to use ident attribute, which means putting (for example)

    *.yaml    ident
    

    in .gitattributes file, and $Id$ in the appropriate place. It would be automatically expanded to SHA-1 identifier of the contents of the file (blob id): this is NOT file version, or the last commit.

    Git does support $Id$ keyword in this way to avoid touching files which were not changed during branch switching, rewinding branch etc. If you really want Git to put commit (version) identifier or description in the file, you can (ab)use filter attribute, using clean/ smudge filter to expand some keyword (e.g. $Revision$) on checkout, and clean it up for commit.

  • You can make build process to do that for you, like Linux kernel or Git itself does.

    Take a look at GIT-VERSION-GEN script and its use in Git Makefile, or for example how this Makefile embeds version information during generation / configuration of gitweb/gitweb.cgi file.

    GIT-VERSION-GEN uses git describe to generate version description. It needs to work better that you tag (using signed / annotated tags) releases / milestones of your project.


When I need to do this, I use a tag, like RELEASE_1_23. I can decide what the tag can be without knowing the SHA-1. I commit then tag. You can store that tag in your program anyway that you like.