我怎样才能让我的C代码自动打印出其Git版本哈希?

有没有简单的方法来编写可以访问其Git版本哈希的C代码?

我用C编写软件来收集实验室的科学数据。 我的代码logging了它收集在.yaml文件中的数据,以供以后分析。 我的实验每天都在变化,我经常需要修改代码。 为了跟踪修订,我使用了一个git仓库。

我希望能够在我的.yaml数据文件中包含Git修订哈希作为注释。 这样,我可以查看.yaml文件,并确切知道用​​什么代码生成该文件中显示的数据。 有没有一种简单的方法来自动做到这一点?

在我的程序中,我将git的版本号和构builddate保存在一个名为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完成了创buildversion.cversion.h所有工作,而make_date_time则如图所示make_date_time了一个string。

在主程序中,我有一个例程

 #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不是很了解,所以如果有更好的方法来做这个,我会很乐意的。

如果您正在使用基于make的版本,则可以将其放在Makefile中:

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

(请参阅man git描述交换机的function)

然后将其添加到您的CFLAGS:

 -DVERSION=\"$(GIT_VERSION)\" 

然后,您可以直接在程序中引用版本,就好像它是#define:

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

默认情况下,这只是打印一个缩写的git commit id,但是可以select使用类似的方式标记特定的版本:

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

那么它会打印出来:

 Version: v1.1-2-g766d 

这意味着,2提交v1.1,以“766d”开头的git commit id。

如果树中有未提交的更改,则会附加“-dirty”。

没有依赖扫描,所以你必须做一个明确的make clean来强制版本被更新。 这可以解决 。

好处是它很简单,不需要额外的构build依赖,比如perl或者awk。 我已经使用这种方法与GNU automake和Android NDK构build。

你的程序可以在运行时或者作为构build过程的一部分进行git describe

我最终使用了类似@ Kinopiko的答案,但我用awk而不是perl。 这是有用的,如果你坚持在窗户机器上安装awk的本质mingw,而不是perl。 这是如何工作的。

我的makefile中有一行代码调用git,date和awk来创buildac文件:

 $(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头文件来访问构build时间和git哈希。 为了包装它,我告诉git通过添加一行到我的.gitignore文件来忽略version.c。 这样,git并不总是给我合并冲突。 希望这可以帮助!

有两件事你可以做:

  • 你可以让Git在你的文件中embedded一些版本信息。

    更简单的方法是使用ident 属性 ,这意味着把(例如)

     *.yaml ident 

    .gitattributes文件中,并在适当的地方添加$Id$ 。 它会自动扩展到文件内容的 SHA-1标识符(blob id):这不是文件版本,或者是最后一次提交。

    Git支持$ Id $关键字,以避免触及在分支切换,倒回分支等过程中未被更改的文件。如果您确实希望Git将提交(版本)标识符或描述放在文件中,您可以(ab)使用filter属性,使用干净/涂抹filter来扩大一些关键字(例如$ Revision $)在结帐,并清理它提交。

  • 你可以让构build过程为你做,如Linux内核或Git本身。

    看一下GIT-VERSION-GEN脚本及其在Git Makefile中的使用,或者例如这个Makefile如何在gitweb/gitweb.cgi文件的生成/configuration过程中embedded版本信息。

    GIT-VERSION-GEN使用git describe来生成版本描述。 它需要更好地工作,以便标记(使用签名/注释标签)发布/项目里程碑。

当我需要这样做时,我使用一个标签 ,如RELEASE_1_23 。 我可以在不知道SHA-1的情况下决定标签的内容。 我承诺然后标记。 无论如何,您可以将该标记存储在程序中。

根据njd27的回答,我正在使用依赖扫描的版本,以及具有默认值的version.h文件,以便以不同的方式构build代码。 所有包含version.h的文件都将被重build。

它还包括修订date作为单独的定义。

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

我也用git来跟踪我的科学代码的变化。 我不想使用外部程序,因为它限制了代码的可移植性(例如,如果有人想对MSVS进行更改)。

我的解决scheme是只使用主分支进行计算,并使用预处理器macros__DATE____TIME__输出构build时间。 这样我可以检查它与git日志,看看我使用的是哪个版本。 ref: http : //gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

解决问题的另一个优雅方法是将gitlogin到可执行文件中。 使用git log创build一个目标文件并将其包含到代码中。 这次你唯一使用的外部程序是objcopy,但是编码较less。 ref: http : //www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967并将数据embedded到C ++程序中

你需要做的是生成一个头文件(例如使用cmd行的echo),如下所示:

 #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 

可能需要使用引号和反斜杠来进行编译,但是你明白了。

你可以看到我是如何做到原始提交中的 memcached 的 。

基本上,偶尔标签,并确保您提供的东西来自make dist或类似。

又一个基于Makefile和shell的变体

 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将以单行包含静态常量char * GIT_COMMIT_SHA =“”;

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