TT Lab
Get started
Learn Learning paths Courses

Git in Practice

The Rule Was in a Hook, and One Word Walked Past It

Continue in TT Lab

Goal

You build a set of client-side hooks that are kept in the repository and shared, and confirm for yourself what they block and what they cannot block.

Why it matters

Hooks are introduced with the phrase "automate the rules", but what a client-side hook actually does is fast feedback. It is not enforcement. If you do not know this difference and hang rules only on the client, you go on for a long time in a state where you believe they are being followed. This lab breaks that belief yourself — you build three rules and get past all of them with one word. And you confirm why it does not work if pre-commit reads the working tree, by creating a state in which the secret is only in the index. The fact that what gets committed is the index and not the working tree is worth knowing even if you never use hooks.

Steps

  1. Create /root/gitx9/repo and set core.hooksPath to .githooks.
  2. Enforce the subject format with .githooks/commit-msg, and leave the record of the rejection in notes/reject.txt.
  3. Block files over 100000 bytes with .githooks/pre-commit and leave it in notes/bigfile.txt.
  4. Fix that hook to look at the index and block strings starting with AKIA, and leave in notes/index.txt why it gets bypassed if you look only at the working tree.
  5. Make one commit that breaks the rules with --no-verify and leave it in notes/bypass.txt.
  6. Take a clone at /root/gitx9/clone and leave in notes/share.txt what comes along and what does not.
  7. Create /root/gitx9/origin.git, and with .githooks/pre-push block a direct push to main, leaving it in notes/prepush.txt.
  8. Summarize the roles of client-side hooks and server-side hooks in notes/report.md.

Notes

Decide to keep the hooks inside the repository

Create /root/gitx9/repo and set core.hooksPath to .githooks.

.git/hooks/ does not come along with a clone. Move the place where hooks are looked up to a directory that the repository tracks, with git config core.hooksPath .githooks. Create the directory beforehand too. You must also set user.email and user.name for each repository for commits to work.

Enforce the subject format

Enforce the subject format with .githooks/commit-msg, and leave the record of the rejection in notes/reject.txt.

The commit-msg hook receives the message file path as $1. Look only at the first line and check whether it has the format <타입>(<범위>): <설명> (type, scope, description), using grep -qE. For the type, feat, fix, docs, refactor, test, chore and build are enough. On a mismatch, say what is wrong on standard error and exit with a non-zero value. Do not forget chmod +x.

Block large files before the commit

Block files over 100000 bytes with .githooks/pre-commit and leave it in notes/bigfile.txt.

pre-commit takes no arguments. You must find out for yourself what is about to be committed with git diff --cached --name-only --diff-filter=ACM. If the size exceeds 100000, say which file is how many bytes and exit with a non-zero value. After confirming for yourself that it is blocked, take that large file out of the staging.

What gets committed is the index, not the working tree

Fix that hook to look at the index and block strings starting with AKIA, and leave in notes/index.txt why it gets bypassed if you look only at the working tree.

The blob that went into the index is the second column of git ls-files -s -- <경로>, and you read the content with git cat-file -p <blob> or git show :<경로>. Measure the size with git cat-file -s <blob> too. Then write AKIAIOSFODNN7EXAMPLE into a file and git add it, and try committing after fixing only the working-tree file clean. It is a commit that would have passed as it is with a hook that reads the working tree.

It all gets past with one word

Make one commit that breaks the rules with --no-verify and leave it in notes/bypass.txt.

git commit --no-verify skips pre-commit and commit-msg. Make one commit that breaks the subject rule and leave it in the history. And also write that this is a design and not a flaw, and so where you must hang a rule that needs enforcement.

The hook files come along but the setting does not

Take a clone at /root/gitx9/clone and leave in notes/share.txt what comes along and what does not.

Run git clone /root/gitx9/repo /root/gitx9/clone, and inside it look at three things — what is in .githooks/, what git config --local core.hooksPath outputs, and what is in .git/hooks/. After checking, turn on core.hooksPath in the clone too.

Try blocking a protected branch on the client

Create /root/gitx9/origin.git, and with .githooks/pre-push block a direct push to main, leaving it in notes/prepush.txt.

pre-push receives on standard input, one line per ref, 로컬참조 로컬해시 원격참조 원격해시 (local ref, local hash, remote ref, remote hash). Block if there is a line where 원격참조 (the remote ref) is refs/heads/main. Create the remote with git init --bare /root/gitx9/origin.git and attach it with git remote add origin. After confirming that the main push is blocked, push only a feature branch.

Which place tells you and which place stops you

Summarize the roles of client-side hooks and server-side hooks in notes/report.md.

One table and a few lines of rules are enough. You must write the three things a client-side hook cannot block, and finish with why you hang the same rule in two places. The practical advice that if a hook is slow, people make bypassing a habit is also valuable.