Automation of the correspondence of executable files to source codes in GIT

    Having a software project with a compiled programming language, the problem arises of having an executable file to determine from which source code it was built. In this article, we will describe how to automate the addition of a commit to executable files and how to obtain the source code from it in the future.

    In general, git is a repository of objects. There are 4 types of objects in git:
    • blob - for storing data.
    • tree - to store links to blob and other trees.
    • commit - to point to a specific tree object, i.e. marking a project snapshot at a specific point in time.
    • tag - to mark commit as some version of the project.

    Any object can be accessed by its “object name” - a set of 40 numbers and letters (hexadecimal representation), which is SHA1 of the data of this object. Thus, the state of the project source codes in GIT can be described by the “object name” of the commit or simply by commit. You can get the current commit in one line and with a comment to it using the following command:
    git log --pretty=oneline -n1
    

    Now you just need to add it to the executable files and display it to the user, for example, when starting the program with the --version parameter.

    This process can be automated with the help of hooks - custom actions that will be performed when some operations are performed by git. There are various hooks , but we only need post-commit (called after processing git commit), post-checkout (called after processing git checkout) and post-merge (called after processing git merge, which is also called during git pull) . Hooks are executables located in the $ GIT_DIR / hooks directory.

    In order for hooks to be in the git repository, it is convenient to create the git_hooks folder and make a symlink to $ GIT_DIR / hooks. Create 3 post-commit, post-checkout, post-merge files with the same content in this directory. For example, we use the C / C ++ project and want to store the commit as a string in the header file, then these files will look like this:

    #!/bin/sh
    # post-commit
    #Файл в который мы будем записывать коммит
    file="git_commit.h"
    file_def=`echo $file|tr . _`
    echo "#ifndef $file_def" > $file
    echo "#define $file_def" >> $file
    echo "static const char *git_commit_str = \""`git log --pretty=oneline -n1`"\";" >> $file
    echo "#endif" >> $file
    # Выведем сообщение о том что все успешно
    echo "--- Git commit is written to $file ---"
    echo "--- !!! NOW PLEASE RECOMPILE YOUR PROJECT TO TAKE EFFECT !!! ---"
    

    Now in the project you can insert the following code that will output the version of the commit.
    #include git_commit.h
    …
    printf("Git commit: %s\n", git_commit_str);
    

    Each time git commit, git checkout, git pull is called (if the local version differs from the remote version), the git_commit.h file will be overwritten and the project will need to be rebuilt in order to display the required commit. In the future, having an executable file, you can get the source code of this file using git checkout COMMIT or see the source code in a web browser if you use github at github.com/USER/PROJECT/tree/COMMIT .

    By the way, in SVN the same operation can be done in a similar way. In SVN, the state of the project source code is described using the global revision number, which can be obtained using the svnversion command, and hooks can be hung on the post-commit and post-revprop-change events.

    Also popular now: