我正在尝试使用Git钩子进行一些高级操作,但实际上我不知道该如何实现(或者是否可能实现)。
我需要做的是:在每次提交时获取其哈希值,然后使用此哈希值更新提交中的一个文件。
有什么想法吗?
我正在尝试使用Git钩子进行一些高级操作,但实际上我不知道该如何实现(或者是否可能实现)。
我需要做的是:在每次提交时获取其哈希值,然后使用此哈希值更新提交中的一个文件。
有什么想法吗?
我建议您做与您想法类似的事情:将SHA1放在一个未跟踪的文件中,该文件是作为构建/安装/部署过程的一部分生成的。这很容易实现(git rev-parse HEAD > filename
或者 git describe [--tags] > filename
),并且避免了像最终的文件与Git跟踪的不同等诡异行为。
当需要版本号时,您的代码可以引用此文件,或者构建过程可以将该信息合并到最终产品中。实际上,这正是Git本身获取其版本号的方式 - 构建过程从存储库中获取版本号,然后将其构建到可执行文件中。
无法编写当前提交哈希:如果您设法预先计算未来的提交哈希,则只要修改任何文件,它就会更改。
但是,有三个选项:
pre-commit
中,存储上一个提交哈希 :) 99.99% 的情况下,您不会修改/插入提交,因此,这将起作用。在最坏的情况下,您仍然可以识别源修订版。我正在编写钩子脚本,'when it's done',将在这里发布,但是——比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) # https://dev59.com/5HI-5IYBdhLWcg3w9thw
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
对转换为真正的提交哈希值 :)
我不知道这种方法是否可以区分合并提交。很快就会去检查一下。
有人向我指出了关于ident的“man gitattributes”部分,其中包括以下内容:
ident
当为路径设置属性ident时,git会将blob对象中的$Id$替换为$Id:,后跟40个字符的十六进制blob对象名称,再跟一个美元符号$。在检出时,任何以$Id:开头并以$结尾的字节序列都会被替换为$Id$。
如果你仔细想一想,这也是CVS、Subversion等工具所做的。如果你查看存储库,你会发现存储库中的文件始终包含例如$Id$的标识符。它从不包含该标识符的扩展。只有在检出时,文本才会被扩展。
ident
是文件本身的哈希值,而不是提交的哈希值。从 http://git-scm.com/book/zh/v2/ 自定义 Git - Git 属性 中得知:"但这个结果的用处有限。如果你曾经使用过 CVS 或 Subversion 中的关键字替换,你可以包含一个日期时间戳——SHA 值并没有太大的帮助,因为它相当随机,并且你无法确定一个 SHA 值比另一个 SHA 值旧还是新。" filter
需要一些工作,但可以将提交信息输入(和输出)到文件中。 - Zach Youngfilter
属性来实现。您需要提供一个smudge
命令来插入提交ID,以及一个clean
命令来删除它,这样插入的文件不会仅仅因为提交ID而更改。跳出提交盒子的思维!
将此放入文件 hooks/post-checkout 中。
#!/bin/sh
git describe --all --long > config/git-commit-version.txt
这个版本将在您使用它的任何地方都可用。
git add config/git-commit-version.txt
。 - Jason Wheeler我认为你实际上不想这样做,因为当提交中的文件被更改时,提交的哈希值也会随之更改。
git describe --all --long > src/assets/git-commit-version.txt
这就是我在 GitHub actions 构建步骤中正在执行的操作。 - Rajendra让我通过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校验和。当您检查此消息时,立即出现两个问题。一个是tree sha1,另一个是提交时间。
现在,通过修改消息并猜测提交所需的时间或安排在特定时间提交,可以轻松解决提交时间的问题。真正的问题是tree sha1,您可以从git ls-tree $(git write-tree) | git mktree
获取它。基本上,您正在对来自ls-tree的消息进行sha1校验和,该消息是所有文件及其sha1校验和的列表。
因此,您的提交sha1校验和取决于您的tree sha1校验和,后者直接取决于文件sha1校验和,这样就完成了循环,并且依赖于提交sha1。因此,我所拥有的技术仍存在循环问题。
使用不太安全的校验和,已经证明可以通过暴力方法将文件的校验和写入文件本身;但是,我不知道是否有任何工作能够使用sha1完成此任务。这并非不可能,但是在我们目前的理解下几乎不可能(但是谁知道也许在几年后它将变得微不足道)。然而,即使更难以暴力破解,因为您必须将(blob)校验和的(tree)校验和的(commit)校验和写入文件中。
hooks/pre-commit
中我写下了以下代码:#!/bin/bash
ver_file=version.txt
> $ver_file
date +"%Y-%m-%d %T %:z" >> $ver_file
echo -n "Parent: " >> $ver_file
git rev-parse HEAD >> $ver_file
git add $ver_file
echo "Date-time and parent commit added to '$ver_file'"
exit 0
version.txt
文件:2023-05-24 01:24:12 +03:30
Parent: 35acd10240a55d164b371aa28812e8e988ab0c8d
version.txt
文件的存储方式与其他文件完全相同,并且也存储在remote
仓库和submodule
中,出于同样的原因,但是你必须确保同样的文件也存在于.git/hooks/pre-commit
中,以供你的子模块或远程仓库使用。GitHub和其他一些网站不支持此方法。
执行合并操作时,version.txt
总会产生冲突,但是除了需要进行commit -am <message>
之外,不需要采取进一步的操作,因为在执行合并的commit
之前,pre-commit也会被运行。