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

我在这里尝试用 Git hook 做一些奇特的事情,但是我真的不知道如何去做(或者是否可能)。

我需要做的是: 在每个提交中,我想获取它的散列,然后用这个散列更新提交中的文件。

有什么想法吗?

63346 次浏览

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

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

我认为您实际上并不希望这样做,因为当提交中的文件发生更改时,提交的散列也会发生更改。

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

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

编写当前的提交散列是不可能的: 如果您设法预先计算未来的提交散列ーー只要修改任何文件,它就会发生变化。

然而,有三种选择:

  1. 使用一个脚本来增加“提交 id”并将其包含在某个地方
  2. . gitigore 的文件,你要存储哈希到。不是很方便
  3. pre-commit中,存储 上一次提交散列:)在99.99% 的情况下您不修改/插入提交,因此,这将工作。在最坏的情况下,您仍然可以识别源修订。

我正在编写一个 hook 脚本,将在“完成后”发布到这里,但仍然比永远的毁灭公爵发布的时间要早:)

更新 : .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://stackoverflow.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对转换为一个真正的提交散列:)

我不知道这种方法是否能区分合并提交

有人指给我看身份证上的“ man gitproperties”部分,有这个:

身份证

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

如果你仔细想想,这也是 CVS、 Subversion 等公司所做的。如果查看存储库,您将看到存储库中的文件总是包含,例如,$Id $。它从来不包含这种扩展。只有在结帐时,文本才会展开。

让我来探讨一下为什么使用 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,另一个是提交时间。

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

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

使用 不太安全的校验和,已经证明可以通过强制手段将文件的校验和写入文件本身; 但是,我不知道有任何工作可以用 sha1完成这项任务。这并非不可能,但以我们目前的理解,这几乎是不可能的(但谁知道也许再过几年,这将变得微不足道)。但是,由于必须将(blob)校验和的(树)校验和的(提交)校验和写入文件,因此这仍然更难强制执行。

跳出承诺框去思考!

将其放入文件 hook/post-checkout

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

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