Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Push discards pre-push hook output, so an interactive hook looks like a hang

未关闭
#420 0 条评论 2 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
1-2 天
新手友好度
76/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
冷清
技术栈
go
领域
cli

调研方向

阅读 internal/git/gitops.go 中 defaultOps.Push 附近的代码和 internal/git/git.go 中 runInteractive 附近的代码,然后对比现有的交互式 commit 路径。复现 pre-push hook 场景并运行集成测试;完成的标准是 hook 提示、输出和 git 进度能够到达终端,同时被拒绝的 push 仍然返回错误且不重复 stderr。

由索引模型根据 Issue 内容生成。

描述

bug topic: cli - push

Summary

Every command that pushes (sync, push, submit, link) runs git push with stdout sent to /dev/null and stderr sent to an in-memory buffer that is only surfaced if the push fails. A pre-push hook that prompts for input therefore prints its question into the void while blocking on /dev/tty, and gh stack sync appears to hang forever with no output and no timeout.

The same discarding affects non-interactive hooks: their logging and git's own transfer progress never appear, so a merely slow hook (a test suite, a linter) is indistinguishable from a hang.

Steps to reproduce

  1. In a repo with a stack, add .git/hooks/pre-push:
#!/bin/sh
echo "Run the full test suite? [y/N] "
read ans < /dev/tty
  1. Run gh stack sync.

Expected: the hook's prompt appears and you can answer it.

Actual: output stops after Pushing N branches to origin... and the command waits indefinitely. Typing y + Enter unblocks it, but nothing on screen indicates input is wanted.

Root cause

internal/git/gitops.godefaultOps.Push ends in runSilent, which routes through the package-level client in internal/git/git.go:16:

var client = &cligit.Client{}

The client is zero-valued, so Stdin, Stdout, and Stderr are nil, and Client.Command copies them straight onto the exec.Cmd (cli/cli/v2/git/client.go:95-97). Nil Stdout on an exec.Cmd means /dev/null; Command.Run swaps nil Stderr for a bytes.Buffer (cli/cli/v2/git/command.go:20-23) that is only read back on failure. The hook process still inherits the controlling terminal, so read < /dev/tty blocks on real keystrokes — but the prompt it wrote to stdout/stderr is gone. context.Background() with no timeout means the wait is unbounded.

Verified with a pre-push hook that wrote to stdout, stderr, and a log file: the log confirmed the hook ran, and neither stream reached the terminal.

The codebase already has the right helper for this — runInteractive (internal/git/git.go:60) wires os.Stdin/Stdout/Stderr and is used for git commit. Push never got equivalent treatment.

Suggested fix

Have Push run through runInteractive so hook prompts, hook output, and git's transfer progress reach the terminal as they are produced. Since git and the hook have then already written their own messages to stderr, the returned error should carry only the exit status rather than folding the buffered stderr back in and printing it twice.

I have this working locally with integration tests covering a real pre-push hook (output visible on success; stderr visible and an error returned when the hook rejects the push). Happy to open a PR if that's welcome, or leave it here for a maintainer to pick up.

Related

  • #135 and #293 both ask for --no-verify. This is a separate problem: those are about skipping hooks, this is about hooks that run correctly but invisibly. A --no-verify flag would not fix the invisible-prompt hang for people who need their hooks to run.
主要语言
Go
星标
1.5k
派生
73
平均合并
1 天 8 小时
30 天内合并 PR
7

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

github/gh-stack 的其他 Issue

查看 github/gh-stack 的全部 Issue

相似的 Issue

更多 Go Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。