如何检查有效的Git分支名称?

时间:2012-08-23 14:17:32

标签: python regex git githooks

我正在开发git post-receive hook in Python。数据在stdin上提供,其行与

类似
ef4d4037f8568e386629457d4d960915a85da2ae 61a4033ccf9159ae69f951f709d9c987d3c9f580 refs/heads/master

第一个哈希是old-ref,第二个是new-ref,第三列是要更新的引用。

我想将其拆分为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

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

6 个答案:

答案 0 :(得分:29)

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

  1. 它们可以包括用于分层(目录)分组的斜杠/,但没有斜杠分隔的组件可以以点.开头或以序列.lock结束。 / p>

    # 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. 它们不能是单个字符@

    (?!@$)
    
  10. 它们不能包含\

    (?!.*\\)
    
  11. 将它们拼凑在一起我们得到了以下怪物:

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

    如果您想要排除那些以build-开头的内容,那么只需添加另一个前瞻:

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

    通过混淆一些寻找常见模式的东西,可以对此进行优化:

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

答案 1 :(得分:11)

git check-ref-format <ref> subprocess.Popen有可能:

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

优点:

  • 如果算法发生变化,检查将自动更新
  • 你肯定会把它弄好,这对怪物正则表达式来说更难了

缺点:

  • 因子进程而变慢。但过早的优化是所有邪恶的根源。
  • 要求Git作为二进制依赖项。但是在钩子的情况下,它总会在那里。
使用C绑定到pygit2

libgit2,如果check-ref-format暴露在那里会更好,因为它会比Popen更快,但我没找到它。

答案 2 :(得分: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 ++的一些代码,虽然我做了一些更正。

答案 3 :(得分: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-开头

答案 4 :(得分:1)

对于前来这个问题寻找PCRE正则表达式以匹配有效Git分支名称的人,它是以下内容:

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

这是由Joey编写的正则表达式的修订版本。但是,在此版本中,不需要倾斜(用于匹配branchName而不是refs/heads/branchName)。

请参阅他的正确answer to this question。 他提供了正则表达式各部分的完整细分,以及它与git-check-ref-format(1) manual pages中指定的每个要求的关系。

答案 5 :(得分:0)

如果要检查引用对pygit2是否有效,可以执行该功能(code copied from documentation):

from pygit2 import reference_is_valid_name
reference_is_valid_name("refs/heads/master")