script run receive hooks example before automatically python regex git githooks

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:

  1. 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)$

  2. 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

  3. No pueden tener dos puntos consecutivos .. cualquier lugar.

    (?!.*/./.)

  4. 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

  5. ¿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

  6. 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)

    ^(?!/) (?<!/)$ (?!.*//)

  7. No pueden terminar con un punto . .

    (?<!/.)$

  8. No pueden contener una secuencia @{ .

    (?!.*@/{)

  9. No pueden ser el único carácter @ .

    (?!@$)

  10. 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.