Skip to content
This repository was archived by the owner on Jan 14, 2026. It is now read-only.

Commit 8b01d9b

Browse files
committed
feat(gitcommit): add cherry-pick/merge abort/continue
- Add cherry_pick_skip operation - Improve error messages with conflict resolution guidance
1 parent 8acda65 commit 8b01d9b

2 files changed

Lines changed: 166 additions & 3 deletions

File tree

lua/codecompanion/_extensions/gitcommit/tools/git.lua

Lines changed: 136 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,94 @@ function GitTool.cherry_pick(commit_hash)
634634
if not commit_hash then
635635
return false, "Commit hash is required for cherry-pick"
636636
end
637+
638+
if not is_git_repo() then
639+
return false, "Not in a git repository"
640+
end
641+
637642
local cmd = "git cherry-pick --no-edit " .. vim.fn.shellescape(commit_hash)
638-
return execute_git_command(cmd)
643+
local output = vim.fn.system(cmd)
644+
local exit_code = vim.v.shell_error
645+
646+
if exit_code == 0 then
647+
return true, output
648+
else
649+
if output:match("CONFLICT") or output:match("conflict") then
650+
return false,
651+
"Cherry-pick conflict detected. Please resolve the conflicts manually.\n"
652+
.. "Options:\n"
653+
.. " • Use 'cherry_pick_continue' after resolving conflicts\n"
654+
.. " • Use 'cherry_pick_abort' to cancel the cherry-pick\n"
655+
.. " • Use 'cherry_pick_skip' to skip this commit"
656+
else
657+
return false, output
658+
end
659+
end
660+
end
661+
662+
---Abort cherry-pick operation
663+
---@return boolean success, string output
664+
function GitTool.cherry_pick_abort()
665+
if not is_git_repo() then
666+
return false, "Not in a git repository"
667+
end
668+
669+
local cmd = "git cherry-pick --abort"
670+
local output = vim.fn.system(cmd)
671+
local exit_code = vim.v.shell_error
672+
673+
if exit_code == 0 then
674+
return true, "Cherry-pick aborted successfully"
675+
else
676+
if output:match("no cherry%-pick") or output:match("not in progress") then
677+
return false, "No cherry-pick in progress to abort"
678+
end
679+
return false, output
680+
end
681+
end
682+
683+
---Continue cherry-pick after resolving conflicts
684+
---@return boolean success, string output
685+
function GitTool.cherry_pick_continue()
686+
if not is_git_repo() then
687+
return false, "Not in a git repository"
688+
end
689+
690+
local cmd = "git cherry-pick --continue"
691+
local output = vim.fn.system(cmd)
692+
local exit_code = vim.v.shell_error
693+
694+
if exit_code == 0 then
695+
return true, "Cherry-pick continued successfully"
696+
else
697+
if output:match("CONFLICT") or output:match("conflict") then
698+
return false, "Conflicts still exist. Please resolve all conflicts before continuing."
699+
elseif output:match("no cherry%-pick") or output:match("not in progress") then
700+
return false, "No cherry-pick in progress to continue"
701+
end
702+
return false, output
703+
end
704+
end
705+
706+
---Skip current commit in cherry-pick
707+
---@return boolean success, string output
708+
function GitTool.cherry_pick_skip()
709+
if not is_git_repo() then
710+
return false, "Not in a git repository"
711+
end
712+
713+
local cmd = "git cherry-pick --skip"
714+
local output = vim.fn.system(cmd)
715+
local exit_code = vim.v.shell_error
716+
717+
if exit_code == 0 then
718+
return true, "Current commit skipped successfully"
719+
else
720+
if output:match("no cherry%-pick") or output:match("not in progress") then
721+
return false, "No cherry-pick in progress to skip"
722+
end
723+
return false, output
724+
end
639725
end
640726

641727
---Revert a commit
@@ -714,15 +800,62 @@ function GitTool.merge(branch)
714800
if exit_code == 0 then
715801
return true, output
716802
else
717-
if output:match("CONFLICT") then
803+
if output:match("CONFLICT") or output:match("conflict") then
718804
return false,
719-
"Merge conflict detected. Please resolve the conflicts manually. You can use 'git merge --abort' to cancel."
805+
"Merge conflict detected. Please resolve the conflicts manually.\n"
806+
.. "Options:\n"
807+
.. " • Use 'merge_continue' after resolving conflicts\n"
808+
.. " • Use 'merge_abort' to cancel the merge"
720809
else
721810
return false, output
722811
end
723812
end
724813
end
725814

