post-merge 훅: 머지 후 자동화 작업 구성

post-merge 훅의 실행 시점과 squash 머지 판별 인수($1), ORIG_HEAD를 이용한 lockfile 변경 감지로 npm install 자동화, 마이그레이션 실행, 캐시 무효화 패턴을 설명한다.

· 4 min read · PALDYN Team

지난 글에서 push 전 테스트 게이트를 구성하는 pre-push 훅을 다뤘다. 이번에는 머지가 완료된 직후 실행되는 post-merge 훅으로 자동화 작업을 연결하는 방법을 살펴본다.

post-merge 훅 기본

post-merge 훅은 git merge(또는 git pull)가 성공적으로 완료된 직후 실행된다. pre-merge-commit과 달리, 이 훅이 실패(exit 1)해도 이미 완료된 머지는 되돌려지지 않는다. 따라서 post-merge는 머지 차단이 아닌 자동화·알림 용도로만 사용한다.

훅에는 인수 하나가 전달된다.

$1 = 0  # 일반 머지
$1 = 1  # squash 머지 (--squash 옵션)

post-merge 훅 실행 흐름

기본 훅 파일

#!/bin/sh
# .git/hooks/post-merge (또는 .githooks/post-merge)

SQUASH_MERGE=$1

echo "post-merge 실행 (squash=$SQUASH_MERGE)"

# 의존성 변경 감지 후 재설치
if git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD \
    | grep -q "package-lock.json\|yarn.lock\|pnpm-lock"; then
  echo "lockfile 변경 감지 → npm install 실행"
  npm install
fi

핵심은 ORIG_HEAD다. Git은 머지가 시작될 때 현재 HEAD를 ORIG_HEAD에 저장한다. git diff-tree ORIG_HEAD HEAD이번 머지에서 바뀐 파일 목록을 얻을 수 있다.

lockfile 변경 감지 패턴

가장 흔한 post-merge 활용은 의존성 파일 변경 감지다.

#!/bin/sh
# .git/hooks/post-merge

changed_files() {
  git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD
}

needs_install() {
  changed_files | grep -qE "^(package\.json|package-lock\.json|yarn\.lock|pnpm-lock\.yaml)$"
}

if needs_install; then
  echo "📦 의존성 변경 감지 — npm install 실행"
  npm install --prefer-offline
fi

lockfile 변경 감지 패턴

git diff-tree 옵션 설명:

옵션의미
-r재귀적으로 하위 파일까지
--name-only파일명만 출력
--no-commit-id커밋 SHA 출력 안 함
ORIG_HEAD HEAD머지 전·후 비교 범위

마이그레이션 자동 실행

DB 스키마 마이그레이션 파일이 머지됐을 때 자동으로 실행하는 패턴이다.

#!/bin/sh
# .git/hooks/post-merge

changed_files() {
  git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD
}

# 마이그레이션 파일 변경 여부 확인
if changed_files | grep -q "^migrations/"; then
  echo "🗄  마이그레이션 변경 감지"
  npm run db:migrate
fi

# 환경 변수 샘플 변경 확인
if changed_files | grep -q "\.env\.example$"; then
  echo "⚠  .env.example이 변경됐습니다. .env 파일을 확인하세요."
fi

squash 머지 구분

git merge --squash 사용 시 $1=1이 전달된다. squash 머지는 단일 커밋으로 합쳐지므로 diff 범위 계산이 다를 수 있다.

#!/bin/sh
SQUASH=$1

if [ "$SQUASH" = "1" ]; then
  # squash 머지: 하나의 새 커밋만 추가됨
  CHANGED=$(git diff-tree -r --name-only --no-commit-id HEAD~1 HEAD)
else
  # 일반 머지: ORIG_HEAD ~ HEAD 비교
  CHANGED=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)
fi

echo "$CHANGED" | grep -q "package-lock.json" && npm install

Husky에서 설정

# .husky/post-merge
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

if git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD \
    | grep -q "package-lock.json"; then
  echo "npm install 실행 중..."
  npm install --prefer-offline
fi

Husky 4.x 이하 방식(package.json"hooks" 키)에서는 post-merge를 직접 등록한다.

{
  "husky": {
    "hooks": {
      "post-merge": "if git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD | grep -q 'package-lock.json'; then npm install; fi"
    }
  }
}

주의: exit 1의 의미

pre-commit이나 commit-msg와 달리, post-merge에서 exit 1을 반환해도 이미 완료된 머지가 취소되지 않는다. 훅이 실패하면 터미널에 에러가 출력될 뿐이다.

머지 자체를 차단하려면 pre-merge-commit 훅을 사용한다. post-merge는 순수하게 “머지 후 해야 할 일”을 자동화하는 용도다.


지난 글: pre-push 훅: push 전 테스트와 브랜치 보호

다음 글: 서버 사이드 훅: pre-receive, update, post-receive


읽어주셔서 감사합니다. 😊