Skip to content

Commit 397c8c1

Browse files
committed
feat(gitcommit): add support for git commit --amend
- implement amend detection with is_amending() function - add get_contextual_diff() to handle staged and amend scenarios - update error messages to differentiate between staged and amend contexts - modify commit command to use --amend flag when appropriate - enhance notification messages to reflect commit vs amend actions
1 parent 43cabb6 commit 397c8c1

File tree

3 files changed

+107
-20
lines changed

3 files changed

+107
-20
lines changed

lua/codecompanion/_extensions/gitcommit/buffer.lua

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,13 @@ function Buffer._generate_and_insert_commit_message(bufnr)
6262
return
6363
end
6464

65-
-- Get staged changes
66-
local diff = Git.get_staged_diff()
65+
-- Get relevant changes (staged or amend)
66+
local diff, context = Git.get_contextual_diff()
6767
if not diff then
68-
vim.notify("No staged changes found. Please stage your changes first.", vim.log.levels.ERROR)
68+
local msg = context == "no_changes"
69+
and (Git.is_amending() and "No changes to amend" or "No staged changes found. Please stage your changes first.")
70+
or "Failed to get git changes"
71+
vim.notify(msg, vim.log.levels.ERROR)
6972
return
7073
end
7174

lua/codecompanion/_extensions/gitcommit/git.lua

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,91 @@ function Git.is_repository()
3333
return vim.v.shell_error == 0 and vim.trim(result) == "true"
3434
end
3535

36-
---Get git diff for staged changes
37-
---@return string|nil diff The staged changes diff, nil if no changes or error
38-
function Git.get_staged_diff()
36+
---Check if currently in git commit --amend state
37+
---@return boolean
38+
function Git.is_amending()
3939
if not Git.is_repository() then
40-
return nil
40+
return false
4141
end
4242

43-
local diff = vim.fn.system("git diff --no-ext-diff --staged")
43+
-- Check if COMMIT_EDITMSG exists and we're in a rebase/merge state
44+
local git_dir = vim.fn.system("git rev-parse --git-dir"):gsub("\n", "")
4445
if vim.v.shell_error ~= 0 then
45-
return nil
46+
return false
47+
end
48+
49+
-- Check for COMMIT_EDITMSG file which indicates we're editing a commit
50+
local commit_editmsg = git_dir .. "/COMMIT_EDITMSG"
51+
local stat = vim.uv.fs_stat(commit_editmsg)
52+
if not stat then
53+
return false
4654
end
4755

48-
if vim.trim(diff) == "" then
56+
-- Additional check: see if we have HEAD commit (not initial commit)
57+
local head_check = vim.fn.system("git rev-parse --verify HEAD")
58+
return vim.v.shell_error == 0
59+
end
60+
61+
---Get git diff for staged changes or last commit (for amend)
62+
---@return string|nil diff The changes diff, nil if no changes or error
63+
function Git.get_staged_diff()
64+
if not Git.is_repository() then
4965
return nil
5066
end
5167

