Git 버전 해시를 자동으로 인쇄하도록 C 코드를 얻으려면 어떻게해야합니까?


84

Git 버전 해시에 액세스 할 수있는 C 코드를 작성하는 쉬운 방법이 있습니까?

실험실 환경에서 과학 데이터를 수집하기 위해 C로 소프트웨어를 작성했습니다. 내 코드는 수집 한 데이터를 나중에 분석하기 위해 .yaml 파일에 기록합니다. 내 실험은 매일 바뀌고 종종 코드를 수정해야합니다. 개정을 추적하기 위해 git 저장소를 사용합니다.

내 .yaml 데이터 파일에 주석으로 Git 개정 해시를 포함하고 싶습니다. 이렇게하면 .yaml 파일을보고 해당 파일에 표시된 데이터를 생성하는 데 사용 된 코드를 정확히 알 수 있습니다. 이 작업을 자동으로 수행하는 쉬운 방법이 있습니까?


1
사전 커밋 후크 ( book.git-scm.com/5_git_hooks.html 참조)를 사용하는 것도이 작업을 수행하는 또 다른 방법입니다.
— Yktula 2010

답변:


39

내 프로그램에서 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에 대해 잘 알지 못하기 때문에 더 나은 방법이 있다면 의견을 환영합니다.


1
Perl 스크립트는 모든 것을위한 "1 단계 빌드"인 빌드 스크립트의 일부입니다.

12
이것이 진행되는 한 좋지만 컴파일되는 코드의 해시가 아니라 분기에 대한 최신 커밋의 해시를보고한다는 점을 명심하십시오. 커밋되지 않은 변경 사항이있는 경우에는 분명하지 않습니다.
— Phil Miller

1
git diff는 기본적으로 작업 공간과 색인 간의 차이점을 확인합니다. 인덱스와 HEAD 간의 차이점에 대해 git diff --cached를 시도해 볼 수도 있습니다.
— Karl

6
모든 'const char * name = "value";' 구문은 'const char name [] = "value";'로 현명하게 변경 될 수 있습니다. 이는 32 비트 컴퓨터에서는 항목 당 4 바이트, 64 비트 컴퓨터에서는 항목 당 8 바이트를 절약합니다. 물론, 요즘 GB의 주 메모리에서는 큰 문제는 아니지만 모두 도움이됩니다. 이름을 사용하는 코드는 변경할 필요가 없습니다.
— Jonathan Leffler

1
나는 당신이 제안한대로 그들을 변경했습니다. 내 프로그램의 크기 const char []: 319356 바이트 (제거됨). 내 프로그램의 크기 const char *: 319324 바이트 (제거됨). 그래서 당신의 아이디어는 어떤 바이트도 절약하지 않는 것 같지만 총 수를 32만큼 늘립니다. 이유를 모르겠습니다. 원래 "version.c"에는 세 개의 문자열이 있지만 위의 답변에서 하나가 생략되었습니다. 첫 번째 편집을 보면 여전히 거기에 있습니다.

163

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

즉, "766d"로 시작하는 git commit id로 v1.1 이전에 2 번 커밋됩니다.

트리에 커밋되지 않은 변경 사항이 있으면 "-dirty"를 추가합니다.

종속성 검사가 없으므로 make clean버전을 강제로 업데이트 하려면 명시 적으로 수행해야합니다 . 그러나 이것은 해결할 수 있습니다 .

장점은 간단하고 perl 또는 awk와 같은 추가 빌드 종속성이 필요하지 않다는 것입니다. 이 접근 방식을 GNU automake 및 Android NDK 빌드와 함께 사용했습니다.


6
+1 개인적으로, 저는 makefile #define GIT_VERSION ...이 -D옵션 을 사용하여 명령 줄에 넣는 대신 포함하는 헤더 파일을 생성하도록하는 것을 선호합니다 . 종속성 문제를 제거합니다. 또한 이중 밑줄은 왜? 기술적으로는 예약 된 식별자입니다.
— Dan Molding

8
각각은 그 자체로-내가 말했듯이 장점은 움직이는 부품이 적고 이해할 수 있다는 것입니다. 밑줄을 제거하기 위해 편집했습니다.
— ndyer 2013 년

추가해야합니다. gengetopt를 사용하는 경우 Makefile의 gengetopt에 직접 추가 할 수 있습니다. gengetopt --set-version = $ (GIT_VERSION)
— Trygve

1
첫 번째 문은 따옴표로 묶어야하며 따옴표 GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"없이는 작동하지 않습니다.
— Abel Tom

11

@Kinopiko의 답변과 매우 유사한 것을 사용했지만 perl 대신 awk를 사용했습니다. 이것은 mingw의 특성상 awk가 설치되어 있지만 perl이 아닌 Windows 시스템에 붙어있는 경우 유용합니다. 작동 방식은 다음과 같습니다.

내 makefile에는 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_ */

이제 나머지 코드는 단순히 version.h 헤더를 포함하여 빌드 시간과 git 해시에 액세스 할 수 있습니다. 모든 것을 마무리하기 위해 .gitignore 파일에 한 줄을 추가하여 git에게 version.c를 무시하도록 지시합니다. 이런 식으로 git은 지속적으로 병합 충돌을 일으키지 않습니다. 도움이 되었기를 바랍니다!


부록 ...이 matlab에에서 작동합니다 : mathworks.com/matlabcentral/fileexchange/32864-get-git-info
— AndyL

