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 laterNo pueden tener dos puntos consecutivos
..cualquier lugar.(?!.*/./.)No pueden tener caracteres de control ASCII (es decir, bytes cuyos valores son inferiores a
/040o/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-patterncontinuación para ver una excepción a esta regla.[^/000-/037/177 ~^:?*[]+ # new pattern for allowed charactersNo pueden comenzar ni terminar con una barra diagonal o contener varias barras diagonales consecutivas (consulte la opción
--normalizecontinuació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.