Chapter 17Lesson 05~165 minutes

Checkpoint Lab — Hooks, Aliases, Templates, Attributes, and Local Automation

Checkpoint local Git automation by proving what tracked scripts, attributes, aliases, hook activation, local metadata, templates, and server policies do and do not carry into a second repository context.

CheckpointPolicy layersClone portabilityEnforcement

Learning objectives

  • Predict which customizations travel through clone and verify each prediction.
  • Activate a reviewed tracked hook in a second context and diagnose a broken hooksPath.
  • Break and repair an attribute path rule using git check-attr evidence.
  • Contrast optional local feedback with an authoritative local receive hook.
  • Write a policy assigning checks to local development, CI, and server/hosting enforcement.

1. Checkpoint scenario — prove what travels and what enforces policy

You are preparing a small internal repository standard. The source repository will contain one tracked hook script, one tracked .gitattributes policy, and one tracked .gitignore. It will also have one local Git alias and local core.hooksPath activation. Separately, an init template contains a harmless metadata notice. You will clone to a second context and prove which pieces travel automatically.

2. Predictions before setup

  1. Will the tracked .githooks/commit-msg file appear in a normal clone?
  2. Will the source repository's local core.hooksPath setting appear in the clone?
  3. Will tracked .gitattributes rules apply in the clone?
  4. Will a file copied by a separate init template into one repository's .git metadata appear in an unrelated clone?

3. Create the isolated source repository

Git Bash, Bash, or zsh

mkdir git-policy-checkpoint
cd git-policy-checkpoint
git init -b trunk source
cd source
git config user.name "Policy Checkpoint"
git config user.email "policy-checkpoint@example.invalid"

mkdir -p .githooks docs scripts assets

cat > .githooks/commit-msg <<'EOF'
#!/bin/sh
msgfile=$1
gitdir=$(git rev-parse --git-dir)
printf 'commit-msg arg=%s\n' "$msgfile" >> "$gitdir/policy-hook.log"
case "$(sed -n '1p' "$msgfile")" in
  "POLICY: "*) exit 0 ;;
  *)
    echo "policy hook: subject must start with POLICY: " >&2
    exit 1
    ;;
esac
EOF
chmod +x .githooks/commit-msg

