Czy istnieje łatwy sposób na napisanie kodu C, który może uzyskać dostęp do skrótu jego wersji Git?
Napisałem oprogramowanie w C do zbierania danych naukowych w warunkach laboratoryjnych. Mój kod zapisuje zebrane dane w pliku .yaml do późniejszej analizy. Moje eksperymenty zmieniają się z dnia na dzień i często muszę modyfikować kod. Aby śledzić zmiany, używam repozytorium git.
Chciałbym móc dołączyć skrót wersji Git jako komentarz w moich plikach danych .yaml. W ten sposób mogłem spojrzeć na plik .yaml i dokładnie wiedzieć, jaki kod został użyty do wygenerowania danych pokazanych w tym pliku. Czy istnieje łatwy sposób na zrobienie tego automatycznie?
c
git
version-control
workflow
AndyL
źródło
źródło
Odpowiedzi:
W swoim programie numer wersji git i datę kompilacji trzymam w osobnym pliku o nazwie
version.c
, który wygląda tak:#include "version.h" const char * build_date = "2009-11-10 11:09"; const char * build_git_sha = "6b54ea36e92d4907aba8b3fade7f2d58a921b6cd";
Istnieje również plik nagłówkowy, który wygląda następująco:
#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 */
Zarówno plik nagłówkowy, jak i plik C są generowane przez skrypt Perla, który wygląda następująco:
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_");
Tutaj
hash_to_c_file
wykonuje całą pracę tworzeniaversion.c
iversion.h
imake_date_time
sprawia, że ciąg, jak pokazano.W programie głównym mam rutynę
#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); }
Nie mam zbyt dużej wiedzy na temat git, więc mile widziane komentarze, jeśli istnieje lepszy sposób na zrobienie tego.
źródło
const char []
: 319356 bajtów (pozbawiony). Rozmiar mojego programu zconst char *
: 319324 bajtów (pozbawiony). Więc twój pomysł nie wydaje się zapisywać żadnych bajtów, ale zwiększa całkowitą liczbę o 32. Nie mam pojęcia dlaczego. W oryginalnym pliku „version.c” występują trzy ciągi, ale jeden został pominięty w powyższej odpowiedzi. Jeśli spojrzysz na pierwszą edycję, nadal tam jest.Jeśli używasz kompilacji opartej na marce, możesz umieścić to w pliku Makefile:
GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
(Zobacz man git, opis, co robią przełączniki)
następnie dodaj to do swoich CFLAGS:
-DVERSION=\"$(GIT_VERSION)\"
Następnie możesz po prostu odwołać się do wersji bezpośrednio w programie, tak jakby była to #define:
printf("Version: %s\n", VERSION);
Domyślnie to po prostu wyświetla skrócony identyfikator zatwierdzenia git, ale opcjonalnie możesz oznaczyć poszczególne wydania czymś takim jak:
git tag -a v1.1 -m "Release v1.1"
wtedy wydrukuje:
Version: v1.1-2-g766d
co oznacza, że 2 zatwierdza się po wersji 1.1, a identyfikator zatwierdzenia git zaczyna się od „766d”.
Jeśli w twoim drzewie są niezatwierdzone zmiany, doda "-dirty".
Nie ma skanowania zależności, więc musisz zrobić jawnie,
make clean
aby wymusić aktualizację wersji. To może być rozwiązany jednak.Zaletą jest to, że jest prosty i nie wymaga żadnych dodatkowych zależności budowania, takich jak perl czy awk. Użyłem tego podejścia w GNU automake i kompilacjach Android NDK.
źródło
#define GIT_VERSION ...
zamiast umieszczać go w linii poleceń z-D
opcją; eliminuje problem zależności. Poza tym, dlaczego podwójne podkreślenie? Technicznie jest to zarezerwowany identyfikator.GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
, nie działa bez cudzysłowów.Skończyło się na tym, że użyłem czegoś bardzo podobnego do odpowiedzi @ Kinopiko, ale użyłem awk zamiast perl. Jest to przydatne, jeśli utknąłeś na komputerach z systemem Windows, na których zainstalowano awk z natury mingw, ale nie perl. Oto jak to działa.
Mój plik makefile zawiera wiersz, który wywołuje git, date i awk w celu utworzenia pliku 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
Za każdym razem, gdy kompiluję swój kod, polecenie awk generuje plik version.c, który wygląda następująco:
/* version.c */ #include "version.h" const char * build_git_sha = "ac5bffc90f0034df9e091a7b3aa12d150df26a0e"; const char * build_git_time = "Thu Dec 3 18:03:58 EST 2009";
Mam statyczny plik version.h, który wygląda następująco:
/*version.h*/ #ifndef VERSION_H_ #define VERSION_H_ extern const char * build_git_time; extern const char * build_git_sha; #endif /* VERSION_H_ */
Reszta mojego kodu może teraz uzyskać dostęp do czasu kompilacji i skrótu git, po prostu dołączając nagłówek version.h. Aby zakończyć to wszystko, mówię gitowi, aby zignorował version.c, dodając linię do mojego pliku .gitignore. W ten sposób git nie daje mi ciągle konfliktów scalania. Mam nadzieję że to pomoże!
źródło
FORCE
to był dobry pomysł, ponieważ makefile nigdy nie będzie satysfakcjonujący (za każdym razem, gdy robisz nowy nagłówek). Zamiast tego możesz po prostu dodać zależności do odpowiednich plików git w formule$(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD
. PlikCOMMIT_EDITMSG
zmienia się za każdym razem, gdy wykonujesz zatwierdzenie iHEAD
zmienia się za każdym razem, gdy przeglądasz historię, dlatego plik jest aktualizowany za każdym razem, gdy ma znaczenie.Twój program może zostać wyrzucony
git describe
w czasie wykonywania lub jako część procesu kompilacji.źródło
git help describe
: „Pokaż najnowszy tag, do którego można dotrzeć z zatwierdzenia” - nie o to chodzi w pytaniu. Zgadzam się jednak z resztą twojej odpowiedzi. Aby było poprawne, polecenie powinno byćgit rev-parse HEAD
.git describe
jest tym , czego używa większość innych projektów, ponieważ zawiera również informacje o tagach czytelne dla człowieka. Jeśli nie znajdujesz się dokładnie na tagu, dodaje on liczbę zatwierdzeń od najbliższego tagu i skrócony hash rewizji.Możesz zrobić dwie rzeczy:
Możesz zmusić Git do osadzenia niektórych informacji o wersji w pliku.
Prostszym sposobem jest użycie
ident
atrybutu , co oznacza umieszczenie (na przykład)w
.gitattributes
pliku iw$Id$
odpowiednim miejscu. Zostałby automatycznie rozszerzony do identyfikatora SHA-1 zawartości pliku (identyfikator obiektu blob): NIE jest to wersja pliku ani ostatnie zatwierdzenie.Git obsługuje słowo kluczowe $ Id $ w ten sposób, aby uniknąć dotykania plików, które nie zostały zmienione podczas przełączania gałęzi, przewijania gałęzi itp. Jeśli naprawdę chcesz, aby Git umieścił identyfikator zatwierdzenia (wersji) lub opis w pliku, możesz (ab) użyć
filter
atrybut, używając filtra clean / smudge, aby rozwinąć niektóre słowa kluczowe (np. $ Revision $) przy pobieraniu i wyczyścić je przed zatwierdzeniem.Można uczynić proces kompilacji to zrobić dla Ciebie, jak Linux kernel lub Git sam robi.
Przyjrzyj się skryptowi GIT-VERSION-GEN i jego zastosowaniu w Git Makefile , lub na przykład, jak ten
gitweb/gitweb.cgi
plik Makefile osadza informacje o wersji podczas generowania / konfiguracji pliku.GIT-VERSION-GEN używa git opis do generowania opisu wersji. Powinno działać lepiej, aby oznaczać (używając podpisanych / opatrzonych adnotacjami znaczników) wydania / kamienie milowe projektu.
źródło
Kiedy muszę to zrobić, używam tagu , na przykład
RELEASE_1_23
. Mogę zdecydować, jaki może być tag, nie znając SHA-1. Zatwierdzam, a następnie oznaczam. Możesz przechowywać ten tag w swoim programie tak, jak chcesz.źródło
Opierając się na odpowiedzi udzielonej przez njd27, używam wersji ze skanowaniem zależności, w połączeniu z plikiem version.h z domyślnymi wartościami dla sytuacji, gdy kod jest budowany w inny sposób. Wszystkie pliki zawierające version.h zostaną odbudowane.
Zawiera również datę rewizji jako osobną definicję.
# 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)
źródło
Używam również git do śledzenia zmian w moim kodzie naukowym. nie chciałem używać zewnętrznego programu, ponieważ ogranicza to przenośność kodu (jeśli ktoś chciałby wprowadzić zmiany na przykład na MSVS).
moim rozwiązaniem było użycie tylko głównej gałęzi do obliczeń i sprawienie, by wyświetlał czas kompilacji za pomocą makr preprocesora
__DATE__
i__TIME__
. w ten sposób mogę to sprawdzić za pomocą dziennika git i zobaczyć, której wersji używam. ref: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.htmlinnym eleganckim sposobem rozwiązania problemu jest dołączenie dziennika git do pliku wykonywalnego. zrobić plik obiektowy z dziennika git i dołączyć go do kodu. tym razem jedynym zewnętrznym programem jakiego używasz to objcopy, ale jest mniej kodowania. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 i Osadź dane w programie C ++
źródło
To, co musisz zrobić, to wygenerować plik nagłówkowy (np. Używając echo z linii cmd), coś takiego:
#define GIT_HASH \ "098709a0b098c098d0e"
Aby go wygenerować, użyj czegoś takiego:
echo #define GIT_HASH \ > file.h echo " > file.h echo git status <whatever cmd to get the hash> > file.h echo " > file.h
Być może trzeba będzie trochę pobawić się cudzysłowami i odwrotnymi ukośnikami, aby go skompilować, ale masz pomysł.
źródło
--assume-unchanged
na niej flagę (git update-index --assume-unchanged
)Jest to rozwiązanie dla projektu CMake, które działa w systemach Windows i Linux, bez konieczności instalowania jakichkolwiek innych programów (np. Języków skryptowych).
Skrót git jest zapisywany do pliku .h przez skrypt, który jest skryptem bash podczas kompilacji w systemie Linux lub skryptem wsadowym systemu Windows podczas kompilacji w systemie Windows, a klauzula if w CMakeLists.txt wybiera skrypt odpowiadający platformie kod jest kompilowany.
Następujące 2 skrypty są zapisywane w tym samym katalogu co 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
W CMakeLists.txt dodaje się następujące wiersze
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)
W kodzie zawarty jest wygenerowany plik,
#include <my_project/githash.h>
a skrót git może być wydrukowany na terminalu za pomocąstd::cout << "Software version: " << kGitHash << std::endl;
lub zapisany w pliku yaml (lub dowolnym) w podobny sposób.źródło
Kolejna odmiana oparta na Makefile i powłoce
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)
Plik git_commit_filename.h kończy się pojedynczą linią zawierającą statyczną stałą char * GIT_COMMIT_SHA = "";
Od https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406
źródło
Możesz zobaczyć, jak zrobiłem to dla memcached w oryginalnym zatwierdzeniu .
Zasadniczo oznaczaj od czasu do czasu tagi i upewnij się, że dostarczana przesyłka pochodzi
make dist
lub jest podobna.źródło