将git-commit和git-diff作为字符串放入二进制文件中
Putting git commit and git diff into binary as strings?
我是一名研究生,主要从事软件模拟研究。我有一些C++代码用来生成结果,但我的大问题是,为了争取可重复性,我想在二进制文件中保存足够的元数据,这样我就可以回到生成该二进制文件的确切源代码(主要是看看我发现的一些错误是否使我之前生成的一些结果无效)。
换句话说,当我生成一组输出文件时,我希望二进制文件转储当前修订的git提交以及任何未完成的更改。这将允许我(理论上)检查提交,应用保存的补丁,并返回到创建二进制文件的确切源代码。
我知道我可以通过手动保存信息或其他方式在带外完成这项工作,但为了确保完全一致性,我想直接将信息烘焙到二进制文件中,这样每个二进制文件都可以追溯到其确切的来源。
我熟悉在makefile中设置#define标志来存储类似gitcommitSHA1的东西,但我认为我需要一种更聪明的方法来将整个gitdiff存储为二进制文件中的字符串。
所以我有几个问题:
- 这是个糟糕的主意吗?有没有更好的方法可以将二进制文件追溯到源代码
- 实现这一目标的最佳方式是什么
谢谢。
编辑:我想我没有明确表示,我想保存差异的原因是为了在当前HEAD上捕获任何未提交的更改。我可以存储散列,但如果我错误地使用了一个二进制文件,其中包含一些未提交的内容,那么我就无法返回正确的源。
在代码中保存git"id"数字(哈希)不是一个坏主意。保存diff是毫无意义的,因为散列(以及它来自哪个分支)应该可以让你回到原始代码。
只需确保您的构建和测试系统设置为不能使用尚未提交的内容,这样您就不能在构建中进行一些未提交的随机更改。
编辑:在您的机器上,在项目的本地副本中进行测试,和使用检查所有内容的测试套件进行测试是有区别的——这是您用来确认一切正常的方法,对吧?请注意,在其他人获得该代码的副本之前,你测试什么并不重要——在提交代码之前,不要让其他人看到你的代码,也不要允许保存测试结果的完整测试套件用于发布说明等,如果你还没有提交所有的代码,那么就运行它[或者更好的是,有一个单独的目录/机器,它只从中央回购中获得新的代码-如果你这样做了,那么你就不可能使用未提交的代码。
我参与过几个这样工作的项目——你可以在本地目录中使用未提交的代码进行构建,但所有的"官方构建"都是在不同的机器上完成的,代码总是直接来自repo,没有本地更改。
如果你没有两台机器,也许有一台"充当独立机器"的虚拟机,或者只使用第二个目录[或不同的用户?]进行"官方测试"。
实际上,你可以简单地检查是否有一些差异,然后在"这就是哈希"的同时,如果有任何差异,添加一个额外的"-带有未提交的更改"或类似的内容。您可以使用git diff --exit-code
为"无更改"或"更改"提供0或1退出代码。
- 你描述了两个想法:只有一个是可怕的。只需保存散列即可。您可以从中恢复所有其他元数据,包括diff
- 只需存储散列(请参阅1)-您已经知道如何执行此操作
我认为最好的答案是"不要那样做"。如果您想要可重复的构建,请仅针对提交的更改进行构建,而不是使用脏的工作目录。如果需要,可以在一个分支上提交实验性的东西,如果它不起作用,你可以把它扔掉,或者可能把它留在你的存储库中(例如,对于你重复构建的用例),但放弃那个分支,转而使用另一个新分支。如果您只想返回到特定的构建点,请考虑在这些特定的提交上删除适当的标记。
从技术角度来看,存储git SHA1 id对于您想要实现的目标来说是足够公平的。
未提交的更改如果存在构建过程,则将其设置为失败
如果建造工程太难/工作量太大,就表现出更多的纪律。:)
编辑:
通过外壳脚本构建。在构建更改检查之前,git diff --exit-code
可能会有所帮助。
第2版:
如果您必须调试许多版本的代码,git help bisect
会派上用场。
git版本
将git信息烘焙到二进制文件中。
目标
考虑以下C++程序:
#include <iostream>
#include <format>
#include "gitversion.h"
int main()
{
std::cout << std::format("Build Info: {}n{}n{}n{}",
FromGit::Branch, FromGit::Tag, FromGit::Commit, FromGit::Date) <<
std::endl;
return 0;
}
目标是拥有一个gitversion.h
文件,该文件在包含时允许我们访问存储库的分支、标记、提交和日期信息。
建议
为了实现这一点,建议构建树位于源树外构建)。要使用命令行实现这一目标:
mkdir ../build
cd ../build
或者,如果您正在使用QtCreator,请转到IDE左侧的Projects选项卡,选择正确的工具包并在构建选项卡上选择阴影构建。
如果您正在使用Microsoft Visual Studio,请右键单击CMakeLists.txt
并选择CMake Settings for,然后将构建根更改为如下内容:
${projectDir}..build${name}
如果您反对此建议并将构建目录放入源代码中,则每次运行cmake
或在IDE中更改项目设置时,git status
都可能发生更改,并且不需要的更改将反映在二进制文件中。
CMakeLists文件
以下是CMakeLists.txt
:的内容
cmake_minimum_required(VERSION 3.20)
project(Tutorial)
# Where we get information from the git and put it into the gitversion.h file.
include(${PROJECT_SOURCE_DIR}/gitversion.cmake)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_executable(Tutorial tutorial.cxx)
# Must include the build tree to get access to the gitversion.h file.
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
.cmake文件
这就是大部分工作发生的地方:
# find_package(Git) -> finds the location of the git executable
# git describe --always --abbrev=8 -> gives the abbreviated hash
# git status --short -> checks for uncommitted work
# git describe --exact-match --tags -> gives the tag
# git rev-parse --abbrev-ref HEAD -> gives current branch
# git log -n 1 --pretty=%cd --pretty=%cI -> gives the time of the last commit
find_package(Git)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=8
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT)
execute_process(COMMAND ${GIT_EXECUTABLE} status --short
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_STATUS)
if (("${GIT_COMMIT}" STREQUAL "") OR (NOT "${GIT_STATUS}" STREQUAL ""))
set(GIT_COMMIT "N/A")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} describe --exact-match --tags
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
ERROR_QUIET)
if ("${GIT_TAG}" STREQUAL "")
set(GIT_TAG "N/A")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH)
if ("${GIT_BRANCH}" STREQUAL "")
set(GIT_BRANCH "N/A")
endif()
execute_process(COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=%cd --pretty=%cI
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DATE)
if ("${GIT_DATE}" STREQUAL "")
set(GIT_DATE "N/A")
endif()
string(STRIP "${GIT_COMMIT}" GIT_COMMIT)
string(STRIP "${GIT_TAG}" GIT_TAG)
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
string(STRIP "${GIT_DATE}" GIT_DATE)
# replaces matching variabels in gitversion.h.in file and writes it to gitversion.h
configure_file(gitversion.h.in gitversion.h)
.in文件
这个文件实际上是一个.h
文件,它加载了要在cmake
执行期间替换的cmake相关变量:
#pragma once
#include <string_view>
namespace FromGit
{
const std::string_view Commit{"@GIT_COMMIT@"};
const std::string_view Tag{"@GIT_TAG@"};
const std::string_view Branch{"@GIT_BRANCH@"};
const std::string_view Date{"@GIT_DATE@"};
}
最后一步
最后在我们的构建目录中运行:
cmake ../Tutorial
cmake --build .
确保Tutorial
目录已用git
初始化。
此解决方案基于Matt Keeter的工作。
- 如何从二进制文件中读取字符串
- 将位字符串转储到二进制文件的最佳方法是什么
- 如何检查二进制文件中是否存在字符串
- 将大型二进制字符串写入二进制文件
- 如何在 c++ 中将二进制文件读取为字符串
- 在C++中使用 fread 从二进制文件中读取字符串及其长度
- C++实现代码中的字符串不应存在于输出二进制文件中.如何解决
- 无法使用 fstream 从二进制文件中读取字符串,而是显示奇怪的符号
- C 字符串到C char数组,以写入二进制文件
- 将"8 bit"(字节)字符串写入二进制文件?
- 我正在尝试读取一个包含字符串和整数的二进制文件..并打印出与字符串对应的整数
- 从二进制文件 C++ 读取的字符串数组写入
- 如何在 c++ 中使用二进制文件输入/输出读取/写入结构的字符串类型成员
- 将从二进制文件读取的字符串转换为整数
- 将 int 的二进制文件读取到字符串 c++
- 在二进制文件中读取字符串C
- C/C++ 读取具有多个字符串和字符的二进制文件
- 调试读/写字符串到二进制文件
- 如何用C++递归地从二进制文件中读取自定义字符串
- 将二进制字符串写入二进制文件