【问题标题】:How can I populate the Git commit ID into a file when I commit?提交时如何将 Git 提交 ID 填充到文件中?
【发布时间】:2013-05-13 14:13:45
【问题描述】:

我想创建 Git 钩子,它将在我的源代码中将我即将制作的提交的提交 ID 填充到文件中(基本上是变量替换)。这对 Git 可行吗?或者是通过将变量解析为 git id 的事实,我将更改 sha 1,从而以“鸡或蛋”问题结束。

【问题讨论】:

  • 由于 sha1 是校验和,因此不可能将其放在已提交集中。有可能通过压缩提交来实现。你到底想做什么?你确定你在更大规模地解决正确的问题吗?
  • 我认为注释不会改变 SHA1,因此它们将成为此类信息的完美候选者。那就是:如果我的假设是真的。
  • 这是可取的原因:我存储在 git 中的不是编译的代码,而是部署到 ETL 服务器的 ETL 作业配置。我希望在 ETL 服务器上部署的内容和 Git 中的内容之间具有可追溯性。因此,当登录到 ETL 服务器时,我希望能够查看我创建的名为“git_id”的变量,该变量将包含与部署在 ETL 服务器上的作业版本相关联的提交 ID。我当然可以通过其他方式实现可追溯性,但如果可能的话,这将是最简单的。
  • (如果不可能,那很好,但值得研究。)

标签: git


【解决方案1】:

我用于类似情况的解决方案是这样的:

  1. 将字符串 $Id$ 放在您想要识别的文件中的某个位置(例如 test.html),可能位于文件的注释或其他不会导致问题的非功能部分中。
  2. 在您的.gitattributes 中,使用ident 关键字(例如*.html ident)标记有问题的文件。

这样做的结果是,当git checkout 将文件从对象数据库复制到您的工作目录时,它会将$Id$ 字符串扩展为读取$Id: <sha-1 of file>$,并且git add 在您想要时反转该转换签入,因此您的对象数据库中该文件的版本只包含$Id$,而不包含扩展表单。

这是一个开始,但不幸的是,找到包含具有特定哈希的文件的提交并不容易,也不一定是一对一的。因此,此外,我还使用export-subst 属性标记这些文件(例如.gitattributes 中的*.html ident export-subst),并在文件的某处添加一个附加字符串,例如$Format:%ci$ ($Format:%h$)

git checkoutgit add 不会影响这些标签,因此我的存储库中的版本总是具有该字符串。为了扩展这些标签,您必须使用 git archive 创建项目特定版本的 tar-ball(或 .zip),然后使用它来部署该版本 - 您将无法只需复制文件,或 make install 或其他什么,因为 git archive 是唯一会扩展这些标签的东西。

我作为示例给出的两个标签扩展为YYYY-MM-DD HH:MM:SS +TZOFFSET (HASH),在这种情况下HASH 是实际的提交哈希,所以它更有用。

您可以在git log 帮助页面的--pretty-format 说明符下找到其他可能有用的$Format:$ 说明符。

【讨论】:

【解决方案2】:

不可能做你想做的事:提交的 SHA-1 哈希是在包括每个成员文件在内的整个存储库快照上计算的,因此存在先有鸡还是先有蛋的问题——要计算提交的哈希,你需要知道所有文件的内容组成它的文件。

【讨论】:

  • 不知道为什么你会被否决,你只是在说什么。
  • @jthill,感谢支持!让我们假设在这种情况下我正在扮演乔丹诺·布鲁诺 ;-)
  • 这不是真的,你可以在gitignore中添加这个文件并在其中写入当前的提交哈希。
【解决方案3】:

您可以使用post-commit 挂钩来完成此操作。这是the git-scm website的摘录

在整个提交过程完成后,post-commit 钩子运行。它不带任何参数,但您可以通过运行 git log -1 HEAD 轻松获取最后一次提交。通常,此脚本用于通知或类似的东西。

获取git log -1 HEAD 的输出,然后使用sed 之类的工具替换文件中的变量。但是,这会修改您的工作目录,除非您打算丢弃这些更改,否则您最终会得到一个永久修改的工作目录。

如果您只想在代码中的某个变量中使用当前提交哈希,您可以执行 git log -1 HEADcat .git/HEAD 并将输出存储在您的变量中

如果您只想要问题标题中的 id(哈希),您可以使用 --format 标志。 git log -1 HEAD --format=%H

【讨论】:

  • 这样的post-commit 钩子会修改工作目录。每次提交时,都会存储上一次提交的 SHA1,然后修改工作目录以包含上一次提交的 SHA1。
  • @Andomar 该钩子可能会修改工作目录,但如果修改是针对.gitignored 的文件(即,出于构建目的或其他目的,git 不会跟踪),这仍然很有用。不过,从提问者那里了解更多关于确切用例的信息可能会有所帮助......
  • +1 这篇文章很有用,很有帮助,不知道为什么它被否决了!
  • 另外:请不要编辑以改变帖子的含义。添加 cmets 或发布您自己的答案。
