1package git23import (4 "os"5 "path"6 "strings"7)89// HookName is the name of a Git hook.10type HookName string1112// A list of Git server hooks' name that are supported.13const (14 HookPreReceive HookName = "pre-receive"15 HookUpdate HookName = "update"16 HookPostReceive HookName = "post-receive"17)1819var (20 // ServerSideHooks contains a list of Git hooks that are supported on the server21 // side.22 ServerSideHooks = []HookName{HookPreReceive, HookUpdate, HookPostReceive}23 // ServerSideHookSamples contains samples of Git hooks that are supported on the24 // server side.25 ServerSideHookSamples = map[HookName]string{26 HookPreReceive: `#!/bin/sh27#28# An example hook script to make use of push options.29# The example simply echoes all push options that start with 'echoback='30# and rejects all pushes when the "reject" push option is used.31#32# To enable this hook, rename this file to "pre-receive".3334if test -n "$GIT_PUSH_OPTION_COUNT"35then36 i=037 while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"38 do39 eval "value=\$GIT_PUSH_OPTION_$i"40 case "$value" in41 echoback=*)42 echo "echo from the pre-receive-hook: ${value#*=}" >&243 ;;44 reject)45 exit 146 esac47 i=$((i + 1))48 done49fi50`,51 HookUpdate: `#!/bin/sh52#53# An example hook script to block unannotated tags from entering.54# Called by "git receive-pack" with arguments: refname oid-old oid-new55#56# To enable this hook, rename this file to "update".57#58# Config59# ------60# hooks.allowunannotated61# This boolean sets whether unannotated tags will be allowed into the62# repository. By default they won't be.63# hooks.allowdeletetag64# This boolean sets whether deleting tags will be allowed in the65# repository. By default they won't be.66# hooks.allowmodifytag67# This boolean sets whether a tag may be modified after creation. By default68# it won't be.69# hooks.allowdeletebranch70# This boolean sets whether deleting branches will be allowed in the71# repository. By default they won't be.72# hooks.denycreatebranch73# This boolean sets whether remotely creating branches will be denied74# in the repository. By default this is allowed.75#7677# --- Command line78refname="$1"79oldrev="$2"80newrev="$3"8182# --- Safety check83if [ -z "$GIT_DIR" ]; then84 echo "Don't run this script from the command line." >&285 echo " (if you want, you could supply GIT_DIR then run" >&286 echo " $0 <ref> <oldrev> <newrev>)" >&287 exit 188fi8990if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then91 echo "usage: $0 <ref> <oldrev> <newrev>" >&292 exit 193fi9495# --- Config96allowunannotated=$(git config --bool hooks.allowunannotated)97allowdeletebranch=$(git config --bool hooks.allowdeletebranch)98denycreatebranch=$(git config --bool hooks.denycreatebranch)99allowdeletetag=$(git config --bool hooks.allowdeletetag)100allowmodifytag=$(git config --bool hooks.allowmodifytag)101102# check for no description103projectdesc=$(sed -e '1q' "$GIT_DIR/description")104case "$projectdesc" in105"Unnamed repository"* | "")106 echo "*** Project description file hasn't been set" >&2107 exit 1108 ;;109esac110111# --- Check types112# if $newrev is 0000...0000, it's a commit to delete a ref.113zero="0000000000000000000000000000000000000000"114if [ "$newrev" = "$zero" ]; then115 newrev_type=delete116else117 newrev_type=$(git cat-file -t $newrev)118fi119120case "$refname","$newrev_type" in121 refs/tags/*,commit)122 # un-annotated tag123 short_refname=${refname##refs/tags/}124 if [ "$allowunannotated" != "true" ]; then125 echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2126 echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2127 exit 1128 fi129 ;;130 refs/tags/*,delete)131 # delete tag132 if [ "$allowdeletetag" != "true" ]; then133 echo "*** Deleting a tag is not allowed in this repository" >&2134 exit 1135 fi136 ;;137 refs/tags/*,tag)138 # annotated tag139 if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1140 then141 echo "*** Tag '$refname' already exists." >&2142 echo "*** Modifying a tag is not allowed in this repository." >&2143 exit 1144 fi145 ;;146 refs/heads/*,commit)147 # branch148 if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then149 echo "*** Creating a branch is not allowed in this repository" >&2150 exit 1151 fi152 ;;153 refs/heads/*,delete)154 # delete branch155 if [ "$allowdeletebranch" != "true" ]; then156 echo "*** Deleting a branch is not allowed in this repository" >&2157 exit 1158 fi159 ;;160 refs/remotes/*,commit)161 # tracking branch162 ;;163 refs/remotes/*,delete)164 # delete tracking branch165 if [ "$allowdeletebranch" != "true" ]; then166 echo "*** Deleting a tracking branch is not allowed in this repository" >&2167 exit 1168 fi169 ;;170 *)171 # Anything else (is there anything else?)172 echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2173 exit 1174 ;;175esac176177# --- Finished178exit 0179`,180 HookPostReceive: `#!/bin/sh181#182# An example hook script for the "post-receive" event.183#184# The "post-receive" script is run after receive-pack has accepted a pack185# and the repository has been updated. It is passed arguments in through186# stdin in the form187# <oldrev> <newrev> <refname>188# For example:189# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master190191while read oldrev newrev refname192do193 branch=$(git rev-parse --symbolic --abbrev-ref $refname)194 if [ "master" = "$branch" ]; then195 # Do something196 fi197done`,198 }199)200201// Hook contains information of a Git hook.202type Hook struct {203 name HookName204 path string // The absolute file path of the hook.205 isSample bool // Indicates whether this hook is read from the sample.206 content string // The content of the hook.207}208209// Name returns the name of the Git hook.210func (h *Hook) Name() HookName {211 return h.name212}213214// Path returns the path of the Git hook.215func (h *Hook) Path() string {216 return h.path217}218219// IsSample returns true if the content is read from the sample hook.220func (h *Hook) IsSample() bool {221 return h.isSample222}223224// Content returns the content of the Git hook.225func (h *Hook) Content() string {226 return h.content227}228229// Update writes the content of the Git hook on filesystem. It updates the230// memory copy of the content as well.231func (h *Hook) Update(content string) error {232 h.content = strings.TrimSpace(content)233 h.content = strings.ReplaceAll(h.content, "\r", "")234235 if err := os.MkdirAll(path.Dir(h.path), os.ModePerm); err != nil {236 return err237 } else if err = os.WriteFile(h.path, []byte(h.content), os.ModePerm); err != nil {238 return err239 }240241 h.isSample = false242 return nil243}