@@ -60,8 +60,21 @@ Output styles:
6060
6161-- Helper function to get commit details with diffs
6262local function get_detailed_commits (from_ref , to_ref )
63- -- Get commit log with full details
64- local range = from_ref .. " .." .. to_ref
63+ -- Build the range correctly
64+ -- We want commits AFTER from_ref up to and including to_ref
65+ -- Use ^..to_ref to include commits after from_ref (not including from_ref itself)
66+ local range
67+
68+ -- Check if from_ref looks like a relative reference (e.g., HEAD~10)
69+ if from_ref :match (" ^HEAD[~^]" ) then
70+ -- For relative refs, use as-is
71+ range = from_ref .. " .." .. (to_ref or " HEAD" )
72+ else
73+ -- For tags/commits, use ^ to get commits AFTER the from_ref
74+ -- This ensures we don't include the from_ref commit itself in the release notes
75+ range = from_ref .. " ^.." .. (to_ref or " HEAD" )
76+ end
77+
6578 local escaped_range = vim .fn .shellescape (range )
6679
6780 -- Get commits with more details
@@ -73,6 +86,11 @@ local function get_detailed_commits(from_ref, to_ref)
7386 return nil , " Failed to get commit history"
7487 end
7588
89+ -- Handle empty output
90+ if not output or vim .trim (output ) == " " then
91+ return {}, nil
92+ end
93+
7694 local commits = {}
7795 for line in output :gmatch (" [^\r\n ]+" ) do
7896 local parts = vim .split (line , " |" )
@@ -81,9 +99,13 @@ local function get_detailed_commits(from_ref, to_ref)
8199 local subject = parts [2 ]
82100 local body = parts [3 ] ~= " " and parts [3 ] or nil
83101
84- -- Get diff stats for this commit
85- local diff_cmd = string.format (" git diff --stat %s~1 %s" , hash , hash )
86- local _ , diff_stats = pcall (vim .fn .system , diff_cmd )
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
108+ end
87109
88110 -- Parse conventional commit type
89111 local commit_type , scope = subject :match (" ^(%w+)%((.-)%):" )
@@ -100,7 +122,7 @@ local function get_detailed_commits(from_ref, to_ref)
100122 date = parts [6 ],
101123 type = commit_type ,
102124 scope = scope ,
103- diff_stats = vim . v . shell_error == 0 and diff_stats or nil ,
125+ diff_stats = diff_stats ,
104126 })
105127 end
106128 end
@@ -230,24 +252,52 @@ AIReleaseNotes.cmds = {
230252
231253 -- Get tags if not specified
232254 if not to_tag or not from_tag then
255+ -- Try to get tags sorted by version
233256 local success , tags_output = pcall (vim .fn .system , " git tag --sort=-version:refname" )
234- if success and vim .v .shell_error == 0 then
257+ if success and vim .v .shell_error == 0 and tags_output and vim . trim ( tags_output ) ~= " " then
235258 local tags = {}
236259 for tag in tags_output :gmatch (" [^\r\n ]+" ) do
237- if tag ~= " " then
238- table.insert (tags , tag )
260+ local trimmed = vim .trim (tag )
261+ if trimmed ~= " " then
262+ table.insert (tags , trimmed )
239263 end
240264 end
241265
266+ -- Set to_tag if not specified
267+ if not to_tag then
268+ if # tags > 0 then
269+ to_tag = tags [1 ] -- Use latest tag
270+ else
271+ to_tag = " HEAD" -- No tags, use HEAD
272+ end
273+ end
274+
275+ -- Set from_tag if not specified
276+ if not from_tag then
277+ if # tags > 1 then
278+ from_tag = tags [2 ] -- Use previous tag
279+ elseif # tags == 1 then
280+ -- Only one tag, get first commit as starting point
281+ local first_commit_cmd = " git rev-list --max-parents=0 HEAD"
282+ local fc_success , first_commit_output = pcall (vim .fn .system , first_commit_cmd )
283+ if fc_success and vim .v .shell_error == 0 then
284+ from_tag = vim .trim (first_commit_output ):sub (1 , 8 )
285+ else
286+ -- Fallback to 10 commits ago
287+ from_tag = " HEAD~10"
288+ end
289+ else
290+ -- No tags at all, use HEAD~10 as a reasonable default
291+ from_tag = " HEAD~10"
292+ end
293+ end
294+ else
295+ -- No tags or git command failed
242296 if not to_tag then
243- to_tag = tags [ 1 ] or " HEAD"
297+ to_tag = " HEAD"
244298 end
245- if not from_tag and # tags > 1 then
246- from_tag = tags [2 ]
247- elseif not from_tag then
248- -- Get the first commit if no previous tag
249- local first_commit = vim .fn .system (" git rev-list --max-parents=0 HEAD" ):gsub (" \n " , " " )
250- from_tag = first_commit :sub (1 , 8 )
299+ if not from_tag then
300+ from_tag = " HEAD~10" -- Default to last 10 commits
251301 end
252302 end
253303 end
0 commit comments