Как я могу заставить свой C-код автоматически распечатывать хеш версии Git?
Есть ли простой способ написать C-код, который может получить доступ к хеш-версии версии Git?
Я написал программное обеспечение на C для сбора научных данных в лабораторных условиях. Мой код записывает данные, которые он собирает в файле .yaml для последующего анализа. Мои эксперименты меняются изо дня в день, и мне часто приходится изменять код. Чтобы отслеживать изменения, я использую репозиторий Git.
Я хотел бы иметь возможность включать хэш-версию Git в качестве комментария в мои файлы данных .yaml. Таким образом, я мог бы посмотреть файл .yaml и точно знать, какой код использовался для генерации данных, отображаемых в этом файле. Есть ли простой способ сделать это автоматически?
Ответы
Ответ 1
В моей программе я держу номер версии 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 script, который выглядит следующим образом:
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
Если вы используете сборку на основе 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, но при желании вы можете пометить определенные выпуски чем-то вроде:
git tag -a v1.1 -m "Release v1.1"
тогда он распечатает:
Version: v1.1-2-g766d
что означает, 2 коммитов после v1.1, с идентификатором git commit, начинающимся с "766d".
Если в вашем дереве есть незафиксированные изменения, к нему добавится "-dirty".
Нет сканирования зависимостей, поэтому вы должны сделать явный make clean
, чтобы принудительно обновить версию. Однако это можно решить.
Преимущества в том, что он прост и не требует никаких дополнительных сборочных зависимостей, таких как perl или awk. Я использовал этот подход с GNU automake и со сборками Android NDK.
Ответ 3
В итоге я использовал что-то очень похожее на @Kinopiko, но я использовал awk вместо perl. Это полезно, если вы застряли на машинах с Windows, у которых awk установлен по характеру mingw, но не perl. Вот как это работает.
В моем файле makefile есть строка, которая вызывает git, дату и 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_ */
Остальная часть моего кода теперь может получить доступ к времени сборки и хешу git, просто включив заголовок version.h. Чтобы обернуть все это, я скажу git игнорировать version.c, добавив строку в мой файл .gitignore. Таким образом, git не постоянно дает мне слияние конфликтов. Надеюсь, это поможет!
Ответ 4
Ваша программа может отключиться до git describe
, либо во время выполнения, либо как часть процесса сборки.
Ответ 5
Есть две вещи, которые вы можете сделать:
-
Вы можете сделать Git, чтобы вставить в файл некоторую информацию о версии в файле.
Более простой способ - использовать ident
attribute, что означает помещение (например)
*.yaml ident
в файле .gitattributes
и $Id$
в соответствующем месте. Он будет автоматически расшифрован до идентификатора SHA-1 содержимого файла (blob id): это НЕ версия файла или последняя фиксация.
Git поддерживает ключевое слово $Id $таким образом, чтобы избежать касания файлов, которые не были изменены во время переключения ветвления, перематывания ветки и т.д. Если вы действительно хотите, чтобы Git помещал в файл идентификатор фиксации (версии) или описание, вы можете (ab) использовать атрибут filter
, используя фильтр clean/smudge, чтобы развернуть ключевое слово (например, $Revision $) при проверке и очистить его для фиксации.
-
Вы можете сделать процесс сборки для этого, как это делает ядро Linux или Git.
Взгляните на GIT-VERSION-GEN script и его использование в Git Makefile или, например, как этот Makefile включает информацию о версии во время генерации/конфигурации файла gitweb/gitweb.cgi
.
GIT -VERSION-GEN использует Git описать, чтобы сгенерировать описание версии. Он должен работать лучше, чем вы отмечаете (используя подписанные/аннотированные теги) релизы/этапы вашего проекта.
Ответ 6
Когда мне нужно это сделать, я использую tag, например RELEASE_1_23
. Я могу решить, что такое тег, не зная SHA-1. Затем я фиксирую тег. Вы можете сохранить этот тег в своей программе в любом случае, который вам нравится.
Ответ 7
Основываясь на ответе 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 - [email protected] || echo '$(GIT_VERSION) $(GIT_DATE)' > [email protected]
version.h: version~
@touch [email protected]
@echo Git version $(GIT_VERSION) $(GIT_DATE)
Ответ 8
Что вам нужно сделать, так это создать заголовочный файл (например, используя эхо из строки cmd) примерно так:
#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
Возможно, вам нужно немного сыграть с котировками и обратными косыми чертами, чтобы получить их для компиляции, но вы получите эту идею.
Ответ 9
Я также использую git для отслеживания изменений в моем научном коде. я не хотел использовать внешнюю программу, потому что она ограничивает переносимость кода (если кто-то захочет внести изменения в MSVS, например).
Мое решение состояло в том, чтобы использовать только основную ветвь для вычислений и сделать вывод времени сборки с использованием макросов препроцессора __DATE__
и __TIME__
. таким образом я могу проверить его с журналом git и посмотреть, какую версию я использую. ref: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
еще один элегантный способ решить эту проблему - включить git в исполняемый файл.
сделать объектный файл из журнала git и включить его в код. на этот раз единственная внешняя программа, которую вы используете, является objcopy, но есть меньше кодирования. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 и Вставить данные в программу на С++
Ответ 10
Еще одна вариация, основанная на 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 = "";
От https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406
Ответ 11
Вы можете увидеть, как я сделал это для memcached в исходный коммит.
В принципе, иногда указывайте, и убедитесь, что вещь, которую вы доставляете, поступает от make dist
или аналогичного.
Ответ 12
Это решение для проекта CMake, которое работает для Windows и Linux без необходимости установки каких-либо других программ (например, языков сценариев).
Хэш git записывается в файл .h скриптом, который является скриптом bash при компиляции в Linux или пакетным скриптом Windows при компиляции в Windows, а условие if в CMakeLists.txt выбирает скрипт, соответствующий платформе код компилируется на.
Следующие 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 (или любой другой).