53

i use git as a version tracker for my c++ project.

sometimes i need to repeat a calculation and i would like to know which version of the program i used.

what would be a good way to put a # of the commit into the main executable? in other words. i'd like the program to tell me the # of the current commit in an introductory message as i run the program.

one way i can think of is to make the c++ program lunch "git log" from shell and extract the commit # but i'm not sure how to do it during make.

(I use linux)

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
kirill_igum
  • 3,953
  • 5
  • 47
  • 73
  • Very similar to http://stackoverflow.com/questions/1704907/how-can-i-get-my-c-code-to-automatically-print-out-its-git-version-hash – 0 _ Jun 23 '15 at 03:38

4 Answers4

42

Probably the easiest way to do this would be to add to your makefile a rule to generate a .c file with the current git commit ID:

gitversion.c: .git/HEAD .git/index
    echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@

Now simply add gitversion.c to your build process as normal. Make sure to remove it on make clean, and add it to .gitignore so it's not added to the git repository accidentally. Add an extern const char *gitversion; to a header somewhere, and you can access it like that.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • 1) i'm not that familiar with make or git. i tried a quick search but still couldn't understand the part ".git/HEAD .git/index". – kirill_igum Jun 29 '11 at 20:10
  • 6
    The files listed after `gitversion.c:` are the files it depends on. `.git` is a hidden directory; `HEAD` is a file in there. Therefore, when `.git/HEAD`or `.git/index` changes, `gitversion.c:` is rebuilt using the command on the next line. – MSalters Jun 29 '11 at 20:23
  • Yes; this isn't entirely complete, mind you; it might be better to also reference .git/packed-refs and all files under .git/refs... – bdonlan Jun 29 '11 at 20:27
  • 2) i think i misunderstood smtn. here is what i do: 1. put those 2 lines in the Makefile after main.o rule. 2. include the line "extern const char *gitversion;" into main.cc. 3. put cout << *gitversion; into main.cc 4. run make. there is no gitversion.c in the directory (it's not created). i also get an error "main.cc:(.text+0x89): undefined reference to `gitversion'" did i miss something? – kirill_igum Jun 29 '11 at 20:28
  • You need to actually reference gitversion.c from your link rule as you would any other .c file. – bdonlan Jun 29 '11 at 20:30
  • ok, most of it works now. but when i "git rev-parse HEAD" within makefile, it doesn't produce anything but it works perfect from bash. (git describe gives an error, so i use rev_parse) – kirill_igum Jun 29 '11 at 20:56
  • What error does git describe give? It could be the same problem. Also make sure it's running in the right directory... – bdonlan Jun 29 '11 at 21:07
  • > git describe fatal: No names found, cannot describe anything. ; it's in the same directory. in the makefile i have 'echo "const char *gitversion = \"$(git rev-parse --short HEAD)\";" > $@'. the first line of the make output is 'echo "const char *gitversion = \"\";" > gitversion.c'. cat gitversion.c gives 'const char *gitversion = "";'. however if i run in bash 'echo "const char *gitversion = \"$(git rev-parse HEAD)\";"', i get 'const char *gitversion = "00d768cf9fc6d3c6c78dd91dbb42c46fc43b6285";' – kirill_igum Jun 29 '11 at 21:11
  • @kirill_igum let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/988/discussion-between-bdonlan-and-kirill-igum) – bdonlan Jun 29 '11 at 21:14
  • thanks to @bdonlan instead of $(git rev-parse HEAD), use $(shell git rev-parse HEAD) – kirill_igum Jun 29 '11 at 21:36
  • @IoannisFilippidis - no, that version is far inferior as unlike this it only updates when explicitly cleaned. – Chris Stratton Apr 04 '18 at 16:21
  • @ChrisStratton Good point. Relevant to note that this one updates only after committing new changes. Uncommitted changes result in `make` reporting that `gitversion.c` "is up to date". So running `make clean` may still be required. – 0 _ Apr 04 '18 at 20:01
  • @IoannisFilippidis I usually put the check directly into my link rule for that reason, and often feed the compiler source on its command line so there isn't even a .c file on disk. – Chris Stratton Apr 04 '18 at 23:00
  • @ChrisStratton Thanks for outlining an approach to avoid this issue. – 0 _ Apr 04 '18 at 23:07
  • This is probably obvious, but this is not cross platform, you'll need some tweaking to make this work on windows. – Vasantha Ganesh Jan 02 '23 at 12:58
