git_auto 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/lib/git_auto/commands/commit_message_command.rb +6 -3
- data/lib/git_auto/commands/config_command.rb +4 -3
- data/lib/git_auto/commands/setup_command.rb +1 -0
- data/lib/git_auto/config/settings.rb +8 -8
- data/lib/git_auto/services/ai_service.rb +139 -81
- data/lib/git_auto/services/git_service.rb +13 -12
- data/lib/git_auto/validators/commit_message_validator.rb +11 -5
- data/lib/git_auto/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22c8ad3c04eb8a867d883b2691754ad36929589071ecad9c2fb7e064405ccc3b
|
4
|
+
data.tar.gz: 820042feceec36279aa4a1de8fb396d9c30d58257ded1ba0976b8d1c843ce2d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cc3d42b26dad5d0425201c8e9dfd674fe4ffef4fd89ba26671a2d0bbfb4c2f053ae31a92afdd8279015c3688f9c62c0fb846ad2a8464737b8c5fa6f4f0dd38d
|
7
|
+
data.tar.gz: 853b80adf98ed8b3be60f2bf6a84f4c861d564c01bd67981adec30091cfd85136a8f3ab7b3479a0159c5aec797016ea37eef20f872446f163e5256423a454417
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.2.1] - 2024-03-21
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
- Fixed error when displaying repository status with no staged files
|
12
|
+
- Improved error handling in repository status display
|
13
|
+
- Simplified status output when no changes are staged
|
14
|
+
|
15
|
+
## [0.2.0] - 2024-03-20
|
16
|
+
|
17
|
+
### Added
|
18
|
+
- Support for multiple AI providers (OpenAI and Anthropic)
|
19
|
+
- New commit message styles (minimal, conventional, simple)
|
20
|
+
- Commit history analysis
|
21
|
+
- Pattern detection for commit types and scopes
|
22
|
+
- Secure API key storage with encryption
|
23
|
+
|
24
|
+
### Changed
|
25
|
+
- Improved error messages and validation
|
26
|
+
- Enhanced diff formatting and preview
|
27
|
+
- Better handling of commit message generation
|
28
|
+
|
29
|
+
### Fixed
|
30
|
+
- Various bug fixes and performance improvements
|
31
|
+
|
1
32
|
## [0.1.1] - 2024-12-13
|
2
33
|
|
3
34
|
- Remove debug logging output from API requests for cleaner user experience
|
@@ -51,8 +51,7 @@ module GitAuto
|
|
51
51
|
return if status[:has_staged_changes]
|
52
52
|
|
53
53
|
puts "ℹ️ Status:".blue
|
54
|
-
puts "
|
55
|
-
puts " Staged files: #{status[:staged_files].join(", ")}"
|
54
|
+
puts " No changes staged for commit"
|
56
55
|
puts "\n❌ No changes staged for commit. Use 'git add' to stage changes.".red
|
57
56
|
exit 1
|
58
57
|
end
|
@@ -131,7 +130,11 @@ module GitAuto
|
|
131
130
|
end
|
132
131
|
|
133
132
|
def display_message_and_validation(formatted_message, validation)
|
134
|
-
|
133
|
+
provider = @settings.get(:ai_provider)
|
134
|
+
model = @settings.get(:ai_model)
|
135
|
+
model_info = "(#{provider}/#{model})".light_black
|
136
|
+
|
137
|
+
puts "\n📝 Generated commit message #{model_info}:".blue
|
135
138
|
puts formatted_message
|
136
139
|
|
137
140
|
display_validation_errors(validation[:errors]) if validation[:errors].any?
|
@@ -151,23 +151,24 @@ module GitAuto
|
|
151
151
|
|
152
152
|
def configure_commit_style
|
153
153
|
style = @prompt.select("Choose commit message style:", {
|
154
|
+
"Minimal (type: description)" => "minimal",
|
154
155
|
"Conventional (type(scope): description)" => "conventional",
|
155
156
|
"Simple (description only)" => "simple"
|
156
157
|
})
|
157
158
|
|
158
|
-
@settings.
|
159
|
+
@settings.save(commit_style: style)
|
159
160
|
puts "✓ Commit style updated to #{style}".green
|
160
161
|
end
|
161
162
|
|
162
163
|
def configure_preferences
|
163
164
|
show_diff = @prompt.yes?("Show diff before committing?")
|
164
|
-
@settings.
|
165
|
+
@settings.save(show_diff: show_diff)
|
165
166
|
puts "✓ Show diff preference updated".green
|
166
167
|
end
|
167
168
|
|
168
169
|
def configure_history_settings
|
169
170
|
save_history = @prompt.yes?("Save commit history for analysis?")
|
170
|
-
@settings.
|
171
|
+
@settings.save(save_history: save_history)
|
171
172
|
puts "✓ History settings updated".green
|
172
173
|
end
|
173
174
|
end
|
@@ -87,6 +87,7 @@ module GitAuto
|
|
87
87
|
@prompt.select(
|
88
88
|
"Select default commit message style:",
|
89
89
|
[
|
90
|
+
{ name: "Minimal (type: subject)", value: "minimal" },
|
90
91
|
{ name: "Conventional (type(scope): description)", value: "conventional" },
|
91
92
|
{ name: "Simple (verb + description)", value: "simple" },
|
92
93
|
{ name: "Detailed (summary + bullet points)", value: "detailed" }
|
@@ -67,7 +67,7 @@ module GitAuto
|
|
67
67
|
private
|
68
68
|
|
69
69
|
def ensure_config_dir
|
70
|
-
FileUtils.mkdir_p(CONFIG_DIR)
|
70
|
+
FileUtils.mkdir_p(CONFIG_DIR)
|
71
71
|
end
|
72
72
|
|
73
73
|
def load_settings
|
@@ -83,13 +83,13 @@ module GitAuto
|
|
83
83
|
raise Error, "Unsupported AI provider: #{options[:ai_provider]}"
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
86
|
+
return unless options[:ai_model]
|
87
|
+
|
88
|
+
provider = options[:ai_provider] || @settings[:ai_provider]
|
89
|
+
valid_models = SUPPORTED_PROVIDERS[provider][:models].values
|
90
|
+
return if valid_models.include?(options[:ai_model])
|
91
|
+
|
92
|
+
raise Error, "Unsupported AI model: #{options[:ai_model]}"
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
@@ -66,17 +66,27 @@ module GitAuto
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def get_system_prompt(style, retry_attempt = 0)
|
69
|
-
base_prompt = case style
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
base_prompt = case style.to_s
|
70
|
+
when "minimal"
|
71
|
+
"You are an expert in writing minimal commit messages that follow the format: <type>: <description>\n" \
|
72
|
+
"Rules:\n" \
|
73
|
+
"1. ALWAYS start with a type from the list above\n" \
|
74
|
+
"2. NEVER include a scope\n" \
|
75
|
+
"3. Keep the message under 72 characters\n" \
|
76
|
+
"4. Use lowercase\n" \
|
77
|
+
"5. Use present tense\n" \
|
78
|
+
"6. Be descriptive but concise\n" \
|
79
|
+
"7. Do not include a period at the end"
|
80
|
+
when "conventional"
|
81
|
+
"You are an expert in writing conventional commit messages..."
|
82
|
+
else
|
83
|
+
"You are an expert in writing clear and concise git commit messages..."
|
84
|
+
end
|
75
85
|
|
76
86
|
# Add variation for retries
|
77
|
-
if retry_attempt
|
87
|
+
if retry_attempt.positive?
|
78
88
|
base_prompt += "\nPlease provide a different perspective or approach than previous attempts."
|
79
|
-
base_prompt += "\nBe more #{
|
89
|
+
base_prompt += "\nBe more #{["specific", "detailed", "creative", "concise"].sample} in this attempt."
|
80
90
|
end
|
81
91
|
|
82
92
|
base_prompt
|
@@ -110,14 +120,30 @@ module GitAuto
|
|
110
120
|
# If diff is too large, use the summarized version
|
111
121
|
diff = @diff_summarizer.summarize(diff) if diff.length > MAX_DIFF_SIZE
|
112
122
|
|
113
|
-
if style == "
|
123
|
+
if style.to_s == "minimal"
|
124
|
+
message = case @settings.get(:ai_provider)
|
125
|
+
when "openai"
|
126
|
+
generate_openai_commit_message(diff, style)
|
127
|
+
when "claude"
|
128
|
+
generate_claude_commit_message(diff, style)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Extract type and description from the message
|
132
|
+
if message =~ /^(\w+):\s*(.+)$/
|
133
|
+
type = ::Regexp.last_match(1)
|
134
|
+
description = ::Regexp.last_match(2)
|
135
|
+
return "#{type}: #{description}"
|
136
|
+
end
|
137
|
+
|
138
|
+
return message
|
139
|
+
elsif style.to_s == "conventional" && scope.nil?
|
114
140
|
# Generate both scope and message in one call
|
115
141
|
message = case @settings.get(:ai_provider)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
142
|
+
when "openai"
|
143
|
+
generate_openai_commit_message(diff, style)
|
144
|
+
when "claude"
|
145
|
+
generate_claude_commit_message(diff, style)
|
146
|
+
end
|
121
147
|
|
122
148
|
# Extract type and scope from the message
|
123
149
|
if message =~ /^(\w+)(?:\(([\w-]+)\))?:\s*(.+)$/
|
@@ -174,24 +200,42 @@ module GitAuto
|
|
174
200
|
|
175
201
|
# Only use temperature variations for retries
|
176
202
|
temperature = retry_attempt ? get_temperature(retry_attempt) : TEMPERATURE_VARIATIONS[0][:openai]
|
177
|
-
commit_types =
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
203
|
+
commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build",
|
204
|
+
"revert"].join("|")
|
205
|
+
|
206
|
+
system_message = case style.to_s
|
207
|
+
when "minimal"
|
208
|
+
"You are a commit message generator that MUST follow the minimal commit format: <type>: <description>\n" \
|
209
|
+
"Valid types are: #{commit_types}\n" \
|
210
|
+
"Rules:\n" \
|
211
|
+
"1. ALWAYS start with a type from the list above\n" \
|
212
|
+
"2. NEVER include a scope\n" \
|
213
|
+
"3. Keep the message under 72 characters\n" \
|
214
|
+
"4. Use lowercase\n" \
|
215
|
+
"5. Use present tense\n" \
|
216
|
+
"6. Be descriptive but concise\n" \
|
217
|
+
"7. Do not include a period at the end"
|
218
|
+
when "conventional"
|
219
|
+
"You are a commit message generator that MUST follow these rules EXACTLY:\n" \
|
220
|
+
"1. ONLY output a single line containing the commit message\n" \
|
221
|
+
"2. Use format: <type>(<scope>): <description>\n" \
|
222
|
+
"3. Valid types are: #{commit_types}\n" \
|
223
|
+
"4. Keep under 72 characters\n" \
|
224
|
+
"5. Use lowercase\n" \
|
225
|
+
"6. Use present tense\n" \
|
226
|
+
"7. Be descriptive but concise\n" \
|
227
|
+
"8. No period at the end\n" \
|
228
|
+
"9. NO explanations or additional text\n" \
|
229
|
+
"10. NO markdown formatting"
|
230
|
+
else
|
231
|
+
"You are an expert in writing clear and concise git commit messages..."
|
232
|
+
end
|
189
233
|
|
190
234
|
user_message = if scope
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
235
|
+
"Generate a conventional commit message with scope '#{scope}' for this diff:\n\n#{diff}"
|
236
|
+
else
|
237
|
+
"Generate a #{style} commit message for this diff:\n\n#{diff}"
|
238
|
+
end
|
195
239
|
|
196
240
|
payload = {
|
197
241
|
model: @settings.get(:ai_model),
|
@@ -206,8 +250,8 @@ module GitAuto
|
|
206
250
|
# log_api_request("openai", payload, temperature) if ENV["DEBUG"]
|
207
251
|
|
208
252
|
response = HTTP.auth("Bearer #{api_key}")
|
209
|
-
|
210
|
-
|
253
|
+
.headers(accept: "application/json")
|
254
|
+
.post(OPENAI_API_URL, json: payload)
|
211
255
|
|
212
256
|
handle_response(response)
|
213
257
|
end
|
@@ -218,33 +262,41 @@ module GitAuto
|
|
218
262
|
|
219
263
|
# Only use temperature variations for retries
|
220
264
|
temperature = retry_attempt ? get_temperature(retry_attempt) : TEMPERATURE_VARIATIONS[0][:claude]
|
221
|
-
|
265
|
+
commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build",
|
266
|
+
"revert"].join("|")
|
267
|
+
|
268
|
+
system_message = case style.to_s
|
269
|
+
when "minimal"
|
270
|
+
"You are a commit message generator that MUST follow the minimal commit format: <type>: <description>\n" \
|
271
|
+
"Valid types are: #{commit_types}\n" \
|
272
|
+
"Rules:\n" \
|
273
|
+
"1. ALWAYS start with a type from the list above\n" \
|
274
|
+
"2. NEVER include a scope\n" \
|
275
|
+
"3. Keep the message under 72 characters\n" \
|
276
|
+
"4. Use lowercase\n" \
|
277
|
+
"5. Use present tense\n" \
|
278
|
+
"6. Be descriptive but concise\n" \
|
279
|
+
"7. Do not include a period at the end"
|
280
|
+
when "conventional"
|
281
|
+
"You are a commit message generator that MUST follow these rules EXACTLY:\n" \
|
282
|
+
"1. ONLY output a single line containing the commit message\n" \
|
283
|
+
"2. Use format: <type>(<scope>): <description>\n" \
|
284
|
+
"3. Valid types are: #{commit_types}\n" \
|
285
|
+
"4. Keep under 72 characters\n" \
|
286
|
+
"5. Use lowercase\n" \
|
287
|
+
"6. Use present tense\n" \
|
288
|
+
"7. Be descriptive but concise\n" \
|
289
|
+
"8. No period at the end\n" \
|
290
|
+
"9. NO explanations or additional text\n" \
|
291
|
+
"10. NO markdown formatting"
|
292
|
+
else
|
293
|
+
"You are an expert in writing clear and concise git commit messages..."
|
294
|
+
end
|
222
295
|
|
223
|
-
commit_types = %w[feat fix docs style refactor test chore perf ci build revert].join('|')
|
224
296
|
user_message = if scope
|
225
|
-
"Generate
|
226
|
-
"Format: <type>: <description>\n" \
|
227
|
-
"Example: feat: add user authentication\n\n" \
|
228
|
-
"Rules:\n" \
|
229
|
-
"1. Keep the commit message under 72 characters\n" \
|
230
|
-
"2. Use lowercase\n" \
|
231
|
-
"3. Use present tense\n" \
|
232
|
-
"4. Make it unique and different from previous suggestions\n" \
|
233
|
-
"5. MUST start with one of the valid types followed by a colon\n\n" \
|
234
|
-
"Here's the diff:\n#{diff}" +
|
235
|
-
previous_suggestions_prompt
|
297
|
+
"Generate a conventional commit message with scope '#{scope}' for this diff:\n\n#{diff}"
|
236
298
|
else
|
237
|
-
"Generate
|
238
|
-
"Format: <type>: <description>\n" \
|
239
|
-
"Example: feat: add user authentication\n\n" \
|
240
|
-
"Rules:\n" \
|
241
|
-
"1. Keep the commit message under 72 characters\n" \
|
242
|
-
"2. Use lowercase\n" \
|
243
|
-
"3. Use present tense\n" \
|
244
|
-
"4. Make it unique and different from previous suggestions\n" \
|
245
|
-
"5. MUST start with one of the valid types followed by a colon\n\n" \
|
246
|
-
"Here's the diff:\n#{diff}" +
|
247
|
-
previous_suggestions_prompt
|
299
|
+
"Generate a #{style} commit message for this diff:\n\n#{diff}"
|
248
300
|
end
|
249
301
|
|
250
302
|
payload = {
|
@@ -253,7 +305,7 @@ module GitAuto
|
|
253
305
|
temperature: temperature,
|
254
306
|
top_k: 50,
|
255
307
|
top_p: 0.9,
|
256
|
-
system:
|
308
|
+
system: system_message,
|
257
309
|
messages: [
|
258
310
|
{
|
259
311
|
role: "user",
|
@@ -272,10 +324,10 @@ module GitAuto
|
|
272
324
|
# log_api_response(response.body)
|
273
325
|
|
274
326
|
response = HTTP.headers({
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
327
|
+
"Content-Type" => "application/json",
|
328
|
+
"x-api-key" => api_key,
|
329
|
+
"anthropic-version" => "2023-06-01"
|
330
|
+
}).post(CLAUDE_API_URL, json: payload)
|
279
331
|
|
280
332
|
message = handle_response(response)
|
281
333
|
message = message.downcase.strip
|
@@ -292,6 +344,8 @@ module GitAuto
|
|
292
344
|
"simple commit message"
|
293
345
|
when :scope, "scope"
|
294
346
|
"commit scope suggestion"
|
347
|
+
when :minimal, "minimal"
|
348
|
+
"minimal commit message"
|
295
349
|
else
|
296
350
|
"commit message"
|
297
351
|
end
|
@@ -301,34 +355,38 @@ module GitAuto
|
|
301
355
|
case response.code
|
302
356
|
when 200
|
303
357
|
json = JSON.parse(response.body.to_s)
|
304
|
-
|
358
|
+
puts "\nDebug - API Response: #{json.inspect}"
|
359
|
+
|
305
360
|
case @settings.get(:ai_provider)
|
306
361
|
when "openai"
|
307
362
|
message = json.dig("choices", 0, "message", "content")
|
308
363
|
if message.nil? || message.empty?
|
309
|
-
|
364
|
+
puts "Debug - No content in response: #{json}"
|
310
365
|
raise Error, "No message content in response"
|
311
366
|
end
|
367
|
+
|
368
|
+
puts "Debug - OpenAI message: #{message}"
|
312
369
|
message.split("\n").first.strip
|
370
|
+
|
313
371
|
when "claude"
|
314
372
|
content = json.dig("content", 0, "text")
|
315
|
-
|
373
|
+
puts "Debug - Claude content: #{content.inspect}"
|
316
374
|
|
317
375
|
if content.nil? || content.empty?
|
318
|
-
|
376
|
+
puts "Debug - No content in response: #{json}"
|
319
377
|
raise Error, "No message content in response"
|
320
378
|
end
|
321
379
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
message = lines.first
|
380
|
+
# Extract the first actual commit message from the response
|
381
|
+
commit_message = content.scan(/(?:feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(?:\([^)]+\))?:.*/)&.first
|
326
382
|
|
327
|
-
if
|
383
|
+
if commit_message.nil?
|
384
|
+
puts "Debug - No valid commit message pattern found in content"
|
328
385
|
raise Error, "No valid commit message found in response"
|
329
386
|
end
|
330
387
|
|
331
|
-
message
|
388
|
+
puts "Debug - Extracted commit message: #{commit_message}"
|
389
|
+
commit_message.strip
|
332
390
|
end
|
333
391
|
when 401
|
334
392
|
raise APIKeyError, "Invalid API key" unless ENV["RACK_ENV"] == "test"
|
@@ -348,18 +406,18 @@ module GitAuto
|
|
348
406
|
end
|
349
407
|
|
350
408
|
def infer_scope_from_diff(diff)
|
351
|
-
files = diff.scan(
|
409
|
+
files = diff.scan(%r{^diff --git.*?b/(.+)$}).flatten
|
352
410
|
return nil if files.empty?
|
353
411
|
|
354
412
|
scopes = files.map do |file|
|
355
|
-
parts = file.split(
|
413
|
+
parts = file.split("/")
|
356
414
|
if parts.length > 1
|
357
415
|
parts.first
|
358
416
|
else
|
359
|
-
basename = File.basename(file,
|
417
|
+
basename = File.basename(file, ".*")
|
360
418
|
|
361
419
|
if basename =~ /^(.*?)\d*$/
|
362
|
-
|
420
|
+
::Regexp.last_match(1)
|
363
421
|
else
|
364
422
|
basename
|
365
423
|
end
|
@@ -367,19 +425,19 @@ module GitAuto
|
|
367
425
|
end.compact
|
368
426
|
|
369
427
|
# Filter out overly generic scopes
|
370
|
-
scopes.reject! { |s|
|
428
|
+
scopes.reject! { |s| ["rb", "js", "py", "ts", "css", "html", "md"].include?(s) }
|
371
429
|
return nil if scopes.empty?
|
372
430
|
|
373
431
|
# Return the most common scope
|
374
432
|
scope = scopes.group_by(&:itself)
|
375
|
-
|
376
|
-
|
433
|
+
.max_by { |_, group| group.length }
|
434
|
+
&.first
|
377
435
|
|
378
436
|
# Convert to snake_case if needed
|
379
437
|
scope&.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
380
|
-
|
381
|
-
|
382
|
-
|
438
|
+
&.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
439
|
+
&.tr("-", "_")
|
440
|
+
&.downcase
|
383
441
|
end
|
384
442
|
end
|
385
443
|
end
|
@@ -26,7 +26,7 @@ module GitAuto
|
|
26
26
|
|
27
27
|
def get_commit_history(limit = nil)
|
28
28
|
validate_git_repository!
|
29
|
-
format =
|
29
|
+
format = "%H%n%s%n%an%n%aI"
|
30
30
|
command = ["log", "--pretty=format:#{format}", "--no-merges"]
|
31
31
|
command << "-#{limit}" if limit
|
32
32
|
|
@@ -55,20 +55,23 @@ module GitAuto
|
|
55
55
|
private
|
56
56
|
|
57
57
|
def validate_git_repository!
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
return if File.directory?(".git")
|
59
|
+
|
60
|
+
raise Error, "Not a git repository (or any of the parent directories)"
|
61
61
|
end
|
62
62
|
|
63
63
|
def validate_staged_changes!
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
return if has_staged_changes?
|
65
|
+
|
66
|
+
raise Error, "No changes staged for commit"
|
67
67
|
end
|
68
68
|
|
69
69
|
def has_staged_changes?
|
70
|
-
|
71
|
-
|
70
|
+
# git diff --cached --quiet returns:
|
71
|
+
# - exit status 0 (success) if there are no changes
|
72
|
+
# - exit status 1 (failure) if there are changes
|
73
|
+
system("git diff --cached --quiet")
|
74
|
+
!$CHILD_STATUS.success?
|
72
75
|
end
|
73
76
|
|
74
77
|
def is_clean?
|
@@ -85,9 +88,7 @@ module GitAuto
|
|
85
88
|
def execute_git_command(*args)
|
86
89
|
output = IO.popen(["git", *args], err: [:child, :out], &:read)
|
87
90
|
|
88
|
-
unless $CHILD_STATUS.success?
|
89
|
-
raise Error, "Git command failed: git #{args.join(' ')}\n#{output}"
|
90
|
-
end
|
91
|
+
raise Error, "Git command failed: git #{args.join(" ")}\n#{output}" unless $CHILD_STATUS.success?
|
91
92
|
|
92
93
|
output
|
93
94
|
end
|
@@ -20,6 +20,12 @@ module GitAuto
|
|
20
20
|
"revert" => "Reverts a previous commit"
|
21
21
|
}.freeze
|
22
22
|
|
23
|
+
MINIMAL_COMMIT_PATTERN = /
|
24
|
+
^(?<type>#{TYPES.keys.join("|")}) # Commit type
|
25
|
+
:\s # Colon and space separator
|
26
|
+
(?<description>.+) # Commit description
|
27
|
+
/x
|
28
|
+
|
23
29
|
CONVENTIONAL_COMMIT_PATTERN = %r{
|
24
30
|
^(?<type>#{TYPES.keys.join("|")}) # Commit type
|
25
31
|
(\((?<scope>[a-z0-9/_-]+)\))? # Optional scope in parentheses
|
@@ -71,12 +77,12 @@ module GitAuto
|
|
71
77
|
|
72
78
|
errors << "Header exceeds #{HEADER_MAX_LENGTH} characters" if header.length > HEADER_MAX_LENGTH
|
73
79
|
|
74
|
-
# Validate header format for conventional commits
|
75
|
-
|
76
|
-
|
80
|
+
# Validate header format for conventional and minimal commits
|
81
|
+
minimal_pattern = MINIMAL_COMMIT_PATTERN
|
82
|
+
conventional_pattern = CONVENTIONAL_COMMIT_PATTERN
|
77
83
|
|
78
|
-
unless
|
79
|
-
errors << "Header must follow
|
84
|
+
unless minimal_pattern.match?(header) || conventional_pattern.match?(header)
|
85
|
+
errors << "Header must follow either minimal format: <type>: <description> or conventional format: <type>(<scope>): <description>"
|
80
86
|
end
|
81
87
|
|
82
88
|
# Suggest using lowercase for consistency
|
data/lib/git_auto/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_auto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guillermo Diaz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|