我想创建 Git 钩子,它将在我的源代码中将我即将制作的提交的提交 ID 填充到文件中(基本上是变量替换)。这对 Git 可行吗?或者是通过将变量解析为 git id 的事实,我将更改 sha 1,从而以“鸡或蛋”问题结束。
9 回答
我用于类似情况的解决方案是这样的:
- 将字符串
$Id$
放在您想要识别的文件中的某个位置(例如test.html
),可能位于文件的注释或其他不会导致问题的非功能部分中。 - 在您的, 用关键字(例如)
.gitattributes
标记有问题的文件。ident
*.html ident
这样做的结果是,当git checkout
将文件从对象数据库复制到您的工作目录时,它会将$Id$
字符串展开为 read $Id: <sha-1 of file>$
,并git add
在您想要签入时反转该转换,因此对象数据库中的该文件的版本仅永远包含$Id$
,而不是扩展的形式。
这是一个开始,但不幸的是,找到包含具有特定散列的文件的提交并不是那么容易,也不一定是一对一的。因此,此外,我还使用export-subst
属性(例如*.html ident export-subst
in .gitattributes
)标记这些文件,并添加一个额外的字符串,就像$Format:%ci$ ($Format:%h$)
文件中的某处一样。
git checkout
不过,git add
不要影响这些标签,所以我的存储库中的版本总是有那个字符串。为了扩展这些标签,您必须使用git archive
创建项目特定版本的 tar-ball(或 .zip),然后使用它来部署该版本 - 您将无法仅复制文件make install
或其他任何东西,因为git archive
这是唯一可以扩展这些标签的东西。
我作为示例给出的两个标签扩展为YYYY-MM-DD HH:MM:SS +TZOFFSET (HASH)
,HASH
在这种情况下,这里是实际的提交哈希,所以它更有用。
$Format:$
您可以在说明符下的git log
帮助页面中找到其他可能有用的说明--pretty-format
符。
做你想做的事是不可能的:提交的 SHA-1 哈希是在包括每个成员文件在内的整个存储库快照上计算的,所以存在先有鸡还是先有蛋的问题——要计算提交的哈希,你需要知道所有文件的内容包括它。
你可以用post-commit
钩子做到这一点。这是git-scm 网站的摘录
整个提交过程完成后,提交后挂钩运行。它不带任何参数,但您可以通过运行 git log -1 HEAD 轻松获取最后一次提交。通常,此脚本用于通知或类似的东西。
这将是获取输出的情况git log -1 HEAD
,然后使用类似sed
替换文件中变量的工具。但是,这会修改您的工作目录,除非您要丢弃这些更改,否则您最终会得到一个永久修改的工作目录。
如果您只想在代码中的某个变量中使用当前提交哈希,您可以执行git log -1 HEAD
或cat .git/HEAD
将输出存储在您的变量中
如果您只想要问题标题中的 id(哈希),则可以使用该--format
标志。git log -1 HEAD --format=%H
您可以创建一个过滤器,在提交和签出时对文件进行替换。这些被称为“涂抹”和“清洁”过滤器,它们的操作通过 控制.gitattributes
。例如:
*.c filter=yourfilter
这告诉 git为所有文件运行yourfilter
过滤器。.c
然后你必须告诉 git 是什么yourfilter
意思:
git config --global filter.yourfilter.clean script1
git config --global filter.yourfilter.smudge script2
然后,您将编写一个脚本(sed、Perl、Python 或其他任何东西)来替换一个表达式,例如$LastSha$
用$LastSha: <sha>$
on checkout(“smudge”)。另一个脚本在提交之前反转扩展(“干净”。)
在 Pro Git 书籍中搜索“关键字扩展”以获取详细示例。
好的,受 Jon Cairns 回答的启发,我想出了这个可以放入 Makefile 的小片段。
version.h:
git log -n 1 --format=format:"#define GIT_COMMIT \"%h\"%n" HEAD > $@
这不是一个完全通用的解决方案,但它可以派上用场。我知道我将使用它的一两个地方。
我一直在寻找这个问题的答案。提交 ID 已写入文件,您只需要知道在哪里查找即可。在主分支上提交后,您可以
./.git/refs/heads/master
在我们的持续交付解决方案中找到提交哈希(它会下载 .git 文件夹以及源代码),我们可以简单地
cat ./.git/refs/heads/${BRANCH}
将当前提交哈希与我们的构建相关联
正如其他人所提到的,您不能在同一提交期间将提交本身的 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] 开发人员都熟悉的事情:
- vi 许可证
- Shift-G # 转到文件末尾
- dd # 删除当前行
- ZZ # 保存文件并退出
- git diff # diff 显示删除尾随换行符 注意:diff没有显示对 $FrauBSD$ 值的更改 [尚未]
- git 添加许可证
- git diff # 无(没有未分级的更改)
- git diff --cached # diff 显示尾随换行符的删除 注意:diff [仍然] 不显示对 $FrauBSD$ 值的更改
- git status # 显示修改后的 LICENSE
- git commit # $EDITOR 出现
- Ctrl-Z # 将 $EDITOR 放在后台以便我们进行调查
- git diff --cached # diff [now] 显示 $FrauBSD$ 更新以及删除尾随换行符
- fg # 恢复 $EDITOR
- :问!# quit Editor without changes 注意:因为您中止了提交,所以 $FrauBSD$ 已恢复
- git diff --cached # diff [再次] 仅显示尾随换行符删除
- git commit # 这次我们不会中止
- BumpZZ # 插入“Bump”,保存退出
- 文件按原样提交
注意:提交后无需对文件进行任何操作
那是因为我的项目中有以下文件:
- .git/hooks/pre-commit(../../.hooks/pre-commit 的符号链接)
- .git/hooks/commit-msg(../../.hooks/commit-msg 的符号链接)
- .hooks/预提交
- .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
这里的关键是将您的修订 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 最终出现在部署的可执行文件中。
我一直在寻找类似的东西,因为我想要一个可以添加到前端资源文件(如 CSS/JS)末尾的唯一变量,这将允许我们设置非常长的缓存时间以减少带宽并增加性能,但很容易在任何提交后强制它们重新加载。本质上是文件版本控制,但完全自动化。我不在乎它是最新的,只要它在我们所有的应用服务器中是独一无二的、自动化的和一致的。
我们的部署脚本只是使用 'git clone' 将最新代码的副本下载到我们的应用服务器中,但我们通过 .htaccess 限制对这些文件和目录的访问。
该/.git/
目录包含一个名为的文件,该文件ORIG_HEAD
在任何合并(或任何其他危险操作)后使用其前身的提交 ID 进行更新。由于我们使用 git flow,这是完美的,因为每次我们将 arelease
或 a推fix
送到master分支并部署时,它都会更新。
我假设你可以用任何脚本语言来做到这一点,但在我们的例子中,PHP,我是这样做的......
define("MY_VERSION",substr(file_get_contents(realpath(__DIR__.'/../.git/ORIG_HEAD')),0,3));
您的路径显然必须根据您自己的目的进行调整,但这会导致 3 个字符的唯一id 足以满足我们的目的,它现在被附加到我们的资源 URL 的末尾。
希望对处于相同情况的人有所帮助。