52-
return diff
68+
-- First try to get staged changes
69+
local staged_diff = vim.fn.system("git diff --no-ext-diff --staged")
70+
if vim.v.shell_error == 0 and vim.trim(staged_diff) ~= "" then
71+
return staged_diff
72+
end
73+
74+
-- If no staged changes and we're in amend mode, get the last commit's changes
75+
if Git.is_amending() then
76+
local last_commit_diff = vim.fn.system("git diff --no-ext-diff HEAD~1")
77+
if vim.v.shell_error == 0 and vim.trim(last_commit_diff) ~= "" then
78+
return last_commit_diff
79+
end
80+
81+
-- Fallback: if HEAD~1 doesn't exist (initial commit), show all files
82+
local show_diff = vim.fn.system("git show --no-ext-diff --format= HEAD")
83+
if vim.v.shell_error == 0 and vim.trim(show_diff) ~= "" then
84+
return show_diff
85+
end
86+
end
87+
88+
return nil
89+
end
90+
91+
---Get contextual diff based on current git state
92+
---@return string|nil diff The relevant diff, nil if no changes
93+
---@return string context The context of what diff represents
94+
function Git.get_contextual_diff()
95+
if not Git.is_repository() then
96+
return nil, "not_in_repo"
97+
end
98+
99+
-- Check for staged changes first
100+
local staged_diff = vim.fn.system("git diff --no-ext-diff --staged")
101+
if vim.v.shell_error == 0 and vim.trim(staged_diff) ~= "" then
102+
return staged_diff, "staged"
103+
end
104+
105+
-- Check if we're amending
106+
if Git.is_amending() then
107+
-- Try to get the last commit's diff
108+
local last_commit_diff = vim.fn.system("git diff --no-ext-diff HEAD~1")
109+
if vim.v.shell_error == 0 and vim.trim(last_commit_diff) ~= "" then
110+
return last_commit_diff, "amend_with_parent"
111+
end
112+
113+
-- Fallback for initial commit amend
114+
local show_diff = vim.fn.system("git show --no-ext-diff --format= HEAD")
115+
if vim.v.shell_error == 0 and vim.trim(show_diff) ~= "" then
116+
return show_diff, "amend_initial"
117+
end
118+
end
119+
120+
return nil, "no_changes"
53121
end
54122

55123
---Commit changes with the provided message
@@ -61,10 +129,16 @@ function Git.commit_changes(message)
61129
return false
62130
end
63131

64-
-- Check if there are staged changes
65-
local diff = Git.get_staged_diff()
132+
-- Check if there are changes to commit
133+
local diff, context = Git.get_contextual_diff()
66134
if not diff then
67-
vim.notify("No staged changes found. Please stage your changes first.", vim.log.levels.ERROR)
135+
if context == "no_changes" then
136+
if Git.is_amending() then
137+
vim.notify("No changes to amend. The commit already exists.", vim.log.levels.WARN)
138+
else
139+
vim.notify("No staged changes found. Please stage your changes first.", vim.log.levels.ERROR)
140+
end
141+
end
68142
return false
69143
end
70144

@@ -79,16 +153,23 @@ function Git.commit_changes(message)
79153
file:write(message)
80154
file:close()
81155

82-
-- Execute git commit
83-
local cmd = string.format("git commit -F %s", vim.fn.shellescape(temp_file))
156+
-- Execute appropriate git commit command
157+
local cmd
158+
if Git.is_amending() then
159+
cmd = string.format("git commit --amend -F %s", vim.fn.shellescape(temp_file))
160+
else
161+
cmd = string.format("git commit -F %s", vim.fn.shellescape(temp_file))
162+
end
163+
84164
local result = vim.fn.system(cmd)
85165
local exit_code = vim.v.shell_error
86166

87167
-- Clean up temporary file
88168
os.remove(temp_file)
89169

90170
if exit_code == 0 then
91-
vim.notify("Successfully committed changes!", vim.log.levels.INFO)
171+
local action = Git.is_amending() and "amended" or "committed"
172+
vim.notify(string.format("Successfully %s changes!", action), vim.log.levels.INFO)
92173
return true
93174
else
94175
local error_msg = vim.trim(result)

lua/codecompanion/_extensions/gitcommit/init.lua

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ function M.generate_commit_message()
1616
return
1717
end
1818

19-
-- Get staged changes
20-
local diff = Git.get_staged_diff()
19+
-- Get relevant changes (staged or amend)
20+
local diff, context = Git.get_contextual_diff()
2121
if not diff then
22-
vim.notify("No staged changes found. Please stage your changes first.", vim.log.levels.ERROR)
22+
local msg = context == "no_changes"
23+
and (Git.is_amending() and "No changes to amend" or "No staged changes found. Please stage your changes first.")
24+
or "Failed to get git changes"
25+
vim.notify(msg, vim.log.levels.ERROR)
2326
return
2427
end
2528

0 commit comments

Comments
 (0)