CコードでGitバージョンのハッシュを自動的に出力するにはどうすればよいですか?


84

GitバージョンのハッシュにアクセスできるCコードを書く簡単な方法はありますか?

私は実験室の設定で科学的データを収集するためにCでソフトウェアを書きました。私のコードは、収集したデータを後で分析するために.yamlファイルに記録します。私の実験は日々変化し、コードを変更しなければならないことがよくあります。リビジョンを追跡するために、私はgitリポジトリを使用します。

.yamlデータファイルにコメントとしてGitリビジョンハッシュを含めることができるようにしたいと思います。そうすれば、.yamlファイルを見て、そのファイルに表示されるデータを生成するために使用されたコードを正確に知ることができます。これを自動的に行う簡単な方法はありますか?


1
事前コミットフック(book.git-scm.com/5_git_hooks.htmlを参照)を使用することは、これを行うためのもう1つの方法です。
yktula 2010

回答:


39

私のプログラムでは、gitのバージョン番号とビルドの日付を、という別のファイルに保持していますversion.c。これは次のようになります。

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

次のようなヘッダーファイルもあります。

#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 */

ヘッダーファイルとCファイルはどちらも、次のようなPerlスクリプトによって生成されます。

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_");

ここでhash_to_c_fileは、作成version.cと作成のすべての作業を行いますversion.hしてmake_date_time示すように、文字列になります。

メインプログラムでは、私はルーチンを持っています

#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);
}

私はgitについてそれほど知識がないので、これを行うためのより良い方法があればコメントを歓迎します。


1
Perlスクリプトはビルドスクリプトの一部であり、すべての「ワンステップビルド」です。

12
これは、それに関しては問題ありませんが、コンパイルされるコードのハッシュではなく、ブランチでの最新のコミットのハッシュを報告することに注意してください。コミットされていない変更がある場合、それらは明らかではありません。
Phil Miller

1
git diffはデフォルトで、ワークスペースとインデックスの違いをチェックします。インデックスとHEADの違いについては、git diff --cachedを試してみることもできます
Karl

6
それらすべての 'const char * name = "value";' 構成は賢明に 'const char name [] = "value";'に変更できます。これにより、32ビットマシンではアイテムあたり4バイト、64ビットマシンではアイテムあたり8バイトが節約されます。確かに、最近のGBのメインメモリでは、それは大きな問題ではありませんが、すべて役に立ちます。名前を使用するコードはどれも変更する必要がないことに注意してください。
ジョナサンレフラー

1
あなたが提案するように私はそれらを変更しました。私のプログラムのサイズconst char []:319356バイト(削除)。私のプログラムのサイズconst char *:319324バイト(削除)。したがって、あなたのアイデアはバイトを節約していないようですが、総数を32増やします。理由はわかりません。元の「version.c」には3つの文字列がありますが、上記の回答から1つが省略されています。最初の編集を見ると、まだそこにあります。

163

makeベースのビルドを使用している場合は、これをMakefileに入れることができます。

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

(スイッチの機能についてはman gitの説明を参照してください)

次に、これをCFLAGSに追加します。

-DVERSION=\"$(GIT_VERSION)\"

次に、それが#defineであるかのように、プログラムで直接バージョンを参照できます。

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

デフォルトでは、これは省略されたgit commit idを出力するだけですが、オプションで、特定のリリースに次のようなタグを付けることができます。

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

その後、印刷されます:

Version: v1.1-2-g766d

つまり、2はv1.1を超えてコミットし、git commitidは「766d」で始まります。

ツリーにコミットされていない変更がある場合は、「-dirty」が追加されます。

依存関係のスキャンはないためmake clean、バージョンを強制的に更新するには明示的に行う必要があります。ただし、これは解決できます

利点は、シンプルで、perlやawkのような追加のビルド依存関係を必要としないことです。私はこのアプローチをGNUautomakeとAndroidNDKビルドで使用しました。


6
+1個人的に#define GIT_VERSION ...は、-Dオプションを指定してコマンドラインに配置するのではなく、makefileにを含むヘッダーファイルを生成させることを好みます。依存関係の問題を排除します。また、なぜ二重アンダースコアなのですか?技術的には、これは予約済みの識別子です。
ダンモールディング2013年

8
それぞれ独自に-私が言うように、利点は、可動部品が少なく、理解できることです。アンダースコアを削除するように編集しました。
ndyer 2013年

gengetoptを使用する場合は、これをMakefileのgengetoptに直接追加できることを追加する必要があります。gengetopt--set-version= $(GIT_VERSION)
Trygve

1
最初のステートメントは引用符で囲む必要があり、引用符GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"なしでは機能しません。
アベルトム・

11