cat > .gitattributes <<'EOF'
* text=auto
*.sh text eol=lf
*.png binary
docs/** export-ignore
EOF

printf "*.log\n" > .gitignore
printf "# Policy Demo\n" > README.md
printf "# Public guide\n" > docs/guide.md
printf "# Internal guide\n" > docs/internal.md
printf "echo build\n" > scripts/build.sh
printf "fake image\n" > assets/logo.png

git config --local core.hooksPath .githooks
git config --local alias.st 'status --short --branch'

git add .
git commit -m "POLICY: establish repository automation"
SOURCE_TIP=$(git rev-parse HEAD)

PowerShell setup alternative

Use the same repository content with Set-Content and create the hook file from a here-string. In Git for Windows, verify that the hook interpreter/execution model actually invokes the script; do not assume POSIX permission bits behave identically on every filesystem.

4. Add a local-only attribute override and prove it is not history

printf "local-only.dat custom=source-only\n" > .git/info/attributes
git check-attr custom -- local-only.dat
git status --short

The result should report custom: source-only, but status remains clean because .git/info/attributes is repository metadata rather than tracked content.

5. Create one init-template file and initialize a separate repository

cd ..
mkdir template-seed
printf "policy-template-version=1\n" > template-seed/policy-notice.txt

git init --template=template-seed -b trunk seeded
cat seeded/.git/policy-notice.txt

This proves template seeding separately from cloning. The notice belongs to seeded/.git; it is not source repository history.

6. Preflight the source customizations

git -C source config --show-origin --get core.hooksPath
git -C source config --get-regexp '^alias\.'
git -C source check-attr --all -- scripts/build.sh
git -C source check-attr --all -- assets/logo.png
git -C source check-attr export-ignore -- docs/internal.md
git -C source check-ignore -v -- debug.log || true
test -f source/.githooks/commit-msg
test -f source/.git/policy-hook.log

The hook log should exist because the initial POLICY commit triggered it. It is local metadata and will become an important comparison point.

7. Clone to a second context and compare distribution

git clone source clone
git -C clone config user.name "Clone Learner"
git -C clone config user.email "clone-learner@example.invalid"

test -f clone/.githooks/commit-msg && echo "tracked hook script traveled"
test -f clone/.gitattributes && echo "attributes traveled"
test -f clone/.gitignore && echo "ignore policy traveled"

git -C clone config --get core.hooksPath || echo "core.hooksPath did not travel"
git -C clone config --get-regexp '^alias\.' || echo "local aliases did not travel"
test ! -f clone/.git/policy-hook.log && echo "source hook log did not travel"
test ! -f clone/.git/policy-notice.txt && echo "separate template notice did not travel"

Verify predictions 1–4: tracked files travel; source local config/metadata and an unrelated template seed do not.

8. Verify tracked attributes are active in the clone immediately

git -C clone check-attr text eol -- scripts/build.sh
git -C clone check-attr text diff merge -- assets/logo.png
git -C clone check-attr export-ignore -- docs/internal.md
git -C clone check-attr custom -- local-only.dat

The shared attributes resolve from tracked history. The custom=source-only rule should be unspecified because the source's .git/info/attributes did not clone.

9. Prove the tracked hook is not active until configured

cd clone
printf "clone context\n" > clone-note.txt
git add clone-note.txt
git commit -m "plain subject succeeds before hook activation"
git log -1 --oneline
test ! -f .git/policy-hook.log && echo "hook did not run"

The tracked script exists, but the clone has no core.hooksPath=.githooks setting. This is direct evidence that distribution and activation are separate.

10. Activate the reviewed hook in the clone

git config --local core.hooksPath .githooks
git config --show-origin --get core.hooksPath

printf "activation test\n" > activation.txt
git add activation.txt
git commit -m "invalid subject"
echo "invalid commit exit=$?"
git status --short

git commit -m "POLICY: verify activated hook"
cat .git/policy-hook.log

The invalid commit should fail and leave the staged file intact; the valid POLICY commit should succeed.

11. Break the hook path deliberately and diagnose false confidence

git config --local core.hooksPath .missing-hooks
git config --path --get core.hooksPath
test ! -d .missing-hooks && echo "configured hook directory is missing"

printf "broken path demo\n" > broken-path.txt
git add broken-path.txt
git commit -m "plain subject succeeds because configured hook is missing"

This commit succeeding is the failure: policy appeared configured but no hook program existed at the selected location. Diagnose and repair:

git config --local core.hooksPath .githooks
git config --path --get core.hooksPath
test -x .githooks/commit-msg && echo "hook candidate exists and is executable"

12. Break one attribute path rule and repair it from evidence

cp .gitattributes .gitattributes.good
sed 's#docs/\*\* export-ignore#docs/ export-ignore#' .gitattributes.good > .gitattributes

git check-attr export-ignore -- docs/internal.md
git diff -- .gitattributes

The attribute becomes unspecified because a directory-only pattern does not recurse in gitattributes. Restore the correct recursive pattern:

mv .gitattributes.good .gitattributes
git check-attr export-ignore -- docs/internal.md
git add .gitattributes
git status --short

13. Add an authoritative local receive boundary

cd ..
git init --bare central.git

cat > central.git/hooks/pre-receive <<'EOF'
#!/bin/sh
while read old_oid new_oid ref_name
do
  case "$ref_name" in
    refs/heads/trunk)
      echo "server policy: direct trunk updates are blocked in this demo" >&2
      exit 1
      ;;
  esac
done
exit 0
EOF
chmod +x central.git/hooks/pre-receive

git -C clone remote add central ../central.git
git -C clone push central HEAD:refs/heads/review/demo
git -C clone push central HEAD:refs/heads/trunk
echo "trunk push exit=$?"

The review branch push should succeed and the trunk push should fail. This demonstrates the boundary between optional/bypassable local feedback and authoritative receive policy.

14. Write the local/CI/server responsibility policy

Rule Local CI Server/hosting
Commit message starts with project prefix commit-msg fast feedback Validate submitted range Enforce if organizationally mandatory
Shell scripts use LF Tracked .gitattributes Verify checkout/build No special receive hook normally needed
Tests pass Optional fast subset Mandatory full suite Allow trunk update only after required result
Protected trunk update Cannot authoritatively enforce Produces approval signal Mandatory gate
Custom diff/merge tools Explicit trusted install Controlled runner image Not a client trust substitute

15. Verification checklist

  • Source hook script is tracked and source core.hooksPath is local config.
  • Source alias is local config and does not appear in clone.
  • Tracked .gitattributes, .gitignore, and .githooks files appear in clone.
  • Source .git/info/attributes and hook-observation log do not appear in clone.
  • The separate init-template notice exists only in the repository seeded with that template.
  • The clone's tracked hook is inactive until core.hooksPath is set.
  • After activation, an invalid message is rejected and staged work remains.
  • A deliberately missing hooksPath lets a plain commit through, proving false-confidence risk.
  • docs/ export-ignore fails recursively; docs/** export-ignore succeeds.
  • The local server allows a review branch but rejects direct trunk updates.

16. Cleanup

Everything is disposable. Confirm the parent path before deletion.

Git Bash / Bash / zsh

cd ../..
pwd
rm -rf git-policy-checkpoint

PowerShell

Set-Location ../..
Get-Location
Remove-Item -Recurse -Force git-policy-checkpoint

17. Knowledge check

Question 1. Why did the .githooks file travel while core.hooksPath did not?

Question 2. What proved that the source's local attribute override did not travel?

Question 3. A plain commit succeeds after hooksPath is changed to .missing-hooks. What failed?

Question 4. Why did the server reject trunk even though the client created the commit successfully?

Question 5. Which chapter principle should guide automation placement?

18. What Chapter 17 adds to a production Git operating model

You can now distinguish executable local automation from versioned repository policy, seed repositories without confusing templates with ongoing management, diagnose attribute/ignore behavior from Git's effective view, and design layered enforcement that remains understandable across Windows, Linux, macOS, CI, and remote servers.

19. Chapter checkpoint summary

The operational rule is straightforward: know what travels, what executes, and what can enforce. Tracked files are reviewable but not automatically active; local configuration is powerful but non-portable; templates are init-time seeds; server policy is centralized; custom drivers and hooks are code and deserve a trust review.

Next chapter

Plumbing Commands, Index Internals, cat-file, hash-object, and commit-tree

Chapter 18 exposes the lower-level interfaces beneath porcelain so you can reason about the index and object database directly while keeping experiments isolated from the live index/ref state.

Authoritative references

 githooks
 git-config
 git-init
 gitattributes
 git-check-attr
 git-check-ignore

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.