在 Git 中,如何将当前提交哈希写入同一提交中的文件

Posted

技术标签:

【中文标题】在 Git 中,如何将当前提交哈希写入同一提交中的文件【英文标题】:In Git, how can I write the current commit hash to a file in the same commit 【发布时间】:2011-03-27 10:51:05 【问题描述】:

我正在尝试用 Git 钩子做一些花哨的事情,但我真的不知道该怎么做(或者如果可能的话)。

我需要做的是:在每次提交中,我都想获取它的哈希值,然后用这个哈希值更新提交中的文件。

有什么想法吗?

【问题讨论】:

基本上我有一个 Web 应用程序,我想将该应用程序的已安装版本与该版本关联的确切提交相关联。我最初的想法是使用提交哈希更新一种 about.html 文件。但是在研究了git的对象模型之后,我意识到这是不可能的=/ 这是一个很实际的问题。我也遇到过! 就我而言,我希望我的程序在日志中写入这样的消息:“myprog 正在启动,v.56c6bb2”。这样,如果有人提交了错误并将日志文件发送给我,我可以确切地找出我的程序正在运行的版本。 @Jefromi,实际用例其实很常见,很容易打到初学者。将真实版本以某种方式“印”到基线文件中是基本需求,而且为什么它会是一个错误的想法远非显而易见,例如因为这几乎是您使用手动修订控制黑客的唯一选择。 (记住初学者。)此外,许多项目根本没有任何类型的构建/安装/部署步骤,可以将版本抓取并标记到实时文件中。无论如何,即使在这些情况下,结帐后挂钩也可以提供帮助,而不是预先提交。 这不可能!如果你能做到这一点,你就破坏了 SHA-1 哈希算法......ericsink.com/vcbe/html/cryptographic_hashes.html 【参考方案1】:

这可以通过使用gitattributes 中的filter 属性来实现。您需要提供一个插入提交 ID 的 smudge 命令和一个删除它的 clean 命令,这样插入的文件不会因为提交 ID 而改变。

因此,提交 ID 永远不会存储在文件的 blob 中;它只是在您的工作副本中扩展。 (实际上将提交 id 插入到 blob 中将成为一个无限递归的任务。☺)任何克隆这棵树的人都需要为自己设置属性。

【讨论】:

不可能任务,不是递归任务。提交散列取决于树散列,树散列取决于文件散列,文件散列取决于文件内容。你必须获得自我一致性。除非你会为 SHA-1 哈希找到一种 [generalized] 定点 @Jakub,git 中是否有某种技巧可以创建不修改结果哈希的跟踪文件?也许有某种方法可以覆盖它的哈希值。这将是一个解决方案:) @o_O Tync:不可能。更改的文件意味着(文件的)更改的哈希 - 这是设计使然,并且是哈希函数的定义。 这是一个很好的解决方案,但请记住,这涉及到在克隆存储库时必须手动安装的钩子。【参考方案2】:

我认为您实际上并不想这样做,因为当提交中的文件更改时,提交的哈希值也会更改。

【讨论】:

【参考方案3】:

我建议您按照您的想法做一些事情:将 SHA1 放在一个 untracked 文件中,该文件是作为构建/安装/部署过程的一部分生成的。这显然很容易做到(git rev-parse HEAD > filename 或者 git describe [--tags] > filename),并且它避免了做任何疯狂的事情,比如最终得到一个与 git 跟踪的文件不同的文件。

然后,您的代码可以在需要版本号时引用此文件,或者构建过程可以将信息合并到最终产品中。后者实际上是 git 自己获取版本号的方式——构建过程从 repo 中获取版本号,然后将其构建到可执行文件中。

【讨论】:

有人可以进一步详细说明如何执行此操作吗?或者至少是朝着正确的方向轻推? @Joel 怎么办?我提到了如何将哈希放入文件中;其余的可能与您的构建过程有关?如果你想问这部分,也许是一个新问题。 在我的例子中,我向我的 Makefile 添加了一个规则,该规则在每次构建时生成一个“gitversion.h”文件。见***.com/a/38087913/338479 您可以使用“git-checkout”钩子自动执行此操作。问题是必须手动安装挂钩。【参考方案4】:

不可能写出当前的提交哈希:如果你设法预先计算未来的提交哈希——一旦你修改任何文件,它就会改变。

但是,有三个选项:

    使用脚本来增加“提交 ID”并将其包含在某处。丑 .gitignore 要将哈希存储到的文件。不是很方便 在pre-commit 中,存储上一个提交哈希 :) 在 99.99% 的情况下,您不会修改/插入提交,因此,这将起作用。在最坏的情况下,您仍然可以识别源版本。

