规则挂在钩子上,一个词就绕过去了
한국어 원문으로 표시합니다.
목표
저장소에 담아 공유되는 클라이언트 훅 셋을 만들고, 그것이 무엇을 막고 무엇을 못 막는지 직접 확인합니다.
왜 중요한가
훅은 "규칙을 자동화한다" 는 말로 소개되지만, 클라이언트 훅이 실제로 하는 일은 빠른
피드백입니다. 강제가 아닙니다. 이 차이를 모르고 규칙을 클라이언트에만 걸어 두면,
지켜지고 있다고 믿는 상태로 오래 갑니다. 이 실습은 그 믿음을 직접 깨 봅니다 — 규칙을
셋 만들고, 한 낱말로 전부 지나갑니다. 그리고 pre-commit 이 작업 트리를 읽으면 왜
안 되는지를, 인덱스에만 비밀이 있는 상태를 만들어 확인합니다. 커밋되는 것은 인덱스이지
작업 트리가 아니라는 사실은 훅을 쓰지 않더라도 알아 둘 값이 있습니다.
단계
/root/gitx9/repo를 만들고core.hooksPath를.githooks로 지정합니다..githooks/commit-msg로 제목 형식을 강제하고, 거절당한 기록을notes/reject.txt에 남깁니다..githooks/pre-commit으로 100000 바이트를 넘는 파일을 막고notes/bigfile.txt에 남깁니다.- 그 훅이 인덱스를 보게 고쳐
AKIA로 시작하는 문자열을 막고, 작업 트리만 보면 왜 뚫리는지를notes/index.txt에 남깁니다. --no-verify로 규칙을 어긴 커밋을 하나 만들고notes/bypass.txt에 남깁니다./root/gitx9/clone을 떠서 무엇이 따라오고 무엇이 안 따라오는지를notes/share.txt에 남깁니다./root/gitx9/origin.git을 만들고.githooks/pre-push로main직접 push 를 막아notes/prepush.txt에 남깁니다.notes/report.md에 클라이언트 훅과 서버 훅의 역할을 정리합니다.
참고
- 훅 파일에는 반드시 실행 권한(
chmod +x)을 주세요. 없으면 git 이 조용히 건너뜁니다. - 이 이미지에는 python3 이 없습니다. 훅은
sh와git·grep·awk로 씁니다. - 4단계의 시험 문자열은
AKIAIOSFODNN7EXAMPLE입니다. 패턴은AKIA뒤에 대문자·숫자 16자로 잡으세요. - 흔한 실수: 훅을 만들고 커밋하지 않는 것입니다. 저장소에 담겨야 공유됩니다.
- 흔한 실수: 7단계에서
main을 끝내 push 해 버리는 것입니다. 막히는 것을 확인하고 기능 브랜치만 올려야 합니다.
훅을 저장소 안에 두기로 한다
/root/gitx9/repo 를 만들고 core.hooksPath 를 .githooks 로 지정합니다.
.git/hooks/ 는 clone 으로 따라오지 않습니다. git config core.hooksPath .githooks 로 훅을 찾는 자리를 저장소가 추적하는 디렉터리로 옮기세요. 디렉터리도 미리 만들어 둡니다. 저장소마다 user.email 과 user.name 도 지정해야 커밋이 됩니다.
제목 형식을 강제한다
.githooks/commit-msg 로 제목 형식을 강제하고, 거절당한 기록을 notes/reject.txt 에 남깁니다.
commit-msg 훅은 메시지 파일 경로를 $1 로 받습니다. 첫 줄만 보고 <타입>(<범위>): <설명> 형식인지 grep -qE 로 검사하세요. 타입은 feat·fix·docs·refactor·test·chore·build 정도면 됩니다. 어긋나면 표준 오류로 무엇이 잘못됐는지 말하고 0이 아닌 값으로 끝내야 합니다. chmod +x 를 잊지 마세요.
큰 파일을 커밋 전에 막는다
.githooks/pre-commit 으로 100000 바이트를 넘는 파일을 막고 notes/bigfile.txt 에 남깁니다.
pre-commit 은 인자를 받지 않습니다. 무엇이 커밋되려는지는 git diff --cached --name-only --diff-filter=ACM 으로 스스로 알아내야 합니다. 크기가 100000 을 넘으면 어떤 파일이 몇 바이트인지 말하고 0이 아닌 값으로 끝내세요. 막히는 것을 직접 확인한 뒤에는 그 큰 파일을 스테이징에서 내려 두세요.
커밋되는 것은 작업 트리가 아니라 인덱스다
그 훅이 인덱스를 보게 고쳐 AKIA 로 시작하는 문자열을 막고, 작업 트리만 보면 왜 뚫리는지를 notes/index.txt 에 남깁니다.
인덱스에 올라간 blob 은 git ls-files -s -- <경로> 의 둘째 칸이고, 내용은 git cat-file -p <blob> 또는 git show :<경로> 로 읽습니다. 크기도 git cat-file -s <blob> 로 재세요. 그다음 파일에 AKIAIOSFODNN7EXAMPLE 를 써서 git add 하고, 작업 트리 쪽 파일만 깨끗하게 고친 뒤 커밋해 보세요. 작업 트리를 읽는 훅이라면 그대로 통과했을 커밋입니다.
한 낱말로 전부 지나간다
--no-verify 로 규칙을 어긴 커밋을 하나 만들고 notes/bypass.txt 에 남깁니다.
git commit --no-verify 는 pre-commit 과 commit-msg 를 건너뜁니다. 제목 규칙을 어긴 커밋을 하나 만들어 이력에 남기세요. 그리고 이것이 결함이 아니라 설계라는 것, 그래서 강제력이 필요한 규칙은 어디에 걸어야 하는지를 함께 적습니다.
훅 파일은 따라오고 설정은 안 따라온다
/root/gitx9/clone 을 떠서 무엇이 따라오고 무엇이 안 따라오는지를 notes/share.txt 에 남깁니다.
git clone /root/gitx9/repo /root/gitx9/clone 을 하고, 그 안에서 세 가지를 보세요 — .githooks/ 에 무엇이 있는지, git config --local core.hooksPath 가 무엇을 내는지, .git/hooks/ 에 무엇이 있는지. 확인한 뒤 clone 에서도 core.hooksPath 를 켜세요.
보호 브랜치를 클라이언트에서 막아 본다
/root/gitx9/origin.git 을 만들고 .githooks/pre-push 로 main 직접 push 를 막아 notes/prepush.txt 에 남깁니다.
pre-push 는 표준 입력으로 줄마다 로컬참조 로컬해시 원격참조 원격해시 를 받습니다. 원격참조 가 refs/heads/main 인 줄이 있으면 막으세요. 원격은 git init --bare /root/gitx9/origin.git 으로 만들고 git remote add origin 으로 붙입니다. main push 가 막히는 것을 확인한 뒤 기능 브랜치만 올리세요.
어느 자리가 알려 주고 어느 자리가 막는가
notes/report.md 에 클라이언트 훅과 서버 훅의 역할을 정리합니다.
표 하나와 규칙 몇 줄이면 됩니다. 클라이언트 훅이 못 막는 세 가지를 반드시 적고, 그래서 같은 규칙을 두 자리에 거는 이유로 마무리하세요. 훅이 느리면 사람들이 우회를 습관으로 쓴다는 실무 조언도 값이 있습니다.