summaryrefslogtreecommitdiff
path: root/Documentation/howto/update-hook-example.txt
blob: dacaf17c2efe3044d6f7cc0b0ff616c6cb21a255 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
From: Junio C Hamano <junkio@cox.net>
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.