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

Commit 8acda65

Browse files
committed
feat(gitcommit): add remote, fetch, pull support
1 parent ba5b316 commit 8acda65

2 files changed

Lines changed: 209 additions & 13 deletions

File tree

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

Lines changed: 106 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -515,15 +515,15 @@ function GitTool.push(remote, branch, force, set_upstream, tags, tag_name)
515515
-- Handle tag pushing - single tag takes priority over all tags
516516
if tag_name and vim.trim(tag_name) ~= "" then
517517
-- Push single tag: git push origin tag_name
518-
if remote then
519-
cmd = cmd .. " " .. vim.fn.shellescape(remote)
520-
end
518+
-- Default to "origin" if no remote specified to avoid git interpreting tag_name as remote
519+
local push_remote = remote or "origin"
520+
cmd = cmd .. " " .. vim.fn.shellescape(push_remote)
521521
cmd = cmd .. " " .. vim.fn.shellescape(tag_name)
522522
elseif tags then
523523
-- Push all tags: git push origin --tags
524-
if remote then
525-
cmd = cmd .. " " .. vim.fn.shellescape(remote)
526-
end
524+
-- Default to "origin" if no remote specified
525+
local push_remote = remote or "origin"
526+
cmd = cmd .. " " .. vim.fn.shellescape(push_remote)
527527
cmd = cmd .. " --tags"
528528
else
529529
-- Regular branch push: git push origin branch
@@ -558,15 +558,13 @@ function GitTool.push_async(remote, branch, force, set_upstream, tags, tag_name,
558558
-- Handle tag pushing - single tag takes priority over all tags
559559
if tag_name and vim.trim(tag_name) ~= "" then
560560
-- Push single tag: git push origin tag_name
561-
if remote then
562-
table.insert(cmd, remote)
563-
end
561+
-- Default to "origin" if no remote specified to avoid git interpreting tag_name as remote
562+
table.insert(cmd, remote or "origin")
564563
table.insert(cmd, tag_name)
565564
elseif tags then
566565
-- Push all tags: git push origin --tags
567-
if remote then
568-
table.insert(cmd, remote)
569-
end
566+
-- Default to "origin" if no remote specified
567+
table.insert(cmd, remote or "origin")
570568
table.insert(cmd, "--tags")
571569
else
572570
-- Regular branch push with optional upstream setting
@@ -944,5 +942,101 @@ function GitTool.generate_release_notes(from_tag, to_tag, format)
944942
return true, release_notes, user_msg, llm_msg
945943
end
946944

945+
---Add a new remote
946+
---@param name string Remote name
947+
---@param url string Remote URL
948+
---@return boolean success, string output
949+
function GitTool.add_remote(name, url)
950+
if not name or vim.trim(name) == "" then
951+
return false, "Remote name is required"
952+
end
953+
if not url or vim.trim(url) == "" then
954+
return false, "Remote URL is required"
955+
end
956+
local cmd = "git remote add " .. vim.fn.shellescape(name) .. " " .. vim.fn.shellescape(url)
957+
return execute_git_command(cmd)
958+
end
959+
960+
---Remove a remote
961+
---@param name string Remote name
962+
---@return boolean success, string output
963+
function GitTool.remove_remote(name)
964+
if not name or vim.trim(name) == "" then
965+
return false, "Remote name is required"
966+
end
967+
local cmd = "git remote remove " .. vim.fn.shellescape(name)
968+
return execute_git_command(cmd)
969+
end
970+
971+
---Rename a remote
972+
---@param old_name string Current remote name
973+
---@param new_name string New remote name
974+
---@return boolean success, string output
975+
function GitTool.rename_remote(old_name, new_name)
976+
if not old_name or vim.trim(old_name) == "" then
977+
return false, "Current remote name is required"
978+
end
979+
if not new_name or vim.trim(new_name) == "" then
980+
return false, "New remote name is required"
981+
end
982+
local cmd = "git remote rename " .. vim.fn.shellescape(old_name) .. " " .. vim.fn.shellescape(new_name)
983+
return execute_git_command(cmd)
984+
end
985+
986+
---Set remote URL
987+
---@param name string Remote name
988+
---@param url string New URL
989+
---@return boolean success, string output
990+
function GitTool.set_remote_url(name, url)
991+
if not name or vim.trim(name) == "" then
992+
return false, "Remote name is required"
993+
end
994+
if not url or vim.trim(url) == "" then
995+
return false, "Remote URL is required"
996+
end
997+
local cmd = "git remote set-url " .. vim.fn.shellescape(name) .. " " .. vim.fn.shellescape(url)
998+
return execute_git_command(cmd)
999+
end
1000+
1001+
---Fetch from remote
1002+
---@param remote? string Remote name (default: all remotes)
1003+
---@param branch? string Specific branch to fetch
1004+
---@param prune? boolean Remove remote-tracking references that no longer exist
1005+
---@return boolean success, string output
1006+
function GitTool.fetch(remote, branch, prune)
1007+
local cmd = "git fetch"
1008+
if prune then
1009+
cmd = cmd .. " --prune"
1010+
end
1011+
if remote then
1012+
cmd = cmd .. " " .. vim.fn.shellescape(remote)
1013+
if branch then
1014+
cmd = cmd .. " " .. vim.fn.shellescape(branch)
1015+
end
1016+
else
1017+
cmd = cmd .. " --all"
1018+
end
1019+
return execute_git_command(cmd)
1020+
end
1021+
1022+
---Pull from remote
1023+
---@param remote? string Remote name (default: origin)
1024+
---@param branch? string Branch to pull (default: current branch)
1025+
---@param rebase? boolean Use rebase instead of merge
1026+
---@return boolean success, string output
1027+
function GitTool.pull(remote, branch, rebase)
1028+
local cmd = "git pull"
1029+
if rebase then
1030+
cmd = cmd .. " --rebase"
1031+
end
1032+
if remote then
1033+
cmd = cmd .. " " .. vim.fn.shellescape(remote)
1034+
if branch then
1035+
cmd = cmd .. " " .. vim.fn.shellescape(branch)
1036+
end
1037+
end
1038+
return execute_git_command(cmd)
1039+
end
1040+
9471041
M.GitTool = GitTool
9481042
return M

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

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ GitEdit.schema = {
2929
"gitignore_add",
3030
"gitignore_remove",
3131
"push",
32+
"fetch",
33+
"pull",
34+
"add_remote",
35+
"remove_remote",
36+
"rename_remote",
37+
"set_remote_url",
3238
"cherry_pick",
3339
"revert",
3440
"create_tag",
@@ -140,6 +146,26 @@ GitEdit.schema = {
140146
type = "string",
141147
description = "An optional commit hash to tag",
142148
},
149+
remote_name = {
150+
type = "string",
151+
description = "Name of the remote",
152+
},
153+
remote_url = {
154+
type = "string",
155+
description = "URL of the remote repository",
156+
},
157+
new_remote_name = {
158+
type = "string",
159+
description = "New name for the remote (used in rename_remote)",
160+
},
161+
prune = {
162+
type = "boolean",
163+
description = "Remove remote-tracking references that no longer exist (for fetch)",
164+
},
165+
rebase = {
166+
type = "boolean",
167+
description = "Use rebase instead of merge (for pull)",
168+
},
143169
},
144170
additionalProperties = false,
145171
},
@@ -176,14 +202,25 @@ GitEdit.system_prompt = [[# Git Edit Tool (`git_edit`)
176202
| `reset` | Reset to commit | commit_hash (required), mode? |
177203
| `gitignore_add` | Add .gitignore rules | gitignore_rules (required) |
178204
| `gitignore_remove` | Remove .gitignore rules | gitignore_rule (required) |
179-
| `push` | Push to remote | remote?, branch?, set_upstream? |
205+
| `push` | Push to remote | remote?, branch?, set_upstream?, tags?, single_tag_name? |
206+
| `fetch` | Fetch from remote | remote?, branch?, prune? |
207+
| `pull` | Pull from remote | remote?, branch?, rebase? |
208+
| `add_remote` | Add new remote | remote_name (required), remote_url (required) |
209+
| `remove_remote` | Remove remote | remote_name (required) |
210+
| `rename_remote` | Rename remote | remote_name (required), new_remote_name (required) |
211+
| `set_remote_url` | Change remote URL | remote_name (required), remote_url (required) |
180212
| `cherry_pick` | Apply commit | cherry_pick_commit_hash (required) |
181213
| `revert` | Revert commit | revert_commit_hash (required) |
182214
| `create_tag` | Create tag | tag_name (required), tag_message? |
183215
| `delete_tag` | Delete tag | tag_name (required) |
184216
| `merge` | Merge branch | branch (required) |
185217
| `help` | Show help | - |
186218
219+
## PUSH OPERATION NOTES
220+
- To push a single tag: use `single_tag_name` parameter (remote defaults to "origin")
221+
- To push all tags: use `tags: true` parameter
222+
- Do NOT use `single_tag_name` as the `branch` parameter
223+
187224
## SAFETY RESTRICTIONS
188225
- Never use force push without explicit user confirmation.
189226
- Always verify staged changes before committing.
@@ -206,6 +243,12 @@ local VALID_OPERATIONS = {
206243
"gitignore_add",
207244
"gitignore_remove",
208245
"push",
246+
"fetch",
247+
"pull",
248+
"add_remote",
249+
"remove_remote",
250+
"rename_remote",
251+
"set_remote_url",
209252
"cherry_pick",
210253
"revert",
211254
"create_tag",
@@ -245,6 +288,12 @@ Available write-access Git operations:
245288
• gitignore_add: Add rule to .gitignore
246289
• gitignore_remove: Remove rule from .gitignore
247290
• push: Push changes to a remote repository (WARNING: force push is dangerous)
291+
• fetch: Fetch from remote (prune option available)
292+
• pull: Pull from remote (rebase option available)
293+
• add_remote: Add a new remote repository
294+
• remove_remote: Remove a remote repository
295+
• rename_remote: Rename a remote repository
296+
• set_remote_url: Change URL of a remote repository
248297
• cherry_pick: Apply changes from existing commits
249298
• revert: Revert a commit
250299
• create_tag: Create a new tag
@@ -425,6 +474,59 @@ Available write-access Git operations:
425474
return param_err
426475
end
427476
success, output = GitTool.merge(op_args.branch)
477+
elseif operation == "fetch" then
478+
param_err = validation.first_error({
479+
validation.optional_string(op_args.remote, "remote", TOOL_NAME),
480+
validation.optional_string(op_args.branch, "branch", TOOL_NAME),
481+
validation.optional_boolean(op_args.prune, "prune", TOOL_NAME),
482+
})
483+
if param_err then
484+
return param_err
485+
end
486+
success, output = GitTool.fetch(op_args.remote, op_args.branch, op_args.prune)
487+
elseif operation == "pull" then
488+
param_err = validation.first_error({
489+
validation.optional_string(op_args.remote, "remote", TOOL_NAME),
490+
validation.optional_string(op_args.branch, "branch", TOOL_NAME),
491+
validation.optional_boolean(op_args.rebase, "rebase", TOOL_NAME),
492+
})
493+
if param_err then
494+
return param_err
495+
end
496+
success, output = GitTool.pull(op_args.remote, op_args.branch, op_args.rebase)
497+
elseif operation == "add_remote" then
498+
param_err = validation.first_error({
499+
validation.require_string(op_args.remote_name, "remote_name", TOOL_NAME),
500+
validation.require_string(op_args.remote_url, "remote_url", TOOL_NAME),
501+
})
502+
if param_err then
503+
return param_err
504+
end
505+
success, output = GitTool.add_remote(op_args.remote_name, op_args.remote_url)
506+
elseif operation == "remove_remote" then
507+
param_err = validation.require_string(op_args.remote_name, "remote_name", TOOL_NAME)
508+
if param_err then
509+
return param_err
510+
end
511+
success, output = GitTool.remove_remote(op_args.remote_name)
512+
elseif operation == "rename_remote" then
513+
param_err = validation.first_error({
514+
validation.require_string(op_args.remote_name, "remote_name", TOOL_NAME),
515+
validation.require_string(op_args.new_remote_name, "new_remote_name", TOOL_NAME),
516+
})
517+
if param_err then
518+
return param_err
519+
end
520+
success, output = GitTool.rename_remote(op_args.remote_name, op_args.new_remote_name)
521+
elseif operation == "set_remote_url" then
522+
param_err = validation.first_error({
523+
validation.require_string(op_args.remote_name, "remote_name", TOOL_NAME),
524+
validation.require_string(op_args.remote_url, "remote_url", TOOL_NAME),
525+
})
526+
if param_err then
527+
return param_err
528+
end
529+
success, output = GitTool.set_remote_url(op_args.remote_name, op_args.remote_url)
428530
else
429531
return validation.format_error(TOOL_NAME, "Unknown Git edit operation: " .. tostring(operation))
430532
end

0 commit comments

Comments
 (0)