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.cversion.hmake_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-5967C ++ 프로그램에 데이터 포함


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.