"Fossies" - the Fresh Open Source Software Archive

Member "howto/update-hook-example.txt" (15 Dec 2018, 6281 Bytes) of package /linux/misc/git-htmldocs-2.20.1.tar.xz:


As a special service "Fossies" has tried to format the requested text file into HTML format (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "update-hook-example.txt": 2.19.1_vs_2.19.2.

    1 From: Junio C Hamano <gitster@pobox.com> and Carl Baldwin <cnb@fc.hp.com>
    2 Subject: control access to branches.
    3 Date: Thu, 17 Nov 2005 23:55:32 -0800
    4 Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net>
    5 Abstract: An example hooks/update script is presented to
    6  implement repository maintenance policies, such as who can push
    7  into which branch and who can make a tag.
    8 Content-type: text/asciidoc
    9 
   10 How to use the update hook
   11 ==========================
   12 
   13 When your developer runs git-push into the repository,
   14 git-receive-pack is run (either locally or over ssh) as that
   15 developer, so is hooks/update script.  Quoting from the relevant
   16 section of the documentation:
   17 
   18     Before each ref is updated, if $GIT_DIR/hooks/update file exists
   19     and executable, it is called with three parameters:
   20 
   21            $GIT_DIR/hooks/update refname sha1-old sha1-new
   22 
   23     The refname parameter is relative to $GIT_DIR; e.g. for the
   24     master head this is "refs/heads/master".  Two sha1 are the
   25     object names for the refname before and after the update.  Note
   26     that the hook is called before the refname is updated, so either
   27     sha1-old is 0{40} (meaning there is no such ref yet), or it
   28     should match what is recorded in refname.
   29 
   30 So if your policy is (1) always require fast-forward push
   31 (i.e. never allow "git-push repo +branch:branch"), (2) you
   32 have a list of users allowed to update each branch, and (3) you
   33 do not let tags to be overwritten, then you can use something
   34 like this as your hooks/update script.
   35 
   36 [jc: editorial note.  This is a much improved version by Carl
   37 since I posted the original outline]
   38 
   39 ----------------------------------------------------
   40 #!/bin/bash
   41 
   42 umask 002
   43 
   44 # If you are having trouble with this access control hook script
   45 # you can try setting this to true.  It will tell you exactly
   46 # why a user is being allowed/denied access.
   47 
   48 verbose=false
   49 
   50 # Default shell globbing messes things up downstream
   51 GLOBIGNORE=*
   52 
   53 function grant {
   54   $verbose && echo >&2 "-Grant-		$1"
   55   echo grant
   56   exit 0
   57 }
   58 
   59 function deny {
   60   $verbose && echo >&2 "-Deny-		$1"
   61   echo deny
   62   exit 1
   63 }
   64 
   65 function info {
   66   $verbose && echo >&2 "-Info-		$1"
   67 }
   68 
   69 # Implement generic branch and tag policies.
   70 # - Tags should not be updated once created.
   71 # - Branches should only be fast-forwarded unless their pattern starts with '+'
   72 case "$1" in
   73   refs/tags/*)
   74     git rev-parse --verify -q "$1" &&
   75     deny >/dev/null "You can't overwrite an existing tag"
   76     ;;
   77   refs/heads/*)
   78     # No rebasing or rewinding
   79     if expr "$2" : '0*$' >/dev/null; then
   80       info "The branch '$1' is new..."
   81     else
   82       # updating -- make sure it is a fast-forward
   83       mb=$(git merge-base "$2" "$3")
   84       case "$mb,$2" in
   85         "$2,$mb") info "Update is fast-forward" ;;
   86 	*)	  noff=y; info "This is not a fast-forward update.";;
   87       esac
   88     fi
   89     ;;
   90   *)
   91     deny >/dev/null \
   92     "Branch is not under refs/heads or refs/tags.  What are you trying to do?"
   93     ;;
   94 esac
   95 
   96 # Implement per-branch controls based on username
   97 allowed_users_file=$GIT_DIR/info/allowed-users
   98 username=$(id -u -n)
   99 info "The user is: '$username'"
  100 
  101 if test -f "$allowed_users_file"
  102 then
  103   rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
  104     while read heads user_patterns
  105     do
  106       # does this rule apply to us?
  107       head_pattern=${heads#+}
  108       matchlen=$(expr "$1" : "${head_pattern#+}")
  109       test "$matchlen" = ${#1} || continue
  110 
  111       # if non-ff, $heads must be with the '+' prefix
  112       test -n "$noff" &&
  113       test "$head_pattern" = "$heads" && continue
  114 
  115       info "Found matching head pattern: '$head_pattern'"
  116       for user_pattern in $user_patterns; do
  117         info "Checking user: '$username' against pattern: '$user_pattern'"
  118         matchlen=$(expr "$username" : "$user_pattern")
  119         if test "$matchlen" = "${#username}"
  120         then
  121           grant "Allowing user: '$username' with pattern: '$user_pattern'"
  122         fi
  123       done
  124       deny "The user is not in the access list for this branch"
  125     done
  126   )
  127   case "$rc" in
  128     grant) grant >/dev/null "Granting access based on $allowed_users_file" ;;
  129     deny)  deny  >/dev/null "Denying  access based on $allowed_users_file" ;;
  130     *) ;;
  131   esac
  132 fi
  133 
  134 allowed_groups_file=$GIT_DIR/info/allowed-groups
  135 groups=$(id -G -n)
  136 info "The user belongs to the following groups:"
  137 info "'$groups'"
  138 
  139 if test -f "$allowed_groups_file"
  140 then
  141   rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
  142     while read heads group_patterns
  143     do
  144       # does this rule apply to us?
  145       head_pattern=${heads#+}
  146       matchlen=$(expr "$1" : "${head_pattern#+}")
  147       test "$matchlen" = ${#1} || continue
  148 
  149       # if non-ff, $heads must be with the '+' prefix
  150       test -n "$noff" &&
  151       test "$head_pattern" = "$heads" && continue
  152 
  153       info "Found matching head pattern: '$head_pattern'"
  154       for group_pattern in $group_patterns; do
  155         for groupname in $groups; do
  156           info "Checking group: '$groupname' against pattern: '$group_pattern'"
  157           matchlen=$(expr "$groupname" : "$group_pattern")
  158           if test "$matchlen" = "${#groupname}"
  159           then
  160             grant "Allowing group: '$groupname' with pattern: '$group_pattern'"
  161           fi
  162         done
  163       done
  164       deny "None of the user's groups are in the access list for this branch"
  165     done
  166   )
  167   case "$rc" in
  168     grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;;
  169     deny)  deny  >/dev/null "Denying  access based on $allowed_groups_file" ;;
  170     *) ;;
  171   esac
  172 fi
  173 
  174 deny >/dev/null "There are no more rules to check.  Denying access"
  175 ----------------------------------------------------
  176 
  177 This uses two files, $GIT_DIR/info/allowed-users and
  178 allowed-groups, to describe which heads can be pushed into by
  179 whom.  The format of each file would look like this:
  180 
  181     refs/heads/master   junio
  182     +refs/heads/pu      junio
  183     refs/heads/cogito$  pasky
  184     refs/heads/bw/.*    linus
  185     refs/heads/tmp/.*   .*
  186     refs/tags/v[0-9].*  junio
  187 
  188 With this, Linus can push or create "bw/penguin" or "bw/zebra"
  189 or "bw/panda" branches, Pasky can do only "cogito", and JC can
  190 do master and pu branches and make versioned tags.  And anybody
  191 can do tmp/blah branches. The '+' sign at the pu record means
  192 that JC can make non-fast-forward pushes on it.