我正在编写一个钩子脚本,将在“完成后”将其发布在此处,但仍然 - 在 Duke Nukem Forever 发布之前 :))

更新.git/hooks/pre-commit的代码:

#!/usr/bin/env bash
set -e

#=== 'prev-commit' solution by o_O Tync
#commit_hash=$(git rev-parse --verify HEAD)
commit=$(git log -1 --pretty="%H%n%ci") # hash \n date
commit_hash=$(echo "$commit" | head -1)
commit_date=$(echo "$commit" | head -2 | tail -1) # 2010-12-28 05:16:23 +0300

branch_name=$(git symbolic-ref -q HEAD) # http://***.com/questions/1593051/#1593487
branch_name=$branch_name##refs/heads/
branch_name=$branch_name:-HEAD # 'HEAD' indicates detached HEAD situation

# Write it
echo -e "prev_commit='$commit_hash'\ndate='$commit_date'\nbranch='$branch'\n" > gitcommit.py

现在我们唯一需要的是将prev_commit,branch 对转换为真正的提交哈希的工具:)

我不知道这种方法是否可以区分合并提交。很快就会去看看

【讨论】:

【参考方案5】:

有人指点我到 ident 上的“man gitattributes”部分,其中有:

身份

当为路径设置属性 ident 时,git 将 blob 对象中的 $Id$ 替换为 $Id:,后跟 40 个字符的十六进制 blob 对象名称,结帐时后跟美元符号 $。任何字节序列 以 $Id: 开头并以 $ 结尾的工作树文件在签入时替换为 $Id$。

如果你仔细想想,CVS、Subversion 等也是这样做的。如果查看存储库,您会发现存储库中的文件始终包含例如 $Id$。它从不包含那个的扩展。只有在结帐时才会扩展文本。

【讨论】:

ident 是文件本身的哈希值,而不是提交的哈希值。来自git-scm.com/book/en/…:“但是,该结果的用途有限。如果您在 CVS 或 Subversion 中使用过关键字替换,则可以包含一个日期戳——SHA 并不是很有帮助,因为它是相当随机的,您可以”不知道一个 SHA 是比另一个旧还是新。” filter 需要工作,但它可以将提交信息输入(和输出)文件。【参考方案6】:

让我探讨一下为什么使用 git 内部是一个具有挑战性的问题。您可以通过

获取当前提交的sha1
#!/bin/bash
commit=$(git cat-file commit HEAD) #
sha1=($((printf "commit %s\0" $(echo "$commit" | wc -c); echo "$commit") | sha1sum))
echo $sha1[0]

基本上你对git cat-file commit HEAD返回的消息运行一个sha1校验和。当您检查此消息时,有两件事会立即成为问题。一是树sha1,二是提交时间。

现在,通过更改消息并猜测提交或安排在特定时间提交需要多长时间,可以轻松处理提交时间。真正的问题是树 sha1,您可以从 git ls-tree $(git write-tree) | git mktree 获得。本质上,您正在对来自 ls-tree 的消息进行 sha1 校验和,这是所有文件及其 sha1 校验和的列表。

因此你的提交 sha1 校验和取决于你的树 sha1 校验和,它直接取决于文件 sha1 校验和,它完成了循环并依赖于提交 sha1。因此,您对我自己可用的技术有一个循环问题。

使用less secure checksums,已经证明可以通过蛮力将文件的校验和写入文件本身;但是,我不知道任何使用 sha1 完成该任务的工作。这不是不可能的,但以我们目前的理解几乎是不可能的(但谁知道可能在几年后它会变得微不足道)。但是,这仍然更难暴力破解,因为您必须将(blob)校验和的(树)校验和的(提交)校验和写入文件。

【讨论】:

有没有一种方法可以提交文件,然后进行签出并将最新的提交哈希作为注释放在每个源代码文件的开头?然后从中构建并运行?【参考方案7】:

跳出提交框思考!

将其放入文件 hooks/post-checkout

#!/bin/sh
git describe --all --long > config/git-commit-version.txt

该版本将在您使用它的任何地方可用。

【讨论】:

我稍微修改了您的答案,以确保版本文件始终包含在提交中,方法是在末尾添加:git add config/git-commit-version.txt

以上是关于在 Git 中,如何将当前提交哈希写入同一提交中的文件的主要内容,如果未能解决你的问题,请参考以下文章

如何将提交哈希添加到可执行文件的详细信息

如何将我当前的更改提交到 Git 中的不同分支 [重复]

获取 Node 中最近一次 git 提交的哈希值

如何使 Git 提交哈希在 C++ 代码中可用而无需重新编译?

sh 提交哈希中的git show文件

通过 Git 中的 SHA 哈希恢复提交? [复制]