diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a5cd69..1e5703f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,23 +1,84 @@ cmake_minimum_required(VERSION 3.2...3.27) -project(cmake_git_version_tracking - LANGUAGES C) - -# Define the two required variables before including -# the source code for watching a git repository. -set(PRE_CONFIGURE_FILE "git.c.in") -set(POST_CONFIGURE_FILE "${CMAKE_CURRENT_BINARY_DIR}/git.c") -include(git_watcher.cmake) - -# Create a library out of the compiled post-configure file. -# -# Note that the include is a system include. This was done -# so downstream projects don't suffer from warnings on a -# 3rdparty library. -add_library(${PROJECT_NAME} STATIC ${POST_CONFIGURE_FILE}) -target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -add_dependencies(${PROJECT_NAME} check_git) - -# The C99 standard is only required because we're using . -# This could be removed if it's a problem for users, but would require the -# cmake configure() commands to translate true/false literals to 1/0. -set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) +# gtml - git tracking meta library +project(gtml + LANGUAGES CXX) + +set(NBL_GTML_TRACKED_TARGETS CACHE INTERNAL "") +set(NBL_GTML_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "") +set(NBL_GTML_PROJECT_NAME "${PROJECT_NAME}" CACHE INTERNAL "") +set(NBL_GTML_STAMP_FILE "${NBL_GTML_BINARY_DIR}/stamp" CACHE INTERNAL "") + +# useage notes: +# 1. `add_subdirectory` this project +# 2. use function `NBL_ADD_GIT_TRACKING_META_LIBRARY("name" "GIT_PATH")` to track any git_repo +# 3. call `NBL_GENERATE_GIT_TRACKING_META()` to finalize and configure required headers/sources +# 4. link this anywhere you need `target_link_libraries(TARGET PUBLIC gtml)` +# 5. #include "git_info.h" in your code +# 6. profit + +function(NBL_ADD_GIT_TRACKING_META_LIBRARY GTML_NAME GIT_WORKING_DIR) + if(NOT GTML_NAME MATCHES "^[a-zA-Z0-9_]+$") + message(SEND_ERROR "Name \"${GTML_NAME}\" contains illegal characters, name needs to match '^[a-zA-Z0-9_]+$' regex") + endif() + if(GTML_NAME IN_LIST NBL_GTML_TRACKED_TARGETS) + message(SEND_ERROR "Name \"${GTML_NAME}\" is already specified") + endif() + + set(SRC_DIR "${CMAKE_CURRENT_FUNCTION_LIST_DIR}") + set(PRE_CONFIGURE_FILENAME "template_git_info.cpp.in") + set(PRE_CONFIGURE_FILENAME_JSON "template_git_info.json.in") + set(PRE_CONFIGURE_FILE "${SRC_DIR}/${PRE_CONFIGURE_FILENAME}") + set(PRE_CONFIGURE_FILE_JSON "${SRC_DIR}/${PRE_CONFIGURE_FILENAME_JSON}") + + set(GTML_INFO "/* This file is auto-generated by CMake's configure_file function from the template: `${PRE_CONFIGURE_FILENAME}`. * Do not edit this file manually. */") + + set(GTML_TARGET_NAME "${GTML_NAME}_check_git") + set(POST_CONFIGURE_FILE "${NBL_GTML_BINARY_DIR}/${GTML_NAME}_git_info.cpp") + set(POST_CONFIGURE_FILE_JSON "${NBL_GTML_BINARY_DIR}/${GTML_NAME}_git_info.json") + set(GIT_STATE_FILE "${CMAKE_CURRENT_BINARY_DIR}/${GTML_NAME}_git-state-hash") + include("${SRC_DIR}/git_watcher.cmake") + + set(NBL_GTML_TRACKED_TARGETS ${NBL_GTML_TRACKED_TARGETS} "${GTML_NAME}" CACHE INTERNAL "") +endfunction(NBL_ADD_GIT_TRACKING_META_LIBRARY) + +# TODO: for QoL auto-generate instead of relying of function call to finalize configuration +# finalize configuration, generates headers +function(NBL_GENERATE_GIT_TRACKING_META) + set(SRC_DIR "${CMAKE_CURRENT_FUNCTION_LIST_DIR}") + + list(SORT NBL_GTML_TRACKED_TARGETS) + + set(GTML_TARGET_NAMES ${NBL_GTML_TRACKED_TARGETS}) + set(GTML_TARGET_SOURCES ${NBL_GTML_TRACKED_TARGETS}) + set(GTML_ENUMS ${NBL_GTML_TRACKED_TARGETS}) + set(GTML_EXTERNS ${NBL_GTML_TRACKED_TARGETS}) + set(GTML_ARRAY ${NBL_GTML_TRACKED_TARGETS}) + list(TRANSFORM GTML_TARGET_NAMES APPEND _check_git) + list(TRANSFORM GTML_TARGET_SOURCES REPLACE "^(.+)$" "${NBL_GTML_BINARY_DIR}/\\1_git_info.cpp") + list(TRANSFORM GTML_ENUMS TOUPPER) + list(TRANSFORM GTML_ENUMS PREPEND EGRM_) + string(REGEX REPLACE ";" ", " GTML_ENUMS "${GTML_ENUMS}") + list(TRANSFORM GTML_EXTERNS REPLACE "^(.+)$" "\nextern const GitInfo \\1_git_info") + list(TRANSFORM GTML_ARRAY REPLACE "^(.+)$" "\\1_git_info") + string(REGEX REPLACE ";" ", " GTML_ARRAY "${GTML_ARRAY}") + + set(HEADER_NAME "git_info.h.in") + set(SOURCE_NAME "git_info.cpp.in") + + set(GTML_INFO "/* This file is auto-generated by CMake's configure_file function from the template: `${HEADER_NAME}`.\n * Do not edit this file manually. */") + configure_file("${SRC_DIR}/${HEADER_NAME}" "${NBL_GTML_BINARY_DIR}/git_info.h" @ONLY) + + set(GTML_INFO "/* This file is auto-generated by CMake's configure_file function from the template: `${SOURCE_NAME}`.\n * Do not edit this file manually. */") + configure_file("${SRC_DIR}/${SOURCE_NAME}" "${NBL_GTML_BINARY_DIR}/git_info.cpp" @ONLY) + + add_library("${NBL_GTML_PROJECT_NAME}" STATIC ${GTML_TARGET_SOURCES} "${NBL_GTML_BINARY_DIR}/git_info.cpp") + target_include_directories("${NBL_GTML_PROJECT_NAME}" SYSTEM PUBLIC "${NBL_GTML_BINARY_DIR}") + set_target_properties("${NBL_GTML_PROJECT_NAME}" PROPERTIES UNITY_BUILD TRUE) + + # triggers all git check custom commands each build + add_custom_target("gtml_git_check" ALL COMMAND "${CMAKE_COMMAND}" -E touch "${NBL_GTML_STAMP_FILE}" + BYPRODUCTS "${NBL_GTML_STAMP_FILE}" + ) + add_dependencies("${NBL_GTML_PROJECT_NAME}" "gtml_git_check") + +endfunction(NBL_GENERATE_GIT_TRACKING_META) diff --git a/README.md b/README.md index 2181aed..c35f7b9 100644 --- a/README.md +++ b/README.md @@ -10,21 +10,8 @@ The core capability is baked into single self-contained - C Compiler (with C99 standard support) - Git -## Quickstart via FetchContent -You can use CMake's `FetchContent` module to build the static library `cmake_git_version_tracking`: -```cmake -include(FetchContent) -FetchContent_Declare(cmake_git_version_tracking - GIT_REPOSITORY https://github.com/andrew-hardin/cmake-git-version-tracking.git - GIT_TAG 904dbda1336ba4b9a1415a68d5f203f576b696bb -) -FetchContent_MakeAvailable(cmake_git_version_tracking) - -target_link_libraries(your_target - cmake_git_version_tracking -) -``` -Then [`#include git.h`](./git.h) and use the provided functions to retrieve git metadata. +## How to use +read note in CMakeLists.txt ## Intended use case You're continuously shipping prebuilt binaries for an diff --git a/git.c.in b/git.c.in deleted file mode 100644 index a26d27c..0000000 --- a/git.c.in +++ /dev/null @@ -1,32 +0,0 @@ -#include "git.h" - -bool git_IsPopulated() { - return @GIT_RETRIEVED_STATE@; -} -bool git_AnyUncommittedChanges() { - return @GIT_IS_DIRTY@; -} -const char* git_AuthorName() { - return "@GIT_AUTHOR_NAME@"; -} -const char* git_AuthorEmail() { - return "@GIT_AUTHOR_EMAIL@"; -} -const char* git_CommitSHA1() { - return "@GIT_HEAD_SHA1@"; -} -const char* git_CommitDate() { - return "@GIT_COMMIT_DATE_ISO8601@"; -} -const char* git_CommitSubject() { - return "@GIT_COMMIT_SUBJECT@"; -} -const char* git_CommitBody() { - return "@GIT_COMMIT_BODY@"; -} -const char* git_Describe() { - return "@GIT_DESCRIBE@"; -} -const char* git_Branch() { - return "@GIT_BRANCH@"; -} diff --git a/git.h b/git.h deleted file mode 100644 index 91ce40b..0000000 --- a/git.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once -// git.h -// https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/git.h -// -// Released under the MIT License. -// https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/LICENSE - -#include - -#ifdef __cplusplus -#define GIT_VERSION_TRACKING_EXTERN_C_BEGIN extern "C" { -#define GIT_VERSION_TRACKING_EXTERN_C_END } -#else -#define GIT_VERSION_TRACKING_EXTERN_C_BEGIN -#define GIT_VERSION_TRACKING_EXTERN_C_END -#endif - -// Don't mangle the C function names if included in a CXX file. -GIT_VERSION_TRACKING_EXTERN_C_BEGIN - -/// Is the metadata populated? -// -/// We may not have metadata if there wasn't a .git directory -/// (e.g. downloaded source code without revision history). -bool git_IsPopulated(); - -/// Were there any uncommitted changes that won't be reflected -/// in the CommitID? -bool git_AnyUncommittedChanges(); - -/// The commit author's name. -const char* git_AuthorName(); - -/// The commit author's email. -const char* git_AuthorEmail(); - -/// The commit SHA1. -const char* git_CommitSHA1(); - -/// The ISO8601 commit date. -const char* git_CommitDate(); - -/// The commit subject. -const char* git_CommitSubject(); - -/// The commit body. -const char* git_CommitBody(); - -/// The commit describe. -const char* git_Describe(); - -/// The symbolic reference tied to HEAD. -const char* git_Branch(); - -GIT_VERSION_TRACKING_EXTERN_C_END -#undef GIT_VERSION_TRACKING_EXTERN_C_BEGIN -#undef GIT_VERSION_TRACKING_EXTERN_C_END - -#ifdef __cplusplus - -/// This is a utility extension for C++ projects. -/// It provides a "git" namespace that wraps the -/// C methods in more(?) ergonomic types. -/// -/// This is header-only in an effort to keep the -/// underlying static library C99 compliant. - - -// We really want to use std::string_view if it appears -// that the compiler will support it. If that fails, -// revert back to std::string. -#define GIT_VERSION_TRACKING_CPP_17_STANDARD 201703L -#if __cplusplus >= GIT_VERSION_TRACKING_CPP_17_STANDARD -#define GIT_VERSION_USE_STRING_VIEW 1 -#else -#define GIT_VERSION_USE_STRING_VIEW 0 -#endif - - -#if GIT_VERSION_USE_STRING_VIEW -#include -#include -#else -#include -#endif - -namespace git { - -#if GIT_VERSION_USE_STRING_VIEW -using StringOrView = std::string_view; -#else -typedef std::string StringOrView; -#endif - -namespace internal { - -/// Short-hand method for initializing a std::string or std::string_view given a C-style const char*. -inline const StringOrView InitString(const char* from_c_interface) { - #if GIT_VERSION_USE_STRING_VIEW - return StringOrView { from_c_interface, std::strlen(from_c_interface) }; - #else - return std::string(from_c_interface); - #endif -} - -} // namespace internal - -inline bool IsPopulated() { - return git_IsPopulated(); -} -inline bool AnyUncommittedChanges() { - return git_AnyUncommittedChanges(); -} -inline const StringOrView& AuthorName() { - static const StringOrView kValue = internal::InitString(git_AuthorName()); - return kValue; -} -inline const StringOrView AuthorEmail() { - static const StringOrView kValue = internal::InitString(git_AuthorEmail()); - return kValue; -} -inline const StringOrView CommitSHA1() { - static const StringOrView kValue = internal::InitString(git_CommitSHA1()); - return kValue; -} -inline const StringOrView CommitDate() { - static const StringOrView kValue = internal::InitString(git_CommitDate()); - return kValue; -} -inline const StringOrView CommitSubject() { - static const StringOrView kValue = internal::InitString(git_CommitSubject()); - return kValue; -} -inline const StringOrView CommitBody() { - static const StringOrView kValue = internal::InitString(git_CommitBody()); - return kValue; -} -inline const StringOrView Describe() { - static const StringOrView kValue = internal::InitString(git_Describe()); - return kValue; -} -inline const StringOrView Branch() { - static const StringOrView kValue = internal::InitString(git_Branch()); - return kValue; -} - -} // namespace git - - -// Cleanup our defines to avoid polluting. -#undef GIT_VERSION_USE_STRING_VIEW -#undef GIT_VERSION_TRACKING_CPP_17_STANDARD - -#endif // __cplusplus diff --git a/git_info.cpp.in b/git_info.cpp.in new file mode 100644 index 0000000..d03b9c6 --- /dev/null +++ b/git_info.cpp.in @@ -0,0 +1,9 @@ +@GTML_INFO@ + +#include "git_info.h" + +namespace nbl::gtml { + const GitInfo gitMeta[EGRM_COUNT] = { + @GTML_ARRAY@ + }; +} // namespace nbl::gtml \ No newline at end of file diff --git a/git_info.h.in b/git_info.h.in new file mode 100644 index 0000000..5440fff --- /dev/null +++ b/git_info.h.in @@ -0,0 +1,40 @@ +#ifndef _GIT_INFO_H_INCLUDED_ +#define _GIT_INFO_H_INCLUDED_ +@GTML_INFO@ + +// based on https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/git.h +// > Released under the MIT License. +// > https://raw.githubusercontent.com/andrew-hardin/cmake-git-version-tracking/master/LICENSE + +#include +#include + +namespace nbl::gtml { + struct GitInfo { + bool isPopulated; + std::optional hasUncommittedChanges; + const char* commitAuthorName; + const char* commitAuthorEmail; + const char* commitHash; + const char* commitShortHash; + const char* commitDate; + const char* commitSubject; + const char* commitBody; + const char* describe; + const char* branchName; + const char* latestTag; + const char* latestTagName; + }; + + enum E_GIT_REPO_META : uint8_t + { + @GTML_ENUMS@, + EGRM_COUNT + }; + + extern const GitInfo gitMeta[EGRM_COUNT]; + @GTML_EXTERNS@; + +} // namespece nbl::gtml + +#endif //_GIT_INFO_H_INCLUDED_ \ No newline at end of file diff --git a/git_watcher.cmake b/git_watcher.cmake index 32313a3..839ee6f 100644 --- a/git_watcher.cmake +++ b/git_watcher.cmake @@ -81,10 +81,13 @@ endmacro() CHECK_REQUIRED_VARIABLE(PRE_CONFIGURE_FILE) CHECK_REQUIRED_VARIABLE(POST_CONFIGURE_FILE) +CHECK_REQUIRED_VARIABLE(PRE_CONFIGURE_FILE_JSON) +CHECK_REQUIRED_VARIABLE(POST_CONFIGURE_FILE_JSON) CHECK_OPTIONAL_VARIABLE(GIT_STATE_FILE "${CMAKE_CURRENT_BINARY_DIR}/git-state-hash") CHECK_OPTIONAL_VARIABLE(GIT_WORKING_DIR "${CMAKE_SOURCE_DIR}") CHECK_OPTIONAL_VARIABLE_NOPATH(GIT_FAIL_IF_NONZERO_EXIT TRUE) CHECK_OPTIONAL_VARIABLE_NOPATH(GIT_IGNORE_UNTRACKED FALSE) +CHECK_OPTIONAL_VARIABLE_NOPATH(GIT_EXCLUDE_IS_DIRTY FALSE) # Check the optional git variable. # If it's not set, we'll try to find it using the CMake packaging system. @@ -97,6 +100,7 @@ CHECK_REQUIRED_VARIABLE(GIT_EXECUTABLE) set(_state_variable_names GIT_RETRIEVED_STATE GIT_HEAD_SHA1 + GIT_HEAD_SHORT_SHA1 GIT_IS_DIRTY GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL @@ -105,6 +109,8 @@ set(_state_variable_names GIT_COMMIT_BODY GIT_DESCRIBE GIT_BRANCH + GIT_TAG + GIT_TAG_NAME # >>> # 1. Add the name of the additional git variable you're interested in monitoring # to this list. @@ -159,24 +165,38 @@ function(GetGitState _working_dir) else() set(untracked_flag "-unormal") endif() - RunGitCommand(status --porcelain ${untracked_flag}) - if(NOT exit_code EQUAL 0) - set(ENV{GIT_IS_DIRTY} "false") + + if(GIT_EXCLUDE_IS_DIRTY) + set(ENV{GIT_IS_DIRTY} "std::nullopt, // ") else() - if(NOT "${output}" STREQUAL "") - set(ENV{GIT_IS_DIRTY} "true") - else() + RunGitCommand(status --porcelain ${untracked_flag}) + if(NOT exit_code EQUAL 0) set(ENV{GIT_IS_DIRTY} "false") + else() + if(NOT "${output}" STREQUAL "") + set(ENV{GIT_IS_DIRTY} "true") + else() + set(ENV{GIT_IS_DIRTY} "false") + endif() endif() endif() # There's a long list of attributes grabbed from git show. + # TODO: Tadzio, execute everything within single execute_command - the reason is all COMMANDS + # within single invocation will be tried concurently and you can gain a lot of perf there + # total 11 separate execute_commands gets executed currently + set(object HEAD) RunGitCommand(show -s "--format=%H" ${object}) if(exit_code EQUAL 0) set(ENV{GIT_HEAD_SHA1} ${output}) endif() + RunGitCommand(show -s "--format=%h" ${object}) + if(exit_code EQUAL 0) + set(ENV{GIT_HEAD_SHORT_SHA1} "${output}") + endif() + RunGitCommand(show -s "--format=%an" ${object}) if(exit_code EQUAL 0) set(ENV{GIT_AUTHOR_NAME} "${output}") @@ -231,6 +251,22 @@ function(GetGitState _working_dir) set(ENV{GIT_DESCRIBE} "${output}") endif() + # Get latest tag + RunGitCommand(describe --tags ${object}) + if(NOT exit_code EQUAL 0) + set(ENV{GIT_TAG} "unknown") + else() + set(ENV{GIT_TAG} "${output}") + endif() + + # Get latest tag name + RunGitCommand(describe --tags --abbrev=0 ${object}) + if(NOT exit_code EQUAL 0) + set(ENV{GIT_TAG_NAME} "unknown") + else() + set(ENV{GIT_TAG_NAME} "${output}") + endif() + # Convert HEAD to a symbolic ref. This can fail, in which case we just # set that variable to HEAD. set(_permit_git_failure ON) @@ -260,6 +296,7 @@ function(GitStateChangedAction) set(${var_name} $ENV{${var_name}}) endforeach() configure_file("${PRE_CONFIGURE_FILE}" "${POST_CONFIGURE_FILE}" @ONLY) + configure_file("${PRE_CONFIGURE_FILE_JSON}" "${POST_CONFIGURE_FILE_JSON}" @ONLY) endfunction() @@ -297,7 +334,7 @@ function(CheckGit _working_dir _state_changed) # Update the state to include the SHA256 for the pre-configure file. # This forces the post-configure file to be regenerated if the # pre-configure file has changed. - file(SHA256 ${PRE_CONFIGURE_FILE} preconfig_hash) + file(SHA256 "${PRE_CONFIGURE_FILE}" preconfig_hash) string(SHA256 state "${preconfig_hash}${state}") # Check if the state has changed compared to the backup on disk. @@ -324,23 +361,26 @@ endfunction() # check the state of git before every build. If the state has # changed, then a file is configured. function(SetupGitMonitoring) - add_custom_target(check_git - ALL - DEPENDS ${PRE_CONFIGURE_FILE} - BYPRODUCTS - ${POST_CONFIGURE_FILE} - ${GIT_STATE_FILE} - COMMENT "Checking the git repository for changes..." + add_custom_command( + OUTPUT "${POST_CONFIGURE_FILE}" + DEPENDS "${PRE_CONFIGURE_FILE}" "${NBL_GTML_STAMP_FILE}" + BYPRODUCTS "${GIT_STATE_FILE}" + COMMENT "Checking target \"${GTML_NAME}\"'s git repository for changes..." COMMAND - ${CMAKE_COMMAND} + "${CMAKE_COMMAND}" -D_BUILD_TIME_CHECK_GIT=TRUE - -DGIT_WORKING_DIR=${GIT_WORKING_DIR} - -DGIT_EXECUTABLE=${GIT_EXECUTABLE} - -DGIT_STATE_FILE=${GIT_STATE_FILE} - -DPRE_CONFIGURE_FILE=${PRE_CONFIGURE_FILE} - -DPOST_CONFIGURE_FILE=${POST_CONFIGURE_FILE} + -DGIT_WORKING_DIR="${GIT_WORKING_DIR}" + -DGIT_EXECUTABLE="${GIT_EXECUTABLE}" + -DGIT_STATE_FILE="${GIT_STATE_FILE}" + -DPRE_CONFIGURE_FILE="${PRE_CONFIGURE_FILE}" + -DPOST_CONFIGURE_FILE="${POST_CONFIGURE_FILE}" + -DPRE_CONFIGURE_FILE_JSON="${PRE_CONFIGURE_FILE_JSON}" + -DPOST_CONFIGURE_FILE_JSON="${POST_CONFIGURE_FILE_JSON}" -DGIT_FAIL_IF_NONZERO_EXIT=${GIT_FAIL_IF_NONZERO_EXIT} -DGIT_IGNORE_UNTRACKED=${GIT_IGNORE_UNTRACKED} + -DGIT_EXCLUDE_IS_DIRTY=${GIT_EXCLUDE_IS_DIRTY} + -DGTML_NAME="${GTML_NAME}" + -DGTML_INFO="${GTML_INFO}" -P "${CMAKE_CURRENT_LIST_FILE}") endfunction() diff --git a/template_git_info.cpp.in b/template_git_info.cpp.in new file mode 100644 index 0000000..3df2887 --- /dev/null +++ b/template_git_info.cpp.in @@ -0,0 +1,20 @@ +@GTML_INFO@ +#include "git_info.h" + +namespace nbl::gtml { + const GitInfo @GTML_NAME@_git_info = { + .isPopulated = @GIT_RETRIEVED_STATE@, + .hasUncommittedChanges = @GIT_IS_DIRTY@, + .commitAuthorName = "@GIT_AUTHOR_NAME@", + .commitAuthorEmail = "@GIT_AUTHOR_EMAIL@", + .commitHash = "@GIT_HEAD_SHA1@", + .commitShortHash = "@GIT_HEAD_SHORT_SHA1@", + .commitDate = "@GIT_COMMIT_DATE_ISO8601@", + .commitSubject = "@GIT_COMMIT_SUBJECT@", + .commitBody = "@GIT_COMMIT_BODY@", + .describe = "@GIT_DESCRIBE@", + .branchName = "@GIT_BRANCH@", + .latestTag = "@GIT_TAG@", + .latestTagName = "@GIT_TAG_NAME@" + }; +} // namespace nbl::gtml \ No newline at end of file diff --git a/template_git_info.json.in b/template_git_info.json.in new file mode 100644 index 0000000..df84298 --- /dev/null +++ b/template_git_info.json.in @@ -0,0 +1,15 @@ +{ + "isPopulated": @GIT_RETRIEVED_STATE@, + "hasUncommittedChanges": @GIT_IS_DIRTY@, + "commitAuthorName": "@GIT_AUTHOR_NAME@", + "commitAuthorEmail": "@GIT_AUTHOR_EMAIL@", + "commitHash": "@GIT_HEAD_SHA1@", + "commitShortHash": "@GIT_HEAD_SHORT_SHA1@", + "commitDate": "@GIT_COMMIT_DATE_ISO8601@", + "commitSubject": "@GIT_COMMIT_SUBJECT@", + "commitBody": "@GIT_COMMIT_BODY@", + "describe": "@GIT_DESCRIBE@", + "branchName": "@GIT_BRANCH@", + "latestTag": "@GIT_TAG@", + "latestTagName": "@GIT_TAG_NAME@" +} \ No newline at end of file