From: Junio C Hamano 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. 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: #!/bin/sh # This is a sample hooks/update script, written by JC # in his e-mail buffer, so naturally it is not tested # but hopefully would convey the idea. umask 002 case "$1" in refs/tags/*) # No overwriting an existing tag if test -f "$GIT_DIR/$1" then exit 1 fi ;; refs/heads/*) # No rebasing or rewinding if expr "$2" : '0*$' >/dev/null then # creating a new branch ; else # updating -- make sure it is a fast forward mb=`git-merge-base "$2" "$3"` case "$mb,$2" in "$2,$mb") ;; # fast forward -- happy *) exit 1 ;; # unhappy esac fi ;; *) # No funny refs allowed exit 1 ;; esac # Is the user allowed to update it? me=`id -u -n` ;# e.g. "junio" while read head_pattern users do if expr "$1" : "$head_pattern" >/dev/null then case " $users " in *" $me "*) exit 0 ;; # happy ' * ') exit 0 ;; # anybody esac fi done exit 1 For the sake of simplicity, I assumed that you keep something like this in $GIT_DIR/info/allowed-pushers file: refs/heads/master 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 I can do master branch and make versioned tags. And anybody can do tmp/blah branches. This assumes all the users are in a single group that can write into $GIT_DIR/ and underneath.