Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,22 @@ repos:

- repo: local
hooks:
- id: no-conflict-markers
name: No committed git conflict markers (belt-and-suspenders)
# Belt-and-suspenders companion to the `check-merge-conflict` hook
# from pre-commit-hooks above. That hook is Python-based and covers
# the standard three-way conflict syntax; this shell hook uses
# `git grep` which is executed by git's own C binary and is
# essentially free (< 5ms on a typical staged tree). If either fires,
# the merge wasn't fully resolved. PR #50 shipped conflict markers
# into 70 docs and source files; this hook would have blocked it.
entry: scripts/ci/check-conflict-markers.sh
language: script
# Always fire — conflict markers can appear in YAML, C, Python,
# Markdown, etc., regardless of which file types were staged.
pass_filenames: false
always_run: true

- id: agent-worktree-drift-guard
name: ADR-0332 agent worktree-drift guard
entry: scripts/ci/check-agent-worktree-drift.sh
Expand Down
1 change: 1 addition & 0 deletions changelog.d/changed/pre-commit-conflict-marker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **pre-commit**: add `no-conflict-markers` shell hook (`scripts/ci/check-conflict-markers.sh`) as a belt-and-suspenders companion to the existing `check-merge-conflict` entry. The new hook uses `git grep` (git's C binary, < 5ms) and fires on every commit regardless of file type. Closes the coverage gap illustrated by PR #50, which shipped unresolved conflict markers into 70 files.
38 changes: 38 additions & 0 deletions scripts/ci/check-conflict-markers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Copyright 2026 Lusoris
# SPDX-License-Identifier: BSD-3-Clause-Plus-Patent
#
# check-conflict-markers.sh — belt-and-suspenders conflict-marker guard.
#
# Companion to the `check-merge-conflict` pre-commit-hooks entry (Python).
# This shell hook uses `git grep` (git's own C binary) for near-zero overhead
# (< 5ms on a typical staged tree). Both hooks fire at pre-commit time;
# if either triggers, the merge was not fully resolved.
#
# PR #50 shipped unresolved conflict markers into 70 files across docs,
# YAML, CI workflows, and a CUDA source file. This hook would have blocked it.
#
# Exit 0 = no markers found (clean commit).
# Exit 1 = at least one marker found (commit blocked, offending lines printed).

set -euo pipefail

# Collect the names of staged (cached) text files.
staged=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null)

if [ -z "$staged" ]; then
exit 0
fi

# git grep -I skips binary files. -l lists only filenames; -n adds line numbers.
# We search for all three conflict marker patterns simultaneously. The patterns
# are anchored at the start of a line (^) to avoid false positives in prose.
# shellcheck disable=SC2086 # $staged is intentionally word-split (one file per word)
if git grep -I -n -E "^(<{7} |={7}$|>{7} )" -- $staged 2>/dev/null; then
echo ""
echo "ERROR: git conflict markers found in staged files."
echo "Resolve all merge conflicts before committing."
exit 1
fi

exit 0
Loading