@@ -4,7 +4,7 @@ COMMIT_MSG_FILE="$1"
4
4
5
5
# If the commit message file already contains non-comment lines, do nothing.
6
6
if grep -qE ' ^[^[:space:]#]' " $COMMIT_MSG_FILE " ; then
7
- exit 0
7
+ exit 0
8
8
fi
9
9
10
10
# Gather a list of staged (changed) files.
@@ -34,6 +34,119 @@ INLINE_MSG=$(cat <<'EOF'
34
34
EOF
35
35
)
36
36
37
+ # AICommit uses an LLM (via ollama) to generate commit messages that match git
38
+ # commit style by learning from previous commits.
39
+ # Inspired by https://github.com/acrosa/aicommits.
40
+ MODEL=" qwen2.5-coder"
41
+ SUGGESTED_COMMITMSG=
42
+ AICOMMIT=$( git config --get core.aicommit || echo ' auto' )
43
+ if [[ " $AICOMMIT " == " always" ]] || [[ " $AICOMMIT " == " auto" && -t 1 ]] && \
44
+ git diff --cached --name-only | grep -qiE " \.(c|h|cpp|hpp)$" ; then
45
+ # Build commit history list from the last non-merge commit messages.
46
+ commit_history=$( git log -n 70 --no-merges --pretty=format:' "%s",' | \
47
+ sed -E ' s/ \(\#[0-9]+\)//; $ s/,$//' )
48
+ commit_history=" [$commit_history ]"
49
+
50
+ # Capture the staged diff.
51
+ staged_diff=$( git diff --cached)
52
+
53
+ # Create a style prompt from commit history.
54
+ style_prompt="
55
+ You are a specialized system for generating high-quality Git commit messages based on 'git diff --cached' output and optional developer descriptions.
56
+ # Task:
57
+ Analyze the following commit messages and produce **accurate, concise, and meaningful commit messages** that clearly describe the changes:
58
+ - $commit_history
59
+
60
+ # Output:
61
+ Provide a concise description of the style without quoting commit content.
62
+ "
63
+ echo " Running ollama... "
64
+ style_description=$( echo " $style_prompt " | ollama run " $MODEL " )
65
+
66
+ # Build the commit message prompt.
67
+ prompt="
68
+ # Context:
69
+ Style: $style_description
70
+
71
+ # Instructions:
72
+ - Analyze the diff below and generate a commit message based solely on its content.
73
+ - Mimic the style described above (tone, length, structure) without copying previous messages.
74
+ - Use clear action verbs and be concise.
75
+ - Output ONLY the commit message (subject, a blank line, then body).
76
+ - Separate the subject from the body with a blank line.
77
+ - Remove triple backticks; replace backticks with single quotes.
78
+ - Keep the first line (subject) under 50 characters
79
+ - Ensure no line exceeds 72 characters.
80
+ - Avoid vague messages like 'Updates' or 'Fixed bug'
81
+ - Always write in **plain text** without markdown or HTML.
82
+ - No concluding remarks.
83
+ - Do NOT use conventional commit prefixes (like 'feat:', 'fix:', 'docs:')
84
+ - Avoid the redundant message like 'Updated commit messages'
85
+
86
+ # Diff:
87
+ <diff>$staged_diff </diff>
88
+
89
+ Commit message:"
90
+ if [ " $2 " = " --show-prompt" ]; then
91
+ echo " Full style prompt:"
92
+ echo " $style_prompt "
93
+ echo " Extracted style:"
94
+ echo " $style_description "
95
+ echo " Full commit prompt:"
96
+ echo " $prompt "
97
+ fi
98
+
99
+ # Generate commit message using ollama.
100
+ SUGGESTED_COMMITMSG=$( echo " $prompt " | ollama run " $MODEL " )
101
+
102
+ # Post-process the commit message.
103
+ # - Trim whitespace.
104
+ # - Remove triple backticks.
105
+ # - Replace backticks with single quotes.
106
+ # - Wrap lines at 72 characters.
107
+ SUGGESTED_COMMITMSG=$( echo " $SUGGESTED_COMMITMSG " | sed ' s/^[[:space:]]*//; s/[[:space:]]*$//' )
108
+ SUGGESTED_COMMITMSG=$( echo " $SUGGESTED_COMMITMSG " | sed -E ' /^(Author:|Date:|Commit message:)/d' )
109
+ SUGGESTED_COMMITMSG=" $(
110
+ echo " $SUGGESTED_COMMITMSG " \
111
+ | sed -E ' /^```(markdown|diff|text|plaintext)?$/d; s/\*\*([^*]+)\*\*/\1/g; s/`([^`]+)`/' \' ' \1' \' ' /g' \
112
+ | awk -v w=72 '
113
+ function sp(n){ return sprintf("%" n "s", "") }
114
+ function wrap(bp, txt){
115
+ gsub(/^ +| +$/,"",txt)
116
+ if(!length(txt)){ print bp; return }
117
+ n = split(txt, a, /[ \t]+/)
118
+ l = bp; len = length(bp)
119
+ for(i = 1; i <= n; i++){
120
+ wl = length(a[i])
121
+ if((len > length(bp) ? len + 1 : len) + wl > w){
122
+ print l; l = sp(length(bp)) a[i]; len = length(bp) + wl
123
+ } else if(len == length(bp)){
124
+ l = bp a[i]; len = length(bp) + wl
125
+ } else {
126
+ l = l " " a[i]; len++; len += wl
127
+ }
128
+ }
129
+ if(len > length(bp)) print l
130
+ }
131
+ BEGIN { paragraph = ""; bullet = "" }
132
+ {
133
+ line = $0; gsub(/^ +| +$/, "", line)
134
+ if(!length(line)){
135
+ if(length(paragraph)){ wrap(bullet, paragraph); paragraph = ""; bullet = "" }
136
+ print ""; next
137
+ }
138
+ if(match(line, /^( *[0-9]+\.[ \t]+| *-[ \t]+| *\*[ \t]+)/)){
139
+ if(length(paragraph)){ wrap(bullet, paragraph); paragraph = ""; bullet = "" }
140
+ bp = substr(line, RSTART, RLENGTH); rest = substr(line, RSTART + RLENGTH)
141
+ gsub(/^[ \t]+/, "", rest); wrap(bp, rest)
142
+ } else {
143
+ if(!length(paragraph)) paragraph = line; else paragraph = paragraph " " line
144
+ }
145
+ }
146
+ END { if(length(paragraph)) wrap(bullet, paragraph) }
147
+ ' ) "
148
+ fi
149
+
37
150
# Write an empty line, the guidelines, and the changed files into the commit message.
38
151
{
39
152
echo
44
157
else
45
158
echo " # (No staged files detected.)"
46
159
fi
160
+ if [ -n " $SUGGESTED_COMMITMSG " ]; then
161
+ echo " #"
162
+ echo " # ✅Suggested commit messages:"
163
+ echo " $SUGGESTED_COMMITMSG " | sed ' s/^/# /'
164
+ fi
47
165
} > " $COMMIT_MSG_FILE "
48
166
49
167
# Prompt the user about aborting the commit.
0 commit comments