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