Как я могу получить свой код C для автоматической распечатки хеш-версии Git? - PullRequest
74 голосов
/ 10 ноября 2009

Есть ли простой способ написания кода на C, который может получить доступ к хэш-версии Git?

Я написал программное обеспечение на C для сбора научных данных в лабораторных условиях. Мой код записывает данные, которые он собирает, в файл .yaml для последующего анализа. Мои эксперименты меняются изо дня в день, и мне часто приходится модифицировать код. Чтобы отслеживать ревизии, я использую git-репозиторий.

Я бы хотел включить хэш редакции Git в качестве комментария в мои файлы данных .yaml. Таким образом, я мог посмотреть на файл .yaml и точно знать, какой код использовался для генерации данных, показанных в этом файле. Есть ли простой способ сделать это автоматически?

Ответы [ 11 ]

144 голосов
/ 11 сентября 2012

Если вы используете сборку на основе 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 commit, начинающимся с "766d".

Если в вашем дереве есть незафиксированные изменения, к нему добавится "-dirty".

Нет сканирования зависимостей, поэтому вы должны сделать явное make clean, чтобы принудительно обновить версию. Это может быть решено однако.

Преимущества в том, что он прост и не требует дополнительных сборочных зависимостей, таких как perl или awk. Я использовал этот подход с GNU automake и со сборками Android NDK.

36 голосов
/ 10 ноября 2009

В моей программе я храню номер версии 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, поэтому буду рад комментариям, если есть лучший способ сделать это.

11 голосов
/ 04 декабря 2009

В итоге я использовал нечто очень похожее на ответ @ Kinopiko, но вместо perl я использовал awk. Это полезно, если вы застряли на Windows-машинах, на которых установлен awk по своей природе, но не на perl. Вот как это работает.

В моем make-файле есть строка, которая вызывает 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_ */

Остальная часть моего кода теперь может получить доступ к времени сборки и хешу git, просто включив заголовок version.h. Чтобы завершить все это, я говорю git игнорировать version.c, добавив строку в мой файл .gitignore. Таким образом, git не дает мне постоянных конфликтов слияния. Надеюсь, это поможет!

9 голосов
/ 10 ноября 2009

Ваша программа может выложить на git describe либо во время выполнения, либо как часть процесса сборки.

7 голосов
/ 10 ноября 2009

Есть две вещи, которые вы можете сделать:

  • Вы можете сделать Git , чтобы вставить некоторую информацию о версии в файл для вас.

    Более простой способ - использовать атрибут ident , что означает (например)

    *.yaml    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 description для генерации описания версии. Он должен работать лучше, если вы помечаете (используя подписанные / аннотированные теги) выпуски / вехи вашего проекта.

4 голосов
/ 10 ноября 2009

Когда мне нужно это сделать, я использую тег , например RELEASE_1_23. Я могу решить, что это за метка, не зная SHA-1. Я фиксирую затем тег. Вы можете сохранить этот тег в своей программе так, как вам нравится.

3 голосов
/ 08 января 2016

Исходя из ответа 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)
2 голосов
/ 17 января 2011

Я также использую git для отслеживания изменений в моем научном коде. я не хотел использовать внешнюю программу, потому что она ограничивает переносимость кода (например, если кто-то захочет внести изменения в MSVS).

Мое решение состояло в том, чтобы использовать только основную ветвь для расчетов и заставить ее выводить время сборки, используя макросы препроцессора __DATE__ и __TIME__. таким образом я могу проверить это с помощью git log и посмотреть, какую версию я использую. ref: 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 ++

2 голосов
/ 10 ноября 2009

Что вам нужно сделать, это сгенерировать файл заголовка (например, используя 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

Возможно, нужно немного поиграть с кавычками и обратной косой чертой, чтобы он скомпилировался, но вы поняли идею.

1 голос
/ 29 июня 2017

Еще один вариант, основанный на 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 будет содержать одну строку, содержащую статический константный символ * GIT_COMMIT_SHA = "";

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

...