2008-09-22 15 views
24

En git, le corresponde a cada usuario especificar el autor correcto en su archivo de configuración de git local. Cuando acceden a un repositorio centralizado, los mensajes de confirmación en el repositorio tendrán los nombres de autor que usaron al comprometerse con su propio repositorio.¿Evita que las personas presionen un commit de git con un nombre de autor diferente?

¿Existe alguna manera de hacer cumplir que se utiliza un conjunto de autores conocidos para commits? El repositorio "central" será accesible a través de ssh.

Sé que esto se complica por el hecho de que algunas personas pueden estar presionando compromisos que fueron hechos por otros. Por supuesto, también debería permitir que las personas en las que confía ingresen a sus repositorios, pero sería genial si hubiera una forma de evitar el error del usuario aquí.

¿Hay una solución simple a este problema en git?

+0

Posible duplicado de [Impedir a otros autores de git] (http://stackoverflow.com/questions/28401926/prevent-other-git-authors) –

+0

La respuesta aceptada no es realmente segura. La gente está haciendo bromas ahora: https://github.com/jayphelps/git-blame-someone-else –

Respuesta

9

Nos usar lo siguiente para evitar accidental desconocido-autor se compromete (por ejemplo, cuando se hace una confirmación rápida desde el servidor de un cliente o algo así). Debería colocarse en .git/hooks/pre-receive y hacerse ejecutable.

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import subprocess 
from itertools import islice, izip 
import sys 

old, new, branch = sys.stdin.read().split() 

authors = { 
    "John Doe": "[email protected]" 
} 

proc = subprocess.Popen(["git", "rev-list", "--pretty=format:%an%n%ae%n", "%s..%s" % (old, new)], stdout=subprocess.PIPE) 
data = [line.strip() for line in proc.stdout.readlines() if line.strip()] 

def print_error(commit, author, email, message): 
    print "*" * 80 
    print "ERROR: Unknown Author!" 
    print "-" * 80 
    proc = subprocess.Popen(["git", "rev-list", "--max-count=1", "--pretty=short", commit], stdout=subprocess.PIPE) 
    print proc.stdout.read().strip() 
    print "*" * 80 
    raise SystemExit(1) 

for commit, author, email in izip(islice(data, 0, None, 3), islice(data, 1, None, 3), islice(data, 2, None, 3)): 
    _, commit_hash = commit.split() 
    if not author in authors: 
     print_error(commit_hash, author, email, "Unknown Author") 
    elif authors[author] != email: 
     print_error(commit_hash, author, email, "Unknown Email") 
+4

Este script solo verifica la primera referencia actualizada. Uno podría colarse en commits malos al hacer: git push origen goodbranch: foo evilbranch: master –

+0

Los nuevos push de bifurcaciones deberían manejarse con un comando diferente: 'git rev-list --pretty = format:% an% n% ae% n xyz --no --branches = * 'y las supresiones de ramas deben ignorarse. – mrts

0

Lo que podría hacer es crear varias cuentas de usuario diferentes, ponerlas todas en el mismo grupo y darle a ese grupo acceso de escritura al repositorio. Entonces debería poder escribir un simple gancho entrante que verifique si el usuario que ejecuta el guión es el mismo que el usuario en el conjunto de cambios.

Nunca lo he hecho porque confío en los tipos que verifican el código en mis repositorios, pero si hay una manera, esa es probablemente la explicada anteriormente.

+0

Eso no se trata de empujar los commits que otras personas han hecho como parte del conjunto de cambios. –

0

git no fue inicialmente diseñado para funcionar como svn con un gran repositorio central. ¿Tal vez puede sacar de la gente según sea necesario, y se niegan a tirar si tienen su autor configurado incorrectamente?

0

Si desea gestionar los derechos de un repositorio de git en Internet, le sugiero que consulte Gitosis en lugar de actualizar el suyo. La identidad es provista por pares de claves privadas/públicas.

Léame pimping it here, también.

+3

Err, con gitosis aún puede cambiar el nombre del autor a algo completamente aleatorio, simplemente cambie user.name y user.email. La gitosis no lo impide en absoluto. – Ibrahim

9

Utilice el gancho PRE-RECEIVE (vea githooks(5) para más detalles). Ahí tienes sha viejo y sha nuevo por cada ref actualizada. Y puede enumerar fácilmente los cambios y verificar que tengan el autor adecuado (git rev-list --pretty = format: "% an% ae% n" oldsha..newsha).

Aquí es un script de ejemplo:

#!/bin/bash 
# 
# This pre-receive hooks checks that all new commit objects 
# have authors and emails with matching entries in the files 
# valid-emails.txt and valid-names.txt respectively. 
# 
# The valid-{emails,names}.txt files should contain one pattern per 
# line, e.g: 
# 
# ^.*@0x63.nu$ 
# ^[email protected]$ 
# 
# To just ensure names are just letters the following pattern 
# could be used in valid-names.txt: 
# ^[a-zA-Z ]*$ 
# 


NOREV=0000000000000000000000000000000000000000 

while read oldsha newsha refname ; do 
    # deleting is always safe 
    if [[ $newsha == $NOREV ]]; then 
    continue 
    fi 

    # make log argument be "..$newsha" when creating new branch 
    if [[ $oldsha == $NOREV ]]; then 
    revs=$newsha 
    else 
    revs=$oldsha..$newsha 
    fi 
    echo $revs 
    git log --pretty=format:"%h %ae %an%n" $revs | while read sha email name; do 
    if [[ ! $sha ]]; then 
     continue 
    fi 
     grep -q -f valid-emails.txt <<<"$email" || { 
      echo "Email address '$email' in commit $sha not registred when updating $refname" 
      exit 1 
     } 
     grep -q -f valid-names.txt <<<"$name" || { 
      echo "Name '$name' in commit $sha not registred when updating $refname" 
      exit 1 
     } 
    done 
done 
2

Utilizamos Gitlab, por lo que tiene sentido para nosotros validar autores en contra de los miembros del grupo Gitlab.

La siguiente secuencia de comandos (en base a la respuesta de @ dsvensson) que debe ser instalado como pre-recepción de gancho hace exactamente eso:

from __future__ import print_function 
from __future__ import unicode_literals 

import sys 
import os 
import subprocess 
import urllib2 
import json 
import contextlib 
import codecs 
from itertools import islice, izip 

GITLAB_SERVER = 'https://localhost' 
GITLAB_TOKEN = 'SECRET' 
GITLAB_GROUP = 4 
EMAIL_DOMAIN = 'example.com' 

def main(): 
    commits = get_commits_from_push() 
    authors = get_gitlab_group_members() 
    for commit, author, email in commits: 
     if author not in authors: 
      die('Unknown author', author, commit, authors) 
     if email != authors[author]: 
      die('Unknown email', email, commit, authors) 

def get_commits_from_push(): 
    old, new, branch = sys.stdin.read().split() 
    rev_format = '--pretty=format:%an%n%ae' 
    command = ['git', 'rev-list', rev_format, '{0}..{1}'.format(old, new)] 
    # branch delete, let it through 
    if new == '0000000000000000000000000000000000000000': 
     sys.exit(0) 
    # new branch 
    if old == '0000000000000000000000000000000000000000': 
     command = ['git', 'rev-list', rev_format, new, '--not', '--branches=*'] 
    output = subprocess.check_output(command) 
    commits = [line.strip() for line in unicode(output, 'utf-8').split('\n') if line.strip()] 
    return izip(islice(commits, 0, None, 3), 
      islice(commits, 1, None, 3), 
      islice(commits, 2, None, 3)) 

def get_gitlab_group_members(): 
    url = '{0}/api/v3/groups/{1}/members'.format(GITLAB_SERVER, GITLAB_GROUP) 
    headers = {'PRIVATE-TOKEN': GITLAB_TOKEN} 
    request = urllib2.Request(url, None, headers) 
    with contextlib.closing(urllib2.urlopen(request)) as response: 
     members = json.load(response) 
    return dict((member['name'], '{}@{}'.format(member['username'], EMAIL_DOMAIN)) 
     for member in members) 

def die(reason, invalid_value, commit, authors): 
    message = [] 
    message.append('*' * 80) 
    message.append("ERROR: {0} '{1}' in {2}" 
      .format(reason, invalid_value, commit)) 
    message.append('-' * 80) 
    message.append('Allowed authors and emails:') 
    print('\n'.join(message), file=sys.stderr) 
    for name, email in authors.items(): 
     print(u" '{0} <{1}>'".format(name, email), file=sys.stderr) 
    sys.exit(1) 

def set_locale(stream): 
    return codecs.getwriter('utf-8')(stream) 

if __name__ == '__main__': 
    # avoid Unicode errors in output 
    sys.stdout = set_locale(sys.stdout) 
    sys.stderr = set_locale(sys.stderr) 

    # you may want to skip HTTPS certificate validation: 
    # import ssl 
    # if hasattr(ssl, '_create_unverified_context'): 
    # ssl._create_default_https_context = ssl._create_unverified_context 

    main() 

Ver GitLab custom Git hooks docs las instrucciones de instalación.

Solo get_gitlab_group_members() es específico de Gitlab, otra lógica se aplica a cualquier gancho de pre-recepción (incluido el manejo de eliminaciones y creaciones de ramas).

La secuencia de comandos ahora es available in GitHub, no dude en enviar solicitudes de extracción para cualquier error/mejora.

Cuestiones relacionadas