<!--order=11-->
<h1>Git: Hooks, Attributes, and Config</h1>
<p>Git has a rich configuration system for customizing behaviour at every level. Hooks are scripts that run automatically at specific points in the Git workflow. Attributes define per-path settings for things like line endings, diff output, and merge strategies. The <code>.gitignore</code> and <code>.mailmap</code> files further control what Git tracks and how it labels contributors.</p>

<h2>Commands Covered</h2>
<p><code>git hook</code> · <code>git config</code> (advanced) · <code>git check-attr</code> · <code>git check-ignore</code> · <code>git check-mailmap</code> · <code>git interpret-trailers</code></p>

<h2>git hook</h2>
<p>Hooks are shell scripts in <code>.git/hooks/</code> that Git executes automatically at defined points. They can validate commits, run tests, enforce style, or notify services.</p>
<pre>
# List available hooks
$ git hook list pre-commit

# Run a hook manually
$ git hook run pre-commit

# Hook directory
$ ls .git/hooks/
# applypatch-msg.sample  pre-commit.sample
# commit-msg.sample      pre-push.sample
# post-commit.sample     prepare-commit-msg.sample
# ...

# Enable a hook: remove the .sample extension and make it executable
$ cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
$ chmod +x .git/hooks/pre-commit
</pre>

<h3>Common Hooks</h3>
<table>
  <thead><tr><th>Hook</th><th>When it runs</th><th>Common uses</th></tr></thead>
  <tbody>
    <tr><td><code>pre-commit</code></td><td>Before commit message is entered</td><td>Run linter, tests; reject if they fail</td></tr>
    <tr><td><code>commit-msg</code></td><td>After message is written, before commit</td><td>Enforce message format; check for ticket numbers</td></tr>
    <tr><td><code>prepare-commit-msg</code></td><td>Before editor opens for message</td><td>Pre-fill commit message with branch name or template</td></tr>
    <tr><td><code>post-commit</code></td><td>After commit is created</td><td>Notifications; local CI triggers</td></tr>
    <tr><td><code>pre-push</code></td><td>Before push to remote</td><td>Run test suite; block push on failure</td></tr>
    <tr><td><code>pre-rebase</code></td><td>Before rebase starts</td><td>Warn about rebasing shared branches</td></tr>
    <tr><td><code>post-merge</code></td><td>After a successful merge</td><td>Run dependency install if lockfile changed</td></tr>
    <tr><td><code>post-checkout</code></td><td>After switching branches</td><td>Rebuild, update dependencies</td></tr>
  </tbody>
</table>

<pre>
# Example pre-commit hook — run PHP syntax check:
#!/bin/sh
git diff --cached --name-only --diff-filter=ACM | grep '\.php$' | while read f; do
    php -l "$f" &gt;/dev/null 2&gt;&amp;1
    if [ $? -ne 0 ]; then
        echo "PHP syntax error in $f"
        exit 1
    fi
done

# Example commit-msg hook — enforce prefix format:
#!/bin/sh
MSG=$(cat "$1")
PATTERN="^(feat|fix|docs|refactor|test|chore): .+"
if ! echo "$MSG" | grep -qE "$PATTERN"; then
    echo "Commit message must start with: feat|fix|docs|refactor|test|chore: ..."
    exit 1
fi

# Skip hooks temporarily (e.g. emergency commit):
$ git commit --no-verify -m "Emergency fix"

# Sharing hooks with the team — hooks in .git/hooks/ are not committed.
# Options:
# 1. Store hooks in a repo directory and symlink or copy them.
# 2. Use git config core.hooksPath to point to a committed directory:
$ git config core.hooksPath .githooks
# Then commit .githooks/pre-commit to the repo.
</pre>

<h2>git config (Advanced)</h2>
<pre>
# Config levels (each overrides the previous):
# --system   /etc/gitconfig              (all users on machine)
# --global   ~/.gitconfig or ~/.config/git/config  (your account)
# --local    .git/config                 (this repo only)
# --worktree .git/config.worktree        (specific worktree)

# Show where a setting comes from
$ git config --list --show-origin --show-scope

# Unset a config value
$ git config --unset user.email
$ git config --global --unset core.editor

# Edit config file directly
$ git config --global --edit

