Validate git commit messages, branch naming conventions, and check for sensitive files. Returns structured output with commit format validation, branch name compliance, and sensitive file detection (...
Validate git repository hygiene including commit message format, branch naming conventions, and sensitive file detection.
echo "→ Validating branch name..."
# Get current branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Check against standard patterns
VALID_PATTERNS=(
"^feat/.*"
"^fix/.*"
"^docs/.*"
"^chore/.*"
"^refactor/.*"
"^test/.*"
"^hotfix/.*"
"^release/.*"
"^main$"
"^master$"
"^develop$"
"^development$"
"^claude/.*"
)
BRANCH_VALID="false"
for pattern in "${VALID_PATTERNS[@]}"; do
if echo "$CURRENT_BRANCH" | grep -qE "$pattern"; then
BRANCH_VALID="true"
break
fi
done
if [ "$BRANCH_VALID" = "true" ]; then
echo "✅ Branch name valid: $CURRENT_BRANCH"
BRANCH_STATUS="valid"
else
echo "❌ Branch name invalid: $CURRENT_BRANCH"
echo " Expected: feat/*, fix/*, docs/*, chore/*, etc."
BRANCH_STATUS="invalid"
fi
echo ""
echo "→ Validating recent commit messages..."
# Get last 10 commits (or since main/develop)
COMMITS=$(git log --oneline --no-merges -10 --format="%H %s")
# Conventional commit pattern
COMMIT_PATTERN="^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?!?: .+"
INVALID_COMMITS=()
VALID_COMMITS=0
while IFS= read -r commit; do
HASH=$(echo "$commit" | cut -d' ' -f1)
MSG=$(echo "$commit" | cut -d' ' -f2-)
if echo "$MSG" | grep -qE "$COMMIT_PATTERN"; then
((VALID_COMMITS++))
else
INVALID_COMMITS+=("$HASH:$MSG")
fi
done <<< "$COMMITS"
TOTAL_COMMITS=$((VALID_COMMITS + ${#INVALID_COMMITS[@]}))
if [ ${#INVALID_COMMITS[@]} -eq 0 ]; then
echo "✅ All $VALID_COMMITS commit messages valid"
COMMITS_STATUS="valid"
else
echo "❌ ${#INVALID_COMMITS[@]} invalid commit messages found"
COMMITS_STATUS="invalid"
# Show first 3 invalid commits
for i in "${INVALID_COMMITS[@]:0:3}"; do
HASH=$(echo "$i" | cut -d: -f1)
MSG=$(echo "$i" | cut -d: -f2-)
echo " $HASH: $MSG"
done
fi
echo ""
echo "→ Checking for sensitive files..."
# Patterns for sensitive files
SENSITIVE_PATTERNS=(
"\.env$"
"\.env\..+"
"credentials\.json$"
"\.pem$"
"\.key$"
"\.p12$"
"id_rsa$"
"\.ssh/"
"secret"
"private.*\.key$"
"\.pfx$"
)
SENSITIVE_FILES=()
# Check tracked files
for pattern in "${SENSITIVE_PATTERNS[@]}"; do
while IFS= read -r file; do
if [ -n "$file" ]; then
SENSITIVE_FILES+=("$file")
fi
done < <(git ls-files | grep -E "$pattern")
done
# Check unstaged/untracked
for pattern in "${SENSITIVE_PATTERNS[@]}"; do
while IFS= read -r file; do
# Only files not in .gitignore
if [ -n "$file" ] && ! git check-ignore -q "$file" 2>/dev/null; then
SENSITIVE_FILES+=("$file (untracked)")
fi
done < <(git status --porcelain | awk '{print $2}' | grep -E "$pattern")
done
if [ ${#SENSITIVE_FILES[@]} -eq 0 ]; then
echo "✅ No sensitive files detected"
SENSITIVE_STATUS="clean"
else
echo "⚠️ ${#SENSITIVE_FILES[@]} potential sensitive files found:"
SENSITIVE_STATUS="found"
for file in "${SENSITIVE_FILES[@]:0:5}"; do
echo " - $file"
done
fi
# Aggregate results
if [ "$BRANCH_STATUS" = "valid" ] && [ "$COMMITS_STATUS" = "valid" ] && [ "$SENSITIVE_STATUS" = "clean" ]; then
OVERALL_STATUS="passing"
CAN_PROCEED="true"
STATUS="success"
elif [ "$SENSITIVE_STATUS" = "found" ]; then
OVERALL_STATUS="sensitive-files"
CAN_PROCEED="false"
STATUS="error"
DETAILS="Sensitive files detected - must be removed or added to .gitignore"
elif [ "$BRANCH_STATUS" = "invalid" ] || [ "$COMMITS_STATUS" = "invalid" ]; then
OVERALL_STATUS="format-issues"
CAN_PROCEED="true"
STATUS="warning"
DETAILS="Git hygiene issues detected - should be fixed but not blocking"
else
OVERALL_STATUS="passing"
CAN_PROCEED="true"
STATUS="success"
fi
{
"status": "$STATUS",
"gitHygiene": {
"status": "$OVERALL_STATUS",
"branch": {
"name": "$CURRENT_BRANCH",
"valid": $([ "$BRANCH_STATUS" = "valid" ] && echo 'true' || echo 'false')
},
"commits": {
"total": $TOTAL_COMMITS,
"valid": $VALID_COMMITS,
"invalid": ${#INVALID_COMMITS[@]},
"invalidExamples": $(printf '%s\n' "${INVALID_COMMITS[@]:0:3}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
},
"sensitiveFiles": {
"count": ${#SENSITIVE_FILES[@]},
"files": $(printf '%s\n' "${SENSITIVE_FILES[@]}" | jq -R -s -c 'split("\n") | map(select(length > 0))')
}
},
"canProceed": $CAN_PROCEED,
"details": "$DETAILS"
}
{
"status": "success",
"gitHygiene": {
"status": "passing",
"branch": {
"name": "feat/add-user-profile",
"valid": true
},
"commits": {
"total": 10,
"valid": 10,
"invalid": 0,
"invalidExamples": []
},
"sensitiveFiles": {
"count": 0,
"files": []
}
},
"canProceed": true
}
{
"status": "warning",
"gitHygiene": {
"status": "format-issues",
"branch": {
"name": "update-settings",
"valid": false
},
"commits": {
"total": 10,
"valid": 7,
"invalid": 3,
"invalidExamples": [
"a1b2c3d:updated some stuff",
"e4f5g6h:WIP",
"i7j8k9l:fixed bug"
]
},
"sensitiveFiles": {
"count": 0,
"files": []
}
},
"canProceed": true,
"details": "Git hygiene issues detected - should be fixed but not blocking"
}
{
"status": "error",
"gitHygiene": {
"status": "sensitive-files",
"branch": {
"name": "feat/api-integration",
"valid": true
},
"commits": {
"total": 5,
"valid": 5,
"invalid": 0,
"invalidExamples": []
},
"sensitiveFiles": {
"count": 2,
"files": [
".env.production",
"src/config/credentials.json"
]
}
},
"canProceed": false,
"details": "Sensitive files detected - must be removed or added to .gitignore"
}
Valid commit message format:
<type>(<scope>): <description>
Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert
Scope: optional (component, feature, etc.)
Description: imperative, lowercase, no period
Examples:
✅ feat(auth): add user login endpoint
✅ fix: resolve null pointer in settings
✅ docs(readme): update installation instructions
❌ Updated some files
❌ WIP
❌ Fixed bug
Valid patterns:
feat/* - New features
fix/* - Bug fixes
docs/* - Documentation
chore/* - Maintenance
refactor/* - Code refactoring
test/* - Test additions
hotfix/* - Critical fixes
release/* - Release preparation
claude/* - AI-generated branches
Examples:
✅ feat/user-authentication
✅ fix/settings-crash
✅ claude/implement-validation-011ABC
❌ update-settings
❌ myfeature
Detected patterns:
.env, .env.local, .env.productioncredentials.json, secrets.json*.pem, *.key, *.p12, *.pfxid_rsa, .ssh/ filesUse `validate-git-hygiene` to check branch name:
- Expected: feat/*, fix/*, etc.
- If invalid: Warning but continue
Use `validate-git-hygiene` to check:
- Commit messages format
- No sensitive files
- If sensitive files: BLOCK - cannot proceed
# Rename current branch
git branch -m feat/descriptive-name
# Amend last commit message
git commit --amend -m "feat: proper commit message"
# Interactive rebase to fix multiple commits
git rebase -i HEAD~5
# Remove from git (keep local)
git rm --cached .env
# Add to .gitignore
echo ".env*" >> .gitignore
git add .gitignore
git commit -m "chore: add .env to .gitignore"
create-feature-branch - Creates properly named branchescommit-with-validation - Commits with validationsecurity-pentest - Uses for sensitive file detection