Skip to content

Commit 0f07408

Browse files
committed
refactor(hooks): restructure prepare-commit-msg logic and script
- add helper functions for checks and editor handling - reorganize main logic with case statements for commit types - improve error handling and staged changes validation Signed-off-by: mingcheng <[email protected]>
1 parent 5365842 commit 0f07408

File tree

1 file changed

+154
-57
lines changed

1 file changed

+154
-57
lines changed

hooks/prepare-commit-msg

Lines changed: 154 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,177 @@
1-
#!/bin/sh
1+
#!/usr/bin/env sh
2+
# Copyright (c) 2025 Hangzhou Guanwaii Technology Co., Ltd.
3+
#
4+
# This source code is licensed under the MIT License,
5+
# which is located in the LICENSE file in the source tree's root directory.
6+
#
7+
# File: prepare-commit-msg
8+
# Author: mingcheng <[email protected]>
9+
# File Created: 2025-11-07 12:22:17
10+
#
11+
# Modified By: mingcheng <[email protected]>
12+
# Last Modified: 2025-11-07 12:40:56
13+
##
14+
15+
# Git hook: prepare-commit-msg
16+
# Automatically generates commit messages using aigitcommit.
17+
# Usage: Place this script in `.git/hooks/` and make it executable (chmod +x).
18+
19+
set -e
20+
21+
# ============================================================================
22+
# Configuration and initialization
23+
# ============================================================================
224

3-
# This script is a Git hook that generates a commit message using the `aigitcommit` command.
4-
# It is triggered before the commit message editor is opened.
5-
# Usage: Place this script in the `.git/hooks/` directory of your repository and make it executable.
625
COMMIT_MSG_FILE=$1
726
COMMIT_MSG_TYPE=$2
8-
REPO_ROOT=$(git rev-parse --show-toplevel)
27+
COMMIT_SOURCE=$3
928

10-
# Check the repository root directory is valid
11-
# The `git rev-parse --show-toplevel` command returns the absolute path to the root of the repository
12-
# If the command fails, it means we are not in a Git repository
13-
if [ ! -d "$REPO_ROOT" ]; then
14-
echo "Error: Repository root not found."
29+
# Get repository root
30+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
31+
if [ -z "$REPO_ROOT" ] || [ ! -d "$REPO_ROOT" ]; then
32+
echo "Error: Not in a valid Git repository."
1533
exit 1
1634
fi
1735

18-
# Check if aigitcommit is installed
19-
if ! type aigitcommit >/dev/null 2>&1; then
20-
echo "Error: aigitcommit is not installed. Please install it first."
21-
exit 0
22-
fi
36+
# ============================================================================
37+
# Helper functions
38+
# ============================================================================
2339

24-
# Only run for interactive commits or explicit empty messages
25-
RUN_EDITOR=0
26-
if [ -n "$COMMIT_MSG_TYPE" ]; then
27-
if [ "$COMMIT_MSG_TYPE" != "message" ]; then
40+
# Check if aigitcommit is installed
41+
check_aigitcommit() {
42+
if ! command -v aigitcommit >/dev/null 2>&1; then
43+
echo "Error: aigitcommit is not installed."
44+
echo "Please install it first: https://github.com/mingcheng/aigitcommit"
2845
exit 0
2946
fi
47+
}
3048

31-
# For `git commit -m ""` we still want to generate a message and open the editor
32-
if grep -Eq '^[[:space:]]*[^#[:space:]]' "$COMMIT_MSG_FILE"; then
33-
exit 0
49+
# Check if there are staged changes
50+
has_staged_changes() {
51+
git diff --cached --quiet
52+
return $?
53+
}
54+
55+
# Check if commit message file is effectively empty (only whitespace/comments)
56+
is_message_empty() {
57+
! grep -Eq '^[[:space:]]*[^#[:space:]]' "$COMMIT_MSG_FILE"
58+
}
59+
60+
# Get the appropriate editor
61+
get_editor() {
62+
local editor=""
63+
64+
# Try git's configured editor first
65+
if editor=$(git var GIT_EDITOR 2>/dev/null); then
66+
if [ -n "$editor" ] && [ "$editor" != ":" ]; then
67+
printf '%s' "$editor"
68+
return 0
69+
fi
3470
fi
35-
RUN_EDITOR=1
36-
else
37-
# Plain `git commit`: always run AIGitCommit regardless of template content
38-
:
39-
fi
4071

41-
# Get only the diff of what has already been staged
42-
GIT_DIFF_OUTPUT=$(git diff --cached)
72+
# Fallback to environment variables
73+
if [ -n "$VISUAL" ]; then
74+
printf '%s' "$VISUAL"
75+
return 0
76+
fi
4377

44-
# Check if there are any staged changes to commit
45-
if [ -z "$GIT_DIFF_OUTPUT" ]; then
46-
echo "No staged changes detected. Aborting."
47-
exit 1
48-
fi
78+
if [ -n "$EDITOR" ]; then
79+
printf '%s' "$EDITOR"
80+
return 0
81+
fi
4982

50-
# Generate a temporary file for the commit message
51-
TEMP_FILE=$(mktemp)
83+
# Last resort fallback
84+
printf '%s' "vi"
85+
return 0
86+
}
5287