1
FORCEmakefile은 결코 만족스럽지 않기 때문에 좋은 생각 이라고 생각하지 않습니다 (새 헤더를 만들 때마다). 대신 formula에서 관련 git 파일에 종속성을 추가 할 수 있습니다 $(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD . COMMIT_EDITMSG커밋 할 때마다 파일이 변경되고 HEAD기록을 검색 할 때마다 변경되므로 관련이있을 때마다 파일이 업데이트됩니다.
— Kamil S Jaron 2017

9

프로그램은 git describe런타임에 또는 빌드 프로세스의 일부로으로 쉘 아웃 할 수 있습니다 .


4
From git help describe: "커밋에서 도달 할 수있는 가장 최근 태그 표시"-이것은 질문에서 요구하는 것이 아닙니다. 그래도 나머지 답변에 동의합니다. 정확하려면 명령이이어야합니다 git rev-parse HEAD.
— Mike Mazur

5
@mikem git describe은 사람이 읽을 수있는 태그 정보도 포함하기 때문에 대부분의 다른 프로젝트에서 사용하는 것입니다. 정확히 태그에 있지 않으면 가장 가까운 태그 이후의 커밋 수와 약식 개정 해시에 추가됩니다.
— bdonlan 2009

7

다음 두 가지를 수행 할 수 있습니다.

  • Git 이 파일에 일부 버전 정보를 포함 하도록 만들 수 있습니다 .

    간단한 방법은 사용하는 것입니다 ident 속성 (예를 들어) 퍼팅 의미,

    *.yaml    ident
    

    에서 .gitattributes파일 및 $Id$적절한 장소이다. 파일 내용의 SHA-1 식별자 (blob id) 로 자동 확장됩니다 . 이것은 파일 버전이 아니거나 마지막 커밋입니다.

    Git은 이러한 방식으로 $ Id $ 키워드를 지원하여 분기 전환, 분기 되감기 등에서 변경되지 않은 파일을 건드리지 않도록합니다. 정말로 Git이 파일에 커밋 (버전) 식별자 또는 설명을 입력하도록하려면 (남용) 사용할 수 있습니다. filter결제시 일부 키워드 (예 : $ Revision $)를 확장하기 위해 clean / smudge filter를 사용하고 커밋을 위해 정리합니다.

  • Linux 커널이나 Git 자체처럼 빌드 프로세스 를 만들 수 있습니다 .

    봐 가지고 GIT-VERSION-GEN 스크립트 및 힘내에서의 사용 메이크 파일 이 메이크가 생성 / 구성 중에 버전 정보를 내장하는 방법, 또는 예를 들어 gitweb/gitweb.cgi파일을.

    GIT-VERSION-GEN은 git describe 를 사용하여 버전 설명을 생성합니다. 프로젝트의 릴리스 / 마일스톤에 태그 (서명 / 주석 태그 사용)를 태그하는 것이 더 잘 작동해야합니다.


4

이 작업을 수행해야 할 때 같은 태그를 사용합니다 RELEASE_1_23. SHA-1을 몰라도 태그가 무엇인지 결정할 수 있습니다. 나는 다음 태그를 커밋합니다. 원하는대로 프로그램에 해당 태그를 저장할 수 있습니다.


4

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)

1
나는 당신이 GIT_VERSION과 GIT_DATE를 CFLAGS를 통해 전달했다고 가정한다. 그래서 version.h는 그것들을 사용할 수있다. 멋있는!
— Jesse Chisholm

2

또한 git을 사용하여 과학 코드의 변경 사항을 추적합니다. 코드의 이식성을 제한하기 때문에 외부 프로그램을 사용하고 싶지 않았습니다 (예를 들어 누군가가 MSVS를 변경하려는 경우).

내 솔루션은 계산에 주 분기 만 사용하고 전 처리기 매크로 __DATE__및 __TIME__. 그렇게하면 git log로 확인하고 내가 사용하는 버전을 볼 수 있습니다. 참조 : 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 ++ 프로그램에 데이터 포함


1
전 처리기 매크로의 사용은 매우 영리합니다! 감사합니다.
— AndyL

4
하지만 이전 버전을 체크 아웃 한 다음 컴파일하면 잘못된 커밋으로 안내됩니다.
— 세바스찬 마하

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

컴파일하려면 따옴표와 백 슬래시를 약간 사용해야 할 수도 있지만 아이디어를 얻을 수 있습니다.


그가 그렇게 할 때마다 file.h를 변경하고 소스에 변경 사항을 커밋 할 때마다 git 해시가 변경되지 않을까요?
— Jorge Israel Peña

@Blaenk .. 그게 내가 생각했던 것입니다. 그러나 bdonlan의 프로그램이 런타임에 요청하도록하는 아이디어는이 문제를 해결하는 것 같습니다.
— AndyL 2009

6
이 파일은 .gitignore 아래에 있어야하며 프로젝트를 빌드 할 때마다 생성되어야합니다.
— Igor Zevaka 2009

또는이 파일의 기본 버전을 포함하고 --assume-unchanged플래그를 설정할 수 있습니다 ( git update-index --assume-unchanged)
— Igor Zevaka 2009

1

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


1

이것은 다른 프로그램 (예 : 스크립트 언어)을 설치할 필요없이 Windows 및 Linux에서 작동하는 CMake 프로젝트 용 솔루션입니다.

git 해시는 Linux에서 컴파일 할 때 bash 스크립트, Windows에서 컴파일 할 때 Windows 배치 스크립트 인 스크립트에 의해 .h 파일에 기록되며, 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 해시 std::cout << "Software version: " << kGitHash << std::endl;는를 사용 하여 터미널에 인쇄 하거나 유사한 방식으로 yaml (또는 임의의) 파일에 기록 할 수 있습니다 .


0

원래 커밋 에서 memcached에 대해 어떻게했는지 볼 수 있습니다 .

기본적으로 가끔 태그를 지정하고 배달하는 것이 출처 make dist또는 유사한 것인지 확인하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.