Operations 10 min read

Enforce Custom Commit Message Formats in GitLab with Server Hooks

This guide explains how to create repository‑specific and global GitLab server hooks that validate commit messages against a custom pattern, showing step‑by‑step configuration, a Go pre‑receive script, and verification commands to ensure only properly formatted commits are accepted.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Enforce Custom Commit Message Formats in GitLab with Server Hooks

Introduction

Git provides server‑side hooks such as

pre-receive

,

post-receive

and

update

that can enforce commit policies.

These hooks can be configured per repository or globally on the GitLab server.

Creating a repository‑specific hook

Locate the repository path (e.g.,

/home/git/repositories/<group>/<project>.git

for source installations or

/var/opt/gitlab/git-data/repositories/<group>/<project>.git

for Omnibus installations). For hashed storage, find the hashed directory under

/var/opt/gitlab/git-data/repositories/@hashed/…

.

Select Admin Area → Projects → your project.

Find the Gitaly relative path and create a

custom_hooks

directory under that path.

Inside

custom_hooks

, add a file named

pre-receive

(no extension) and make it executable, owned by

git

.

The hook receives three parameters (old commit ID, new commit ID, ref) via

stdin

. If the hook exits with a non‑zero status, GitLab rejects the push and returns the error message.

Sample pre‑receive hook

The following Go program allows only commit messages that match

(.*build=(yes|no).*deploy=(yes|no).*)|^Merge\ branch(.*)

. If the pattern does not match, it prints a

GL‑HOOK‑ERR

block and exits with status 1.

<code>package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "os/exec"
    "regexp"
    "strings"
)

type CommitType string

const CommitMessagePattern = `(.*build=(yes|no).*deploy=(yes|no).*)|^Merge\ branch(.*)`

const checkFailedMessage = `GL-HOOK-ERR:########################
GL-HOOK-ERR:  Commit message format check failed!
GL-HOOK-ERR:  Expected pattern:
GL-HOOK-ERR:  (.*build=(yes|no).*deploy=(yes|no).*)|^Merge\ branch(.*)
GL-HOOK-ERR:  Example: Update date.html build=no,deploy=yes
GL-HOOK-ERR:########################`

const strictMode = false

var commitMsgReg = regexp.MustCompile(CommitMessagePattern)

func main() {
    input, _ := ioutil.ReadAll(os.Stdin)
    param := strings.Fields(string(input))

    // allow branch/tag delete
    if param[1] == "0000000000000000000000000000000000000000" {
        os.Exit(0)
    }

    commitMsg := getCommitMsg(param[0], param[1])
    for _, tmpStr := range commitMsg {
        commitTypes := commitMsgReg.FindAllStringSubmatch(tmpStr, -1)
        if len(commitTypes) != 1 {
            checkFailed()
        } else {
            fmt.Println(" ")
        }
        if !strictMode {
            os.Exit(0)
        }
    }
}

func getCommitMsg(oldCommitID, commitID string) []string {
    cmd := exec.Command("git", "log", oldCommitID+".."+commitID, "--pretty=format:%s")
    cmd.Stdin = os.Stdin
    cmd.Stderr = os.Stderr
    b, err := cmd.Output()
    if err != nil {
        fmt.Print(err)
        os.Exit(1)
    }
    return strings.Split(string(b), "\n")
}

func checkFailed() {
    fmt.Fprintln(os.Stderr, checkFailedMessage)
    os.Exit(1)
}
</code>

Testing the hook

After placing the script and making it executable, a push with an invalid message is rejected with the formatted error block. Amending the commit to include

[build=no,deploy=no]

satisfies the pattern and the push succeeds.

Creating a global hook

To apply a hook to all repositories, place it in the global server‑hook directory (

/home/git/gitlab-shell/hooks

for source installs or

/opt/gitlab/embedded/service/gitlab-shell/hooks

for Omnibus). Configure

custom_hooks_dir

in the Gitaly configuration (

gitlab.rb

for older versions or

gitaly/config.toml

for newer) to point to the desired directory.

Navigate to the global hooks directory on the GitLab server.

Create a subdirectory such as

pre-receive.d

,

post-receive.d

or

update.d

.

Copy your hook script into this directory and ensure it is executable and owned by

git

.

References:

https://docs.gitlab.com/ee/administration/server_hooks.html

https://mritd.com/2018/05/11/add-commit-message-style-check-to-your-gitlab/

CI/CDDevOpsGitLabcommit-messagegit-hooks
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.