diff --git a/doc/changes/repo-tree-default-branch.md b/doc/changes/repo-tree-default-branch.md new file mode 100644 index 0000000..29c8a64 --- /dev/null +++ b/doc/changes/repo-tree-default-branch.md @@ -0,0 +1,25 @@ +# repo +tree 遵循仓库默认分支 + +## 背景 + +`repo +tree` 未指定 `--ref` 时把 ref 硬编码为 `master`,对默认分支是 +`main`(或其他名称)的仓库直接返回 `[-2] 你访问的文件不存在` +(生产实测 `datawhalechina/paper-chart-tutorial`,默认分支 `main`)。 +平台 `sub_entries` API 在不带 `ref` 参数时会自动使用仓库默认分支。 + +## 变更 + +- `--ref` 未指定时不再发送 `ref` 参数(交由平台落到默认分支),flag 也不再 + 声明 `master` 默认值。 + +## 验证 + +- `go test ./shortcuts/repo/`(默认 ref 断言改为「不携带 ref 参数」) +- 生产 gitlink.org.cn 实测:默认分支为 `main` 与 `master` 的仓库均正常列出根目录。 + +## 追加:branch/pr/release 同类问题一并修复 + +- 新增 `RuntimeContext.DefaultBranch()`:读取仓库详情的 `default_branch`(缺失时回退 master) +- `branch +create --from` / `pr +create --base` 未指定时回退到仓库默认分支(不再硬编码 master) +- `release +create --target` 未指定时省略 `target_commitish`(平台自动落默认分支) +- 生产实测(默认分支为 main 的仓库):`repo +tree` / `branch +create` / `release +create` 均成功 diff --git a/shortcuts/branch/branch.go b/shortcuts/branch/branch.go index 08cf91a..c9afa95 100644 --- a/shortcuts/branch/branch.go +++ b/shortcuts/branch/branch.go @@ -56,7 +56,7 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { Description: tr.T("cmd.branch.create.short"), Flags: []common.Flag{ {Name: "name", Short: "n", Usage: tr.T("flag.branch.name"), Required: true}, - {Name: "from", Short: "f", Usage: tr.T("flag.branch.from"), Default: "master"}, + {Name: "from", Short: "f", Usage: tr.T("flag.branch.from")}, }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { @@ -65,7 +65,10 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { name, _ := ctx.RequireArg("name") from := ctx.Arg("from") if from == "" { - from = "master" + var err error + if from, err = ctx.DefaultBranch(); err != nil { + return err + } } payload := map[string]interface{}{ "new_branch_name": name, diff --git a/shortcuts/branch/branch_test.go b/shortcuts/branch/branch_test.go index c31481d..50c8f56 100644 --- a/shortcuts/branch/branch_test.go +++ b/shortcuts/branch/branch_test.go @@ -81,12 +81,23 @@ func TestBranchCreate(t *testing.T) { } func TestBranchCreateDefaultFrom(t *testing.T) { - // When 'from' is not set, it defaults to "master" + // When 'from' is not set, it falls back to the repository default branch. server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/v1/owner/repo/branches.json" { + switch r.URL.Path { + case "/owner/repo.json": + writeJSON(w, map[string]interface{}{"default_branch": "main"}) + case "/v1/owner/repo/branches.json": + var payload map[string]interface{} + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + t.Fatalf("decode payload: %v", err) + } + if payload["old_branch_name"] != "main" { + t.Fatalf("expected old_branch_name to be default branch main, got %v", payload["old_branch_name"]) + } + writeJSON(w, map[string]interface{}{"name": "feature-y"}) + default: t.Fatalf("unexpected path: %s", r.URL.Path) } - writeJSON(w, map[string]interface{}{"name": "feature-y"}) })) defer server.Close() diff --git a/shortcuts/common/types.go b/shortcuts/common/types.go index 87a052a..ffd4338 100644 --- a/shortcuts/common/types.go +++ b/shortcuts/common/types.go @@ -105,6 +105,20 @@ func (ctx *RuntimeContext) OutputData(data interface{}) error { return output.Print(output.SuccessEnvelope(data, nil), ctx.Format) } +// DefaultBranch fetches the repository's default branch, falling back to "master". +func (ctx *RuntimeContext) DefaultBranch() (string, error) { + env, err := ctx.CallAPI("GET", ctx.RepoPath(), nil) + if err != nil { + return "", err + } + if data, ok := env.Data.(map[string]interface{}); ok { + if branch, ok := data["default_branch"].(string); ok && branch != "" { + return branch, nil + } + } + return "master", nil +} + // RepoPath returns the API path prefix for the current owner/repo. func (ctx *RuntimeContext) RepoPath() string { return fmt.Sprintf("/%s/%s", ctx.Owner, ctx.Repo) diff --git a/shortcuts/pr/pr.go b/shortcuts/pr/pr.go index 57a1264..c9e3451 100644 --- a/shortcuts/pr/pr.go +++ b/shortcuts/pr/pr.go @@ -98,7 +98,7 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { {Name: "title", Short: "t", Usage: tr.T("flag.pr.title"), Required: true}, {Name: "body", Short: "b", Usage: tr.T("flag.pr.body")}, {Name: "head", Usage: tr.T("flag.pr.head"), Required: true}, - {Name: "base", Usage: tr.T("flag.pr.base"), Default: "master"}, + {Name: "base", Usage: tr.T("flag.pr.base")}, }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { @@ -108,7 +108,10 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { head, _ := ctx.RequireArg("head") base := ctx.Arg("base") if base == "" { - base = "master" + var err error + if base, err = ctx.DefaultBranch(); err != nil { + return err + } } payload := map[string]interface{}{ "title": title, diff --git a/shortcuts/pr/pr_test.go b/shortcuts/pr/pr_test.go index 459c29a..f337ff1 100644 --- a/shortcuts/pr/pr_test.go +++ b/shortcuts/pr/pr_test.go @@ -253,6 +253,10 @@ func TestPRCreate(t *testing.T) { func TestPRCreateNoBody(t *testing.T) { var payload map[string]interface{} server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/owner/repo.json" { + writeJSON(t, w, map[string]interface{}{"default_branch": "main"}) + return + } payload = decodeJSON(t, r) writeJSON(t, w, map[string]interface{}{"id": float64(43), "title": "feat: nob"}) })) @@ -268,6 +272,9 @@ func TestPRCreateNoBody(t *testing.T) { if _, ok := payload["body"]; ok { t.Fatal("body should not be in payload when not provided") } + if payload["base"] != "main" { + t.Fatalf("expected base to fall back to default branch main, got %v", payload["base"]) + } } // --- view --- diff --git a/shortcuts/release/release.go b/shortcuts/release/release.go index 21d7ec4..301abb0 100644 --- a/shortcuts/release/release.go +++ b/shortcuts/release/release.go @@ -42,7 +42,7 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { {Name: "tag", Short: "t", Usage: tr.T("flag.release.tag"), Required: true}, {Name: "name", Short: "n", Usage: tr.T("flag.release.name"), Required: true}, {Name: "body", Short: "b", Usage: tr.T("flag.release.body")}, - {Name: "target", Usage: tr.T("flag.release.target"), Default: "master"}, + {Name: "target", Usage: tr.T("flag.release.target")}, {Name: "prerelease", Usage: tr.T("flag.release.prerelease"), Default: "false"}, {Name: "draft", Usage: "Mark as draft (true/false)", Default: "false"}, {Name: "attachment-ids", Usage: "Comma-separated attachment IDs"}, diff --git a/shortcuts/repo/repo.go b/shortcuts/repo/repo.go index 2c07232..7eb1af5 100644 --- a/shortcuts/repo/repo.go +++ b/shortcuts/repo/repo.go @@ -135,21 +135,19 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut { Description: tr.T("cmd.repo.tree.short"), Flags: []common.Flag{ {Name: "path", Short: "p", Usage: tr.T("flag.repo.tree.path")}, - {Name: "ref", Short: "r", Usage: tr.T("flag.repo.tree.ref"), Default: "master"}, + {Name: "ref", Short: "r", Usage: tr.T("flag.repo.tree.ref")}, }, Run: func(ctx *common.RuntimeContext) error { if err := ctx.ResolveOwnerRepo(); err != nil { return err } q := url.Values{} - ref := ctx.Arg("ref") - if ref == "" { - ref = "master" - } if path := ctx.Arg("path"); path != "" { q.Set("filepath", path) } - q.Set("ref", ref) + if ref := ctx.Arg("ref"); ref != "" { + q.Set("ref", ref) + } env, err := ctx.CallAPIWithQuery("GET", ctx.RepoPath()+"/sub_entries", q) if err != nil { return err diff --git a/shortcuts/repo/repo_test.go b/shortcuts/repo/repo_test.go index ba60a41..4013f8e 100644 --- a/shortcuts/repo/repo_test.go +++ b/shortcuts/repo/repo_test.go @@ -161,7 +161,9 @@ func TestRepoTreeListsRootOnDefaultRef(t *testing.T) { if _, ok := r.URL.Query()["filepath"]; ok { t.Fatalf("did not expect filepath query for repository root, got %q", r.URL.Query().Get("filepath")) } - assertEqual(t, r.URL.Query().Get("ref"), "master") + if _, ok := r.URL.Query()["ref"]; ok { + t.Fatalf("did not expect ref query for default ref, got %q", r.URL.Query().Get("ref")) + } writeJSON(t, w, map[string]interface{}{ "entries": []map[string]interface{}{ {"name": "README.md", "type": "file"}, @@ -216,7 +218,7 @@ func TestRepoTreeShortcutRegistersHelpFlags(t *testing.T) { if !ok { t.Fatal("tree shortcut missing ref flag") } - if refFlag.Short != "r" || refFlag.Default != "master" || refFlag.Usage == "" { + if refFlag.Short != "r" || refFlag.Default != "" || refFlag.Usage == "" { t.Fatalf("unexpected ref flag: %+v", refFlag) } }