From c759f3e468c18031f4d75e2a3dc2d3895e9fdfac Mon Sep 17 00:00:00 2001 From: E Spelt Date: Wed, 26 Aug 2026 19:04:40 +0200 Subject: [PATCH] fix(git): stop native checkout/pull from running against the user's project repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit doClone and getWrapperFetch remove the module's .git link file when they finish, but CheckoutNative and PullNative later run plain "git checkout -f " / "git pull --force" with cwd set to modules/. Without a .git there, git walks up the directory tree, discovers the user's own project repository, and runs the command against it — silently switching the user's project to the dependency's branch (e.g. from development to master) and pulling it. Fix: - CheckoutNative and PullNative now write the .git link file (pointing at the cache git dir) before running, and remove it afterwards, same as getWrapperFetch already does for reset/fetch. - runCommand sets GIT_CEILING_DIRECTORIES to the modules dir so repo discovery can never escape into an enclosing repository, even if a .git link is missing for any other reason. Co-Authored-By: Claude Fable 5 --- internal/adapters/secondary/git/git_native.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/adapters/secondary/git/git_native.go b/internal/adapters/secondary/git/git_native.go index d5716f1..a8d59d0 100644 --- a/internal/adapters/secondary/git/git_native.go +++ b/internal/adapters/secondary/git/git_native.go @@ -134,6 +134,8 @@ 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()) + writeDotGitFile(dep) + defer func() { _ = os.Remove(filepath.Join(dirModule, ".git")) }() cmd := exec.CommandContext(context.Background(), "git", "checkout", "-f", referenceName.Short()) // #nosec G204 -- Controlled git checkout command cmd.Dir = dirModule @@ -143,6 +145,8 @@ 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()) + writeDotGitFile(dep) + defer func() { _ = os.Remove(filepath.Join(dirModule, ".git")) }() cmd := exec.CommandContext(context.Background(), "git", "pull", "--force") cmd.Dir = dirModule return runCommand(cmd) @@ -154,7 +158,11 @@ func runCommand(cmd *exec.Cmd) error { cmd.Stdout = &stdoutBuf cmd.Stderr = &stderrBuf - cmd.Env = os.Environ() + // Stop git from discovering an enclosing repository (e.g. the user's own + // project) when a module's .git link file is missing: a bare + // "git checkout"/"git pull" in modules/ would otherwise run against + // the parent work tree and silently switch the user's project branch. + cmd.Env = append(os.Environ(), "GIT_CEILING_DIRECTORIES="+env.GetModulesDir()) if err := cmd.Start(); err != nil { return fmt.Errorf("failed to start command: %w", err)