Closed
pr view --json 对已合并 PR 返回 merged=false,与 state=merged 矛盾 #484
aflyingtoAdd on  Jul 27Closed at  Jul 29
aflyingtoCollaborators
Jul 27 Create

现象

gc pr view <n> --json 对已合并 PR 返回 state=merged 但 merged=false,两个字段自相矛盾。依赖 merged 布尔值判断合入状态的脚本或 AI 代理会误判为“未合并”。

实测(gitcode-cli/gc-api-doc 三个已合并 PR):

PR#1 state=merged merged=False
PR#2 state=merged merged=False
PR#3 state=merged merged=False

根因

不是字段映射错误,而是上游返回 null。直接调 API 可见:

GET /api/v5/repos/gitcode-cli/gc-api-doc/pulls/3
  "state": "merged"
  "merged": null
  "merged_at": "2026-07-27T17:31:13+08:00"
  "closed_at": "2026-07-27T17:31:13+08:00"

三个 PR 的 merged 全部为 null,说明这是 GitCode API 的一贯行为,而非个例。

api/queries_pr.go:23 将其声明为值类型:

Merged bool `json:"merged"`

Go 把 JSON null 反序列化进 bool 会保留零值,所以 merged 必然为 false。权威信号实际在 state 和 merged_at。

影响范围

已修正的路径(有 State 兜底,行为正确):

  • pkg/output/prs.go:126、pkg/output/prs.go:148:pr.Merged || pr.State == "merged"
  • gc pr view 文本输出经 State 显示为 merged(实测正确)
  • gc pr list --state merged 实测正确

受影响的路径:

  • gc pr view --json:直接输出 merged: false,无兜底,这是最容易被代理消费的出口
  • pkg/cmd/pr/view/view.go:333:else if pr.Merged 分支永远不成立,仅因前面的 State 分支才碰巧正确
  • pkg/cmd/pr/merge/merge.go:161:if mergedPR != nil && mergedPR.Merged 恒为假,result.Merged 依赖硬编码的 true 兜底

建议修复方向

  1. 将 Merged 改为 *bool,区分 null 与 false
  2. 增加派生判断(如 IsMerged()),以 state == "merged" 或 merged_at != nil 为准,供各调用点统一使用
  3. --json 输出保证 merged 与 state 语义一致
  4. 补充 merged=null 的反序列化测试用例

具体取舍建议由维护者确认后再实施,尤其是 --json 字段语义变更可能影响既有消费方。

环境

  • CLI 版本 0.8.0(commit 6ba5d62)
  • 平台 Windows
  • 验证仓库 gitcode-cli/gc-api-doc(非 infra-test,因该现象需已合并 PR 才能复现;全部为只读查询,未产生写入)
likedislike
AaflyingtoCollaborators
Jul 27 Added label: risk/highscope/agent-friendlyscope/pr, status/triage,type/bug
aflyingtoCollaborators
Jul 27 Comments:

Verification record

  • Version/branch: GitCode CLI 0.8.0 (commit 6ba5d62); local main was aligned with origin/main at db6744b during reproduction
  • Command: gitcode pr list -R infra-test/gctest1 --state merged --limit 5 --json
  • Actual result: merged PRs 13, 12, and 1 all returned state="merged", merged=false, and a non-empty merged_at
  • API fact: the upstream detail response uses merged: null; decoding that into a Go bool produces the zero value false
  • Impact: text output is correct because it falls back to state, while machine-readable JSON is contradictory
  • Conclusion: reproduced on the permitted infra-test repository; continue with the fix
likedislike
AaflyingtoCollaborators
Jul 27 Added label: status/verified
AaflyingtoCollaborators
Jul 27 Removed label: status/triage
AaflyingtoCollaborators
Jul 27 Added label: status/in-progress
AaflyingtoCollaborators
Jul 27 Removed label: status/verified
AaflyingtoCollaborators
Jul 27 Associated pull request
aflyingtoCollaborators
Jul 27 Comments:

Development progress

  • Root cause: GitCode returns merged: null for merged PRs; decoding into Go bool produced false
  • Main change: normalize merged from explicit flag, state=merged, or non-empty merged_at at API boundaries
  • Tests: unit, full race, system, core regression, and real read-only commands passed
  • Real command repository: infra-test/gctest1
  • CI: https://github.com/gitcode-cli/cli/actions/runs/30264943095 (all jobs successful)
  • Security: no token, authentication, permission, or write-path behavior changed
  • Documentation: COMMANDS, AI-GUIDE, and REGRESSION updated
  • Risk: high because this corrects a JSON contract; independent review and human confirmation remain required
  • Associated PR: https://gitcode.com/gitcode-cli/cli/merge_requests/418
likedislike
AaflyingtoCollaborators
Jul 29 Close issue
AaflyingtoCollaborators
Jul 29 Added label: status/merged
AaflyingtoCollaborators
Jul 29 Removed label: status/in-progress