【解决方案4】:

您可以创建一个过滤器,在提交和签出时替换文件。这些被称为“涂抹”和“清洁”过滤器,它们的操作由.gitattributes 控制。例如:

*.c     filter=yourfilter

这告诉 git 为所有 .c 文件运行 yourfilter 过滤器。然后你必须告诉 git yourfilter 是什么意思:

git config --global filter.yourfilter.clean script1
git config --global filter.yourfilter.smudge script2

然后,您将编写一个脚本(sed、Perl、Python 或其他任何东西),在结帐时将$LastSha$ 之类的表达式替换为$LastSha: <sha>$(“涂抹”)。另一个脚本在提交之前反转扩展(“干净”。)

Search the Pro Git book 用于“关键字扩展”的详细示例。

【讨论】:

  • 这不是我想要做的——这将允许您在结帐时解析变量并在签入时取消解析。但是我需要在签入时专门修改文件,以便它包含文件的哈希 id。
  • @BestPractices 这根本做不到,见kostix's answer
  • 我可能只对特定文件运行此过滤器。
  • @BestPractices 将提交 ID 放入已提交的文件是解决某些问题或要求的方法。你想达到或避免什么?
  • @jthill:如果您想看一下,请在问题下方的 cmets 中解决您的问题
【解决方案5】:

好的,受 Jon Cairns 回答的启发,我想出了这个可以放入 Makefile 的小 sn-p。

version.h:
        git log -n 1 --format=format:"#define GIT_COMMIT \"%h\"%n" HEAD > $@

这不是一个完全通用的解决方案,但它可以派上用场。我知道我会用它的一两个地方。

【讨论】:

  • 顺便说一句:我将增强和改进留给用户。特别是,您可能需要一个 FORCE 标记或其他东西来确保它在提交 id 更改时得到重建。
  • 查看Git pretty-formats Docs 了解如何构建该格式字符串。
【解决方案6】:

我一直在寻找这个问题的答案。提交 ID 已写入文件,您只需要知道在哪里查找即可。在 master 分支上提交后,您可以在以下位置找到提交哈希 ./.git/refs/heads/master 因此,在我们的持续交付解决方案中(它会下载 .git 文件夹以及源代码),我们可以简单地 cat ./.git/refs/heads/${BRANCH} 为了将当前提交哈希与我们的构建相关联

【讨论】:

  • 谢谢。这是最准确的答案。
【解决方案7】:

正如其他人所提到的,您不能在同一提交期间将提交本身的 SHA-1 放入文件中。无论如何,这将是有限的,因为查看两个文件您将无法立即判断哪个是新的。

话虽如此,实际上有一种方法可以自动将版本跟踪信息放入提交的文件中。我为我当前的项目(FrauBSD;我正在开发的 FreeBSD 的一个分支)做了这个。

我不是通过使用 git-attributes 过滤器来实现的。虽然 git-attributes 过滤器可以轻松实现相反的效果(在结帐时将信息放入文件中),但我想要的是在提交时扩展某些关键字,以便数据进入存储库(例如,在“git push origin master”之后,github 在提交的文件中显示扩展值)。使用 git-attributes 过滤器实现后者非常困难,因为一个简单的“git diff”将调用 filter.clean 属性,并且在我的情况下,如果您将日期/时间信息放入扩展中,则具有每次执行“git diff”时的值更改都是不受欢迎且不可接受的。

所以我开发了一个 pre-commit 钩子和一个 commit-msg 钩子,它们共同作用,解决了如何(特别是在 FrauBSD 案例中)替换已提交文件中的以下内容的问题:

$FrauBSD$

在签入前使用类似于以下内容的内容(扩展值向上游发送以供其他人签出):

$FrauBSD: 文件路径 YYYY-MM-DD HH:MM:ZZ GMTOFFSET 提交者 $

当任何人在 github 上浏览文件或执行文件的签出或合并时,扩展的信息会随之而来。

注意:扩展的值永远不会改变,除非分别有另一个(不相关的)变化伴随。

例如,请参阅以下提交,其中我只是删除了文件的尾随换行符。提交包含删除尾随换行符以及 $FrauBSD$ 关键字中的日期/时间的碰撞:

https://github.com/freebsdfrau/FrauBSD/commit/060d943d86bb6a79726065aad397723a9c704ea4

为了产生那个提交,我做了大多数 [git] 开发人员都熟悉的事情:

  1. vi 许可证
  2. Shift-G # 转到文件末尾
  3. dd #删除当前行
  4. ZZ #保存文件并退出
  5. git diff # diff 显示删除尾随换行符 注意:diff 没有显示对 $FrauBSD$ 值的更改 [尚未]
  6. git 添加许可证
  7. git diff # 无(无未分级更改)
  8. git diff --cached # diff 显示删除尾随换行符 注意:diff [仍然] 显示对 $FrauBSD$ 值的更改
  9. git status # 显示修改后的许可证
  10. git commit # $EDITOR 出现
  11. Ctrl-Z # 将 $EDITOR 置于后台以便我们进行调查
  12. git diff --cached # diff [now] 显示 $FrauBSD$ 更新以及删除尾随换行符
  13. fg # 恢复 $EDITOR
  14. :问! # 退出编辑器而不做任何更改 注意:由于您中止了提交,$FrauBSD$ 已恢复
  15. git diff --cached # diff [再次] 只显示尾随换行符删除
  16. git commit #这次我们不会中止
  17. BumpZZ # 插入“Bump”,保存退出
  18. 文件按原样提交