815+
---Abort merge operation
816+
---@return boolean success, string output
817+
function GitTool.merge_abort()
818+
if not is_git_repo() then
819+
return false, "Not in a git repository"
820+
end
821+
822+
local cmd = "git merge --abort"
823+
local output = vim.fn.system(cmd)
824+
local exit_code = vim.v.shell_error
825+
826+
if exit_code == 0 then
827+
return true, "Merge aborted successfully"
828+
else
829+
if output:match("not merging") or output:match("no merge") then
830+
return false, "No merge in progress to abort"
831+
end
832+
return false, output
833+
end
834+
end
835+
836+
---Continue merge after resolving conflicts
837+
---@return boolean success, string output
838+
function GitTool.merge_continue()
839+
if not is_git_repo() then
840+
return false, "Not in a git repository"
841+
end
842+
843+
local cmd = "git merge --continue"
844+
local output = vim.fn.system(cmd)
845+
local exit_code = vim.v.shell_error
846+
847+
if exit_code == 0 then
848+
return true, "Merge continued successfully"
849+
else
850+
if output:match("CONFLICT") or output:match("conflict") then
851+
return false, "Conflicts still exist. Please resolve all conflicts before continuing."
852+
elseif output:match("not merging") or output:match("no merge") then
853+
return false, "No merge in progress to continue"
854+
end
855+
return false, output
856+
end
857+
end
858+
726859
--- Generate release notes between two tags
727860
---@param from_tag string|nil Starting tag (if not provided, uses second latest tag)
728861
---@param to_tag string|nil Ending tag (if not provided, uses latest tag)

lua/codecompanion/_extensions/gitcommit/tools/git_edit.lua

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,15 @@ GitEdit.schema = {
3636
"rename_remote",
3737
"set_remote_url",
3838
"cherry_pick",
39+
"cherry_pick_abort",
40+
"cherry_pick_continue",
41+
"cherry_pick_skip",
3942
"revert",
4043
"create_tag",
4144
"delete_tag",
4245
"merge",
46+
"merge_abort",
47+
"merge_continue",
4348
"help",
4449
},
4550
description = "The write-access Git operation to perform.",
@@ -210,10 +215,15 @@ GitEdit.system_prompt = [[# Git Edit Tool (`git_edit`)
210215
| `rename_remote` | Rename remote | remote_name (required), new_remote_name (required) |
211216
| `set_remote_url` | Change remote URL | remote_name (required), remote_url (required) |
212217
| `cherry_pick` | Apply commit | cherry_pick_commit_hash (required) |
218+
| `cherry_pick_abort` | Abort cherry-pick | - |
219+
| `cherry_pick_continue` | Continue cherry-pick | - |
220+
| `cherry_pick_skip` | Skip current commit | - |
213221
| `revert` | Revert commit | revert_commit_hash (required) |
214222
| `create_tag` | Create tag | tag_name (required), tag_message? |
215223
| `delete_tag` | Delete tag | tag_name (required) |
216224
| `merge` | Merge branch | branch (required) |
225+
| `merge_abort` | Abort merge | - |
226+
| `merge_continue` | Continue merge | - |
217227
| `help` | Show help | - |
218228
219229
## PUSH OPERATION NOTES
@@ -250,10 +260,15 @@ local VALID_OPERATIONS = {
250260
"rename_remote",
251261
"set_remote_url",
252262
"cherry_pick",
263+
"cherry_pick_abort",
264+
"cherry_pick_continue",
265+
"cherry_pick_skip",
253266
"revert",
254267
"create_tag",
255268
"delete_tag",
256269
"merge",
270+
"merge_abort",
271+
"merge_continue",
257272
"help",
258273
}
259274
local VALID_RESET_MODES = { "soft", "mixed", "hard" }
@@ -295,10 +310,15 @@ Available write-access Git operations:
295310
• rename_remote: Rename a remote repository
296311
• set_remote_url: Change URL of a remote repository
297312
• cherry_pick: Apply changes from existing commits
313+
• cherry_pick_abort: Abort cherry-pick in progress
314+
• cherry_pick_continue: Continue cherry-pick after resolving conflicts
315+
• cherry_pick_skip: Skip current commit in cherry-pick
298316
• revert: Revert a commit
299317
• create_tag: Create a new tag
300318
• delete_tag: Delete a tag
301319
• merge: Merge a branch into the current branch (requires branch parameter)
320+
• merge_abort: Abort merge in progress
321+
• merge_continue: Continue merge after resolving conflicts
302322
]]
303323
return { status = "success", data = help_text }
304324
end
@@ -443,6 +463,12 @@ Available write-access Git operations:
443463
return param_err
444464
end
445465
success, output = GitTool.cherry_pick(op_args.cherry_pick_commit_hash)
466+
elseif operation == "cherry_pick_abort" then
467+
success, output = GitTool.cherry_pick_abort()
468+
elseif operation == "cherry_pick_continue" then
469+
success, output = GitTool.cherry_pick_continue()
470+
elseif operation == "cherry_pick_skip" then
471+
success, output = GitTool.cherry_pick_skip()
446472
elseif operation == "revert" then
447473
param_err = validation.require_string(op_args.revert_commit_hash, "revert_commit_hash", TOOL_NAME)
448474
if param_err then
@@ -474,6 +500,10 @@ Available write-access Git operations:
474500
return param_err
475501
end
476502
success, output = GitTool.merge(op_args.branch)
503+
elseif operation == "merge_abort" then
504+
success, output = GitTool.merge_abort()
505+
elseif operation == "merge_continue" then
506+
success, output = GitTool.merge_continue()
477507
elseif operation == "fetch" then
478508
param_err = validation.first_error({
479509
validation.optional_string(op_args.remote, "remote", TOOL_NAME),

0 commit comments

Comments
 (0)