有没有一种简单的方法来编写可以访问其Git版本哈希的C代码?
我在C中编写软件以在实验室环境中收集科学数据。我的代码将它收集的数据记录在.yaml文件中以供以后分析。我的实验每天都在变化,我经常需要修改代码。为了跟踪修订,我使用了一个git存储库。
我希望能够在我的.yaml数据文件中包含Git版本哈希作为注释。这样,我可以查看.yaml文件并确切地知道用于生成该文件中显示的数据的代码。有一种简单的方法可以自动执行此操作吗?
答案 0 :(得分:143)
如果您使用的是基于make的构建,可以将其放在Makefile:
中GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
(参见man git describe了解开关的用途)
然后将其添加到您的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 commit id以“766d”开头。
如果树中存在未提交的更改,则会附加“-dirty”。
没有依赖项扫描,因此您必须执行显式make clean
以强制更新版本。但是can be solved。
优点是它很简单,不需要任何额外的构建依赖,如perl或awk。我已经在GNU automake和Android NDK版本中使用了这种方法。
答案 1 :(得分:36)
在我的程序中,我将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知之甚少,所以如果有更好的方法,我会欢迎评论。
答案 2 :(得分:11)
我最终使用的东西与@ Kinopiko的答案非常相似,但我使用的是awk而不是perl。如果你坚持使用mingw性质而不是perl安装了awk的windows机器,这是很有用的。这是它的工作原理。
我的makefile中有一行调用git,date和awk来创建一个c文件:
$(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哈希。为了将它全部包装起来,我告诉git通过在我的.gitignore文件中添加一行来忽略version.c.这样git不会经常给我合并冲突。希望这有帮助!
答案 3 :(得分:9)
您的程序可以在运行时或作为构建过程的一部分发送到git describe
。
答案 4 :(得分:7)
您可以做两件事:
您可以让 Git 为您嵌入一些版本信息。
更简单的方法是使用ident
attribute,这意味着放置(例如)
*.yaml ident
在.gitattributes
文件中,$Id$
在适当的位置。它会自动扩展为文件 (blob id)的SHA-1标识符 :这不是文件版本,也不是最后一次提交。
Git以这种方式支持$ Id $关键字,以避免触摸在分支切换,倒带分支等期间未更改的文件。如果您真的希望Git将提交(版本)标识符或描述放在文件中,您可以( ab)使用filter
属性,使用clean / smudge过滤器在结帐时展开一些关键字(例如$ Revision $),并清理它以进行提交。
你可以让构建过程为你做这件事,比如Linux内核或Git本身。
查看GIT-VERSION-GEN脚本及其在Git Makefile中的用法,或者例如,在生成/配置gitweb/gitweb.cgi
文件期间,此Makefile如何嵌入版本信息。
GIT-VERSION-GEN使用 git describe 来生成版本说明。它需要更好地标记(使用签名/注释标签)项目的发布/里程碑。
答案 5 :(得分:4)
当我需要这样做时,我会使用tag,例如RELEASE_1_23
。我可以在不知道SHA-1的情况下决定标签是什么。然后我提交标签。无论如何,您可以将该标签存储在您的程序中。
答案 6 :(得分:3)
根据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)
答案 7 :(得分:2)
你需要做的是生成一个头文件(例如使用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
可能需要使用引号和反斜杠来进行编译,但是你明白了。
答案 8 :(得分:2)
我还使用git来跟踪科学代码中的变化。我不想使用外部程序,因为它限制了代码的可移植性(例如,如果有人想要在MSVS上进行更改)。
我的解决方案是仅使用主分支进行计算,并使用预处理器宏__DATE__
和__TIME__
输出构建时间。这样我就可以用git log检查它,看看我正在使用哪个版本。参考:http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
另一种解决问题的优雅方法是将git log包含在可执行文件中。 用git日志创建一个目标文件并将其包含在代码中。这次你使用的唯一外部程序是objcopy,但编码较少。参考:http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967和Embed data in a C++ program
答案 9 :(得分:1)
另一种基于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最终将包含一行 static const char * GIT_COMMIT_SHA =&#34;&#34 ;;
来自https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406
答案 10 :(得分:0)
你可以在the original commit中看到我是如何为memcached做的。
基本上,偶尔标记,并确保您提供的内容来自make dist
或类似内容。
答案 11 :(得分:0)
这是适用于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 hash可以使用std::cout << "Software version: " << kGitHash << std::endl;
打印到终端,或者以类似的方式写入yaml(或任何)文件。 / p>