What Is the Post-Receive Hook in no-mistakes? A Deep Dive into Git Integration

The post-receive hook in no-mistakes acts as a non-blocking bridge between Git pushes and the no-mistakes daemon, automatically triggering pipeline runs by resolving the bare repository path and notifying the daemon of every ref update.

The post-receive hook in no-mistakes serves as the critical integration point between Git operations and the continuous integration system. Located in internal/git/hook.go, this component generates a shell script that intercepts every push to execute the daemon notify-push command, ensuring that code changes immediately trigger appropriate pipeline runs without blocking the developer's workflow.

How the Hook Script Is Generated

The hook begins its lifecycle in the PostReceiveHookScript function found in internal/git/hook.go (lines 15-20). This function constructs a POSIX-compliant shell script that handles the communication between Git and the no-mistakes daemon.

The generated script performs several critical tasks:

  • Resolves the absolute path to the no-mistakes binary
  • Determines the bare repository directory (GATE_DIR) using git rev-parse --absolute-git-dir
  • Iterates through every ref being pushed
  • Invokes no-mistakes daemon notify-push with appropriate flags
  • Handles errors gracefully without failing the push

Resolving the Repository Path Reliably

One of the most sophisticated aspects of the implementation is the absolute gate resolution logic in internal/git/hook.go (lines 39-58). This handles edge cases where Git executes hooks from a "." working directory, which would cause --gate . to resolve incorrectly.

The script first attempts to determine the repository location via git rev-parse --absolute-git-dir. If this command fails, it falls back to deriving the path from the hook file's own location. This dual strategy ensures the daemon notify-push command always receives a valid, absolute path to the bare repository, preventing pipeline runs from targeting the wrong directory.

The Notification Mechanism

When a developer executes git push, the post-receive hook reads each ref update from stdin and enters the notify loop located in internal/git/hook.go (lines 62-78).

For every reference updated, the script constructs and executes the following command:

no-mistakes daemon notify-push \
  --gate "$GATE_DIR" \
  --ref "$refname" \
  --old "$oldrev" \
  --new "$newrev"

The daemon, implemented in internal/daemon/manager.go, receives this notification and immediately queues a new pipeline run. The hook passes any additional push options through to the daemon, allowing for parameterized pipeline execution based on the specific push characteristics.

Non-Blocking Semantics and Error Handling

A key design principle of the no-mistakes post-receive hook is that it never blocks the Git client. According to the implementation in internal/git/hook.go (lines 79-86), the hook's exit status is intentionally ignored by Git, meaning:

  • Push operations succeed regardless of hook failures
  • Network issues or daemon downtime cannot prevent code pushes
  • Developers receive immediate feedback that their code was accepted

When errors do occur, they are written to stderr and appended to notify-push.log inside the bare repository. This log file serves as the primary diagnostic tool for administrators debugging why a pipeline failed to trigger.

Upon successful notification, the script prints a success banner (lines 90-99) encouraging users to run no-mistakes to review the pipeline status, providing a seamless transition from push to monitoring.

Installing and Managing the Hook

The no-mistakes CLI provides first-class support for hook lifecycle management through two primary functions in internal/git/hook.go:

InstallPostReceiveHook writes the generated script into <bare-repo>/hooks/post-receive, ensuring the correct shebang and permissions are set for immediate execution.

RefreshManagedPostReceiveHook updates existing hooks only if they were originally installed by no-mistakes. This safety check prevents overwriting custom user hooks or those managed by other tools like Husky.

Isolating from External Configuration Changes

To prevent other Git tools from accidentally disabling the hook, the IsolateHooksPath function (lines 74-84) pins the core.hookspath configuration in the per-work-tree Git config. This protective measure ensures that global Git configuration changes or other hook management frameworks cannot redirect the hook execution away from the no-mistakes script.

Practical Implementation Examples

Installing the hook for a new bare repository

import "github.com/kunchenguid/no-mistakes/internal/git"

func setupBareRepo(path string) error {
    // Create the bare repository first...
    return git.InstallPostReceiveHook(path)
}

Refreshing an existing installation

changed, err := git.RefreshManagedPostReceiveHook(bareRepoPath)
if err != nil {
    log.Fatal(err)
}
if changed {
    fmt.Println("no-mistakes hook updated to latest version")
}

Examining the generated script

The actual shell script generated by PostReceiveHookScript follows this pattern:

#!/bin/sh

# no-mistakes post-receive hook

# Notifies the daemon of the push. Non-blocking: post-receive exit code is

# ignored by git, so we never reject the push here.

NM_BIN='no-mistakes'   # Resolved automatically at generation time

# Resolve the bare repo directory reliably

GATE_DIR=$(git rev-parse --absolute-git-dir 2>/dev/null) || \
  GATE_DIR=$(cd "$(dirname "$0")/../.." && pwd)

# Read stdin for ref updates and notify daemon for each

while read oldrev newrev refname; do
    NM_HOOK_HELPER=1 "$NM_BIN" daemon notify-push \
        --gate "$GATE_DIR" \
        --ref "$refname" \
        --old "$oldrev" \
        --new "$newrev"
done

# Error handling: log to notify-push.log but don't exit non-zero

# ... success banner printed to user ...

Summary

  • The post-receive hook in no-mistakes is generated dynamically by PostReceiveHookScript in internal/git/hook.go to ensure the correct binary paths and repository locations.
  • Absolute gate resolution prevents path resolution errors by using git rev-parse with a fallback to the hook's own location.
  • The hook invokes no-mistakes daemon notify-push for every ref update, passing the gate path and revision hashes to trigger pipeline runs.
  • Execution is non-blocking by design—the hook always exits cleanly to avoid interfering with Git operations, with errors logged to notify-push.log.
  • Installation helpers like InstallPostReceiveHook and RefreshManagedPostReceiveHook provide safe lifecycle management without overwriting custom user hooks.
  • The IsolateHooksPath function protects the hook from being disabled by external Git configuration changes.

Frequently Asked Questions

What happens if the post-receive hook fails to notify the daemon?

The push succeeds regardless of hook failures. According to the implementation in internal/git/hook.go (lines 79-86), errors are written to stderr and appended to notify-push.log inside the bare repository, but the hook always exits with status 0 to avoid blocking the Git client.

How does no-mistakes handle existing custom hooks?

The RefreshManagedPostReceiveHook function in internal/git/hook.go checks whether the existing hook is a no-mistakes-managed hook before updating it. This ensures that custom user hooks or those managed by other tools remain untouched, preventing accidental overwrites while allowing seamless updates for no-mistakes installations.

Where can I find logs from the post-receive hook execution?

Hook execution logs are stored in notify-push.log inside the bare repository's root directory. This file captures stderr output from both the hook script itself and the daemon notify-push command, making it the primary location for debugging why a pipeline failed to trigger after a push.

Can the post-receive hook reject or block Git pushes?

No, the hook is explicitly designed to be non-blocking. As noted in the generated script comments and implemented in internal/git/hook.go (lines 79-86), Git ignores the exit status of post-receive hooks. The no-mistakes hook leverages this behavior to ensure that daemon availability or pipeline configuration issues never prevent developers from pushing code.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →