Squashed 'third_party/git/' content from commit cb71568594
git-subtree-dir: third_party/git git-subtree-split: cb715685942260375e1eb8153b0768a376e4ece7
This commit is contained in:
commit
1b593e1ea4
3629 changed files with 1139935 additions and 0 deletions
192
Documentation/howto/update-hook-example.txt
Normal file
192
Documentation/howto/update-hook-example.txt
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
From: Junio C Hamano <gitster@pobox.com> and Carl Baldwin <cnb@fc.hp.com>
|
||||
Subject: control access to branches.
|
||||
Date: Thu, 17 Nov 2005 23:55:32 -0800
|
||||
Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net>
|
||||
Abstract: An example hooks/update script is presented to
|
||||
implement repository maintenance policies, such as who can push
|
||||
into which branch and who can make a tag.
|
||||
Content-type: text/asciidoc
|
||||
|
||||
How to use the update hook
|
||||
==========================
|
||||
|
||||
When your developer runs git-push into the repository,
|
||||
git-receive-pack is run (either locally or over ssh) as that
|
||||
developer, so is hooks/update script. Quoting from the relevant
|
||||
section of the documentation:
|
||||
|
||||
Before each ref is updated, if $GIT_DIR/hooks/update file exists
|
||||
and executable, it is called with three parameters:
|
||||
|
||||
$GIT_DIR/hooks/update refname sha1-old sha1-new
|
||||
|
||||
The refname parameter is relative to $GIT_DIR; e.g. for the
|
||||
master head this is "refs/heads/master". Two sha1 are the
|
||||
object names for the refname before and after the update. Note
|
||||
that the hook is called before the refname is updated, so either
|
||||
sha1-old is 0{40} (meaning there is no such ref yet), or it
|
||||
should match what is recorded in refname.
|
||||
|
||||
So if your policy is (1) always require fast-forward push
|
||||
(i.e. never allow "git-push repo +branch:branch"), (2) you
|
||||
have a list of users allowed to update each branch, and (3) you
|
||||
do not let tags to be overwritten, then you can use something
|
||||
like this as your hooks/update script.
|
||||
|
||||
[jc: editorial note. This is a much improved version by Carl
|
||||
since I posted the original outline]
|
||||
|
||||
----------------------------------------------------
|
||||
#!/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"
|
||||
----------------------------------------------------
|
||||
|
||||
This uses two files, $GIT_DIR/info/allowed-users and
|
||||
allowed-groups, to describe which heads can be pushed into by
|
||||
whom. The format of each file would look like this:
|
||||
|
||||
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
|
||||
|
||||
With this, Linus can push or create "bw/penguin" or "bw/zebra"
|
||||
or "bw/panda" branches, Pasky can do only "cogito", and JC can
|
||||
do master and pu branches and make versioned tags. And anybody
|
||||
can do tmp/blah branches. The '+' sign at the pu record means
|
||||
that JC can make non-fast-forward pushes on it.
|
||||
Loading…
Add table
Add a link
Reference in a new issue