install.sh: expand @cfg_name@ in processed scripts
[girocco.git] / hooks / pre-receive
blobfe5e858ff1077f8919941fcb79427e8eb1b66646
1 #!/bin/sh
3 # Keep track of the last time we modified the object store
5 # Beware, we MAY be running in a chroot!
7 set -e
9 umask 002
11 if ! [ -x @perlbin@ ]; then
12 # We are INSIDE the chroot
13 PATH=/bin && export PATH
14 else
15 # We are NOT inside the chroot
16 basedir=@basedir@
17 list_packs() { command "$basedir/bin/list_packs" "$@"; }
18 strftime() { command "$basedir/bin/strftime" "$@"; }
21 # Make sure the current directory is where we expect to be
22 [ "${GIT_DIR+set}" != "set" ] || { [ -n "$GIT_DIR" ] && [ -d "$GIT_DIR" ]; } || unset GIT_DIR
23 [ -n "$GIT_DIR" ] || GIT_DIR="$(git rev-parse --git-dir)"
24 [ -n "$GIT_DIR" ] && cd -P "${GIT_DIR:-.}" || exit 1
25 case "${PWD%/*}" in */worktrees)
26 # Gah!
28 # But it COULD just be a coincidence...
29 [ -s commondir ] && [ -s HEAD ] &&
30 _cmndir= && read -r _cmndir <commondir 2>/dev/null &&
31 [ -n "$_cmndir" ] && [ -d "$_cmndir" ]
32 then
33 # ...it is not, fix it!
34 cd -P "$_cmndir" || exit 1
36 esac
37 GIT_DIR="." GIT_PREFIX= && export GIT_DIR
39 # Get out of the mob
40 case "$PWD" in *?/mob)
41 cd ..
42 GIROCCO_PERSONAL_MOB=1
43 esac
45 git config gitweb.lastreceive "$(date '+%a, %d %b %Y %T %z')"
47 # Read the incoming refs and freshen old loose objects
48 # If we waited until post-receive a gc could have already nuked them
49 # We freshen the new ref in case it's being resurrected to protect it from gc
50 # We probably do not need to do it for new refs as Git tries to do that,
51 # but since we're already doing it for old refs (which Git does not do),
52 # it's almost no extra work for new refs, just in case. We also attempt to
53 # make all packs user and group writable so they can be touched/renamed later.
55 # Starting with Git v2.11.0 receive-pack packs/objects end up in a quarantine
56 # object directory that is just discarded immediately if pre-receive declines
57 # to accept the push. This is a good thing. However, it means that the
58 # incoming objects are NOT located in objects/... as GIT_OBJECT_DIRECTORY and
59 # GIT_QUARANTINE_PATH are both set to the quarantine objects directory and the
60 # original objects directory is appended to GIT_ALTERNATE_OBJECT_DIRECTORIES
61 # (but it will just be the absolute path to objects). The simple bottom line
62 # is that we should also try everything in the GIT_QUARANTINE_PATH directory if
63 # it's set.
64 [ -z "$GIT_QUARANTINE_PATH" ] || [ -d "$GIT_QUARANTINE_PATH" ] || unset GIT_QUARANTINE_PATH
66 # We also record changes to a ref log. We do it here rather than in
67 # post-receive so that we can guarantee all potential changes are recorded in
68 # the log before they take place. It's possible that the update hook will
69 # ultimately deny one or more updates but waiting until post-receive could
70 # result in updates being left out of the log.
72 hexdig='[0-9a-f]'
73 octet="$hexdig$hexdig"
74 octet4="$octet$octet$octet$octet"
75 octet20="$octet4$octet4$octet4$octet4$octet4"
76 _make_packs_ugrw() {
77 find -L "$1" -maxdepth 1 -type f ! -perm -ug+rw,o+r \
78 -name "pack-$octet20*.*" -exec chmod ug+rw,o+r '{}' + || :
79 } 2>/dev/null
80 _make_packs_ugrw objects/pack
81 if [ -n "$GIT_QUARANTINE_PATH" ]; then
82 # gc.sh will be unable remove stale incoming-* dirs without this
83 chmod ug+rwx,o+rx "$GIT_QUARANTINE_PATH" "$GIT_QUARANTINE_PATH/pack" >/dev/null 2>&1 || :
84 chmod g+s "$GIT_QUARANTINE_PATH" "$GIT_QUARANTINE_PATH/pack" >/dev/null 2>&1 || :
85 _make_packs_ugrw "$GIT_QUARANTINE_PATH/pack"
88 # Trigger a mini-gc if there are at least 20 packs present.
89 # Our current pack that contains this push's data will have a .keep while
90 # this hook is running so it will be excluded from the count, but the count
91 # is only an approximate guide anyway and being off by one or two will not
92 # cause any performance problems. We could drop the check to 19 instead to
93 # compensate but there could be other simultaneous pushes and it's highly
94 # unlikely that it would end up making any difference anyway. We also
95 # deliberately ignore anything in the "$GIT_QUARANTINE_PATH/pack" area because
96 # at this point it will have a .keep (if it exists) and because of the way
97 # it's created there can never be more than one pack in there anyway.
98 # The mini-gc code contains the logic to sort out small packs vs. non-small
99 # packs and which should be combined in what order so we do not need to
100 # do any more complicated testing here.
101 if ! [ -e .needsgc ]; then
102 packs=
103 { packs="$(list_packs --quiet --count --exclude-no-idx --exclude-keep objects/pack || :)" || :; } 2>/dev/null
104 if [ -n "$packs" ] && [ "$packs" -ge 20 ]; then
105 >.needsgc
109 # Make sure we have a reflogs directory and abort the update if we cannot
110 # create one. Normally project creation will make sure this directory exists.
111 [ -d reflogs ] || mkdir -p reflogs >/dev/null 2>&1 || :
112 [ -d reflogs ]
114 # Multiple push operations could be occurring simultaneously so we need to
115 # guarantee they do not step on one another and we do this by generating a
116 # unique log file name. We use a microseconds timestamp and the current process
117 # id and we guarantee that this process is kept alive for the entire microsecond
118 # of the timestamp thereby guaranteeing that we are the only possible process
119 # that could use that pid during that particular microsecond (ignoring leap seconds).
120 # To do this we need to sleep until the microsecond turns over, grab the timestamp
121 # and then sleep until the microsecond turns over again. This will introduce a
122 # guaranteed 2 microsecond delay into every push. This should not generally be
123 # noticeable. The strftime utility does this for us when using %N with current time.
124 # We always use UTC for the timestamp so that chroot and non-chroot match up.
125 # Log entries are the lines sent to the pre-receive hook with hhmmss prepended.
126 lognamets="$(TZ=UTC strftime '%Y%m%d_%H%M%S%N')"
127 lognamets="${lognamets%???}"
128 loghhmmss="${lognamets##*_}"
129 loghhmmss="${loghhmmss%??????}"
131 # We write to a temp ref log and then move it into place so that the reflogs
132 # collector can assume that log files with their final name are immutable
133 logname="reflogs/$lognamets.$$"
134 lognametmp="reflogs/tmp_$lognamets.$$"
136 while read -r old new ref; do
137 echo "$loghhmmss $old $new $ref" >&3
138 args=
139 if [ "$old" != "0000000000000000000000000000000000000000" ]; then
140 # freshen mod time on recently unref'd loose objects
141 fn="${old#??}"
142 shard="${old%$fn}"
143 args="$args 'objects/$shard/$fn'"
144 [ -z "$GIT_QUARANTINE_DIRECTORY" ] || args="$args '$GIT_QUARANTINE_DIRECTORY/$shard/$fn'"
146 if [ "$new" != "0000000000000000000000000000000000000000" ]; then
147 # prevent imminent pruning of a ref being resurrected
148 fn="${new#??}"
149 shard="${new%$fn}"
150 args="$args 'objects/$shard/$fn'"
151 [ -z "$GIT_QUARANTINE_DIRECTORY" ] || args="$args '$GIT_QUARANTINE_DIRECTORY/$shard/$fn'"
153 eval "chmod ug+w $args" 2>/dev/null || :
154 eval "touch -c $args" 2>/dev/null || :
155 done 3>"$lognametmp"
156 mv "$lognametmp" "$logname"
158 # While unlikely, it is conceivable that several ref updates have occurred that
159 # did not actually push any packs. In that case we could build up a large
160 # number of log files so request a mini gc if there are 50 or more of them now.
161 if ! [ -e .needsgc ]; then
162 logfiles=
163 { logfiles="$(($(find -L reflogs -maxdepth 1 -type f -print | wc -l || :)+0))" || :; } 2>/dev/null
164 if [ -n "$logfiles" ] && [ "$logfiles" -ge 50 ]; then
165 >.needsgc