python - run - github
¿Cómo verifico los nombres válidos de las sucursales de Git? (5)
Estoy desarrollando un gancho de post-receive
git
en Python . Los datos se suministran en stdin
con líneas similares a
ef4d4037f8568e386629457d4d960915a85da2ae 61a4033ccf9159ae69f951f709d9c987d3c9f580 refs/heads/master
El primer hash es el antiguo-ref, el segundo el nuevo-ref y la tercera columna es la referencia que se está actualizando.
Quiero dividir esto en 3 variables, mientras que también validar la entrada. ¿Cómo validar el nombre de la rama?
Actualmente estoy usando la siguiente expresión regular
^([0-9a-f]{40}) ([0-9a-f]{40}) refs/heads/([0-9a-zA-Z]+)$
Esto no acepta todos los nombres de sucursales posibles, tal como lo establece man git-check-ref-format . Por ejemplo, excluye una rama por el nombre de build-master
, que es válido.
Marcas de bonificación
De hecho, quiero excluir cualquier rama que comience con "build-". ¿Se puede hacer esto en la misma expresión regular?
Pruebas
Dadas las excelentes respuestas a continuación, escribí algunas pruebas, que se pueden encontrar en https://github.com/alexchamberlain/githooks/blob/master/miscellaneous/git-branch-re-test.py .
Estado: todas las expresiones regulares siguientes no se compilan. Esto podría indicar que hay un problema con mi script o sintaxis incompatibles.
Al tomar las reglas directamente de la página vinculada, la siguiente expresión regular debe coincidir solo con los nombres de rama válidos en refs/heads
no comiencen con "build-":
refs/heads/(?!.)(?!build-)((?!/./.)(?!@{)[^/cA-/cZ ~^:?*[//])+))(?<!/.)(?<!/.lock)
Esto comienza con refs/heads
como el tuyo.
Luego (?!build-)
verifica que los siguientes 6 caracteres no sean build-
y (?!.)
Verifica que la rama no comience con a .
.
El grupo completo (((?!/./.)(?!@{)[^/cA-/cZ ~^:?*[//])+)
coincide con el nombre de la rama.
(?!/./.)
verifica que no haya instancias de dos períodos seguidos, y (?!@{)
verifica que la rama no contenga @{
.
Luego [^/cA-/cZ ~^:?*[//]
coincide con cualquiera de los caracteres permitidos al excluir los caracteres de control /cA-/cZ
y todos los demás caracteres que están específicamente prohibidos.
Finalmente, (?<!/.)
asegura de que el nombre de la rama no termine con un punto y (?<!.lock)
verifique que no termine con ./lock
.
Esto se puede extender para que coincida de manera similar con los nombres de las sucursales válidas en carpetas arbitrarias.
(?!.)((?!/./.)(?!@{)[^/cA-/cZ ~^:?*[//])+))(/(?!.)((?!/./.)(?!@{)[^/cA-/cZ ~^:?*[//])+)))*?/(?!.)(?!build-)((?!/./.)(?!@{)[^/cA-/cZ ~^:?*[//])+))(?<!/.)(?<!/.lock)
Esto aplica básicamente las mismas reglas a cada parte del nombre de la rama, pero solo verifica que la última no comience con la build-
Analicemos las distintas reglas y construyamos partes de expresiones regulares a partir de ellas:
Pueden incluir barra diagonal
/
para agrupación jerárquica (directorio), pero ningún componente separado por barra puede comenzar con un punto.
o terminar con la secuencia.lock
.# must not contain /. (?!.*//.) # must not end with .lock (?<!/.lock)$
Deben contener al menos una
/
. Esto impone la presencia de una categoría como heads /, tags / etc. pero los nombres reales no están restringidos. Si se usa la opción--allow-onelevel
, esta regla no se aplica..+/.+ # may get more precise later
No pueden tener dos puntos consecutivos
..
cualquier lugar.(?!.*/./.)
No pueden tener caracteres de control ASCII (es decir, bytes cuyos valores son inferiores a
/040
o/177 DEL
), espacio, tilde~
, caret^
o dos puntos:
cualquier lugar.[^/000-/037/177 ~^:]+ # pattern for allowed characters
¿No pueden tener un signo de interrogación
?
, asterisco*
, o corchete abierto[
cualquier lugar. Consulte la opción--refspec-pattern
continuación para ver una excepción a esta regla.[^/000-/037/177 ~^:?*[]+ # new pattern for allowed characters
No pueden comenzar ni terminar con una barra diagonal o contener varias barras diagonales consecutivas (consulte la opción
--normalize
continuación para obtener una excepción a esta regla)^(?!/) (?<!/)$ (?!.*//)
No pueden terminar con un punto
.
.(?<!/.)$
No pueden contener una secuencia
@{
.(?!.*@/{)
No pueden ser el único carácter
@
.(?!@$)
No pueden contener un
/
.(?!.*//)
Uniendo todo esto llegamos a la siguiente monstruosidad:
^(?!.*//.)(?!.*/./.)(?!/)(?!.*//)(?!.*@/{)(?!@$)(?!.*//)[^/000-/037/177 ~^:?*[]+/[^/000-/037/177 ~^:?*[]+(?<!/.lock)(?<!/)(?<!/.)$
Y si desea excluir aquellos que comienzan con build-
entonces simplemente agregue otro lookahead:
^(?!build-)(?!.*//.)(?!.*/./.)(?!/)(?!.*//)(?!.*@/{)(?!@$)(?!.*//)[^/000-/037/177 ~^:?*[]+/[^/000-/037/177 ~^:?*[]+(?<!/.lock)(?<!/)(?<!/.)$
Esto también se puede optimizar un poco al combinar algunas cosas que buscan patrones comunes:
^(?!@$|build-|/|.*([/.]/.|//|@/{|//))[^/000-/037/177 ~^:?*[]+/[^/000-/037/177 ~^:?*[]+(?<!/.lock|[/.])$
No hay necesidad de escribir monstruosidades en Perl. Solo usa / 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 ++ para algunos de los códigos, aunque hice algunas correcciones.
Para cualquiera que venga a esta pregunta que busca la expresión regular PCRE para que coincida con un nombre de rama Git válido, es lo siguiente:
^(?!/|.*([/.]/.|//|@/{|////))[^/040/177 ~^:?*/[]+(?<!/.lock|[/.])$
Esta es una versión modificada de la expresión regular escrita por . En esta versión, sin embargo, no se requiere un oblicuo (es para hacer coincidir branchName
lugar de branchName
refs/heads/branchName
).
Por favor refiérase a su respuesta correcta a esta pregunta . Proporciona un desglose completo de cada parte de la expresión regular, y cómo se relaciona con cada requisito especificado en las páginas de manual de git-check-ref-format(1)
.
git check-ref-format <ref>
con subprocess.Popen
es una posibilidad:
import subprocess
process = subprocess.Popen(["git", "check-ref-format", ref])
exit_status = process.wait()
Ventajas:
- Si el algoritmo cambia, la comprobación se actualizará automáticamente.
- estás seguro de hacerlo bien, lo cual es mucho más difícil con un monstruo Regex
Desventajas:
- Más lento porque subproceso. Pero la optimización prematura es la raíz de todo mal.
- Requiere Git como una dependencia binaria. Pero en el caso de un gancho siempre estará ahí.
pygit2 , que usa enlaces de C a libgit2 , sería una posibilidad aún mejor si el check-ref-format
está expuesto allí, ya que sería más rápido que Popen
, pero no lo he encontrado.