防止他人使用不同的作者名称推送git提交?

26
在 Git 中,每个用户都需要在本地的 git 配置文件中指定正确的作者。当他们推送到集中式裸仓库时,该仓库上的提交消息将使用他们在提交到自己的仓库时使用的作者名称。
是否有一种方法可以强制使用一组已知的作者来进行提交?“中央”仓库将通过 SSH 访问。
我知道这很复杂,因为有些人可能会推送其他人提交的更改。当然,你也应该只允许你信任的人推送到你的仓库,但如果有一种方式可以防止用户错误,那就太好了。
在 Git 中是否有解决这个问题的简单方法?

可能是重复的问题,参见 防止其他Git作者 - Deer Hunter
被接受的答案并不是真正安全的。现在有人正在开玩笑:https://github.com/jayphelps/git-blame-someone-else - Deer Hunter
6个回答

10

使用 PRE-RECEIVE 钩子(请参阅 githooks(5) 了解详细信息)。在那里,您可以获取每个更新的引用的旧 SHA 和新 SHA。并且可以轻松列出更改并检查它们是否具有正确的作者(git rev-list --pretty=format:"%an %ae%n" oldsha..newsha)。

这是一个示例脚本:

#!/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$
# ^allowed@example.com$
#
# 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

10

我们使用以下方法来防止意外的未知作者提交(例如当从客户服务器上进行快速提交时)。它应该放置在.git/hooks/pre-receive中并被赋予可执行权限。

#!/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": "john.doe@example.com"
}

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
此脚本仅检查第一个更新的引用。通过执行以下操作,可以混入错误提交:git push origin goodbranch:foo evilbranch:master。 - Anders Waldenborg
新的分支推送应该使用不同的命令处理:git rev-list --pretty=format:%an%n%ae%n xyz --not --branches=*,并且应忽略分支删除。 - mrts

2
我们使用Gitlab,因此验证作者是否为Gitlab组成员是有意义的。
以下脚本(基于@dsvensson的答案)应该安装为pre-receive钩子,可以完全实现这一点:
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()

请查看GitLab自定义Git Hook文档以获取安装指南。
只有get_gitlab_group_members()是GitLab特有的,其他逻辑适用于任何pre-receive hook(包括处理分支删除和创建)。
该脚本现在可以在GitHub上获取,欢迎就错误/改进发送pull请求。

0
你可以创建一堆不同的用户账户,将它们全部放在同一组中,并给该组写入存储库的权限。然后,您应该能够编写一个简单的传入钩子,检查执行脚本的用户是否与更改集中的用户相同。
我从未这样做过,因为我信任检查代码的人,但如果有方法的话,那可能就是上面解释的方法。

这并不涉及将其他人作为更改集的一部分所做的提交推送。 - Aaron Maenpaa

0

Git最初并不是像SVN一样设计为使用一个大型中央仓库。也许你可以根据需要从其他人那里拉取代码,如果他们的作者设置不准确,则可以拒绝拉取。


0
如果你想管理一个面向互联网的Git仓库的权限,我建议你看看Gitosis而不是自己动手。身份验证由私钥/公钥对提供。
同时也请阅读pimping it here

3
使用gitosis,你仍然可以将作者名称更改为完全随意的内容,只需更改user.name和user.email。Gitosis根本不会阻止这种更改。 - Ibrahim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接