2010-03-26 10 views
23

Viniendo de CVS, tenemos una política que los mensajes de confirmación deben etiquetarse con un número de error (sufijo simple "... [9999]"). Una secuencia de comandos CVS comprueba esto durante las confirmaciones y rechaza la confirmación si el mensaje no se ajusta.¿Cómo verificamos los mensajes de confirmación para un empujón?

El git hook commit-msg hace esto en el lado del desarrollador, pero nos parece útil tener sistemas automatizados de verificación y recordar esto.

Durante un git push, commit-msg no se ejecuta. ¿Hay otro gancho durante el envío que pueda verificar los mensajes de compromiso?

¿Cómo verificamos los mensajes de confirmación durante un git push?

Respuesta

23

Usando el gancho de actualización

Usted sabe acerca de ganchos - Por favor, lea la documentation sobre ellos! El gancho que probablemente quiera es la actualización, que se ejecuta una vez por ref. (El gancho de pre-recepción se ejecuta una vez durante todo el proceso). Hay toneladas y toneladas de preguntas y respuestas sobre estos ganchos que ya están en SO; dependiendo de lo que quieras hacer, probablemente puedas encontrar orientación sobre cómo escribir el enlace si lo necesitas.

Destacar que esto realmente es posible, una cita de la documentación:

Este gancho se puede utilizar para evitar la actualización forzada de ciertos árbitros, asegurándose de que el nombre del objeto es un objeto de comprometerse que es una descendiente del objeto commit nombrado por el antiguo nombre del objeto. Es decir, para hacer cumplir una política de "avance rápido".

También se podría utilizar para registrar el antiguo ... nuevo estado.

Y los detalles:

El gancho se ejecuta una vez para cada árbitro que actualizarse, y toma tres parámetros:

  • el nombre de la ref en proceso de actualización,
  • la nombre de objeto antiguo almacenado en la referencia,
  • y el nuevo nombre de objeto que se almacenará en la referencia.

Así, por ejemplo, si usted quiere asegurarse de que ninguno de los sujetos cometen son más de 80 caracteres, una aplicación muy rudimentaria sería:

#!/bin/bash 
long_subject=$(git log --pretty=%s $2..$3 | egrep -m 1 '.{81}') 
if [ -n "$long_subject" ]; then 
    echo "error: commit subject over 80 characters:" 
    echo " $long_subject" 
    exit 1 
fi 

Por supuesto, eso es una ejemplo de juguete; en el caso general, debe usar un resultado de registro que contenga el mensaje de compromiso completo, dividirlo por compromiso y llamar a su código de verificación en cada mensaje de compromiso individual.

Por qué desea que el gancho de actualización

Esto ha sido discutido/aclarado en los comentarios; aquí hay un resumen.

El gancho de actualización se ejecuta una vez por ref. Una referencia es un puntero a un objeto; en este caso, estamos hablando de ramas y etiquetas, y generalmente solo ramas (las personas no presionan las etiquetas con frecuencia, ya que generalmente son solo para marcar versiones).

Ahora, si un usuario está empujando cambios a dos ramas, principal y experimental:

o - o - o (origin/master) - o - X - o - o (master) 
\ 
    o - o (origin/experimental) - o - o (experimental) 

Supongamos que X es el "malo" cometer, es decir, la que fallaría el gancho cometer-msg. Claramente, no queremos aceptar el empuje para dominar. Por lo tanto, el gancho de actualización rechaza eso. ¡Pero no hay nada malo con los commits en experimental! El gancho de actualización acepta ese. Por lo tanto, origin/master se mantiene sin cambios, pero el origen/experimental se actualiza:

o - o - o (origin/master) - o - X - o - o (master) 
\ 
    o - o - o - o (origin/experimental, experimental) 

El pre-recibir el gancho se ejecuta sólo una vez, justo antes de comenzar a actualizar árbitros (antes de la primera vez que el gancho de actualización se ejecuta). Si lo usaste, tendrías que hacer que falle el impulso completo, diciendo que debido a que hubo un mensaje de compromiso erróneo en el maestro, de alguna manera ya no confías en que los commits en experimental sean buenos, ¡aunque sus mensajes estén bien!

+0

Creo que el gancho que el OP está buscando es pre-recibir, ya que él/ella quiere rechazar todo el impulso dependiendo del mensaje de confirmación. Sin embargo, AFAIK, ni pre-recibir ni actualizar recibir el mensaje de confirmación como entrada. Entonces, usar commit-msg probablemente sea la mejor solución. –

+0

@ Can: Estoy bastante seguro de que el OP quiere actualizar, no pre-recibir. "Todo el empuje" significa el empuje para todas las ramas. Si el usuario intenta insertar actualizaciones en tres ramas, y solo una contiene mensajes de confirmación inválidos, ¡los otros dos aún deben ser aceptados! – Cascabel

+0

@Can: Y no, el mensaje de confirmación no forma parte de la entrada, pero sí lo son los nombres de objeto (confirmación) antiguos y nuevos (SHA1). Tenga en cuenta que el enlace de actualización se ejecuta justo antes de actualizar los refs (después de que se hayan recibido los objetos de confirmación). Por lo tanto, el gancho puede usar git log para inspeccionar lo que quiera sobre las confirmaciones entre antiguas y nuevas, incluidos sus mensajes de confirmación. – Cascabel