@Kinopikoの答えに非常によく似たものを使用することになりましたが、perlの代わりにawkを使用しました。これは、mingwの性質上awkがインストールされているがperlではないWindowsマシンでスタックしている場合に役立ちます。仕組みは次のとおりです。

私のmakefileには、git、date、awkを呼び出してacファイルを作成する行があります。

$(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 

コードをコンパイルするたびに、awkコマンドは次のようなversion.cファイルを生成します。

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

次のような静的なversion.hファイルがあります。

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

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


#endif /* VERSION_H_ */

コードの残りの部分は、version.hヘッダーを含めるだけで、ビルド時とgitハッシュにアクセスできるようになりました。すべてをまとめるために、.gitignoreファイルに行を追加してversion.cを無視するようにgitに指示します。このように、gitは常にマージの競合を引き起こしているわけではありません。お役に立てれば!


補遺...これは、MATLABで動作します:mathworks.com/matlabcentral/fileexchange/32864-get-git-info
AndyL

1
FORCEmakefileは決して満足されないので(新しいヘッダーを作成するたびに)、それは良い考えではないと思います。代わりに、数式で関連するgitファイルに依存関係を追加することができます $(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD 。ファイルCOMMIT_EDITMSGはコミットHEADするたびに変更され、履歴を参照するたびに変更されるため、ファイルは関連するたびに更新されます。
カミルSジャロン2017

9

プログラムはgit describe、実行時またはビルドプロセスの一部として、にシェルアウトできます。


4
From git help describe:「コミットから到達可能な最新のタグを表示する」-これは質問が求めるものではありません。しかし、私はあなたの残りの答えに同意します。正しくするために、コマンドはである必要がありますgit rev-parse HEAD
マイクマズール

5
@mikemは、git describe人間が読める形式のタグ情報も含まれているため、他のほとんどのプロジェクトで使用されています。タグを正確に使用していない場合は、最も近いタグ以降のコミット数と、省略されたリビジョンハッシュが追加されます。
bdonlan 2009年

7

あなたができることは2つあります:

  • Gitを作成して、ファイルにバージョン情報を埋め込むことができます。

    より簡単な方法は、ident 属性を使用することです。これは、(たとえば)を置くことを意味します

    *.yaml    ident
    

    .gitattributes、ファイル、および$Id$適切な場所インチ これは、ファイルの内容のSHA-1識別子(blob id)に自動的に展開されます。これはファイルバージョンでも、最後のコミットでもありません。

    Gitはこの方法で$ Id $キーワードをサポートし、ブランチの切り替えやブランチの巻き戻しなどで変更されなかったファイルに触れないようにします。Gitにコミット(バージョン)識別子または説明をファイルに入れたい場合は、(乱用)使用できます。filter属性。clean/ smudgeフィルターを使用して、チェックアウト時にキーワード($ Revision $など)を展開し、コミットのためにクリーンアップします。

  • LinuxカーネルやGit自体が行うように、ビルドプロセス作成してそれを行うことができます。

    見てみましょうGIT-VERSION-GENスクリプトとのGitでの使用Makefileを、あるいは、例えばどのようにこのMakefileはの世代/設定時にバージョン情報を埋め込みますgitweb/gitweb.cgiファイルを。

    GIT-VERSION-GENは、gitdescribeを使用してバージョンの説明を生成します。プロジェクトのリリース/マイルストーンに(署名付き/注釈付きタグを使用して)タグを付けると、より適切に機能する必要があります。


4

これを行う必要がある場合は、のようなタグを使用しRELEASE_1_23ます。SHA-1を知らなくても、タグが何であるかを決めることができます。コミットしてからタグを付けます。とにかく、そのタグをプログラムに保存できます。


4

njd27の回答に基づいて、依存関係スキャンを備えたバージョンを、コードが別の方法でビルドされた場合のデフォルト値を備えたversion.hファイルと組み合わせて使用​​しています。version.hを含むすべてのファイルが再構築されます。

また、個別の定義として改訂日も含まれています。

# Get git commit version and date
GIT_VERSION := $(shell git --no-pager describe --tags --always --dirty)
GIT_DATE := $(firstword $(shell git --no-pager show --date=short --format="%ad" --name-only))

# recompile version.h dependants when GIT_VERSION changes, uses temporary file version~
.PHONY: force
version~: force
    @echo '$(GIT_VERSION) $(GIT_DATE)' | cmp -s - $@ || echo '$(GIT_VERSION) $(GIT_DATE)' > $@
version.h: version~
    @touch $@
    @echo Git version $(GIT_VERSION) $(GIT_DATE)

1
version.hがそれらを使用できるように、CFLAGSを介してGIT_VERSIONとGIT_DATEが渡されていると思います。涼しい!
ジェシーチザム

2

また、gitを使用して科学コードの変更を追跡しています。コードの移植性が制限されるため、外部プログラムを使用したくありませんでした(たとえば、誰かがMSVSで変更を加えたい場合)。

私の解決策は、計算にメインブランチのみを使用し、プリプロセッサマクロ__DATE__とを使用してビルド時間を出力するようにすること__TIME__でした。そうすれば、git logで確認して、使用しているバージョンを確認できます。参照:http//gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

この問題を解決するもう1つの洗練された方法は、実行可能ファイルにgitlogを含めることです。gitログからオブジェクトファイルを作成し、コードに含めます。今回使用する外部プログラムはobjcopyだけですが、コーディングは少なくなります。参照:http//www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967およびC ++プログラムにデータ埋め込む


1
プリプロセッサマクロの使用は非常に賢いです!ありがとうございました。
AndyL 2011年

4
しかし、古いバージョンをチェックアウトしてからコンパイルすると、間違ったコミットに誘導されます。
セバスチャンマッハ

2

あなたがする必要があるのは、次のようなヘッダーファイルを生成することです(例えば、コマンドラインからのエコーを使用して):

#define GIT_HASH \
"098709a0b098c098d0e"

それを生成するには、次のようなものを使用します。

echo #define GIT_HASH \ > file.h
echo " > file.h
echo git status <whatever cmd to get the hash> > file.h
echo " > file.h

コンパイルするには、引用符と円記号を少し操作する必要があるかもしれませんが、アイデアは得られます。


不思議に思うのですが、彼がそれを実行してfile.hを変更し、その変更をソースにコミットするたびに、gitハッシュが変更されるのではないでしょうか。
ホルヘイスラエルペーニャ

@Blaenk ..それも私が考えていたものです。しかし、実行時にプログラムに要求させるというbdonlanのアイデアは、この問題を回避しているようです。
AndyL 2009年

6
このファイルは.gitignoreの下にあり、プロジェクトをビルドするたびに生成される必要があります。
Igor Zevaka

または、このファイルの基本バージョンを含めて--assume-unchangedフラグを設定することもできます(git update-index --assume-unchanged
Igor Zevaka

1

Makefileとシェルに基づくさらに別のバリエーション

GIT_COMMIT_FILE=git_commit_filename.h

$(GIT_COMMIT_FILE): phony
    $(eval GIT_COMMIT_SHA=$(shell git describe --abbrev=6 --always 2>/dev/null || echo 'Error'))
    @echo SHA=$(GIT_COMMIT_SHA)
    echo -n "static const char *GIT_COMMIT_SHA = \"$(GIT_COMMIT_SHA)\";" > $(GIT_COMMIT_FILE)

ファイルgit_commit_filename.hは、static const char * GIT_COMMIT_SHA = "";を含む1行になります。

https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406から


1

これは、他のプログラム(スクリプト言語など)をインストールしなくても、WindowsおよびLinuxで機能するCMakeプロジェクトのソリューションです。

gitハッシュはスクリプトによって.hファイルに書き込まれます。スクリプトはLinuxでコンパイルする場合はbashスクリプト、Windowsでコンパイルする場合はWindowsバッチスクリプトであり、CMakeLists.txtのif句はプラットフォームに対応するスクリプトを選択します。コードはコンパイルされます。

次の2つのスクリプトは、CMakeLists.txtと同じディレクトリに保存されます。

get_git_hash.sh:

#!/bin/bash
hash=$(git describe --dirty --always --tags)
echo "#ifndef GITHASH_H" > include/my_project/githash.h
echo "#define GITHASH_H" >> include/my_project/githash.h
echo "const std::string kGitHash = \"$hash\";" >> include/my_project/githash.h
echo "#endif // GITHASH_H" >> include/my_project/githash.h

get_git_hash.cmd:

@echo off
FOR /F "tokens=* USEBACKQ" %%F IN (`git describe --dirty --always --tags`) DO (
SET var=%%F
)
ECHO #ifndef GITHASH_H > include/my_project/githash.h
ECHO #define GITHASH_H >> include/my_project/githash.h
ECHO const std::string kGitHash = "%var%"; >> include/my_project/githash.h
ECHO #endif // GITHASH_H >> include/my_project/githash.h

CMakeLists.txtに、次の行が追加されます

if(WIN32)
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND get_git_hash.cmd
  )
else()
  add_custom_target(
    run ALL
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
    COMMAND ./get_git_hash.sh
  )
endif()

include_directories(include)

コードでは、生成されたファイルがに含まれて#include <my_project/githash.h>おり、gitハッシュは、を使用してターミナルに出力するstd::cout << "Software version: " << kGitHash << std::endl;か、同様の方法でyaml(または任意の)ファイルに書き込むことができます。


0

元のコミットでmemcachedに対してどのように実行したかを確認できます。

基本的に、時々タグを付けて、あなたが配達するものがmake distまたは類似のものであることを確認してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.