预接收钩中的差异

时间:2018-07-12 23:54:54

标签: python git githooks

我用Python编写了一个简单的服务器端git pre-receive钩子。目标是分析差异和拒绝包含某些我们认为无效的文本的推送。我使用以下命令集编写了该钩子:

git ls-tree
git diff --name-only
git cat-file 

但是我只是注意到我正在扫描作为提交一部分推送的整个文件。但是我只想扫描差异,即在此推送中更改的行。

其原因是一些无效的文本可能是假阳性,并且可以。可以用力推动。但是,如果再次编辑同一文件并添加有效文本,则推送将被拒绝,因为该文件以前具有无效文本。每次编辑文件时都会发生这种情况,这很烦人

所以基本上,问题是,如何在服务器端挂钩代码的当前推送中仅获取更改后的linesdiff,而不是扫描完整文件。

谢谢

1 个答案:

答案 0 :(得分:2)

  

...如何只获得更改的行

此问题不完整。假设我告诉你,有些人包括爱丽丝,鲍勃,卡罗尔,等等。现在我告诉你鲍勃与众不同。 不同于谁或什么?

在预接收挂钩中,您必须从标准输入中读取行。每行的格式为:

old-hash new-hash reference-name

这些是什么意思? (尽管答案位于下面的最后一节中,但是在继续下一节之前,这是您要回答的练习。)

获得差异需要选择两个项目

提交是文件的快照-冻结在该提交中的每个文件的完整副本。没有涉及差异;只有完整的文件。

但是,您希望有所不同。为了使某些文件file.ext有所不同,您必须选择file.ext的某些其他版本并比较两者。什么是正确的“其他版本”?

对于某些提交,您很幸运:file.ext有一个非常清晰正确的“其他版本”,即:该提交的 parent file.ext的副本承诺。实际上,对于提交中的每个文件都会重复此操作:我们想比较该文件的提交版本和该文件的父版本,以查看更改了什么。

为此有一个方便的脚本命令(“管道”),它是git diff-tree:给定普通非合并提交的哈希ID,git diff-tree将提交的父级与提交进行比较。添加-p--patch以获得文本差异(这自动意味着-r选项)。考虑使用-U0删除上下文行。当然,您仍然需要解析输出行,以检测块头和添加/删除的标记。

简单的git diff-tree <hash>不适用于以下两种提交情况:

  • root 提交没有父级。幸运的是,empty tree得以解决:git diff-tree -p $(git hash-object -t tree /dev/null) $hash可以解决问题。

  • 一个 merge 提交有两个或多个父级。在此git diff-tree默认情况下会产生组合差异。如果可以,您可以忽略这种情况。如果不是,您可以考虑使用--first-parent -m或仅使用-m来拆分合并,并针对每个父级(默认)或第一个父级(--first-parent)获得多个差异。

这使您获得一个提交的区别,所以现在我们进入最后一部分。

现在该处理钩子的stdin输入行了

阅读每一行时,您的工作是:

  • 检查旧的和新的哈希以查找特殊的全零数字空哈希。在Python中,有多种表达方式。一个是:

    def is_null(hash):
        return all(i == '0' for i in hash)
    

    如果旧哈希为空,则在新哈希处创建引用。如果新哈希为null,则该引用曾经具有给定的旧哈希,并且正在被删除。否则-两个哈希都不为空-引用将被更新:它具有旧的哈希,并且将具有新的哈希。

  • 弄清楚对特定参考的更改要做什么。是否允许删除?允许创作吗?这是分支名称(以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将每个提交与其父目录或空树进行比较。因此,现在您可以编写您喜欢的预接收钩子了。