From c9aea1d983a34e9ca7b87475c365bf16da96c9e0 Mon Sep 17 00:00:00 2001 From: Mengz <2567587994@qq.com> Date: Thu, 25 Jun 2026 18:13:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(pr):=20=E5=9C=A8=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E4=B8=AD=E6=98=BE=E7=A4=BA=20PR=20=E7=BC=96=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++ doc/changes/pr-list-number-column.md | 13 ++++++++ internal/output/formatter.go | 2 +- internal/output/formatter_test.go | 20 ++++++++----- shortcuts/pr/pr.go | 37 +++++++++++++++++++++++ shortcuts/pr/pr_test.go | 44 ++++++++++++++++++++++++++++ 6 files changed, 110 insertions(+), 9 deletions(-) create mode 100644 doc/changes/pr-list-number-column.md diff --git a/README.md b/README.md index e5e4318..0e0c5d0 100644 --- a/README.md +++ b/README.md @@ -402,6 +402,9 @@ gitlink-cli label +delete --owner Gitlink --repo forgeplus -i 42 # List PRs gitlink-cli pr +list --owner Gitlink --repo forgeplus +# List PRs with the user-facing PR number column +gitlink-cli pr +list --owner Gitlink --repo forgeplus --format table + # Create a PR (same-repo branch) gitlink-cli pr +create --owner Gitlink --repo forgeplus -t "feat: Search feature" --head feature/search --base master diff --git a/doc/changes/pr-list-number-column.md b/doc/changes/pr-list-number-column.md new file mode 100644 index 0000000..c1e8034 --- /dev/null +++ b/doc/changes/pr-list-number-column.md @@ -0,0 +1,13 @@ +## PR list output now shows the user-facing PR number + +`pr +list` already returned the GitLink PR sequence as `index`, but the default +table output did not make that value easy to spot. This change copies the same +value into a stable `number` field during list normalization and prioritizes the +`number` column in table rendering. + +As a result: + +- `gitlink-cli pr +list --format table` shows the PR number in a dedicated + leading column. +- JSON and YAML output also include `number`, making the list output align with + `pr +view --id ` semantics and with the PR number shown in the web UI. diff --git a/internal/output/formatter.go b/internal/output/formatter.go index dd0b59c..b8c5d2d 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -145,7 +145,7 @@ func printMapTable(w io.Writer, m map[string]interface{}) error { func collectKeys(m map[string]interface{}) []string { keys := make([]string, 0, len(m)) // Prefer common keys first - priority := []string{"id", "name", "login", "title", "status", "state", "created_at", "updated_at"} + priority := []string{"number", "id", "name", "login", "title", "status", "state", "created_at", "updated_at"} seen := map[string]bool{} for _, k := range priority { if _, ok := m[k]; ok { diff --git a/internal/output/formatter_test.go b/internal/output/formatter_test.go index 80430b9..5178109 100644 --- a/internal/output/formatter_test.go +++ b/internal/output/formatter_test.go @@ -200,6 +200,7 @@ func TestHasComplexValues(t *testing.T) { func TestCollectKeys(t *testing.T) { m := map[string]interface{}{ + "number": float64(3), "title": "test", "id": float64(1), "status": "open", @@ -207,17 +208,20 @@ func TestCollectKeys(t *testing.T) { } keys := collectKeys(m) // Priority keys should come first - if len(keys) != 4 { - t.Fatalf("expected 4 keys, got %d", len(keys)) + if len(keys) != 5 { + t.Fatalf("expected 5 keys, got %d", len(keys)) } - if keys[0] != "id" { - t.Fatalf("first key should be 'id', got %q", keys[0]) + if keys[0] != "number" { + t.Fatalf("first key should be 'number', got %q", keys[0]) } - if keys[1] != "title" { - t.Fatalf("second key should be 'title', got %q", keys[1]) + if keys[1] != "id" { + t.Fatalf("second key should be 'id', got %q", keys[1]) } - if keys[2] != "status" { - t.Fatalf("third key should be 'status', got %q", keys[2]) + if keys[2] != "title" { + t.Fatalf("third key should be 'title', got %q", keys[2]) + } + if keys[3] != "status" { + t.Fatalf("fourth key should be 'status', got %q", keys[3]) } } diff --git a/shortcuts/pr/pr.go b/shortcuts/pr/pr.go index 03f537f..04aed4f 100644 --- a/shortcuts/pr/pr.go +++ b/shortcuts/pr/pr.go @@ -86,6 +86,7 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { if err != nil { return err } + normalizePullRequestListNumbers(env) return ctx.Output(env) }, }, @@ -470,6 +471,33 @@ func extractIssueID(env *output.Envelope) (int64, error) { return int64(idFloat), nil } +func normalizePullRequestListNumbers(env *output.Envelope) { + if env == nil { + return + } + + data, ok := env.Data.(map[string]interface{}) + if !ok { + return + } + + pulls, ok := data["pulls"].([]interface{}) + if !ok { + return + } + + for i, item := range pulls { + pr, ok := item.(map[string]interface{}) + if !ok { + continue + } + if number := firstPullRequestNumber(pr); number != nil { + pr["number"] = number + } + pulls[i] = pr + } +} + func enrichPullRequestClosedAt(ctx *common.RuntimeContext, env *output.Envelope) error { data, ok := env.Data.(map[string]interface{}) if !ok { @@ -559,3 +587,12 @@ func numberField(m map[string]interface{}, key string) (float64, bool) { return 0, false } } + +func firstPullRequestNumber(pr map[string]interface{}) interface{} { + for _, key := range []string{"number", "pull_request_number", "index"} { + if value, ok := pr[key]; ok { + return value + } + } + return nil +} diff --git a/shortcuts/pr/pr_test.go b/shortcuts/pr/pr_test.go index eece6d9..73de43b 100644 --- a/shortcuts/pr/pr_test.go +++ b/shortcuts/pr/pr_test.go @@ -175,6 +175,50 @@ func TestPRListStateAllOmitsStatus(t *testing.T) { } } +func TestNormalizePullRequestListNumbersCopiesIndex(t *testing.T) { + env := &output.Envelope{ + OK: true, + Data: map[string]interface{}{ + "pulls": []interface{}{ + map[string]interface{}{ + "id": float64(11), + "index": float64(7), + "title": "feat: show number", + }, + }, + }, + } + + normalizePullRequestListNumbers(env) + + data := env.Data.(map[string]interface{}) + pulls := data["pulls"].([]interface{}) + pr := pulls[0].(map[string]interface{}) + assertEqual(t, pr["number"], float64(7)) +} + +func TestNormalizePullRequestListNumbersKeepsExistingNumber(t *testing.T) { + env := &output.Envelope{ + OK: true, + Data: map[string]interface{}{ + "pulls": []interface{}{ + map[string]interface{}{ + "number": float64(9), + "index": float64(7), + "title": "feat: keep number", + }, + }, + }, + } + + normalizePullRequestListNumbers(env) + + data := env.Data.(map[string]interface{}) + pulls := data["pulls"].([]interface{}) + pr := pulls[0].(map[string]interface{}) + assertEqual(t, pr["number"], float64(9)) +} + // --- create --- func TestPRCreate(t *testing.T) {