16

I do the following in CMakeLists.txt:

IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
  FIND_PACKAGE(Git)
  IF(GIT_FOUND)
    EXECUTE_PROCESS(
      COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
      WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
      OUTPUT_VARIABLE "kml2maps_BUILD_VERSION"
      ERROR_QUIET
      OUTPUT_STRIP_TRAILING_WHITESPACE)
    MESSAGE( STATUS "Git version: ${kml2maps_BUILD_VERSION}" )
  ELSE(GIT_FOUND)
    SET(kml2maps_BUILD_VERSION 0)
  ENDIF(GIT_FOUND)
ENDIF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)

CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/kml2mapsVersion.h.in ${CMAKE_CURRENT_BINARY_DIR}/kml2mapsVersion.h @ONLY)

So the git rev-parse --short HEAD's output is good to build in the binary.

Naszta
  • 7,560
  • 2
  • 33
  • 49
  • 5
    This approach assumes that you run cmake manually for each commit. – Paul Baltescu May 06 '14 at 13:37
  • And to get it working you need to define something like: `#define GIT_REVISION "@kml2maps_BUILD_VERSION@"` in `kml2mapsVersion.h.in`. – Paul Baltescu May 06 '14 at 13:39
  • 1
    This article "CMake: Use Git Branch & Commit Details in Project" explains the cmake mechanism in more detail: http://xit0.org/2013/04/cmake-use-git-branch-and-commit-details-in-project – Hackless Apr 08 '17 at 19:15
14

I use git describe to get a version which either uses a tag or commit number. This usually gives nice versions like: v0.1-1-g787c667 if the tip of the branch has additional commits above the 'v0.1' tag.

The git command I use is: git describe --tags --always. I usually use it with the SCons build system and define it as a constant, relevant parts of the SConstruct:

import os, sys 
from subprocess import *

def getGitDesc():   
  return Popen('git describe --tags --always', stdout=PIPE, shell=True).stdout.read ().strip ()

GIT_DESC = getGitDesc () 
print "Building " + getGitDesc () + ".." 
env = Environment ()

# set up environment 
env.Append (CPPDEFINES = { 'GIT_DESC' : ('\\"%s\\"' % GIT_DESC) } )

# build your program
env.Program (....)

In the C or C++ program I can now access GIT_DESC as a string-constant:

# include <iostream>

using namespace std;

int main (int argc, char ** argv) {
  cout << "Version: " << GIT_DESC << endl;
  return 42;
}

note: the --abbrev=N argument to git describe might be useful to achieve consistent version output independent of a users git configuration.

gauteh
  • 16,435
  • 4
  • 30
  • 34
5

If you're using Qt, put this in your project's .pro file:

win32:DEFINES += GIT_BIN='C:\\Git\\bin\\git'
# or 'C:\\Progra~1\\Git\\bin\\git' - ymmv with putting spaces in here
win32:DEFINES += GIT_REVISION='\\"$$system($${GIT_BIN} rev-parse --short HEAD)\\"'
unix:DEFINES += GIT_REVISION='\\"$$system(git rev-parse --short HEAD)\\"'

Then use GIT_REVISION in your code as in the other answers - it behaves as const char *.

(Thanks to Alexander Barthel, who I plundered this tip from.)

Jack Deeth
  • 3,062
  • 3
  • 24
  • 39
  • 1
    It's easier to just manually add the `-D` flag to the list of compiler flags: `QMAKE_CXXFLAGS += -DGIT_COMMIT=\"$(shell git describe --abbrev=4 --dirty --always --tags)\"` – ScumCoder Mar 26 '20 at 16:53