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

Commit 60eb35c

Browse files
committed
refactor(gitcommit): use path_join for file paths
Add GitUtils.path_join to handle platform-specific separators and normalization. Replace manual string concatenation in git.lua and tools/git.lua with the new helper.
1 parent 92aa2bb commit 60eb35c

File tree

4 files changed

+133
-6
lines changed

4 files changed

+133
-6
lines changed

lua/codecompanion/_extensions/gitcommit/git.lua

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ function Git.is_repository()
3838
end
3939

4040
local function check_git_dir(path)
41-
local sep = package.config:sub(1, 1)
42-
local git_path = path .. sep .. ".git"
41+
local git_path = GitUtils.path_join(path, ".git")
4342
local stat = vim.uv.fs_stat(git_path)
4443
return stat ~= nil
4544
end
@@ -77,8 +76,7 @@ function Git.is_amending()
7776
return false
7877
end
7978

80-
local path_sep = package.config:sub(1, 1)
81-
local commit_editmsg = git_dir .. path_sep .. "COMMIT_EDITMSG"
79+
local commit_editmsg = GitUtils.path_join(git_dir, "COMMIT_EDITMSG")
8280
local stat = vim.uv.fs_stat(commit_editmsg)
8381
if not stat then
8482
return false

lua/codecompanion/_extensions/gitcommit/git_utils.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,38 @@ function M.is_windows()
220220
return vim.uv.os_uname().sysname == "Windows_NT"
221221
end
222222

223+
---Join path parts with platform-specific separator
224+
---Normalizes all parts to forward slashes internally, then converts to platform format
225+
---@param ... string Path parts to join
226+
---@return string joined_path
227+
function M.path_join(...)
228+
local parts = { ... }
229+
if #parts == 0 then
230+
return ""
231+
end
232+
233+
-- Normalize all parts to use forward slashes
234+
local normalized = {}
235+
for i, part in ipairs(parts) do
236+
normalized[i] = part:gsub("\\+", "/")
237+
end
238+
239+
-- Remove leading/trailing slashes from middle parts
240+
for i = 2, #normalized - 1 do
241+
normalized[i] = normalized[i]:gsub("^/", ""):gsub("/$", "")
242+
end
243+
244+
-- Join with forward slashes
245+
local result = table.concat(normalized, "/")
246+
247+
-- Convert to Windows backslashes if on Windows
248+
if M.is_windows() then
249+
result = result:gsub("/", "\\")
250+
end
251+
252+
return result
253+
end
254+
223255
---Quote a string for shell command (cross-platform)
224256
---@param str string The string to quote
225257
---@param force_windows? boolean Force Windows quoting style (for testing)

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ local function get_gitignore_path()
3636
if not git_dir or git_dir == "" then
3737
return nil
3838
end
39-
local sep = package.config:sub(1, 1)
40-
return git_dir .. sep .. ".gitignore"
39+
return GitUtils.path_join(git_dir, ".gitignore")
4140
end
4241

4342
function GitTool.get_gitignore()

tests/test_git_utils.lua

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,104 @@ T["shell_quote_windows"]["handles spaces"] = function()
592592
h.eq('"hello world"', result)
593593
end
594594

595+
T["path_join"] = new_set()
596+
597+
T["path_join"]["joins two path parts"] = function()
598+
local result = child.lua([[
599+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
600+
local result = GitUtils.path_join("src", "main.lua")
601+
local has_sep = GitUtils.is_windows() and result:find("\\") ~= nil or result:find("/") ~= nil
602+
return has_sep and result:match("main%.lua") ~= nil
603+
]])
604+
h.eq(true, result)
605+
end
606+
607+
T["path_join"]["joins multiple path parts"] = function()
608+
local result = child.lua([[
609+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
610+
local result = GitUtils.path_join("src", "components", "ui", "Button.tsx")
611+
return result:match("src") ~= nil
612+
and result:match("components") ~= nil
613+
and result:match("ui") ~= nil
614+
and result:match("Button%.tsx") ~= nil
615+
]])
616+
h.eq(true, result)
617+
end
618+
619+
T["path_join"]["handles windows backslashes"] = function()
620+
local result = child.lua([[
621+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
622+
local result = GitUtils.path_join("C:\\\\Users", "test", "file.lua")
623+
local has_backslash = result:find("\\") ~= nil
624+
local has_users = result:find("Users") ~= nil
625+
local has_test = result:find("test") ~= nil
626+
local has_file = result:find("file%.lua") ~= nil
627+
return has_backslash and has_users and has_test and has_file
628+
]])
629+
h.eq(true, result)
630+
end
631+
632+
T["path_join"]["handles mixed separators"] = function()
633+
local result = child.lua([[
634+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
635+
local result = GitUtils.path_join("C:/project", "src", "main.lua")
636+
return result:match("src") ~= nil and result:match("main%.lua") ~= nil
637+
]])
638+
h.eq(true, result)
639+
end
640+
641+
T["path_join"]["handles trailing slashes"] = function()
642+
local result = child.lua([[
643+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
644+
local result = GitUtils.path_join("src/", "test/")
645+
return not result:match("//") and result:match("src") ~= nil and result:match("test") ~= nil
646+
]])
647+
h.eq(true, result)
648+
end
649+
650+
T["path_join"]["handles leading slashes in middle parts"] = function()
651+
local result = child.lua([[
652+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
653+
local result = GitUtils.path_join("src", "/utils.lua")
654+
return not result:match("/utils") and result:match("utils%.lua") ~= nil
655+
]])
656+
h.eq(true, result)
657+
end
658+
659+
T["path_join"]["handles single part"] = function()
660+
local result = child.lua([[
661+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
662+
return GitUtils.path_join("test.lua") == "test.lua"
663+
]])
664+
h.eq(true, result)
665+
end
666+
667+
T["path_join"]["returns empty string for no parts"] = function()
668+
local result = child.lua([[
669+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
670+
return GitUtils.path_join() == ""
671+
]])
672+
h.eq(true, result)
673+
end
674+
675+
T["path_join"]["handles empty middle parts"] = function()
676+
local result = child.lua([[
677+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
678+
local result = GitUtils.path_join("src", "", "test.lua")
679+
return not result:match("//") and result:match("test%.lua") ~= nil
680+
]])
681+
h.eq(true, result)
682+
end
683+
684+
T["path_join"]["preserves dots in path"] = function()
685+
local result = child.lua([[
686+
local GitUtils = require("codecompanion._extensions.gitcommit.git_utils")
687+
local result = GitUtils.path_join("..", "utils.lua")
688+
return result:match("%.%.") ~= nil and result:match("utils%.lua") ~= nil
689+
]])
690+
h.eq(true, result)
691+
end
692+
595693
T["is_windows"] = new_set()
596694

597695
T["is_windows"]["returns boolean"] = function()

0 commit comments

Comments
 (0)