Fundamentals 11 min read

Mastering Git Hooks: Automate Your Workflow with Pre‑Commit, Pre‑Push and More

This article explains how Git Hooks act as an automation engine for common team tasks—code formatting, commit‑message validation, test execution, CI/CD triggers, and branch protection—by showing concrete hook scripts, client‑side and server‑side examples, and how tools like Husky, commitlint and lint‑staged simplify hook management.

Coder Trainee
Coder Trainee
Coder Trainee
Mastering Git Hooks: Automate Your Workflow with Pre‑Commit, Pre‑Push and More

1. What Are Git Hooks?

Git Hooks are built‑in hook scripts that automatically run when specific Git actions (e.g., commit, push, merge) occur. Each repository contains a .git/hooks/ directory; the sample files ending with .sample become active after removing the suffix.

2. Common Client‑Side Hooks

2.1 pre‑commit (run before a commit)

Typical uses: code‑style checks, linting.

# .git/hooks/pre-commit
#!/bin/bash

echo "🔍 运行 pre-commit 检查..."

# Detect unresolved conflict markers
if grep -r "^<<<<<<< " --include="*.java" --include="*.js" --include="*.py" .; then
  echo "❌ 发现未解决的冲突标记,请先解决冲突"
  exit 1
fi

# Warn about TODOs (optional)
if grep -r "TODO" --include="*.java" .; then
  echo "⚠️ 警告:代码中包含 TODO,建议处理后再提交"
fi

echo "✅ pre-commit 检查通过"

2.2 commit‑msg (validate commit message)

Runs after the commit message is entered to enforce a conventional format.

# .git/hooks/commit-msg
#!/bin/bash

commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+$"
if ! echo "$commit_msg" | grep -qE "$pattern"; then
  echo "❌ 提交信息格式错误!"
  echo "请使用以下格式:"
  echo "  feat(scope): 新功能说明"
  echo "  fix(scope):  修复说明"
  echo "  docs(scope):  文档更新"
  echo "  refactor(scope): 重构说明"
  echo "示例: feat(payment): 添加微信支付功能"
  exit 1
fi

echo "✅ 提交信息格式正确"

2.3 pre‑push (run before pushing)

Commonly used to run tests or build steps.

# .git/hooks/pre-push
#!/bin/bash

echo "🔍 运行 pre-push 检查..."

# Run Maven tests if a pom.xml exists
if [ -f "pom.xml" ]; then
  echo "📦 运行 Maven 测试..."
  mvn test
  if [ $? -ne 0 ]; then
    echo "❌ 测试失败,取消推送"
    exit 1
  fi
fi

echo "✅ pre-push 检查通过"

2.4 post‑commit (run after a commit)

Useful for notifications or logging.

# .git/hooks/post-commit
#!/bin/bash

echo "✅ 提交成功!"
echo "📝 提交信息:$(git log -1 --pretty=%B)"

3. Husky: Team‑Level Hook Management

Directly editing .git/hooks/ is not tracked by Git, requires each team member to configure locally, and is hard to version‑control.

Husky stores hooks in the project directory (e.g., .husky/) so they are versioned and shared.

3.1 Installing Husky

# npm install --save-dev husky
npx husky install

# Add to package.json
{
  "scripts": {
    "prepare": "husky install"
  }
}

3.2 Adding Hooks with Husky

# Create a pre‑commit hook that runs lint
npx husky add .husky/pre-commit "npm run lint"
# Create a commit‑msg hook that runs commitlint
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

3.3 commitlint (enforce commit‑message conventions)

# npm install --save-dev @commitlint/config-conventional @commitlint/cli

echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

# Create the hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

3.4 lint‑staged (run checks only on staged files)

# npm install --save-dev lint-staged

# package.json snippet
{
  "lint-staged": {
    "*.java": ["mvn spotless:apply"],
    "*.js": ["eslint --fix"],
    "*.{js,json,md}": ["prettier --write"]
  }
}

# Update pre‑commit hook to use lint‑staged
npx husky add .husky/pre-commit "npx lint-staged"

4. Git Hooks in a Java Project

4.1 Spotless (Java code formatting)

# .husky/pre-commit
#!/bin/bash

echo "🔍 格式化 Java 代码..."

# Use Maven Spotless plugin
mvn spotless:apply

# Re‑add files if formatting changed
if ! git diff --exit-code; then
  echo "📝 代码已格式化,重新添加文件..."
  git add .
fi

echo "✅ 格式化完成"

Spotless Maven plugin configuration (pom.xml):

<plugin>
  <groupId>com.diffplug.spotless</groupId>
  <artifactId>spotless-maven-plugin</artifactId>
  <version>2.43.0</version>
  <configuration>
    <java>
      <googleJavaFormat/>
      <removeUnusedImports/>
    </java>
  </configuration>
</plugin>

4.2 Custom Java Hook Example

# .husky/pre-commit
#!/bin/bash

echo "🔍 运行 Java 项目检查..."

# 1. Code formatting
mvn spotless:apply

# 2. Detect conflict markers
if grep -r "^<<<<<<< " --include="*.java" .; then
  echo "❌ 发现冲突标记"
  exit 1
fi

# 3. Warn about System.out.println
if grep -r "System\.out\.println" --include="*.java" src/; then
  echo "⚠️ 警告:发现 System.out.println,建议使用日志框架"
fi

# 4. (Optional) Run Checkstyle
# mvn checkstyle:check

echo "✅ 检查通过"

5. Server‑Side Hooks

5.1 pre‑receive (checks before accepting a push)

# .git/hooks/pre-receive
#!/bin/bash

# Reject pushes that contain large binary files
while read oldrev newrev refname; do
  for commit in $(git rev-list $oldrev..$newrev); do
    git show $commit | grep -E "Binary files.*differ" | while read line; do
      echo "❌ 拒绝推送:提交包含大文件"
      exit 1
    done
  done
done

5.2 update (branch‑update checks)

# .git/hooks/update
#!/bin/bash

branch=$1
oldrev=$2
newrev=$3

# Protect the main branch
if [ "$branch" = "main" ]; then
  echo "❌ 不允许直接 push 到 main 分支,请使用 PR"
  exit 1
fi

6. Common Scenarios

Scenario 1: Enforce commit‑message format

# .husky/commit-msg
#!/bin/bash
npx commitlint --edit "$1"

Scenario 2: Block commits containing console.log

# .husky/pre-commit
#!/bin/bash
if git diff --cached | grep -E "^\+.*console\.log"; then
  echo "❌ 发现 console.log,请移除后再提交"
  exit 1
fi

Scenario 3: Prevent direct pushes to main

# .husky/pre-push
#!/bin/bash
current_branch=$(git branch --show-current)
if [ "$current_branch" = "main" ]; then
  echo "❌ 不允许直接 push 到 main 分支"
  echo "请使用 PR 合并"
  exit 1
fi

7. Next Episode Preview

Git from Beginner to Practice (Episode 8) will cover production incidents and recovery—reset recovery, file‑deletion restoration, handling leaked secrets, and fixing branch chaos.

I'm 老 J, see you next time.

# Quick reference for this episode
ls .git/hooks/

# Husky installation (Node.js projects)
npm install --save-dev husky
npx husky install

# Create a hook
npx husky add .husky/pre-commit "npm run lint"

# Commitlint setup
npm install --save-dev @commitlint/config-conventional @commitlint/cli
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ci/cdautomationgitgit-hookscommitlinthuskypre-commit
Coder Trainee
Written by

Coder Trainee

Experienced in Java and Python, we share and learn together. For submissions or collaborations, DM us.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.