From 65a6ee149e2d4d418ce9b802b4a9b352435d6545 Mon Sep 17 00:00:00 2001 From: E Spelt Date: Wed, 26 Aug 2026 18:31:53 +0200 Subject: [PATCH] fix(git): force core.longpaths=true on Windows for native git commands Checkouts of dependencies with deep paths (e.g. Kastri's Demos/FCMRebooted/NotificationExtension/...) fail with "Filename too long" and "Could not reset index file to revision 'HEAD'" (exit 128) when the user's git config lacks core.longpaths. The embedded go-git client did not have this limitation, so the switch to native git in 3.x surfaced it as a regression. Prepend -c core.longpaths=true to every native git invocation on Windows so installs work regardless of the user's global git config. Co-Authored-By: Claude Fable 5 --- internal/adapters/secondary/git/git_native.go | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/internal/adapters/secondary/git/git_native.go b/internal/adapters/secondary/git/git_native.go index d5716f1..d976a08 100644 --- a/internal/adapters/secondary/git/git_native.go +++ b/internal/adapters/secondary/git/git_native.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "runtime" git2 "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -17,6 +18,17 @@ import ( "github.com/hashload/boss/pkg/msg" ) +// gitCommand builds a system git command. On Windows it forces core.longpaths=true +// so checkouts of repositories with paths beyond MAX_PATH (260 chars) don't fail +// with "Filename too long" when the user's git config lacks that setting. +func gitCommand(args ...string) *exec.Cmd { + if runtime.GOOS == "windows" { + args = append([]string{"-c", "core.longpaths=true"}, args...) + } + //nolint:gosec,nolintlint // Git command with controlled arguments + return exec.CommandContext(context.Background(), "git", args...) // #nosec G204 -- Controlled git command +} + func checkHasGitClient() { command := exec.CommandContext(context.Background(), "where", "git") _, err := command.Output() @@ -66,8 +78,7 @@ func doClone(dep domain.Dependency) error { args = append(args, dep.GetURL(), dirModule) - //nolint:gosec,nolintlint // Git command with controlled and validated repository URL - cmd := exec.CommandContext(context.Background(), "git", args...) // #nosec G204 -- Controlled git clone command + cmd := gitCommand(args...) if err = runCommand(cmd); err != nil { return err @@ -99,13 +110,13 @@ func getWrapperFetch(dep domain.Dependency) error { } writeDotGitFile(dep) - cmdReset := exec.CommandContext(context.Background(), "git", "reset", "--hard") + cmdReset := gitCommand("reset", "--hard") cmdReset.Dir = dirModule if err := runCommand(cmdReset); err != nil { return err } - cmd := exec.CommandContext(context.Background(), "git", "fetch", "--all") + cmd := gitCommand("fetch", "--all") cmd.Dir = dirModule if err := runCommand(cmd); err != nil { @@ -122,7 +133,7 @@ func getWrapperFetch(dep domain.Dependency) error { func initSubmodulesNative(dep domain.Dependency) error { dirModule := filepath.Join(env.GetModulesDir(), dep.Name()) - cmd := exec.CommandContext(context.Background(), "git", "submodule", "update", "--init", "--recursive") + cmd := gitCommand("submodule", "update", "--init", "--recursive") cmd.Dir = dirModule if err := runCommand(cmd); err != nil { @@ -134,8 +145,7 @@ func initSubmodulesNative(dep domain.Dependency) error { // CheckoutNative switches the dependency repository to the given reference using system git. func CheckoutNative(dep domain.Dependency, referenceName plumbing.ReferenceName) error { dirModule := filepath.Join(env.GetModulesDir(), dep.Name()) - cmd := exec.CommandContext(context.Background(), - "git", "checkout", "-f", referenceName.Short()) // #nosec G204 -- Controlled git checkout command + cmd := gitCommand("checkout", "-f", referenceName.Short()) cmd.Dir = dirModule return runCommand(cmd) } @@ -143,7 +153,7 @@ func CheckoutNative(dep domain.Dependency, referenceName plumbing.ReferenceName) // PullNative fetches and merges updates using system git. func PullNative(dep domain.Dependency) error { dirModule := filepath.Join(env.GetModulesDir(), dep.Name()) - cmd := exec.CommandContext(context.Background(), "git", "pull", "--force") + cmd := gitCommand("pull", "--force") cmd.Dir = dirModule return runCommand(cmd) }