2008-09-22 5 views
24

In git ist es jedem Benutzer überlassen, den korrekten Autor in seiner lokalen Git-Konfigurationsdatei anzugeben. Wenn sie in ein zentrales blasses Repository pushen, haben die Commit-Nachrichten im Repository die Autorennamen, die sie bei der Übergabe an ihr eigenes Repository verwendet haben.Verhindere, dass Leute einen Git Commit mit einem anderen Autorennamen machen?

Gibt es eine Möglichkeit zu erzwingen, dass eine Reihe von bekannten Autoren für Commits verwendet werden? Das "zentrale" Repository wird über ssh erreichbar sein.

Ich weiß, dass dies durch die Tatsache kompliziert wird, dass einige Leute Commits schieben, die von anderen gemacht wurden. Natürlich sollten Sie auch Personen, denen Sie vertrauen, nur erlauben, in Ihre Repositories zu wechseln, aber es wäre großartig, wenn es hier einen Weg gäbe, um Benutzerfehler zu vermeiden.

Gibt es eine einfache Lösung für dieses Problem in Git?

+0

Mögliche Duplikate von [Andere Git Autoren verhindern] (http://stackoverflow.com/questions/28401926/prevent-other-git-authors) –

+0

Die angenommene Antwort ist nicht wirklich sicher. Die Leute laufen jetzt Streiche: https://github.com/jayphelps/git-blame-someone-else –

Antwort

9

Wir verwenden folgende zu verhindern versehentlicher unknown-Autor verpflichtet (zum Beispiel, wenn sie von einem Kunden-Server oder etwas ein schnelles begehen tun). Es sollte in .git/hooks/pre-receive platziert und ausführbar gemacht werden.

#!/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

Dieses Skript prüft nur die erste aktualisierte Referenz. Man könnte sich in schlechte Commits schleichen, indem man tut: git push Herkunft goodbranch: foo evilbranch: master –

+0

Neue Branch Pushs sollten mit einem anderen Befehl behandelt werden: 'git rev-list --pretty = format:% an% n% ae% n xyz --not --branches = * 'und Verzweigungsdeletionen sollten ignoriert werden. – mrts

0

Was könnten Sie tun, ist eine Reihe von verschiedenen Benutzerkonten erstellen, setzen sie alle in der gleichen Gruppe und geben diese Gruppe Schreibzugriff auf das Repository. Dann sollten Sie in der Lage sein, einen einfachen eingehenden Hook zu schreiben, der prüft, ob der Benutzer, der das Skript ausführt, mit dem Benutzer im Änderungsset identisch ist.

Ich habe es nie getan, weil ich die Jungs vertrauen, die Code in meine Repositories überprüfen, aber wenn es einen Weg gibt, das ist wahrscheinlich die, die oben erklärt.

+0

Das geht nicht mit Push-Commits, die andere Leute als Teil des Change-Set gemacht haben. –

0

git war ursprünglich nicht dafür ausgelegt, wie svn mit einem großen zentralen repository zu funktionieren. Vielleicht können Sie bei Bedarf von Leuten ziehen und sich weigern zu ziehen, wenn sie ihren Autor falsch gesetzt haben?

0

Wenn Sie die Rechte an einem Internet-zugewandten Git Repo verwalten möchten, schlage ich vor, dass Sie sich Gitosis ansehen und nicht Ihren eigenen aufpeitschen. Die Identität wird durch private/öffentliche Schlüsselpaare bereitgestellt.

Lesen Sie mich pimping it here, auch.

+3

Err, mit Gitosis kannst du den Namen des Autors immer noch ganz zufällig ändern, ändere einfach user.name und user.email. Gitosis verhindert das überhaupt nicht. – Ibrahim

9

Verwenden der PRE-RECEIVE Haken (siehe githooks(5) für Details). Dort bekommt man alten Sha und neuen Sha für jeden Ref aktualisiert. Und kann die Änderungen leicht auflisten und prüfen, ob sie einen richtigen Autor haben (git rev-list --pretty = format: "% an% ae% n" oldsha...newsha). Hier

ist ein Beispiel Skript:

#!/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

Wir verwenden Gitlab und deshalb ist es sinnvoll, Autoren gegen Gitlab-Gruppenmitglieder zu validieren.

Das folgende Skript (basierend auf @ dsvensson Antwort), die als Pre-Empfang Haken installiert werden soll genau das tut:

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() 

Siehe GitLab custom Git hooks docs für Installationsanweisungen.

Nur get_gitlab_group_members() ist Gitlab-spezifisch, andere Logik gilt für jeden Pre-Receive-Hook (einschließlich der Behandlung von Zweiglöschungen und -erstellungen).

Das Skript ist jetzt available in GitHub, wenden Sie sich bitte Pull-Anforderungen für Fehler/Verbesserungen zu senden.