Как проверить допустимые имена ветвей Git?
Я разрабатываю крючок git
post-receive
в Python. Данные предоставляются на stdin
линиями, аналогичными
ef4d4037f8568e386629457d4d960915a85da2ae 61a4033ccf9159ae69f951f709d9c987d3c9f580 refs/heads/master
Первый хэш - это старый-ref, второй - 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.
Статус: все приведенные ниже выражения не скомпилируются. Это может указывать на наличие проблемы с моими script или несовместимыми синтаксисами.
Ответы
Ответ 1
Позволяет анализировать различные правила и создавать из них регулярные выражения:
-
Они могут включать слэш /
для иерархической (каталожной) группировки, но никакой компонент, разделенный слэш, не может начинаться с точки .
или заканчиваться последовательностью .lock
.
# must not contain /.
(?!.*/\.)
# must not end with .lock
(?<!\.lock)$
-
Они должны содержать хотя бы один /
. Это обеспечивает наличие категории, например head/, tags/и т.д., Но фактические имена не ограничены. Если используется опция --allow-onelevel
, это правило отменяется.
.+/.+ # may get more precise later
-
Они не могут иметь две последовательные точки ..
где угодно.
(?!.*\.\.)
-
Они не могут иметь управляющие символы ASCII (т.е. байты, значения которых ниже \040
или \177 DEL
), пробел, тильда ~
, карет ^
или двоеточие :
где угодно.
[^\000-\037\177 ~^:]+ # pattern for allowed characters
-
У них не может быть вопросительный знак ?
, звездочка *
или открыть скобку [
где угодно. См. Параметр --refspec-pattern
ниже для исключения из этого правила.
[^\000-\037\177 ~^:?*[]+ # new pattern for allowed characters
-
Они не могут начинаться или заканчиваться косой чертой /
или содержать несколько последовательных косых черт (см. параметр --normalize
ниже для исключения из этого правила)
^(?!/)
(?<!/)$
(?!.*//)
-
Они не могут заканчиваться точкой .
.
(?<!\.)$
-
Они не могут содержать последовательность @{
.
(?!.*@\{)
-
Они не могут быть единственным символом @
.
([email protected]$)
-
Они не могут содержать \
.
(?!.*\\)
Соединяя все это вместе, мы приходим к следующему чудовищу:
^(?!.*/\.)(?!.*\.\.)(?!/)(?!.*//)(?!.*@\{)([email protected]$)(?!.*\\)[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock)(?<!/)(?<!\.)$
И если вы хотите исключить те, которые начинаются с build-
, просто добавьте еще один lookahead:
^(?!build-)(?!.*/\.)(?!.*\.\.)(?!/)(?!.*//)(?!.*@\{)([email protected]$)(?!.*\\)[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock)(?<!/)(?<!\.)$
Это можно также оптимизировать, объединив несколько вещей, которые ищут общие шаблоны:
^([email protected]$|build-|/|.*([/.]\.|//|@\{|\\))[^\000-\037\177 ~^:?*[]+/[^\000-\037\177 ~^:?*[]+(?<!\.lock|[/.])$
Ответ 2
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
, но я ее не нашел.
Ответ 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 ++ для некоторого кода, хотя я внес некоторые исправления.
Ответ 4
Принимая правила непосредственно со связанной страницы, следующее регулярное выражение должно соответствовать только допустимым именам ветвей в refs/heads
, не начиная с "build -":
refs/heads/(?!.)(?!build-)((?!\.\.)([email protected]{)[^\cA-\cZ ~^:?*[\\])+))(?<!\.)(?<!\.lock)
Это начинается с refs/heads
, как это делает ваш.
Затем (?!build-)
проверяет, что следующие 6 символов не являются build-
и (?!.)
проверяет, что ветка не начинается с .
.
Вся группа (((?!\.\.)([email protected]{)[^\cA-\cZ ~^:?*[\\])+)
соответствует имени ветки.
(?!\.\.)
проверяет, нет ли экземпляров двух периодов в строке, а ([email protected]{)
проверяет, что ветвь не содержит @{
.
Затем [^\cA-\cZ ~^:?*[\\]
соответствует любому из допустимых символов, исключая управляющие символы \cA-\cZ
и все остальные символы, которые специально запрещены.
Наконец, (?<!\.)
гарантирует, что имя ветки не заканчивается на период, а (?<!.lock)
проверяет, что оно не заканчивается на .\lock
.
Это может быть расширено, чтобы аналогично соответствовать действительным именам ветвей в произвольных папках, вы можете использовать
(?!.)((?!\.\.)([email protected]{)[^\cA-\cZ ~^:?*[\\])+))(/(?!.)((?!\.\.)([email protected]{)[^\cA-\cZ ~^:?*[\\])+)))*?/(?!.)(?!build-)((?!\.\.)([email protected]{)[^\cA-\cZ ~^:?*[\\])+))(?<!\.)(?<!\.lock)
Это относится в основном к тем же правилам к каждой части имени ветки, но только проверяет, что последний не начинается с build-
Ответ 5
Для тех, кто подходит к этому вопросу, ищет регулярное выражение PCRE для соответствия допустимому имени ветки Git, это следующее:
^(?!/|.*([/.]\.|//|@\{|\\\\))[^\040\177 ~^:?*\[]+(?<!\.lock|[/.])$
Это измененная версия регулярного выражения, написанная Joey. Однако в этой версии наклонный не требуется (это соответствует branchName
, а не refs/heads/branchName
).
Обратитесь к его правильному ответу на этот вопрос.
Он обеспечивает полное разбиение каждой части регулярного выражения и то, как он относится к каждому требованию, указанному на страницах git-check-ref-format(1)
.