Git不像SVN或者CVS那样,有明确的一套权限控制方法
在Git中想要实施对仓库的版本控制,大概可以有以下2个办法:

1.Git Hook:


Hooks are little scripts you can place in $GIT_DIR/hooks directory to trigger action at certain points. When git-init is run, a handful of example hooks are copied into the hooks directory of the new repository, but by default they are all disabled. To enable a hook, rename it by removing its .sample suffix


git默认提供了很多类型的hooks,比如pre-commit,post-commit,post-checkout,update,pre-receive....
我们可以在update的hooks中进行控制,当用户在local repository上执行git push,在remote repository更新之前,会调用该hook
该hook存放在$GIT_DIR/hooks/update下

可参考http://www.kernel.org/pub/software/scm/git/docs/howto/update-hook-example.txt

以下是update脚本内容,本人没有测试过是否可用


-- >8 -- beginning of script -- >8 --

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

-- >8 -- end of script -- >8 --
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.


注意:最后需要配置$GIT_DIR/info/allowed-users和 $GIT_DIR/info/allowed-groups这2个文件,里面定义了每个user对应的可操作的分支、标签。

采用hook的方式还是比较简陋的,它要求在git服务器上为每个需要控制的用户都建立一个真实的用户,操作起来比较麻烦,而且还会衍生该用户其它的权限问题,对于一个大的团队来说,并不太合适。

2.Gitosis:

参见http://www.tech126.com/?p=60/

使用Gitosis来管理权限,相对于hook方式来说,省掉了建立n多用户的麻烦,但它是通过ssh来连接git服务器的,所以需要每个用户生成对应的公钥文件,并提交到Gitosis-admin中。

注意:Gitosis的权限控制本质上是针对每个Client的,而不是针对每个user,所以使用它并不能校验user.name是否合法,它只会校验Client是否有权限,对于大家都在一个公共机器上做开发,一定要每个用户都必须git config user.name xxxx。