53-
# Execute aigitcommit to generate the commit message and append existing content
54-
echo "🚀 Generating commit message by using aigitcommit."
55-
echo "This may take a few seconds..."
56-
aigitcommit "$REPO_ROOT" --save "$TEMP_FILE" >/dev/null 2>&1
57-
if [ $? -ne 0 ]; then
58-
echo "Error: aigitcommit failed to generate commit message."
59-
rm -f "$TEMP_FILE"
60-
exit 1
61-
fi
88+
# Generate commit message using aigitcommit
89+
generate_commit_message() {
90+
local temp_file
91+
temp_file=$(mktemp)
6292

63-
cat "$COMMIT_MSG_FILE" >>"$TEMP_FILE" && mv -f "$TEMP_FILE" "$COMMIT_MSG_FILE"
93+
echo "🚀 Generating commit message using aigitcommit..."
94+
echo "This may take a few seconds..."
6495

65-
# When the commit was started with `git commit -m ""`, invoke the editor manually
66-
if [ "$RUN_EDITOR" -eq 1 ]; then
67-
EDITOR_CMD=$(git var GIT_EDITOR 2>/dev/null)
68-
if [ -z "$EDITOR_CMD" ]; then
69-
if [ -n "$VISUAL" ]; then
70-
EDITOR_CMD="$VISUAL"
71-
elif [ -n "$EDITOR" ]; then
72-
EDITOR_CMD="$EDITOR"
96+
if ! aigitcommit "$REPO_ROOT" --save "$temp_file" >/dev/null 2>&1; then
97+
echo "Error: aigitcommit failed to generate commit message."
98+
rm -f "$temp_file"
99+
exit 1
100+
fi
101+
102+
# Preserve existing content and append to generated message
103+
cat "$COMMIT_MSG_FILE" >>"$temp_file"
104+
mv -f "$temp_file" "$COMMIT_MSG_FILE"
105+
}
106+
107+
# ============================================================================
108+
# Main logic
109+
# ============================================================================
110+
111+
check_aigitcommit
112+
113+
# Determine if we should run aigitcommit and possibly open editor
114+
SHOULD_RUN_AIGITCOMMIT=0
115+
SHOULD_OPEN_EDITOR=0
116+
117+
case "$COMMIT_MSG_TYPE" in
118+
message)
119+
# git commit -m "..." was used
120+
if is_message_empty; then
121+
# git commit -m "" (empty message)
122+
# Generate message AND open editor for user to review/edit
123+
SHOULD_RUN_AIGITCOMMIT=1
124+
SHOULD_OPEN_EDITOR=1
73125
else
74-
EDITOR_CMD="vi"
126+
# git commit -m "actual message"
127+
# Don't interfere with explicit non-empty messages
128+
exit 0
75129
fi
130+
;;
131+
template|merge|squash|commit)
132+
# Don't run for: merge commits, squash commits, commit --amend, or template
133+
exit 0
134+
;;
135+
"")
136+
# Plain git commit (interactive mode) - always run
137+
SHOULD_RUN_AIGITCOMMIT=1
138+
# Editor will be opened by git itself, no need to explicitly open it
139+
SHOULD_OPEN_EDITOR=0
140+
;;
141+
*)
142+
# Unknown commit type, skip
143+
exit 0
144+
;;
145+
esac
146+
147+
# Verify there are staged changes
148+
if has_staged_changes; then
149+
echo "No staged changes detected. Aborting commit."
150+
exit 1
151+
fi
152+
153+
# Generate commit message if needed
154+
if [ "$SHOULD_RUN_AIGITCOMMIT" -eq 1 ]; then
155+
generate_commit_message
156+
fi
157+
158+
# Open editor manually if needed (for git commit -m "")
159+
if [ "$SHOULD_OPEN_EDITOR" -eq 1 ]; then
160+
editor_cmd=$(get_editor)
161+
162+
# Debug output (can be removed in production)
163+
# echo "DEBUG: Editor command: [$editor_cmd]" >&2
164+
165+
if [ -z "$editor_cmd" ] || [ "$editor_cmd" = ":" ]; then
166+
echo "Error: Could not determine editor to use."
167+
exit 1
76168
fi
77169

78-
# shellcheck disable=SC2086 -- intentional word splitting to respect editor arguments
79-
$EDITOR_CMD "$COMMIT_MSG_FILE"
170+
# Use eval to properly handle editors with arguments (e.g., "code --wait")
171+
eval "$editor_cmd" '"$COMMIT_MSG_FILE"' || {
172+
echo "Error: Failed to open editor: $editor_cmd"
173+
exit 1
174+
}
80175
fi
176+
177+
exit 0

0 commit comments

Comments
 (0)