From 170d0d57168825188e88f21f04782d1a7aed7619 Mon Sep 17 00:00:00 2001 From: Marius van Niekerk Date: Thu, 11 Jun 2026 20:03:47 -0400 Subject: [PATCH 1/2] hide git runner windows consoles --- git/cmd/gitcmd.go | 10 ++++++++-- git/cmd/gitcmd_other.go | 7 +++++++ git/cmd/gitcmd_windows.go | 18 ++++++++++++++++++ git/cmd/gitcmd_windows_test.go | 21 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 git/cmd/gitcmd_other.go create mode 100644 git/cmd/gitcmd_windows.go create mode 100644 git/cmd/gitcmd_windows_test.go diff --git a/git/cmd/gitcmd.go b/git/cmd/gitcmd.go index d0d7b7b..aa69c25 100644 --- a/git/cmd/gitcmd.go +++ b/git/cmd/gitcmd.go @@ -93,7 +93,7 @@ func (r Runner) Command(ctx context.Context, dir string, args ...string) *exec.C if r.basicAuth != nil { panic("gitcmd: Command cannot be used with WithBasicAuth; use Run or Output so credentials can be cleaned up") } - cmd := exec.CommandContext(ctx, "git", args...) + cmd := gitCommand(ctx, args...) cmd.Dir = dir cmd.Env, _ = r.commandEnv(ctx, dir) return cmd @@ -107,7 +107,7 @@ func (r Runner) Output(ctx context.Context, dir string, args ...string) ([]byte, // Run runs git and returns stdout, stderr, and a *GitError on failure. func (r Runner) Run(ctx context.Context, dir string, stdin io.Reader, args ...string) ([]byte, []byte, error) { - cmd := exec.CommandContext(ctx, "git", args...) + cmd := gitCommand(ctx, args...) cmd.Dir = dir var cleanup func() cmd.Env, cleanup = r.commandEnv(ctx, dir) @@ -131,6 +131,12 @@ func (r Runner) Run(ctx context.Context, dir string, stdin io.Reader, args ...st return stdout.Bytes(), stderr.Bytes(), nil } +func gitCommand(ctx context.Context, args ...string) *exec.Cmd { + cmd := exec.CommandContext(ctx, "git", args...) + prepareGitCommand(cmd) + return cmd +} + type basicAuth struct { username string password string diff --git a/git/cmd/gitcmd_other.go b/git/cmd/gitcmd_other.go new file mode 100644 index 0000000..2c85d29 --- /dev/null +++ b/git/cmd/gitcmd_other.go @@ -0,0 +1,7 @@ +//go:build !windows + +package gitcmd + +import "os/exec" + +func prepareGitCommand(cmd *exec.Cmd) {} diff --git a/git/cmd/gitcmd_windows.go b/git/cmd/gitcmd_windows.go new file mode 100644 index 0000000..2b326e4 --- /dev/null +++ b/git/cmd/gitcmd_windows.go @@ -0,0 +1,18 @@ +//go:build windows + +package gitcmd + +import ( + "os/exec" + "syscall" + + "golang.org/x/sys/windows" +) + +func prepareGitCommand(cmd *exec.Cmd) { + if cmd.SysProcAttr == nil { + cmd.SysProcAttr = &syscall.SysProcAttr{} + } + // Console-less callers otherwise cause git.exe to allocate a visible window. + cmd.SysProcAttr.CreationFlags |= windows.CREATE_NO_WINDOW +} diff --git a/git/cmd/gitcmd_windows_test.go b/git/cmd/gitcmd_windows_test.go new file mode 100644 index 0000000..1d7810b --- /dev/null +++ b/git/cmd/gitcmd_windows_test.go @@ -0,0 +1,21 @@ +//go:build windows + +package gitcmd + +import ( + "context" + "testing" + + "golang.org/x/sys/windows" +) + +func TestRunnerCommandHidesGitConsoleWindowOnWindows(t *testing.T) { + cmd := New().Command(context.Background(), "", "status") + + if cmd.SysProcAttr == nil { + t.Fatal("git command SysProcAttr is nil") + } + if cmd.SysProcAttr.CreationFlags&windows.CREATE_NO_WINDOW == 0 { + t.Fatalf("git command creation flags = %#x, want CREATE_NO_WINDOW", cmd.SysProcAttr.CreationFlags) + } +} From 14e92d45048c1ca30cae6ffdb2820f80784832eb Mon Sep 17 00:00:00 2001 From: Marius van Niekerk Date: Fri, 12 Jun 2026 07:42:51 -0400 Subject: [PATCH 2/2] route safe.directory probes through gitCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The safe.directory config probes in readSafeDirectories still built their git processes with exec.CommandContext directly, bypassing the prepareGitCommand hook this branch adds. On Windows that meant the probe processes could each flash a visible console window before the main git command ran — the exact behavior the branch exists to suppress. Flagged by roborev review of 170d0d5. Validation: go build ./..., go test ./git/cmd, GOOS=windows go build ./git/..., go vet ./git/cmd Generated with Claude Code (claude-fable-5) Co-Authored-By: Claude Fable 5 --- git/cmd/gitcmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git/cmd/gitcmd.go b/git/cmd/gitcmd.go index aa69c25..c3e1f45 100644 --- a/git/cmd/gitcmd.go +++ b/git/cmd/gitcmd.go @@ -237,7 +237,7 @@ func readSafeDirectories(ctx context.Context, env []string, dir string) []string for _, scope := range scopes { // --includes is required for explicit-scope reads to honor include.path // and includeIf directives the way git's default config sequence does. - cmd := exec.CommandContext(ctx, "git", "config", scope, "--includes", "-z", "--get-all", "safe.directory") + cmd := gitCommand(ctx, "config", scope, "--includes", "-z", "--get-all", "safe.directory") cmd.Dir = dir cmd.Env = env out, err := cmd.Output()