Skip to content

Commit 4359e47

Browse files
committed
refactor(gitcommit): improve commit log parsing and handling
- enhance git log parsing with robust multi-line body support - add custom separator to handle complex commit message structures - improve error handling and parsing of commit metadata - optimize commit information extraction for release notes generation
1 parent 074231f commit 4359e47

File tree

1 file changed

+81
-31
lines changed

1 file changed

+81
-31
lines changed

lua/codecompanion/_extensions/gitcommit/tools/ai_release_notes.lua

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,13 @@ local function get_detailed_commits(from_ref, to_ref)
7878
local escaped_range = vim.fn.shellescape(range)
7979

8080
-- Get commits with more details
81-
local commit_cmd =
82-
string.format("git log --pretty=format:'%%H|%%s|%%b|%%an|%%ae|%%ad' --date=short %s", escaped_range)
81+
-- Use git log with a separator to handle multi-line bodies correctly
82+
local separator = "---COMMIT_SEPARATOR---"
83+
local commit_cmd = string.format(
84+
"git log --pretty=format:'%%H||%%s||%%an||%%ae||%%ad||%%b%s' --date=short %s",
85+
separator,
86+
escaped_range
87+
)
8388

8489
local success, output = pcall(vim.fn.system, commit_cmd)
8590
if not success or vim.v.shell_error ~= 0 then
@@ -92,38 +97,83 @@ local function get_detailed_commits(from_ref, to_ref)
9297
end
9398

9499
local commits = {}
95-
for line in output:gmatch("[^\r\n]+") do
96-
local parts = vim.split(line, "|")
97-
if #parts >= 6 then
98-
local hash = parts[1]
99-
local subject = parts[2]
100-
local body = parts[3] ~= "" and parts[3] or nil
101-
102-
-- Get diff stats for this commit (safely handle first commit)
103-
local diff_stats = nil
104-
local diff_cmd = string.format("git diff-tree --stat --root %s", hash)
105-
local diff_success, stats_output = pcall(vim.fn.system, diff_cmd)
106-
if diff_success and vim.v.shell_error == 0 then
107-
diff_stats = stats_output
100+
-- Split by separator to get individual commits
101+
local commit_entries = vim.split(output, separator, { plain = true })
102+
103+
for _, entry in ipairs(commit_entries) do
104+
if entry and vim.trim(entry) ~= "" then
105+
-- Find the first non-empty line with commit info
106+
local lines = vim.split(entry, "\n")
107+
local commit_line = nil
108+
local body_start_idx = 1
109+
110+
-- Find the line with the commit info (has || separators)
111+
for i, line in ipairs(lines) do
112+
if line:match("||") then
113+
commit_line = line
114+
body_start_idx = i + 1
115+
break
116+
end
108117
end
109118

110-
-- Parse conventional commit type
111-
local commit_type, scope = subject:match("^(%w+)%((.-)%):")
112-
if not commit_type then
113-
commit_type = subject:match("^(%w+):")
114-
end
119+
if commit_line then
120+
local parts = vim.split(commit_line, "||", { plain = true })
121+
122+
if #parts >= 5 then
123+
local hash = vim.trim(parts[1] or "")
124+
local subject = vim.trim(parts[2] or "")
125+
local author = vim.trim(parts[3] or "")
126+
local email = vim.trim(parts[4] or "")
127+
local date = vim.trim(parts[5] or "")
128+
129+
-- Handle body (can be in part 6 or in following lines)
130+
local body = nil
131+
local body_lines = {}
132+
133+
-- Check if body is in the 6th part
134+
if #parts > 5 and vim.trim(parts[6]) ~= "" then
135+
table.insert(body_lines, vim.trim(parts[6]))
136+
end
137+
138+
-- Add any additional lines as body
139+
for i = body_start_idx, #lines do
140+
local line = vim.trim(lines[i])
141+
if line ~= "" then
142+
table.insert(body_lines, line)
143+
end
144+
end
145+
146+
if #body_lines > 0 then
147+
body = table.concat(body_lines, "\n")
148+
end
149+
150+
-- Get diff stats for this commit (safely handle first commit)
151+
local diff_stats = nil
152+
local diff_cmd = string.format("git diff-tree --stat --root %s", hash)
153+
local diff_success, stats_output = pcall(vim.fn.system, diff_cmd)
154+
if diff_success and vim.v.shell_error == 0 then
155+
diff_stats = stats_output
156+
end
115157

116-
table.insert(commits, {
117-
hash = hash,
118-
subject = subject,
119-
body = body,
120-
author = parts[4],
121-
email = parts[5],
122-
date = parts[6],
123-
type = commit_type,
124-
scope = scope,
125-
diff_stats = diff_stats,
126-
})
158+
-- Parse conventional commit type
159+
local commit_type, scope = subject:match("^(%w+)%((.-)%):")
160+
if not commit_type then
161+
commit_type = subject:match("^(%w+):")
162+
end
163+
164+
table.insert(commits, {
165+
hash = hash,
166+
subject = subject,
167+
body = body,
168+
author = author,
169+
email = email,
170+
date = date,
171+
type = commit_type,
172+
scope = scope,
173+
diff_stats = diff_stats,
174+
})
175+
end
176+
end
127177
end
128178
end
129179

0 commit comments

Comments
 (0)