Как проверить допустимые имена ветвей 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)
справочника.