20

我正在git post-receive用 Python 开发一个钩子。数据stdin通过类似于以下的行提供

ef4d4037f8568e386629457d4d960915a85da2ae 61a4033ccf9159ae69f951f709d9c987d3c9f580 refs/heads/master

第一个哈希是旧引用,第二个是新引用,第三列是正在更新的引用。

我想把它分成 3 个变量,同时也验证输入。如何验证分支名称?

我目前正在使用以下正则表达式

^([0-9a-f]{40}) ([0-9a-f]{40}) refs/heads/([0-9a-zA-Z]+)$

这不接受所有可能的分支名称,如man git-check-ref-format 所述。例如,它排除了一个名为 的分支build-master,这是有效的。

加分

我实际上想排除任何以“build-”开头的分支。这可以在同一个正则表达式中完成吗?

测试

鉴于下面的好答案,我编写了一些测试,可以在 https://github.com/alexchamberlain/githooks/blob/master/miscellaneous/git-branch-re-test.py找到。

状态:下面的所有正则表达式都无法编译。这可能表明我的脚本有问题或语法不兼容。

4

6 回答 6

41

让我们剖析各种规则并从中构建正则表达式部分:

  1. 它们可以包含/用于分层(目录)分组的斜线,但斜线分隔的组件不能以点开头.或以序列结尾.lock

     # must not contain /.
     (?!.*/\.)
     # must not end with .lock
     (?<!\.lock)$
    
  2. 它们必须至少包含一个/. 这会强制存在诸如 head/、tags/ 等类别,但实际名称不受限制。如果--allow-onelevel使用该选项,则放弃此规则。

     .+/.+  # may get more precise later
    
  3. 他们不能在..任何地方有两个连续的点。

     (?!.*\.\.)
    
  4. 它们不能在任何地方包含 ASCII 控制字符(即值低于\040、 或的字节\177 DEL)、空格、波浪号~、插入符号^或冒号:

     [^\000-\037\177 ~^:]+   # pattern for allowed characters
    
  5. 它们在任何地方都不能有问号?、星号*或左括号[--refspec-pattern有关此规则的例外情况,请参阅下面的选项。

     [^\000-\037\177 ~^:?*[]+   # new pattern for allowed characters
    
  6. 它们不能以斜杠开始或结束,也不能/包含多个连续的斜杠(请参阅--normalize下面的选项以了解此规则的例外情况)

     ^(?!/)
     (?<!/)$
     (?!.*//)
    
  7. 它们不能以点结尾.

     (?<!\.)$
    
  8. 它们不能包含序列@{

     (?!.*@\{)
    
  9. 它们不能包含\.

     (?!.*\\)
    

将它们拼凑在一起,我们得到了以下怪物:

^(?!.*/\.)(?!.*\.\.)(?!/)(?!.*//)(?!.*@\{)(?!.*\\)[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock)(?<!/)(?<!\.)$

如果你想排除那些开始的,build-那么只需添加另一个前瞻:

^(?!build-)(?!.*/\.)(?!.*\.\.)(?!/)(?!.*//)(?!.*@\{)(?!.*\\)[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock)(?<!/)(?<!\.)$

这也可以通过合并一些寻找常见模式的东西来优化:

^(?!@$|build-|/|.*([/.]\.|//|@\{|\\))[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock|[/.])$
于 2012-08-23T14:31:12.667 回答
19

git check-ref-format <ref>subprocess.Popen一种可能性:

import subprocess
process = subprocess.Popen(["git", "check-ref-format", ref])
exit_status = process.wait()

好处:

  • 如果算法发生变化,检查将自动更新
  • 你一定会做对,这对于怪物正则表达式来说更难

缺点:

  • 较慢,因为子进程。但过早的优化是万恶之源。
  • 需要 Git 作为二进制依赖项。但在钩子的情况下,它会一直存在。

pygit2,它使用 C 绑定到libgit2,如果暴露在那里,将是一个更好的可能性check-ref-format,因为它会比 快Popen,但我还没有找到它。

于 2014-07-28T08:41:26.620 回答
3

没有必要用 Perl 编写怪物。只需使用 /x:

# RegExp rules based on git-check-ref-format
my $valid_ref_name = qr%
   ^
   (?!
      # begins with
      /|                # (from #6)   cannot begin with /
      # contains
      .*(?:
         [/.]\.|        # (from #1,3) cannot contain /. or ..
         //|            # (from #6)   cannot contain multiple consecutive slashes
         @\{|           # (from #8)   cannot contain a sequence @{
         \\             # (from #9)   cannot contain a \
      )
   )
                        # (from #2)   (waiving this rule; too strict)
   [^\040\177 ~^:?*[]+  # (from #4-5) valid character rules

   # ends with
   (?<!\.lock)          # (from #1)   cannot end with .lock
   (?<![/.])            # (from #6-7) cannot end with / or .
   $
%x;

foreach my $branch (qw(
   master
   .master
   build/master
   ref/HEAD/blah
   /HEAD/blah
   HEAD/blah/
   master.lock
   head/@{block}
   master.
   build//master
   build\master
   build\\master
),
   'master blaster',
) {
   print "$branch --> ".($branch =~ $valid_ref_name)."\n";
}

Joey++ 的一些代码,虽然我做了一些更正。

于 2013-05-31T12:54:44.103 回答
2

对于任何来此问题寻找 PCRE 正则表达式以匹配有效 Git 分支名称的人,它如下:

^(?!/|.*([/.]\.|//|@\{|\\\\))[^\040\177 ~^:?*\[]+(?<!\.lock|[/.])$

这是由Joey编写的正则表达式的修正版本。然而,在这个版本中,斜线不是必需的(它用于匹配branchName而不是refs/heads/branchName)。

请参考他对这个问题的正确答案。他提供了正则表达式每个部分的完整细分,以及它与git-check-ref-format(1)手册页上指定的每个要求的关系。

于 2012-12-05T14:52:06.033 回答
1

直接从链接页面获取规则,以下正则表达式应仅匹配refs/heads不以“build-”开头的有效分支名称:

refs/heads/(?!.)(?!build-)((?!\.\.)(?!@{)[^\cA-\cZ ~^:?*[\\])+))(?<!\.)(?<!\.lock)

这从refs/heads你的开始。

然后(?!build-)检查接下来的 6 个字符是否不是build-,并(?!.)检查分支是否以..

整个组(((?!\.\.)(?!@{)[^\cA-\cZ ~^:?*[\\])+)与分支名称匹配。

(?!\.\.)检查连续没有两个句点的实例,并(?!@{)检查分支不包含@{.

然后[^\cA-\cZ ~^:?*[\\]通过排除控制字符\cA-\cZ和所有其他明确禁止的字符来匹配任何允许的字符。

最后,(?<!\.)确保分支名称不以句点结尾,并(?<!.lock)检查它不以.\lock.

这可以扩展为类似地匹配任意文件夹中的有效分支名称,您可以使用

(?!.)((?!\.\.)(?!@{)[^\cA-\cZ ~^:?*[\\])+))(/(?!.)((?!\.\.)(?!@{)[^\cA-\cZ ~^:?*[\\])+)))*?/(?!.)(?!build-)((?!\.\.)(?!@{)[^\cA-\cZ ~^:?*[\\])+))(?<!\.)(?<!\.lock)

这对分支名称的每一部分应用基本相同的规则,但只检查最后一个不是以build-

于 2012-08-23T14:33:41.770 回答
1

如果您想使用pygit2检查引用是否有效,您可以执行该功能(从文档复制的代码):

from pygit2 import reference_is_valid_name
reference_is_valid_name("refs/heads/master")
于 2020-06-26T13:57:03.640 回答