# Useful settings:
$ git config --global core.autocrlf input    # LF on commit, leave alone on checkout (Mac/Linux)
$ git config --global core.autocrlf true     # CRLF on checkout, LF on commit (Windows)
$ git config --global core.whitespace trailing-space,space-before-tab
$ git config --global push.default current   # push current branch to same-named remote branch
$ git config --global fetch.prune true       # always prune on fetch
$ git config --global merge.conflictstyle diff3  # show the common ancestor in conflicts
$ git config --global rebase.autoStash true  # auto-stash before rebase
$ git config --global log.date relative      # show "2 hours ago" in log

# Conditional config — use different email for work repos
$ git config --global includeIf.gitdir:/home/user/work/.path work.gitconfig
# In ~/work.gitconfig:
# [user]
#   email = jason@company.com
</pre>

<h2>git check-ignore</h2>
<p>Debugs which <code>.gitignore</code> rule caused a file to be ignored.</p>
<pre>
# Check why a file is ignored
$ git check-ignore -v build/output.log
# .gitignore:5:build/    build/output.log
# Shows: filename:line:pattern  matched-file

# Check a file that isn't ignored (verbose shows nothing is matching)
$ git check-ignore -v src/main.c
# (no output = not ignored)

# Check multiple files
$ git check-ignore -v *.log

# Check even if the file doesn't exist on disk
$ git check-ignore --non-matching -v hypothetical.pyc

# List all ignored files
$ git ls-files --ignored --exclude-standard
</pre>

<h2>git check-attr</h2>
<p>Shows which <code>.gitattributes</code> rules apply to files.</p>
<pre>
# Check a specific attribute
$ git check-attr text README.md
# README.md: text: auto

# Check all attributes for a file
$ git check-attr -a src/image.png
# src/image.png: text: unset
# src/image.png: binary: set
# src/image.png: diff: unset
# src/image.png: merge: unset

# Example .gitattributes for a PHP project:
# * text=auto             # auto-detect text files, normalize line endings
# *.php text eol=lf       # PHP files always use LF
# *.sh text eol=lf executable  # shell scripts: LF + mark executable
# *.png binary            # binary — don't diff or convert
# *.jpg binary
# *.pdf binary
# *.sql diff=sql          # use sql diff driver
# CHANGELOG.md merge=union  # merge conflicts: keep both sides
</pre>

<h2>git check-mailmap</h2>
<p>Shows canonical names and emails for contributors. <code>.mailmap</code> lets you consolidate different author names and emails (someone commits from different machines with different configs) into a single identity for log and shortlog output.</p>
<pre>
# Check how an author appears after mailmap is applied
$ git check-mailmap "Old Name &lt;old@email.com&gt;"

# Example .mailmap file:
# Canonical Name &lt;canonical@email.com&gt; &lt;old@email.com&gt;
# Jason Smith &lt;jason@dispelled.ca&gt; &lt;jason@oldaddress.com&gt;
# Jason Smith &lt;jason@dispelled.ca&gt; Jason Old-Lastname &lt;jason@oldaddress.com&gt;

# After mailmap, git shortlog -sn shows the canonical identity
$ git shortlog -sn
</pre>

<h2>git interpret-trailers</h2>
<p>Adds or parses structured key: value lines (trailers) in commit messages. Trailers are a convention for adding metadata like "Signed-off-by", "Co-authored-by", "Closes", "Reviewed-by".</p>
<pre>
# Add a trailer to a commit message
$ git interpret-trailers --trailer "Signed-off-by: Jason &lt;jason@dispelled.ca&gt;" \
    --in-place .git/COMMIT_EDITMSG

# Parse trailers from the last commit
$ git log -1 --format="%B" | git interpret-trailers --parse

# Amend the last commit with a new trailer
$ git commit --amend --trailer "Reviewed-by: Alice &lt;alice@example.com&gt;"

# Example commit message with trailers:
# Fix null pointer in session handler
#
# The session object was not checked for null before calling getUser().
# Closes #142
#
# Signed-off-by: Jason &lt;jason@dispelled.ca&gt;
# Co-authored-by: Alice &lt;alice@example.com&gt;
</pre>

<h2>References</h2>
<ul>
  <li><a href="https://git-scm.com/docs/githooks" target="_blank" rel="noopener">Git Hooks documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/gitattributes" target="_blank" rel="noopener">gitattributes documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/gitmailmap" target="_blank" rel="noopener">gitmailmap documentation ↗</a></li>
  <li><a href="https://git-scm.com/docs/git-interpret-trailers" target="_blank" rel="noopener">git-interpret-trailers documentation ↗</a></li>
</ul>
