Как проверить допустимые имена ветвей 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 ответов
давайте разберем различные правила и построим из них части регулярных выражений:
- 
они могут включать slash
/для иерархической (директории) группировки, но ни один разделенный косой чертой компонент не может начинаться с точки.или завершите последовательность.lock.# must not contain /. (?!.*/\.) # must not end with .lock (?<!\.lock)$ - 
они должны содержать хотя бы один
/. Это обеспечивает наличие такой категории, как heads/, tags/ etc. но настоящие имена не ограничены. Если--allow-onelevelиспользуется опция, это правило отменяется..+/.+ # may get more precise later - 
у них не может быть двух последовательных точек
..в любом месте.(?!.*\.\.) - 
они не могут иметь символы управления ASCII (т. е. байты, значения которых ниже
0или7 DEL), космос, Тильда~, caret^, или в любом месте.[^0-77 ~^:]+ # pattern for allowed characters - 
у них не может быть вопросительного знака
?звездочки*, или открыть скобка[в любом месте. Вижу--refspec-patternопция ниже для исключения из этого правила.[^0-77 ~^:?*[]+ # new pattern for allowed characters - 
они не могут начинаться или оканчиваться слешем
/или содержат несколько последовательных косых черт (см.--normalizeопция ниже для исключения из этого правила)^(?!/) (?<!/)$ (?!.*//) - 
они не могут закончиться точкой
..(?<!\.)$ - 
они не могут содержать последовательность
@{.(?!.*@\{) - 
они не могут быть один символ
@.(?!@$) - 
они не могут содержать
\.(?!.*\) 
^(?!.*/\.)(?!.*\.\.)(?!/)(?!.*//)(?!.*@\{)(?!@$)(?!.*\)[^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) справочника.