Как проверить допустимые имена ветвей Git?

Я разрабатываю git post-receive крюк в Python. Данные предоставляются на stdin С линиями, похожими на

ef4d4037f8568e386629457d4d960915a85da2ae 61a4033ccf9159ae69f951f709d9c987d3c9f580 refs/heads/master

первый хэш-это старый-ref, второй-новый-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.

Status: все приведенные ниже регулярные выражения не компилируются. Это может означать, что есть проблема с моим скриптом или несовместимыми синтаксисами.

5 ответов


давайте разберем различные правила и построим из них части регулярных выражений:

  1. они могут включать slash / для иерархической (директории) группировки, но ни один разделенный косой чертой компонент не может начинаться с точки . или завершите последовательность .lock.

    # must not contain /.
    (?!.*/\.)
    # must not end with .lock
    (?<!\.lock)$
    
  2. они должны содержать хотя бы один /. Это обеспечивает наличие такой категории, как heads/, tags/ etc. но настоящие имена не ограничены. Если --allow-onelevel используется опция, это правило отменяется.

    .+/.+  # may get more precise later
    
  3. у них не может быть двух последовательных точек .. в любом месте.

    (?!.*\.\.)
    
  4. они не могут иметь символы управления ASCII (т. е. байты, значения которых ниже 0 или 7 DEL), космос, Тильда ~, caret ^, или в любом месте.

    [^0-77 ~^:]+   # pattern for allowed characters
    
  5. у них не может быть вопросительного знака ? звездочки *, или открыть скобка [ в любом месте. Вижу --refspec-pattern опция ниже для исключения из этого правила.

    [^0-77 ~^:?*[]+   # new pattern for allowed characters
    
  6. они не могут начинаться или оканчиваться слешем / или содержат несколько последовательных косых черт (см. --normalize опция ниже для исключения из этого правила)

    ^(?!/)
    (?<!/)$
    (?!.*//)
    
  7. они не могут закончиться точкой ..

    (?<!\.)$
    
  8. они не могут содержать последовательность @{.

    (?!.*@\{)
    
  9. они не могут быть один символ @.

    (?!@$)
    
  10. они не могут содержать \.

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

и если вы хотите исключить те, которые начинаются с build- тогда просто добавьте еще один lookahead:

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

это может быть оптимизирован также объединение нескольких вещей, которые ищут общие шаблоны:

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

git check-ref-format <ref> С subprocess.Popen возможности:

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

плюсы:

  • если алгоритм когда-либо изменится, проверка будет обновляться автоматически
  • вы обязательно получите это право, что намного сложнее с монстром Regex

недостатки:

  • медленнее, потому что подпроцесс. Но преждевременная оптимизация-корень всех зол.
  • требуется Git как двоичная зависимость. Но в случае с крючком он всегда будет там.

pygit2, который использует привязки C до libgit2, было бы еще лучше, если бы check-ref-format выставляется там, так как это было бы быстрее, чем Popen, но я не нашел его.


нет необходимости писать монстры в 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)
   [^07 ~^:?*[]+  # (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++ для некоторого кода, хотя я сделал некоторые исправления.


принимая правила непосредственно со связанной страницы, следующее регулярное выражение должно соответствовать только допустимым именам ветвей в 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-


для тех, кто приходит к этому вопросу, ища регулярное выражение PCRE, чтобы соответствовать действительному имени ветви Git, это следующее:

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

Это измененная версия регулярного выражения, написанного Джой. В этой версии, однако, косой не требуется (это для сопоставления branchName, а не refs/heads/branchName).

пожалуйста, обратитесь к его правильной ответ на этот вопрос. Он обеспечивает полную разбивку каждой части regex, и как это относится к каждому требованию, указанному в git-check-ref-format(1) справочника.