Есть ли простой способ написать код C, который может получить доступ к его хеш-версии Git?
Я написал программное обеспечение на языке C для сбора научных данных в лабораторных условиях. Мой код записывает собранные данные в файл .yaml для последующего анализа. Мои эксперименты меняются изо дня в день, и мне часто приходится изменять код. Чтобы отслеживать изменения, я использую репозиторий git.
Я хотел бы иметь возможность включать хэш версии Git в качестве комментария в мои файлы данных .yaml. Таким образом, я мог посмотреть на файл .yaml и точно знать, какой код использовался для генерации данных, показанных в этом файле. Есть ли простой способ сделать это автоматически?
c
git
version-control
workflow
ЭндиЛ
источник
источник
Ответы:
В моей программе я храню номер версии 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, поэтому буду приветствовать комментарии, если есть лучший способ сделать это.
источник
const char []
: 319356 байт (без вырезок). Размер моей программыconst char *
: 319324 байта (без вырезок). Итак, ваша идея, похоже, не сохраняет никаких байтов, а увеличивает общее количество на 32. Я не знаю почему. В исходном файле "version.c" есть три строки, но одна из приведенных выше ответов не указана. Если вы посмотрите на первую правку, она все еще там.Если вы используете сборку на основе 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, но при желании вы можете пометить определенные выпуски чем-то вроде:
git tag -a v1.1 -m "Release v1.1"
тогда он распечатает:
Version: v1.1-2-g766d
это означает, что 2 фиксируется после v1.1 с идентификатором фиксации git, начинающимся с "766d".
Если в вашем дереве есть незафиксированные изменения, к нему будет добавлен «-dirty».
Сканирования зависимостей нет, поэтому вам нужно явно
make clean
выполнить обновление версии. Этот можно решить .Преимущества в том, что он прост и не требует дополнительных зависимостей сборки, таких как perl или awk. Я использовал этот подход с GNU automake и со сборками Android NDK.
источник
#define GIT_VERSION ...
а не помещал его в командную строку с-D
опцией; это устраняет проблему зависимости. Кроме того, почему двойное подчеркивание? Технически это зарезервированный идентификатор.GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
, без кавычек не работает.В итоге я использовал что-то очень похожее на ответ @Kinopiko, но я использовал awk вместо perl. Это полезно, если вы застряли на машинах с Windows, на которых awk установлен по природе mingw, но не perl. Вот как это работает.
В моем make-файле есть строка, которая вызывает 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_ */
Остальная часть моего кода теперь может получить доступ к времени сборки и хешу git, просто включив заголовок version.h. Подводя итог, я говорю git игнорировать version.c, добавляя строку в мой файл .gitignore. Таким образом, git не вызывает у меня конфликтов слияния постоянно. Надеюсь это поможет!
источник
FORCE
это хорошая идея, поскольку make-файл никогда не будет удовлетворен (каждый раз, когда вы создаете новый заголовок). Вместо этого вы можете просто добавить зависимость для соответствующих файлов git в формулу$(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD
. ФайлCOMMIT_EDITMSG
изменяется каждый раз, когда вы делаете коммит, иHEAD
изменяется каждый раз, когда вы просматриваете историю, поэтому ваш файл обновляется каждый раз, когда это необходимо.Ваша программа может выполнять оболочку
git describe
во время выполнения или как часть процесса сборки.источник
git help describe
: «Показать самый последний тег, доступный после фиксации» - это не то, о чем спрашивает вопрос. Однако я согласен с остальной частью вашего ответа. Для верности команда должна бытьgit rev-parse HEAD
.git describe
- это то , что используют большинство других проектов, потому что он также включает в себя удобочитаемую информацию тегов. Если вы не совсем на теге, он добавляет количество коммитов с момента ближайшего тега и сокращенный хэш редакции.Вы можете сделать две вещи:
Вы можете сделать Git встроить некоторую информацию о версии в файл за вас.
Более простой способ - использовать
ident
атрибут , что означает установку (например)в
.gitattributes
файле и$Id$
в соответствующем месте. Он будет автоматически расширен до идентификатора SHA-1 содержимого файла (идентификатор blob): это НЕ версия файла или последняя фиксация.Git поддерживает ключевое слово $ Id $ таким образом, чтобы не касаться файлов, которые не были изменены во время переключения ветвей, перемотки ветки и т. Д. Если вы действительно хотите, чтобы Git поместил идентификатор или описание фиксации (версии) в файл, вы можете (ab) использовать
filter
атрибут, используя фильтр clean / smudge, чтобы раскрыть некоторые ключевые слова (например, $ Revision $) при оформлении заказа и очистить их для фиксации.Вы можете заставить процесс сборки делать это за вас, как это делает ядро Linux или сам Git.
Взгляните на сценарий GIT-VERSION-GEN и его использование в Git Makefile или, например, как этот Makefile встраивает информацию о версии во время генерации / конфигурации
gitweb/gitweb.cgi
файла.GIT-VERSION-GEN использует git describe для создания описания версии. Он должен работать лучше, если вы помечаете (используя подписанные / аннотированные теги) выпуски / вехи вашего проекта.
источник
Когда мне нужно это сделать, я использую тег , например
RELEASE_1_23
. Я могу решить, что это за тег, не зная SHA-1. Я фиксирую и помечаю. Вы можете сохранить этот тег в своей программе в любом случае.источник
Основываясь на ответе 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)
источник
Я также использую git для отслеживания изменений в моем научном коде. Я не хотел использовать внешнюю программу, потому что она ограничивает переносимость кода (например, если кто-то захочет внести изменения в MSVS).
Мое решение заключалось в том, чтобы использовать только основную ветвь для вычислений и выводить время сборки с помощью макросов препроцессора.
__DATE__
и__TIME__
. таким образом я могу проверить это с помощью журнала git и узнать, какую версию я использую. ссылка: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.htmlеще один элегантный способ решить проблему - включить git log в исполняемый файл. сделать объектный файл из журнала git и включить его в код. на этот раз вы используете только внешнюю программу objcopy, но в ней меньше кода. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 и Вставить данные в программу на C ++
источник
Что вам нужно сделать, так это сгенерировать файл заголовка (например, используя echo из строки 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
Возможно, придется немного поиграть с кавычками и обратной косой чертой, чтобы заставить его скомпилировать, но вы поняли идею.
источник
--assume-unchanged
для нее флаг (git update-index --assume-unchanged
)Это решение для проекта 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 (или любой другой) аналогичным образом.источник
Еще одна вариация на основе 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 = "";
С https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406
источник
Вы можете увидеть, как я это сделал для memcached в исходной фиксации .
В основном, время от времени помечайте теги и убедитесь, что то, что вы доставляете,
make dist
или что-то подобное.источник