Вас может заинтересовать хук обновления , который написал Junio и который Карл улучшил. Поместите код ниже в $GIT_DIR/hooks/update
и не забудьте включить его с помощью chmod +x
.
#!/bin/bash
umask 002
# If you are having trouble with this access control hook script
# you can try setting this to true. It will tell you exactly
# why a user is being allowed/denied access.
verbose=false
# Default shell globbing messes things up downstream
GLOBIGNORE=*
function grant {
$verbose && echo >&2 "-Grant- $1"
echo grant
exit 0
}
function deny {
$verbose && echo >&2 "-Deny- $1"
echo deny
exit 1
}
function info {
$verbose && echo >&2 "-Info- $1"
}
# Implement generic branch and tag policies.
# - Tags should not be updated once created.
# - Branches should only be fast-forwarded unless their pattern starts with '+'
case "$1" in
refs/tags/*)
git rev-parse --verify -q "$1" &&
deny >/dev/null "You can't overwrite an existing tag"
;;
refs/heads/*)
# No rebasing or rewinding
if expr "$2" : '0*$' >/dev/null; then
info "The branch '$1' is new..."
else
# updating -- make sure it is a fast-forward
mb=$(git-merge-base "$2" "$3")
case "$mb,$2" in
"$2,$mb") info "Update is fast-forward" ;;
*) noff=y; info "This is not a fast-forward update.";;
esac
fi
;;
*)
deny >/dev/null \
"Branch is not under refs/heads or refs/tags. What are you trying to do?"
;;
esac
# Implement per-branch controls based on username
allowed_users_file=$GIT_DIR/info/allowed-users
username=$(id -u -n)
info "The user is: '$username'"
if test -f "$allowed_users_file"
then
rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
while read heads user_patterns
do
# does this rule apply to us?
head_pattern=${heads#+}
matchlen=$(expr "$1" : "${head_pattern#+}")
test "$matchlen" = ${#1} || continue
# if non-ff, $heads must be with the '+' prefix
test -n "$noff" &&
test "$head_pattern" = "$heads" && continue
info "Found matching head pattern: '$head_pattern'"
for user_pattern in $user_patterns; do
info "Checking user: '$username' against pattern: '$user_pattern'"
matchlen=$(expr "$username" : "$user_pattern")
if test "$matchlen" = "${#username}"
then
grant "Allowing user: '$username' with pattern: '$user_pattern'"
fi
done
deny "The user is not in the access list for this branch"
done
)
case "$rc" in
grant) grant >/dev/null "Granting access based on $allowed_users_file" ;;
deny) deny >/dev/null "Denying access based on $allowed_users_file" ;;
*) ;;
esac
fi
allowed_groups_file=$GIT_DIR/info/allowed-groups
groups=$(id -G -n)
info "The user belongs to the following groups:"
info "'$groups'"
if test -f "$allowed_groups_file"
then
rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
while read heads group_patterns
do
# does this rule apply to us?
head_pattern=${heads#+}
matchlen=$(expr "$1" : "${head_pattern#+}")
test "$matchlen" = ${#1} || continue
# if non-ff, $heads must be with the '+' prefix
test -n "$noff" &&
test "$head_pattern" = "$heads" && continue
info "Found matching head pattern: '$head_pattern'"
for group_pattern in $group_patterns; do
for groupname in $groups; do
info "Checking group: '$groupname' against pattern: '$group_pattern'"
matchlen=$(expr "$groupname" : "$group_pattern")
if test "$matchlen" = "${#groupname}"
then
grant "Allowing group: '$groupname' with pattern: '$group_pattern'"
fi
done
done
deny "None of the user's groups are in the access list for this branch"
done
)
case "$rc" in
grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;;
deny) deny >/dev/null "Denying access based on $allowed_groups_file" ;;
*) ;;
esac
fi
deny >/dev/null "There are no more rules to check. Denying access"
С этим хуком вы даете конкретным пользователям или группам вносить изменения в репозиторий. Любой, кто увидит его, имеет доступ только для чтения.
Здесь используются два файла, $GIT_DIR/info/allowed-users
и allowed-groups
, для описания того, в какие головы кто-то может толкнуть. Формат каждого файла будет выглядеть следующим образом:
refs/heads/master junio
+refs/heads/pu junio
refs/heads/cogito$ pasky
refs/heads/bw/.* linus
refs/heads/tmp/.* .*
refs/tags/v[0-9].* junio
При этом Линус может выдвигать или создавать ветви bw/penguin
или bw/zebra
или bw/panda
, Pasky может делать только cogito
, а JC может создавать master
и pu
ветви и создавать версионные теги. И любой может сделать tmp/blah
ветви. Знак «+» в записи pu
означает, что JC может делать на него нажатия без ускоренной перемотки вперед.
Если у этого человека еще нет доступа к хосту, на котором находится ваш репозиторий, возможно, у этого человека должен быть только git-shell
доступ, а не неограниченный доступ. Создайте специального пользователя git и в ~git/.ssh/authorized_keys
добавьте внешний ключ SSH в следующей форме. Обратите внимание, что ключ должен быть на одной длинной строке, но я обернул его ниже, чтобы облегчить представление.
no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding,
command="env myorg_git_user=joeuser /usr/local/bin/git-shell -c
\"${SSH_ORIGINAL_COMMAND:-}\"" ssh-rsa AAAAB3...2iQ== joeuser@foo.invalid
В зависимости от вашей локальной настройки, вам может потребоваться настроить путь на git-shell
. Помните, что sshd
крайне параноидален в отношении разрешений для каталога .ssh
, поэтому отключите его биты групповой записи и все файлы под ним.
Проведение всех через пользователя git означает, что вам нужно уметь отличать людей друг от друга, и это является целью переменной окружения myorg_git_user
. Вместо того чтобы полагаться на безусловный username=$(id -u -n)
, настройте ваш хук обновления, чтобы использовать его:
# Implement per-branch controls based on username
allowed_users_file=$GIT_DIR/info/allowed-users
if [ -z "$myorg_git_user" ]; then
username=$(id -u -n)
else
username=$myorg_git_user
fi
info "The user is: '$username'"
При такой настройке ваш друг с доступом только для чтения будет клонировать с помощью команды, аналогичной приведенной ниже. Конкретный путь будет зависеть от вашей настройки. Чтобы сделать хороший путь, либо переместите ваш репозиторий в домашний каталог пользователя git, либо создайте символическую ссылку, которая на него указывает.
$ git clone git@blankman.com.invalid:coolproject.git
но не сможет делать обновления.
$ git push origin mybranch
Total 0 (delta 0), reused 0 (delta 0)
remote: error: hook declined to update refs/heads/mybranch
To git@blankman.com.invalid:coolproject.git
! [remote rejected] mybranch -> mybranch (hook declined)
error: failed to push some refs to 'git@blankman.com.invalid:coolproject.git'
Вы сказали, что работаете в командной среде, поэтому я предполагаю, что ваш центральный репозиторий был создан с опцией --shared
. (См. core.sharedRepository
в git config
документации и --shared
в git init
документации .) Убедитесь, что новый пользователь git является членом группы системы, которая дает всем вам доступ к вашему центральному хранилищу.