MDS048: git-hook-sync
Git artifacts must match the canonical glob-based template derived from .mdsmith.yml.
The .gitattributes managed block and the
pre-merge-commit hook must match a canonical
template. That template is computed from the
project’s .mdsmith.yml ignore patterns. Editing
.mdsmith.yml keeps both git artifacts in sync.
One limitation: ignore patterns that cannot appear
verbatim in .gitattributes are dropped from the
managed block. Negation patterns starting with !
and patterns containing whitespace fall in this
bucket. mdsmith fix itself still honors them, so
the merge-driver scope can diverge from mdsmith fix’s scope when the ignore: list relies on those
forms.
# Rationale
Markdown files can contain generated sections like
<?catalog?>, <?include?>, and <?toc?>. They need
special handling during git merges to regenerate content
and avoid conflicts. Two artifacts cooperate:
.gitattributesassigns themdsmithmerge driver to markdown files. The managed block uses globs (e.g.*.md merge=mdsmith) plus exclude overrides (<pattern> -merge) so the assignment scope tracks.mdsmith.ymlignore patterns rather than enumerating individual files.- The pre-merge-commit hook re-runs
mdsmith fix .once every per-file merge has resolved, so generated sections reflect the final merged state. The hook script is glob-driven (no embedded file list), so its scope tracks the same ignore patterns automatically.
This rule detects when either artifact drifts from the canonical template.
# Settings
This rule has no configuration settings beyond enabling/disabling it.
# Config
Enable (default: disabled):
rules:
git-hook-sync: trueDisable:
rules:
git-hook-sync: false# How It Works
The rule:
- Reads
.mdsmith.ymlignore patterns and computes the canonical glob set: include patterns (default:*.md,*.markdown) followed by an exclude pattern for eachignore:entry. - If
merge.mdsmith.driveris registered in git config, reads.gitattributesand compares the BEGIN/END managed block against the canonical render. - Reads the pre-merge-commit hook (when it carries the mdsmith marker) and compares the script against the canonical glob-based template.
- Reports a warning if either artifact drifts.
.gitattributes does not support negation patterns
(!*.md is a syntax error there). The managed block
exploits last-match-wins instead: include lines come first,
then <exclude> -merge lines override them for matching
paths.
# Fix
This rule is partially auto-fixable:
.gitattributesis auto-fixed bymdsmith fixwhen the merge driver is registered (merge.mdsmith.driverin git config). The fixer rewrites the managed block with the canonical globs, preserving any non-mdsmith entries surrounding it.- Pre-merge-commit hook is not auto-fixed (it is an executable script). You must manually re-run:
mdsmith pre-merge-commit install#
Why Auto-Fix .gitattributes?
.gitattributes is a tracked file. It is not system
configuration, and not executable. So it should be
auto-fixable like other content issues. Build systems
running mdsmith fix reconcile drift automatically.
The pre-merge-commit hook remains manual-only because modifying executable files during automated fixes could be surprising or unsafe.
When mdsmith fix updates .gitattributes, the fixer also
runs git add -- .gitattributes. The regenerated file then
lands in the index next to the markdown files the hook
stages, so a merge commit produced by the
pre-merge-commit hook flow includes both. If staging fails
(for example, the index is locked by another git process),
the on-disk fix is still applied and the failure is
recorded. Check() keeps emitting a “staging failed”
diagnostic until a later Fix() call re-runs git add
successfully.
# Manual Installation
To register the merge driver, write the canonical
.gitattributes block, and install the hook:
mdsmith merge-driver install
mdsmith pre-merge-commit installBoth commands write the canonical glob-based content from
the project’s .mdsmith.yml, so re-running them is
idempotent.
# Examples
# Good
.mdsmith.yml ignores test fixtures, and .gitattributes
mirrors that exactly:
# .mdsmith.yml
ignore:
- "demo/**"
- "vendor/**"# .gitattributes
# BEGIN mdsmith merge-driver
*.md merge=mdsmith
*.markdown merge=mdsmith
demo/** -merge
vendor/** -merge
# END mdsmith merge-driver# Bad
.gitattributes still uses the old per-file format from a
pre-glob install:
# BEGIN mdsmith merge-driver
README.md merge=mdsmith
docs/index.md merge=mdsmith
# END mdsmith merge-driverDiagnostic: .gitattributes managed block is out of sync. The message lists the installed include and exclude
patterns and the canonical patterns the rule expected.
# Meta-Information
- ID: MDS048
- Name:
git-hook-sync - Status: ready
- Default: disabled
- Fixable: partial (
.gitattributesis auto-fixed; hook requires manualmdsmith pre-merge-commit install) - Implementation: source
- Category: structural