# How the no-mistakes Post-Receive Hook Resolves Paths in Bare Repositories

> Learn how the no-mistakes Post-Receive Hook guarantees correct path resolution in bare repositories using its smart two-step strategy for Git daemons.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-13

---

**The post-receive hook generated by no-mistakes uses a two-step resolution strategy that first attempts `git rev-parse --absolute-git-dir` and falls back to deriving the path from the hook's own location (`$0`) to ensure the daemon always receives an absolute path to the bare repository.**

The `no-mistakes` CI/CD system generates a custom **post-receive** hook for bare repositories (called "gates") that must reliably determine its own absolute path before notifying the daemon. Because Git may invoke hooks with a working directory of `.` that the daemon rejects (see issue #269), the hook implements robust path resolution logic in [`internal/git/hook.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/git/hook.go) to guarantee a fully-qualified gate directory.

## Why Absolute Path Resolution Matters

When Git executes a post-receive hook, the current working directory (`PWD`) is often set to the repository root or even `.`, which resolves to a relative path. The no-mistakes daemon strictly requires an absolute path via the `--gate` argument to avoid the "invalid gate path: ." error that previously blocked pipelines. According to the source code in [`internal/git/hook.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/git/hook.go), the hook must therefore resolve the bare repository's location before invoking the daemon manager in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go).

## The Two-Step Path Resolution Strategy

The hook implements a cascading resolution strategy in [`internal/git/hook.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/git/hook.go) that combines Git's native capabilities with portable shell-based path manipulation. This approach is tested in [`internal/git/hook_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/git/hook_test.go), which verifies the fallback behavior when `git rev-parse` is unavailable.

### Step 1: Prefer Git’s Native Absolute Directory

The hook first attempts to use Git's built-in path resolution, available since Git 2.13:

```sh
GATE_DIR=$(git rev-parse --absolute-git-dir 2>/dev/null || :)

```

The `git rev-parse --absolute-git-dir` command returns the absolute path of the `.git` directory. If this succeeds, `GATE_DIR` contains a fully-qualified path and the hook proceeds directly to execution.

### Step 2: Fallback to the Hook’s Own Location

If the Git command fails (older Git versions or restricted environments), the script calculates the path from the hook file's location:

```sh
case "$GATE_DIR" in
  /*) ;;                     # already absolute – keep it

  *)
    HOOK_PATH=$0
    case "$HOOK_PATH" in
      */*) HOOK_DIR=${HOOK_PATH%/*} ;;
      *)   HOOK_DIR=. ;;
    esac
    GATE_DIR=$(cd "$HOOK_DIR/.." 2>/dev/null && (/bin/pwd -P 2>/dev/null || pwd -P) || :)
    ;;
esac

```

This logic extracts the directory containing the hook (`HOOK_DIR`) from `$0`, moves up one level (`..`) to reach the bare repository root, and resolves symlinks using `pwd -P` (or `/bin/pwd -P` for portability).

### Step 3: Guarantee an Absolute Result

A final safety net ensures the path is absolute even if previous steps failed:

```sh
case "$GATE_DIR" in
  /*) ;;                     # already absolute

  *) GATE_DIR=$(/bin/pwd -P 2>/dev/null || pwd -P 2>/dev/null || pwd) ;;
esac

```

This normalization prevents any relative path from reaching the daemon.

## Passing the Resolved Path to the Daemon

Once resolved, the absolute `GATE_DIR` is passed as the `--gate` argument:

```sh
set -- --gate "$GATE_DIR" \
         --ref "$refname" \
         --old "$oldrev" \
         --new "$newrev"

```

The daemon defined in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) receives this reliable path and uses it to start the appropriate pipeline, eliminating the path resolution failures that previously stopped automation.

## Installing and Debugging the Hook

When initializing a gate (handled in [`internal/gate/gate.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/gate/gate.go)), the hook is installed via the Go API:

```go
// Install the hook (normally done by `no-mistakes init`)
if err := git.InstallPostReceiveHook(bareRepoPath); err != nil {
    log.Fatalf("failed to install post‑receive hook: %v", err)
}

```

For debugging, you can manually trigger the hook from any working directory:

```sh

# Assume we are inside a worktree that points to a bare gate at /tmp/gate.git

cd /tmp/my-worktree

# The hook will still resolve the correct gate directory:

./hooks/post-receive

# Internally the script will set GATE_DIR=/tmp/gate.git

```

## Summary

- The post-receive hook in `no-mistakes` must resolve absolute paths to avoid "invalid gate path" errors that prevent pipeline execution.
- **Primary resolution** uses `git rev-parse --absolute-git-dir` (Git 2.13+) for reliable absolute path detection.
- **Fallback resolution** derives the path from `$0` (the hook's location) using shell parameter expansion and `pwd -P` to handle older Git versions.
- A final safety net guarantees absolute paths before passing `--gate` to the daemon in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go).
- This two-step strategy ensures compatibility across Git versions and execution environments, including poisoned `PWD` scenarios.

## Frequently Asked Questions

### What happens if git rev-parse --absolute-git-dir fails?

If the command fails (pre-Git 2.13 or restricted environments), the hook automatically falls back to calculating the path from its own location using `$0` and `pwd -P`, ensuring the daemon still receives a valid absolute path to the bare repository.

### Why does the hook avoid using PWD for path resolution?

Git may invoke the hook with `PWD` set to `.` or a relative path, which the daemon rejects with an "invalid gate path" error (issue #269). The hook specifically avoids relying on the working directory to ensure cross-platform reliability and prevent automation failures.

### How does the daemon validate the gate path?

The daemon in [`internal/daemon/manager.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/daemon/manager.go) receives the `--gate` argument and validates that it is an absolute path to a bare repository before starting the pipeline, preventing execution errors from malformed or relative paths.

### Which Git versions are supported by this resolution strategy?

The strategy supports all Git versions: modern versions (2.13+) use the optimized `git rev-parse --absolute-git-dir` path, while older versions rely on the portable shell fallback that uses `pwd -P` and `$0` manipulation, ensuring broad compatibility.