注意:提交后无需对文件执行任何操作

那是因为我的项目中有以下文件:

  • .git/hooks/pre-commit(../../.hooks/pre-commit 的符号链接)
  • .git/hooks/commit-msg(../../.hooks/commit-msg 的符号链接)
  • .hooks/pre-commit
  • .hooks/commit-msg
  • .filters/fraubsd-keywords

您可以在此处获得的初始版本:

“为预提交涂抹添加挂钩/过滤器”

https://github.com/freebsdfrau/FrauBSD/commit/63fa0edf40fe8f5936673cb9f3e3ed0514d33673

注意:过滤器由钩子使用(不在 git-attributes 中使用)。

这里有一个更新:

https-//github.com/freebsdfrau/FrauBSD/commit/b0a0a6c7b2686db2e8cdfb7253aba7e4d7617432

或者您可以在此处查看头部修订:

https-//github.com/freebsdfrau/FrauBSD/tree/master/.filters

https-//github.com/freebsdfrau/FrauBSD/tree/master/.hooks

注意:冒号更改为 - 在上面的 URL 中,所以我可以发布超过 2 个链接(因为声誉低)

享受, FreeBSDFrau

【讨论】:

  • “您不能在同一次提交期间将提交的 SHA-1 本身放入文件中” 您“可以”,从技术上讲是 可能 ...如果您愿意等待很长时间才能弄清楚如何进行。你大概意思说的是:“用当前的硬件或算法(至少在 2015-2019 年对公众可用)在合理的时间内做到这一点是不可行的”。跨度>
【解决方案8】:

这里的关键是将您的修订 ID 放入 Git 不关心的某个文件中。这是我的一个项目的片段:

. . . AssemblyCS="属性/AssemblyInfo.cs" rev="$(git log -n 1 --date=short --format=format:"rev.%ad.%h" HEAD)" sed "$AssemblyCS" . . .

此脚本作为我的构建过程的一部分运行(它也可以是提交后挂钩)。在这种情况下,Properties/AssemblyInfo.cs 位于 .gitignore 中,而 Properties/AssemblyInfo.cs.in 处于版本控制之下。该构建使用.cs文件,其中包含一个修订ID,该修订ID最终出现在部署的可执行文件中。

【讨论】:

  • 事实上,对于我们的编译代码,我们会按照您在此处的建议进行操作,但不幸的是,这不适用于我们的非编译代码。基本上,我们将 git 中的内容部署到 ETL 服务器。无需编译或打包。
  • 编译不是其中的重要部分。任何包含提交 ID 的文件都可以是 .gitignored,并作为部署过程的一部分生成。例如,如果部署步骤只是一个“git pull”,您可以将其设为post-merge 挂钩。相同的脚本还可以编写已部署树的 MD5 清单。然后给定一个清单文件,您可以 (a) 检查真实的树匹配并 (b) 知道它来自哪个修订版。
【解决方案9】:

我一直在寻找类似的东西,因为我想要一个可以添加到前端资源文件(如 CSS/JS)末尾的唯一变量,这将允许我们设置非常长的缓存时间以减少带宽并提高性能,但很容易在任何提交后强制重新加载它们。本质上是文件版本控制,但完全自动化。我不在乎它是否是最近的MOST,只要它在我们所有的应用服务器中是独一无二的、自动化的和一致的。

我们的部署脚本仅使用“git clone”将最新代码的副本下载到我们的应用服务器中,但我们通过 .htaccess 限制对这些文件和目录的访问。

/.git/ 目录包含一个名为ORIG_HEAD 的文件,该文件在任何合并(或任何其他危险 操作)后使用其前身的提交 ID 进行更新。因为我们使用 git flow,所以这是完美的,因为每次我们将 releasefix 推送到 master 分支并部署时,它都会更新。

我假设你可以用任何脚本语言来做到这一点,但在我们的例子中,PHP,我是这样做的......

define("MY_VERSION",substr(file_get_contents(realpath(__DIR__.'/../.git/ORIG_HEAD')),0,3));

您的路径显然必须根据您自己的目的进行调整,但这会导致 3 个字符的足够唯一 id 用于我们的目的,它现在被附加到我们的资源 URL 的末尾。

希望对处于相同情况的人有所帮助。

【讨论】:

    猜你喜欢
    • 2014-10-04
    • 2019-05-17
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    相关资源
    最近更新 更多