¿Existe una manera fácil de escribir código C que pueda acceder a su hash de versión Git?
Escribí software en C para recopilar datos científicos en un entorno de laboratorio. Mi código registra los datos que recopila en un archivo .yaml para su posterior análisis. Mis experimentos cambian día a día y, a menudo, tengo que modificar el código. Para realizar un seguimiento de las revisiones, uso un repositorio de git.
Me gustaría poder incluir el hash de revisión de Git como comentario en mis archivos de datos .yaml. De esa manera, podría mirar el archivo .yaml y saber exactamente qué código se usó para generar los datos que se muestran en ese archivo. ¿Existe una manera fácil de hacer esto automáticamente?
c
git
version-control
workflow
AndyL
fuente
fuente
Respuestas:
En mi programa, tengo el número de versión de git y la fecha de la compilación en un archivo separado, llamado
version.c
, que se ve así:#include "version.h" const char * build_date = "2009-11-10 11:09"; const char * build_git_sha = "6b54ea36e92d4907aba8b3fade7f2d58a921b6cd";
También hay un archivo de encabezado, que se ve así:
#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 */
Tanto el archivo de encabezado como el archivo C son generados por un script Perl que se ve así:
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_");
Aquí
hash_to_c_file
hace todo el trabajo de crearversion.c
yversion.h
ymake_date_time
hace que una cadena como se muestra.En el programa principal, tengo una rutina
#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); }
No tengo tanto conocimiento sobre git, por lo que agradecería los comentarios si hay una mejor manera de hacer esto.
fuente
const char []
: 319356 bytes (despojados). El tamaño de mi programa conconst char *
: 319324 bytes (despojado). Entonces, su idea no parece ahorrar bytes, pero aumenta el número total en 32. No tengo idea de por qué. En la "version.c" original hay tres cadenas, pero una se omitió en la respuesta anterior. Si miras la primera edición, todavía está ahí.Si está utilizando una compilación basada en make, puede poner esto en el Makefile:
GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
(Ver man git describe lo que hacen los interruptores)
luego agregue esto a sus CFLAGS:
-DVERSION=\"$(GIT_VERSION)\"
Luego, puede hacer referencia a la versión directamente en el programa como si fuera una #define:
printf("Version: %s\n", VERSION);
De forma predeterminada, esto solo imprime un ID de confirmación de git abreviado, pero opcionalmente puede etiquetar lanzamientos particulares con algo como:
git tag -a v1.1 -m "Release v1.1"
luego se imprimirá:
Version: v1.1-2-g766d
lo que significa, 2 confirmaciones pasadas v1.1, con un ID de confirmación de git que comienza con "766d".
Si hay cambios no confirmados en su árbol, agregará "-dirty".
No hay escaneo de dependencias, por lo que debe hacer un análisis explícito
make clean
para forzar la actualización de la versión. Sin embargo, esto se puede solucionar .Las ventajas son que es simple y no requiere ninguna dependencia de compilación adicional como perl o awk. He usado este enfoque con GNU automake y con compilaciones de Android NDK.
fuente
#define GIT_VERSION ...
lugar de ponerlo en la línea de comando con la-D
opción; elimina el problema de la dependencia. Además, ¿por qué el doble subrayado? Técnicamente, es un identificador reservado.GIT_VERSION := "$(shell git describe --abbrev=4 --dirty --always --tags)"
, no funciona sin comillas.Terminé usando algo muy similar a la respuesta de @ Kinopiko, pero usé awk en lugar de perl. Esto es útil si está atascado en máquinas con Windows que tienen awk instalado por naturaleza de mingw, pero no perl. Así es como funciona.
Mi archivo MAKE tiene una línea que invoca git, date y awk para crear un archivo 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
Cada vez que compilo mi código, el comando awk genera un archivo version.c que se ve así:
/* version.c */ #include "version.h" const char * build_git_sha = "ac5bffc90f0034df9e091a7b3aa12d150df26a0e"; const char * build_git_time = "Thu Dec 3 18:03:58 EST 2009";
Tengo un archivo version.h estático que se ve así:
/*version.h*/ #ifndef VERSION_H_ #define VERSION_H_ extern const char * build_git_time; extern const char * build_git_sha; #endif /* VERSION_H_ */
El resto de mi código ahora puede acceder al tiempo de compilación y al hash de git simplemente incluyendo el encabezado version.h. Para terminar, le digo a git que ignore version.c agregando una línea a mi archivo .gitignore. De esta manera, git no me genera conflictos de combinación constantemente. ¡Espero que esto ayude!
fuente
FORCE
sea una buena idea ya que makefile nunca quedará satisfecho (cada vez que hagas que hagas un nuevo encabezado). En su lugar, puede agregar dependencia a los archivos git relevantes en la fórmula$(MyLibs)/version.c : .git/COMMIT_EDITMSG .git/HEAD
. El archivoCOMMIT_EDITMSG
cambia cada vez que realiza una confirmación yHEAD
cambia cada vez que explora el historial, por lo tanto, su archivo se actualiza cada vez que es relevante.Su programa puede pagar
git describe
, ya sea en tiempo de ejecución o como parte del proceso de compilación.fuente
git help describe
: "Mostrar la etiqueta más reciente a la que se puede acceder desde una confirmación": esto no es lo que pide la pregunta. Sin embargo, estoy de acuerdo con el resto de tu respuesta. Para ser correcto, el comando debería sergit rev-parse HEAD
.git describe
es lo que utilizan la mayoría de los otros proyectos, porque también incluye información de etiquetas legible por humanos. Si no está exactamente en una etiqueta, se agrega al número de confirmaciones desde la etiqueta más cercana y el hash de revisión abreviado.Hay dos cosas que puede hacer:
Puede hacer que Git incruste información de la versión en el archivo por usted.
La forma más sencilla es usar
ident
atributo , que significa poner (por ejemplo)en
.gitattributes
archivo y$Id$
en el lugar apropiado. Se expandirá automáticamente al identificador SHA-1 del contenido del archivo (id de blob): esta NO es la versión del archivo ni la última confirmación.Git admite la palabra clave $ Id $ de esta manera para evitar tocar archivos que no se cambiaron durante el cambio de rama, rebobinar la rama, etc.
filter
atributo, usando el filtro limpio / manchado para expandir alguna palabra clave (por ejemplo, $ Revision $) al finalizar la compra, y limpiarla para confirmarla.Puede hacer que el proceso de compilación lo haga por usted, como lo hace el kernel de Linux o el propio Git.
Eche un vistazo al script GIT-VERSION-GEN y su uso en Git Makefile , o por ejemplo, cómo este Makefile incrusta información de versión durante la generación / configuración del
gitweb/gitweb.cgi
archivo.GIT-VERSION-GEN usa git describe para generar la descripción de la versión. Debe funcionar mejor que etiquete (utilizando etiquetas firmadas / anotadas) lanzamientos / hitos de su proyecto.
fuente
Cuando necesito hacer esto, uso una etiqueta , como
RELEASE_1_23
. Puedo decidir cuál puede ser la etiqueta sin conocer el SHA-1. Me comprometo y luego etiqueto. Puede almacenar esa etiqueta en su programa de la forma que desee.fuente
Según la respuesta de njd27, estoy usando una versión con escaneo de dependencias, en combinación con un archivo version.h con valores predeterminados para cuando el código se compila de una manera diferente. Se reconstruirán todos los archivos que incluyan version.h.
También incluye la fecha de revisión como una definición separada.
# 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)
fuente
También uso git para rastrear cambios en mi código científico. No quería usar un programa externo porque limita la portabilidad del código (si alguien quisiera hacer cambios en MSVS, por ejemplo).
mi solución fue usar solo la rama principal para los cálculos y hacer que genere el tiempo de compilación usando macros de preprocesador
__DATE__
y__TIME__
. de esa manera puedo verificarlo con git log y ver qué versión estoy usando. ref: http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.htmlotra forma elegante de resolver el problema es incluir git log en el ejecutable. crea un archivo objeto de git log e inclúyelo en el código. esta vez el único programa externo que usa es objcopy pero hay menos codificación. ref: http://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967 e incrustar datos en un programa C ++
fuente
Lo que debe hacer es generar un archivo de encabezado (por ejemplo, usando eco de la línea cmd) algo como esto:
#define GIT_HASH \ "098709a0b098c098d0e"
Para generarlo usa algo como esto:
echo #define GIT_HASH \ > file.h echo " > file.h echo git status <whatever cmd to get the hash> > file.h echo " > file.h
Es posible que necesite jugar un poco con las comillas y las barras diagonales inversas para que se compile, pero entiendes la idea.
fuente
--assume-unchanged
git update-index --assume-unchanged
Otra variación más basada en Makefile y 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)
El archivo git_commit_filename.h terminará con una sola línea que contiene el carácter estático const * GIT_COMMIT_SHA = "";
De https://gist.github.com/larytet/898ec8814dd6b3ceee65532a9916d406
fuente
Esta es una solución para el proyecto CMake que funciona para Windows y Linux, sin la necesidad de instalar ningún otro programa (por ejemplo, lenguajes de script).
El hash de git se escribe en un archivo .h mediante un script, que es un script bash cuando se compila en Linux o un script por lotes de Windows cuando se compila en Windows, y una cláusula if en CMakeLists.txt selecciona el script correspondiente a la plataforma se compila el código.
Los siguientes 2 scripts se guardan en el mismo directorio que 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
En CMakeLists.txt se agregan las siguientes líneas
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)
En el código, se incluye el archivo generado
#include <my_project/githash.h>
y el hash de git puede imprimirse en la terminalstd::cout << "Software version: " << kGitHash << std::endl;
o escribirse en un archivo yaml (o cualquier otro) de manera similar.fuente
Puedes ver cómo lo hice para memcached en la confirmación original .
Básicamente, etiquete ocasionalmente y asegúrese de que lo que entrega provenga de
make dist
o similar.fuente