python - 预接收挂钩中的差异
问题描述
我用 Python 编写了一个简单的服务器端 git pre-receive 钩子。目标是分析差异并拒绝包含我们认为无效的某些文本的推送。我使用以下命令集编写了钩子:
git ls-tree
git diff --name-only
git cat-file
但是我只是注意到我正在扫描作为提交的一部分推送的整个文件。但我只想扫描差异,即这次推送中更改的行。
原因是一些无效的文本可能是误报并且没关系。它可以被强制推动。但是,如果再次编辑相同的文件并添加有效文本,推送将被拒绝,因为该文件先前具有无效文本。每次编辑文件时都会发生这种情况,这有点烦人
所以基本上问题是,如何在当前推送服务器端钩子代码中获取更改的linesdiff,而不是扫描完整的文件。
谢谢
解决方案
...如何获得更改后的行
这个问题不完整。假设我告诉你有一些人,包括 Alice、Bob、Carol 等等。现在我告诉你鲍勃是不同的。 与谁或什么不同?
在预接收挂钩中,您必须从标准输入中读取行。每行具有以下形式:
old-hash new-hash reference-name
这些是什么意思?(这是您在继续下一部分之前回答的练习,尽管答案包含在下面的最后一部分中。)
获得差异需要您选择两个项目
提交是文件的快照——被冻结到该提交中的每个文件的完整副本。不涉及任何差异;只有完整的文件。
但是,您想要差异。要获得某些文件的差异file.ext
,您必须选择其他版本并file.ext
比较两者。什么是正确的“其他版本”?
对于某些提交,您很幸运:有一个非常明确正确的“其他版本” file.ext
,即:该file.ext
提交的父提交中的副本。事实上,这对提交中的每个文件都重复:我们希望将该文件的提交版本与该文件的父版本进行比较,以查看发生了什么变化。
有一个方便的脚本(“管道”)命令,它是git diff-tree
:给定普通非合并提交的哈希 ID,git diff-tree
将提交的父级与提交进行比较。添加-p
或--patch
获得文本差异(这自动暗示该-r
选项)。考虑使用-U0
删除上下文行。当然,您仍然需要解析输出行,以检测大块标题和添加/删除的标记。
然而,一个简单的git diff-tree <hash>
方法不适用于两种提交情况:
根提交没有父级。幸运的是,空树来拯救:成功
git diff-tree -p $(git hash-object -t tree /dev/null) $hash
了。合并提交有两个或多个父级。这里
git diff-tree
默认产生一个组合差异。如果没问题,你可以忽略这种情况。如果没有,您可能会考虑使用--first-parent -m
或仅-m
针对每个父级(默认)或第一个父级(--first-parent
)拆分合并并获取多个差异。
这让你获得了一次提交的差异,所以现在我们进入最后一部分。
现在是时候处理钩子的标准输入行了
当您阅读每一行时,您的工作是:
检查特殊的全零位空散列的旧散列和新散列。在 Python 中,有多种表达方式;一种是:
def is_null(hash): return all(i == '0' for i in hash)
如果旧散列为空,则在新散列处创建引用。如果新哈希为空,则引用曾经具有给定的旧哈希,并且正在被删除。否则——哈希值都不为空——引用被更新:它有旧的哈希值,并且将有新的哈希值。
弄清楚要做什么,如果有的话,改变特定的参考。是否允许删除?允许创作吗?如果这是一个分支名称(以 开头
refs/heads/
)与一个标签名称(以 开头)还是refs/tags/
完全不同的东西,这有关系吗?创作尤其困难。新引入的名称使给定的对象可以通过该名称访问。如果对象是一个标签或提交,这使得其他对象也可以通过该名称访问。这些对象中的一些或全部可能是新的。这些对象中的一些或全部可能已经存在。经典案例是当有人创建一个新的分支名称时:它可能指向一个现有的提交,已经在某个其他分支上,或者它可能指向一个新的提交,新分支的新提示,它可能有许多额外的新提交在加入一些现有的分支机构之前。
更新是最常见的,通常也是最容易处理的。您知道现有的引用名称使旧对象可访问,而建议的更新是使新对象可访问。如果引用是一个分支名称,那么这两个对象实际上都是提交对象,并且很容易找到哪些提交(如果有)是从提议的新哈希中新近可访问的,以及哪些提交(如果有)正在从可访问性中删除提议的新哈希:
git rev-list $old..$new
生成一组新近可达的哈希 ID,并且:
git rev-list $new..$old
产生不再可达的集合。(使用
git rev-list --left-right $old...$new
带有三个点的 ,一次获得两组哈希 ID,并带有区分标记。您可以使用$new...$old
: 这产生的对称差异本身是对称的,当然除了左右两边是相反的。)
假设您已经以某种方式处理了创建,如果您的目标是检查新近可访问的提交(无论它们对存储库是否是新的),您可以简单地遍历所有新提交,测试每个提交以查看它是否是根提交,普通(单父)提交或合并提交。(提示:添加--parents
到git rev-list
命令以获取包含的父 ID,以便您可以轻松地知道每个提交有多少父。另外,考虑您正在行走的提交图片段的图结构:$old..$new
可能包括合并,这可能会产生很多可访问的提交,对于存储库可能是新的,也可能不是新的。)
您现在拥有所有的提交哈希,以及它们的父级计数。您还知道如何根据git diff-tree
需要将每个提交与其父级或空树进行比较。所以现在你已经准备好编写你喜欢的预接收钩子了。
推荐阅读
- javascript - “元素”和“文档元素”有什么区别?
- android - 如何在 API 级别 28 中弹出通知后唤醒锁定的屏幕?
- reactjs - 我想为我的 React 应用程序从多首歌曲中随机选择一首背景歌曲
- oauth-2.0 - 对于 Google+ API 和 OAuth 关闭,我需要进行哪些更改?
- amazon-web-services - 单个对象的 Cloudfront TPS
- python - Execute lines instead of print (python & cmd)
- ios - 如何在真实的 ios 设备上运行带有热重载的 react-native 应用程序?
- python - Android .apk 与 kivy + matplotlib
- c - 如果数组中存在 0 到 n-1 范围内的所有元素,则返回 0 或 1 的函数,运行时间 O(n)
- ios - 如何使用 Swift 对数组进行 L2 标准化