1

Necesita hacer un script en su pre-recepción.

En este script, recibe la revisión anterior y la nueva. Puede verificar todos los commit y devolver false si uno de estos es malo.

5

Puede hacerlo con la siguiente pre-receive hook. Como las otras respuestas han notado, este es un enfoque conservador, de todo o nada. Tenga en cuenta que protege solo la rama maestra y no impone restricciones a los mensajes de confirmación en las ramas temáticas.

#! /usr/bin/perl 

my $errors = 0; 
while (<>) { 
    chomp; 
    next unless my($old,$new) = 
    m[^([0-9a-f]+) \s+ # old SHA-1 
     ([0-9a-f]+) \s+ # new SHA-1 
     refs/heads/master # ref 
     \s* $ ]x; 

    chomp(my @commits = `git rev-list $old..$new`); 
    if ($?) { 
    warn "git rev-list $old..$new failed\n"; 
    ++$errors, next; 
    } 

    foreach my $sha1 (@commits) { 
    my $msg = `git cat-file commit $sha1`; 
    if ($?) { 
     warn "git cat-file commit $sha1 failed"; 
     ++$errors, next; 
    } 

    $msg =~ s/\A.+? ^$ \s+//smx; 
    unless ($msg =~ /\[\d+\]/) { 
     warn "No bug number in $sha1:\n\n" . $msg . "\n"; 
     ++$errors, next; 
    } 
    } 
} 

exit $errors == 0 ? 0 : 1; 

Requiere todas las confirmaciones en una pulsación para tener un número de error en algún lugar de sus respectivos mensajes de confirmación, no solo en la sugerencia. Por ejemplo:

$ git log --pretty=oneline origin/master..HEAD 
354d783efd7b99ad8666db45d33e30930e4c8bb7 second [123] 
aeb73d00456fc73f5e33129fb0dcb16718536489 no bug number 

$ git push origin master 
Counting objects: 6, done. 
Delta compression using up to 2 threads. 
Compressing objects: 100% (4/4), done. 
Writing objects: 100% (5/5), 489 bytes, done. 
Total 5 (delta 0), reused 0 (delta 0) 
Unpacking objects: 100% (5/5), done. 
No bug number in aeb73d00456fc73f5e33129fb0dcb16718536489: 

no bug number 

To file:///tmp/bare.git 
! [remote rejected] master -> master (pre-receive hook declined) 
error: failed to push some refs to 'file:///tmp/bare.git'

decimos que solucionar el problema de aplastamiento de las dos confirmaciones juntos y empujando el resultado:

$ git rebase -i origin/master 
[...] 

$ git log --pretty=oneline origin/master..HEAD 
74980036dbac95c97f5c6bfd64a1faa4c01dd754 second [123] 

$ git push origin master 
Counting objects: 4, done. 
Delta compression using up to 2 threads. 
Compressing objects: 100% (2/2), done. 
Writing objects: 100% (3/3), 279 bytes, done. 
Total 3 (delta 0), reused 0 (delta 0) 
Unpacking objects: 100% (3/3), done. 
To file:///tmp/bare.git 
    8388e88..7498003 master -> master
2

Ésta es una versión de pitón de pre-receive, que me tomó un tiempo para terminar, la esperanza podría ayudar a otros. Lo uso principalmente con Trac, pero podría modificarse fácilmente para otros fines.

También he puesto las instrucciones para modificar el mensaje de confirmación histórica, que es un poco más complicado de lo que pensaba.

#!/usr/bin/env python 
import subprocess 

import sys 
import re 

def main(): 
    input = sys.stdin.read() 
    oldrev, newrev, refname = input.split(" ") 
    separator = "----****----" 


    proc = subprocess.Popen(["git", "log", "--format=%H%n%ci%n%s%b%n" + separator, oldrev + ".." + newrev], stdout=subprocess.PIPE) 
    message = proc.stdout.read() 
    commit_list = message.strip().split(separator)[:-1] #discard the last line 

    is_valid = True 

    print "Parsing message:" 
    print message 

    for commit in commit_list: 
     line_list = commit.strip().split("\n") 
     hash = line_list[0] 
     date = line_list[1] 
     content = " ".join(line_list[2:]) 
     if not re.findall("refs *#[0-9]+", content): #check for keyword 
      is_valid = False 

    if not is_valid: 
     print "Please hook a trac ticket when commiting the source code!!!" 
     print "Use this command to change commit message (one commit at a time): " 
     print "1. run: git rebase --interactive " + oldrev + "^" 
     print "2. In the default editor, modify 'pick' to 'edit' in the line whose commit you want to modify" 
     print "3. run: git commit --amend" 
     print "4. modify the commit message" 
     print "5. run: git rebase --continue" 
     print "6. remember to add the ticket number next time!" 
     print "reference: http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit" 

     sys.exit(1) 

main() 
1

Usted no ha mencionado lo que es su gestor de fallos, pero si es JIRA, a continuación, el add-on llamado Commit Policy puede hacer esto para sin necesidad de programación.

Puede configurar una condición de confirmación que requiera que el mensaje de confirmación coincida con una expresión regular. Si no lo hace, se rechaza la inserción, y el desarrollador debe corregir (corregir) el mensaje de confirmación y luego volver a presionar.

Cuestiones relacionadas