Merge PR #273: feat(repo): 补齐仓库治理中的转移与模块配置能力
# Conflicts: # shortcuts/repo/repo.go # skills/README.md # skills/gitlink-repo/SKILL.md
This commit is contained in:
commit
0b8aa5ff7f
19
README.md
19
README.md
|
|
@ -249,11 +249,30 @@ gitlink-cli repo +unfollow --owner Gitlink --repo forgeplus --project-id 123
|
|||
gitlink-cli repo +like --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +unlike --owner Gitlink --repo forgeplus --project-id 123
|
||||
|
||||
# List and update repository navigation units
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
|
||||
# Create a repository
|
||||
gitlink-cli repo +create -n my-project -d "Project description"
|
||||
|
||||
# Fork a repository
|
||||
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
||||
|
||||
# List organizations that can receive a repository transfer
|
||||
gitlink-cli repo +transfer-orgs --owner Gitlink --repo forgeplus
|
||||
|
||||
# Preview a repository transfer without changing data
|
||||
gitlink-cli repo +transfer --owner Gitlink --repo forgeplus --target-owner my-org --dry-run
|
||||
|
||||
# Confirm and send a repository transfer request
|
||||
gitlink-cli repo +transfer --owner Gitlink --repo forgeplus --target-owner my-org --yes
|
||||
|
||||
# Preview canceling a pending repository transfer
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --dry-run
|
||||
|
||||
# Confirm canceling a pending repository transfer
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --yes
|
||||
```
|
||||
|
||||
### Webhook Management
|
||||
|
|
|
|||
|
|
@ -260,11 +260,30 @@ gitlink-cli repo +unfollow --owner Gitlink --repo forgeplus --project-id 123
|
|||
gitlink-cli repo +like --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +unlike --owner Gitlink --repo forgeplus --project-id 123
|
||||
|
||||
# 查看和更新仓库导航模块
|
||||
gitlink-cli repo +units --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +set-units --owner Gitlink --repo forgeplus --units code,issues,pulls,wiki
|
||||
|
||||
# 创建仓库
|
||||
gitlink-cli repo +create -n my-project -d "项目描述"
|
||||
|
||||
# Fork 仓库
|
||||
gitlink-cli repo +fork --owner Gitlink --repo forgeplus
|
||||
|
||||
# 列出可接收仓库转移的组织
|
||||
gitlink-cli repo +transfer-orgs --owner Gitlink --repo forgeplus
|
||||
|
||||
# 预览仓库转移请求,不修改线上数据
|
||||
gitlink-cli repo +transfer --owner Gitlink --repo forgeplus --target-owner my-org --dry-run
|
||||
|
||||
# 确认后发起仓库转移
|
||||
gitlink-cli repo +transfer --owner Gitlink --repo forgeplus --target-owner my-org --yes
|
||||
|
||||
# 预览取消待处理的仓库转移
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --dry-run
|
||||
|
||||
# 确认后取消待处理的仓库转移
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --yes
|
||||
```
|
||||
|
||||
### Webhook 管理
|
||||
|
|
|
|||
|
|
@ -88,8 +88,13 @@
|
|||
"cmd.repo.fork.short": "Fork a repository",
|
||||
"cmd.repo.info.short": "Show repository details",
|
||||
"cmd.repo.list.short": "List repositories for a user or organization",
|
||||
"cmd.repo.set_units.short": "Set enabled repository navigation units",
|
||||
"cmd.repo.short": "Repository operations",
|
||||
"cmd.repo.transfer.short": "Transfer a repository to another owner",
|
||||
"cmd.repo.transfer_cancel.short": "Cancel a pending repository transfer",
|
||||
"cmd.repo.transfer_orgs.short": "List organizations that can receive this repository",
|
||||
"cmd.repo.tree.short": "List repository files and directories",
|
||||
"cmd.repo.units.short": "List enabled repository navigation units",
|
||||
"cmd.root.long": "Manage repositories, issues, pull requests, releases, CI and workflows on GitLink.",
|
||||
"cmd.root.short": "GitLink CLI - command-line tool for GitLink",
|
||||
"cmd.search.repos.short": "Search repositories",
|
||||
|
|
@ -215,8 +220,13 @@
|
|||
"flag.repo.description": "Repository description",
|
||||
"flag.repo.name": "Repository name",
|
||||
"flag.repo.private": "Make repository private (true/false)",
|
||||
"flag.repo.target_owner": "Target user or organization login",
|
||||
"flag.repo.transfer_cancel_dry_run": "Preview the cancel request without changing repository transfer state",
|
||||
"flag.repo.transfer_dry_run": "Preview the transfer request without changing repository ownership",
|
||||
"flag.repo.transfer_yes": "Confirm and execute the repository transfer action",
|
||||
"flag.repo.tree.path": "Directory path to list (default: repository root)",
|
||||
"flag.repo.tree.ref": "Branch, tag, or commit ref",
|
||||
"flag.repo.units": "Comma-separated units: code,issues,pulls,devops,versions,wiki,services,resources",
|
||||
"flag.search.keyword": "Search keyword",
|
||||
"flag.sort_by": "Sort field",
|
||||
"flag.sort_direction": "Sort direction: asc, desc",
|
||||
|
|
|
|||
|
|
@ -88,8 +88,13 @@
|
|||
"cmd.repo.fork.short": "Fork 仓库",
|
||||
"cmd.repo.info.short": "显示仓库详情",
|
||||
"cmd.repo.list.short": "列出用户或组织的仓库",
|
||||
"cmd.repo.set_units.short": "设置启用的仓库导航模块",
|
||||
"cmd.repo.short": "仓库操作",
|
||||
"cmd.repo.transfer.short": "将仓库转移给其他所有者",
|
||||
"cmd.repo.transfer_cancel.short": "取消待处理的仓库转移",
|
||||
"cmd.repo.transfer_orgs.short": "列出可接收该仓库的组织",
|
||||
"cmd.repo.tree.short": "列出仓库文件和目录",
|
||||
"cmd.repo.units.short": "列出启用的仓库导航模块",
|
||||
"cmd.root.long": "用于管理 GitLink 上的仓库、议题、拉取请求、发布、CI 和工作流。",
|
||||
"cmd.root.short": "GitLink CLI - GitLink 命令行工具",
|
||||
"cmd.search.repos.short": "搜索仓库",
|
||||
|
|
@ -215,8 +220,13 @@
|
|||
"flag.repo.description": "仓库描述",
|
||||
"flag.repo.name": "仓库名称",
|
||||
"flag.repo.private": "设为私有仓库(true/false)",
|
||||
"flag.repo.target_owner": "目标用户或组织登录名",
|
||||
"flag.repo.transfer_cancel_dry_run": "预览取消请求,不修改仓库转移状态",
|
||||
"flag.repo.transfer_dry_run": "预览转移请求,不修改仓库所有者",
|
||||
"flag.repo.transfer_yes": "确认并执行仓库转移相关操作",
|
||||
"flag.repo.tree.path": "要列出的目录路径(默认:仓库根目录)",
|
||||
"flag.repo.tree.ref": "分支、标签或提交引用",
|
||||
"flag.repo.units": "逗号分隔的模块:code,issues,pulls,devops,versions,wiki,services,resources",
|
||||
"flag.search.keyword": "搜索关键词",
|
||||
"flag.sort_by": "排序字段",
|
||||
"flag.sort_direction": "排序方向:asc、desc",
|
||||
|
|
|
|||
|
|
@ -81,6 +81,41 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "units",
|
||||
Description: tr.T("cmd.repo.units.short"),
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", repoUnitsPath(ctx), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set-units",
|
||||
Description: tr.T("cmd.repo.set_units.short"),
|
||||
Flags: []common.Flag{
|
||||
{Name: "units", Short: "u", Usage: tr.T("flag.repo.units"), Required: true},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
units, err := parseRepoUnits(ctx.Arg("units"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", repoUnitsPath(ctx), map[string]interface{}{"unit_types": units})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "tree",
|
||||
Description: tr.T("cmd.repo.tree.short"),
|
||||
|
|
@ -239,6 +274,89 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer-orgs",
|
||||
Description: tr.T("cmd.repo.transfer_orgs.short"),
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", repoTransferPath(ctx, "organizations"), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer",
|
||||
Description: tr.T("cmd.repo.transfer.short"),
|
||||
Flags: []common.Flag{
|
||||
{Name: "target-owner", Usage: tr.T("flag.repo.target_owner"), Required: true},
|
||||
{Name: "dry-run", Usage: tr.T("flag.repo.transfer_dry_run"), Bool: true, Default: "false"},
|
||||
{Name: "yes", Usage: tr.T("flag.repo.transfer_yes"), Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
targetOwner, err := ctx.RequireArg("target-owner")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetOwner = strings.TrimSpace(targetOwner)
|
||||
if targetOwner == "" {
|
||||
return fmt.Errorf("required flag --target-owner is missing")
|
||||
}
|
||||
payload := map[string]interface{}{"owner_name": targetOwner}
|
||||
path := repoTransferPath(ctx, "")
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"method": "POST",
|
||||
"path": path,
|
||||
"payload": payload,
|
||||
})
|
||||
}
|
||||
if err := requireRepoTransferConfirmation(ctx, "transfer"); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", path, payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer-cancel",
|
||||
Description: tr.T("cmd.repo.transfer_cancel.short"),
|
||||
Flags: []common.Flag{
|
||||
{Name: "dry-run", Usage: tr.T("flag.repo.transfer_cancel_dry_run"), Bool: true, Default: "false"},
|
||||
{Name: "yes", Usage: tr.T("flag.repo.transfer_yes"), Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
path := repoTransferPath(ctx, "cancel")
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"method": "POST",
|
||||
"path": path,
|
||||
})
|
||||
}
|
||||
if err := requireRepoTransferConfirmation(ctx, "transfer-cancel"); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete",
|
||||
Description: tr.T("cmd.repo.delete.short"),
|
||||
|
|
@ -253,277 +371,24 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
|||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
// --- Repository Settings Shortcuts ---
|
||||
{
|
||||
Name: "detail",
|
||||
Description: "Show repository full details",
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/detail", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "simple",
|
||||
Description: "Show repository simple details",
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/simple", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "settings",
|
||||
Description: "Show repository settings",
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/edit", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "units",
|
||||
Description: "Show repository navigation units",
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/project_units", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "units-update",
|
||||
Description: "Update repository navigation units",
|
||||
Flags: []common.Flag{
|
||||
{Name: "units", Usage: "Comma-separated list of unit types (code,issues,pulls,wiki,devops,versions,services)", Required: true},
|
||||
{Name: "dry-run", Usage: "Preview the action without changing repository state", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
unitsRaw, err := ctx.RequireArg("units")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unitTypes := parseCommaSeparatedList(unitsRaw)
|
||||
if len(unitTypes) == 0 {
|
||||
return fmt.Errorf("--units must contain at least one unit type")
|
||||
}
|
||||
validUnits := map[string]bool{
|
||||
"code": true, "issues": true, "pulls": true, "wiki": true,
|
||||
"devops": true, "versions": true, "services": true,
|
||||
}
|
||||
for _, u := range unitTypes {
|
||||
if !validUnits[u] {
|
||||
return fmt.Errorf("invalid unit type %q: must be one of code, issues, pulls, wiki, devops, versions, services", u)
|
||||
}
|
||||
}
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"action": "units-update",
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
"unit_types": unitTypes,
|
||||
})
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"unit_types": unitTypes,
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", ctx.RepoPath()+"/project_units", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "topics",
|
||||
Description: "List project topics",
|
||||
Flags: []common.Flag{
|
||||
{Name: "keyword", Short: "k", Usage: "Filter topics by keyword"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
q := url.Values{}
|
||||
if keyword := ctx.Arg("keyword"); keyword != "" {
|
||||
q.Set("keyword", keyword)
|
||||
}
|
||||
env, err := ctx.CallAPIWithQuery("GET", "/v1/project_topics", q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "topic-add",
|
||||
Description: "Add a topic to the repository",
|
||||
Flags: []common.Flag{
|
||||
{Name: "name", Short: "n", Usage: "Topic name", Required: true},
|
||||
{Name: "dry-run", Usage: "Preview the action without changing repository state", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
name, err := ctx.RequireArg("name")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectID, err := resolveRepoProjectID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"action": "topic-add",
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
"project_id": projectID,
|
||||
"name": name,
|
||||
})
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"name": name,
|
||||
"project_id": projectID,
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", "/v1/project_topics", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "topic-delete",
|
||||
Description: "Delete a topic from the repository",
|
||||
Flags: []common.Flag{
|
||||
{Name: "id", Usage: "Topic ID", Required: true},
|
||||
{Name: "dry-run", Usage: "Preview the action without changing repository state", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
topicID, err := ctx.RequireArg("id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
projectID, err := resolveRepoProjectID(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"action": "topic-delete",
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
"project_id": projectID,
|
||||
"topic_id": topicID,
|
||||
})
|
||||
}
|
||||
q := url.Values{}
|
||||
q.Set("project_id", projectID)
|
||||
env, err := ctx.CallAPIWithQuery("DELETE", fmt.Sprintf("/v1/project_topics/%s", topicID), q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer-orgs",
|
||||
Description: "List organizations that can receive the repository transfer",
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := ctx.CallAPI("GET", ctx.RepoPath()+"/applied_transfer_projects/organizations", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer",
|
||||
Description: "Transfer repository to another owner",
|
||||
Flags: []common.Flag{
|
||||
{Name: "owner", Short: "o", Usage: "Target owner name (user or organization)", Required: true},
|
||||
{Name: "dry-run", Usage: "Preview the action without changing repository state", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
ownerName, err := ctx.RequireArg("owner")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"action": "transfer",
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
"target_owner": ownerName,
|
||||
})
|
||||
}
|
||||
body := map[string]interface{}{
|
||||
"owner_name": ownerName,
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", ctx.RepoPath()+"/applied_transfer_projects", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "transfer-cancel",
|
||||
Description: "Cancel pending repository transfer",
|
||||
Flags: []common.Flag{
|
||||
{Name: "dry-run", Usage: "Preview the action without changing repository state", Bool: true, Default: "false"},
|
||||
},
|
||||
Run: func(ctx *common.RuntimeContext) error {
|
||||
if err := ctx.ResolveOwnerRepo(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ctx.Arg("dry-run") == "true" {
|
||||
return ctx.OutputData(map[string]interface{}{
|
||||
"dry_run": true,
|
||||
"action": "transfer-cancel",
|
||||
"repository": fmt.Sprintf("%s/%s", ctx.Owner, ctx.Repo),
|
||||
})
|
||||
}
|
||||
env, err := ctx.CallAPI("POST", ctx.RepoPath()+"/applied_transfer_projects/cancel", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ctx.Output(env)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func repoTransferPath(ctx *common.RuntimeContext, action string) string {
|
||||
base := ctx.RepoPath() + "/applied_transfer_projects"
|
||||
if action == "" {
|
||||
return base
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", base, action)
|
||||
}
|
||||
|
||||
func requireRepoTransferConfirmation(ctx *common.RuntimeContext, shortcut string) error {
|
||||
if ctx.Arg("yes") == "true" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("refusing to run repo +%s without --yes; use --dry-run to preview the request first", shortcut)
|
||||
}
|
||||
|
||||
func shortcutTranslator(translators ...*i18n.Translator) *i18n.Translator {
|
||||
if len(translators) > 0 && translators[0] != nil {
|
||||
return translators[0]
|
||||
|
|
@ -769,14 +634,40 @@ func parseRepoPositiveInt(value, name string) (int, error) {
|
|||
return parsed, nil
|
||||
}
|
||||
|
||||
func parseCommaSeparatedList(s string) []string {
|
||||
parts := strings.Split(s, ",")
|
||||
result := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
p = strings.TrimSpace(p)
|
||||
if p != "" {
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
return result
|
||||
func repoUnitsPath(ctx *common.RuntimeContext) string {
|
||||
return ctx.RepoPath() + "/project_units"
|
||||
}
|
||||
|
||||
func parseRepoUnits(raw string) ([]string, error) {
|
||||
allowed := map[string]bool{
|
||||
"code": true,
|
||||
"issues": true,
|
||||
"pulls": true,
|
||||
"devops": true,
|
||||
"versions": true,
|
||||
"wiki": true,
|
||||
"services": true,
|
||||
"resources": true,
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
units := []string{}
|
||||
for _, part := range strings.Split(raw, ",") {
|
||||
unit := strings.ToLower(strings.TrimSpace(part))
|
||||
if unit == "" {
|
||||
continue
|
||||
}
|
||||
if !allowed[unit] {
|
||||
return nil, fmt.Errorf("invalid repository unit %q; allowed values: code,issues,pulls,devops,versions,wiki,services,resources", unit)
|
||||
}
|
||||
if seen[unit] {
|
||||
continue
|
||||
}
|
||||
seen[unit] = true
|
||||
units = append(units, unit)
|
||||
}
|
||||
if len(units) == 0 {
|
||||
return nil, fmt.Errorf("at least one repository unit is required")
|
||||
}
|
||||
return units, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -493,6 +493,129 @@ func TestRepoDelete(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferOrgs(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "GET", "/owner/repo/applied_transfer_projects/organizations.json")
|
||||
writeJSON(t, w, map[string]interface{}{
|
||||
"total_count": float64(1),
|
||||
"organizations": []interface{}{map[string]interface{}{"name": "target-org"}},
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
if err := runShortcut(t, server, "transfer-orgs", nil); err != nil {
|
||||
t.Fatalf("transfer-orgs failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransfer(t *testing.T) {
|
||||
var body map[string]interface{}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "POST", "/owner/repo/applied_transfer_projects.json")
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Fatalf("decode body: %v", err)
|
||||
}
|
||||
writeJSON(t, w, map[string]interface{}{"status": "common"})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer", map[string]string{
|
||||
"target-owner": " target-org ",
|
||||
"yes": "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("transfer failed: %v", err)
|
||||
}
|
||||
assertEqual(t, body["owner_name"], "target-org")
|
||||
}
|
||||
|
||||
func TestRepoTransferDryRunDoesNotCallAPI(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("dry-run should not call API, got %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer", map[string]string{
|
||||
"target-owner": "target-org",
|
||||
"dry-run": "true",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("transfer dry-run failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferRequiresExplicitYes(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("transfer without --yes should not call API, got %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer", map[string]string{"target-owner": "target-org"})
|
||||
if err == nil {
|
||||
t.Fatal("expected confirmation error for missing --yes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferFailsWithoutTargetOwner(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("no API call should be made")
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer", map[string]string{})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for missing target owner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferRejectsBlankTargetOwner(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("no API call should be made")
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer", map[string]string{"target-owner": " "})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for blank target owner")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferCancel(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assertRequest(t, r, "POST", "/owner/repo/applied_transfer_projects/cancel.json")
|
||||
writeJSON(t, w, map[string]interface{}{"status": "canceled"})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
if err := runShortcut(t, server, "transfer-cancel", map[string]string{"yes": "true"}); err != nil {
|
||||
t.Fatalf("transfer-cancel failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferCancelDryRunDoesNotCallAPI(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("dry-run should not call API, got %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer-cancel", map[string]string{"dry-run": "true"})
|
||||
if err != nil {
|
||||
t.Fatalf("transfer-cancel dry-run failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoTransferCancelRequiresExplicitYes(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatalf("transfer-cancel without --yes should not call API, got %s %s", r.Method, r.URL.Path)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
err := runShortcut(t, server, "transfer-cancel", nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected confirmation error for missing --yes")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoCreate(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
|
|
|
|||
|
|
@ -64,41 +64,58 @@ skills/
|
|||
├── README.md # 本文件
|
||||
├── gitlink-shared/ # 共享基础规则
|
||||
│ ├── SKILL.md # 认证、全局参数、安全规则、分支约定
|
||||
│ └── references/ # API 参考、错误处理
|
||||
│ └── references/
|
||||
│ ├── api-reference.md # API 详细参考、错误处理
|
||||
│ ├── raw-api-batch.md # 批量 Raw API 调用参考
|
||||
│ └── troubleshooting.md # 常见问题排查
|
||||
├── gitlink-repo/ # 仓库管理
|
||||
│ ├── SKILL.md # 仓库操作指南
|
||||
│ └── references/ # 仓库 API 参考
|
||||
│ └── references/ # 仓库命令参考文档
|
||||
├── gitlink-issue/ # Issue 管理
|
||||
│ ├── SKILL.md # Issue 操作指南
|
||||
│ └── references/ # Issue API 参考
|
||||
│ └── references/ # Issue 命令参考文档
|
||||
├── gitlink-pr/ # Pull Request
|
||||
│ ├── SKILL.md # PR 操作指南
|
||||
│ └── references/ # PR API 参考
|
||||
│ └── references/ # PR 命令参考文档
|
||||
├── gitlink-member/ # 仓库成员管理
|
||||
│ └── SKILL.md # 成员与邀请链接操作指南
|
||||
├── gitlink-branch/ # 分支管理
|
||||
│ ├── SKILL.md # 分支操作指南
|
||||
│ └── examples/
|
||||
│ └── branch-workflow.md # 分支工作流
|
||||
├── gitlink-release/ # 版本发布
|
||||
│ ├── SKILL.md # Release 操作指南
|
||||
│ └── references/ # Release API 参考
|
||||
│ └── references/ # Release 命令参考文档
|
||||
├── gitlink-release-auto/ # 自动化 Release 管理
|
||||
│ └── SKILL.md # 自动发版、版本号推荐、Release Notes 生成
|
||||
├── gitlink-search/ # 搜索功能
|
||||
│ ├── SKILL.md # 搜索操作指南
|
||||
│ └── references/ # 搜索参考
|
||||
│ └── references/ # 搜索命令参考文档
|
||||
├── gitlink-user/ # 用户管理
|
||||
│ └── SKILL.md # 用户操作指南
|
||||
│ ├── SKILL.md # 用户操作指南
|
||||
│ └── references/ # 用户命令参考文档
|
||||
├── gitlink-org/ # 组织管理
|
||||
│ ├── SKILL.md # 组织操作指南
|
||||
│ └── references/ # 组织参考
|
||||
│ └── references/ # 组织命令参考文档
|
||||
├── gitlink-ci/ # CI/CD
|
||||
│ └── SKILL.md # CI 操作指南
|
||||
├── gitlink-pipeline/ # 流水线工作流
|
||||
│ └── SKILL.md # Pipeline 操作指南
|
||||
├── gitlink-wiki/ # Wiki 页面管理
|
||||
│ └── SKILL.md # Wiki 操作指南
|
||||
├── gitlink-pm/ # 项目管理
|
||||
│ └── SKILL.md # PM 操作指南
|
||||
├── gitlink-workflow/ # AI 自动化工作流
|
||||
│ └── SKILL.md # 工作流模板(Issue 分类、PR Review、Release Notes)
|
||||
└── gitlink-maintainer-copilot/ # 维护者驾驶舱
|
||||
├── SKILL.md # 证据包、治理剧本、写入确认
|
||||
├── references/ # 证据包、剧本、治理 Issue 模板
|
||||
└── examples/ # 演示流程和样例报告
|
||||
├── gitlink-health/ # 项目健康度分析
|
||||
│ ├── SKILL.md # 健康度分析指南
|
||||
│ ├── data/
|
||||
│ │ ├── .gitignore # 忽略 *.db 文件
|
||||
│ │ └── .gitkeep # 占位文件
|
||||
│ ├── references/
|
||||
│ │ └── queries.md # SQL 查询参考
|
||||
│ └── asset/
|
||||
│ └── health_report_template.md # 报告模板
|
||||
└── gitlink-workflow/ # AI 自动化工作流
|
||||
└── SKILL.md # 工作流模板(Issue 分类、PR Review、Release Notes)
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -110,11 +127,12 @@ skills/
|
|||
| Skill | 说明 | 常用命令 |
|
||||
|-------|------|----------|
|
||||
| **gitlink-shared** | 认证、全局参数、API 参考、安全规则、分支约定 | `auth login`, `auth status` |
|
||||
| **gitlink-repo** | 仓库管理 | `repo +list`, `repo +create`, `repo +info`, `repo +fork` |
|
||||
| **gitlink-issue** | Issue 管理 | `issue +create`, `issue +list`, `issue +view`, `issue +close`, `issue +batch-close` |
|
||||
| **gitlink-pr** | Pull Request | `pr +list`, `pr +create`, `pr +view`, `pr +merge`, `pr +review` |
|
||||
| **gitlink-repo** | 仓库管理与洞察 | `repo +list`, `repo +info`, `repo +languages`, `repo +contributors`, `repo +code-stats`, `repo +follow`, `repo +like`, `repo +units`, `repo +set-units` |
|
||||
| **gitlink-issue** | Issue 管理 | `issue +create`, `issue +list`, `issue +view`, `issue +close`, `issue +batch-close`, `issue +batch-update`, `issue +batch-delete` |
|
||||
| **gitlink-pr** | Pull Request | `pr +list`, `pr +create`, `pr +view`, `pr +merge`, `pr +versions`, `pr +version-diff`, `pr +reviews`, `pr +review` |
|
||||
| **gitlink-member** | 仓库成员管理 | `member +list`, `member +add`, `member +batch-add`, `member +role`, `member +invite-link` |
|
||||
| **gitlink-branch** | 分支管理 | `branch +list`, `branch +create`, `branch +delete`, `branch +protect` |
|
||||
| **gitlink-release** | 版本发布 | `release +list`, `release +create`, `release +view` |
|
||||
| **gitlink-release** | 版本发布 | `release +list`, `release +create`, `release +edit`, `release +update`, `release +view` |
|
||||
|
||||
### 辅助 Skills
|
||||
|
||||
|
|
@ -124,9 +142,11 @@ skills/
|
|||
| **gitlink-user** | 用户管理 | `user +me`, `user +info` |
|
||||
| **gitlink-org** | 组织管理 | `org +list`, `org +info`, `org +members` |
|
||||
| **gitlink-ci** | CI/CD | `ci +builds`, `ci +logs` |
|
||||
| **gitlink-pipeline** | 流水线工作流 | `pipeline +runs`, `pipeline +run`, `pipeline +logs` |
|
||||
| **gitlink-wiki** | Wiki 页面管理 | `wiki +list`, `wiki +view`, `wiki +create`, `wiki +update`, `wiki +delete` |
|
||||
| **gitlink-pm** | 项目管理 | 通过 Raw API 访问 |
|
||||
| **gitlink-workflow** | AI 工作流 | Issue 分类、PR Review、Release Notes |
|
||||
| **gitlink-maintainer-copilot** | 维护者驾驶舱 | 证据包、治理剧本、确认后创建治理 Issue |
|
||||
| **gitlink-health** | 开源项目健康度 | 详情见SKILL.md |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -152,13 +172,13 @@ gitlink-cli repo +info --owner wbtiger --repo gitlink-cli
|
|||
gitlink-cli issue +create -t "Bug: 登录失败" -b "复现步骤..."
|
||||
|
||||
# 查看 Issue
|
||||
gitlink-cli issue +view -n 123
|
||||
gitlink-cli issue +view -i 123
|
||||
|
||||
# 添加评论
|
||||
gitlink-cli issue +comment -i 123 -b "已修复"
|
||||
|
||||
# 关闭 Issue
|
||||
gitlink-cli issue +close -n 123
|
||||
gitlink-cli issue +close -i 123
|
||||
|
||||
# 预览批量关闭 Issue
|
||||
gitlink-cli issue +batch-close --numbers 123,124 --dry-run
|
||||
|
|
@ -200,17 +220,6 @@ gitlink-cli org +info -i Gitlink
|
|||
|
||||
详见: [gitlink-search/SKILL.md](gitlink-search/SKILL.md)
|
||||
|
||||
### 场景 5:生成维护者驾驶舱
|
||||
|
||||
```bash
|
||||
# Agent 会先只读采集证据,再输出治理计划
|
||||
gitlink-cli repo +info --owner Gitlink --repo gitlink-cli --format json
|
||||
gitlink-cli issue +list --owner Gitlink --repo gitlink-cli --state open --format json
|
||||
gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --state open --format json
|
||||
```
|
||||
|
||||
详见: [gitlink-maintainer-copilot/SKILL.md](gitlink-maintainer-copilot/SKILL.md)
|
||||
|
||||
---
|
||||
|
||||
## 📚 文档导航
|
||||
|
|
@ -227,23 +236,22 @@ gitlink-cli pr +list --owner Gitlink --repo gitlink-cli --state open --format js
|
|||
**仓库操作**:
|
||||
- [gitlink-repo/SKILL.md](gitlink-repo/SKILL.md) - 仓库命令
|
||||
- [gitlink-branch/SKILL.md](gitlink-branch/SKILL.md) - 分支命令
|
||||
- [gitlink-repo/SKILL.md](gitlink-repo/SKILL.md) - 完整工作流
|
||||
|
||||
**Issue 和 PR**:
|
||||
- [gitlink-issue/SKILL.md](gitlink-issue/SKILL.md) - Issue 命令
|
||||
- [gitlink-pr/SKILL.md](gitlink-pr/SKILL.md) - PR 命令
|
||||
- [gitlink-issue/SKILL.md](gitlink-issue/SKILL.md) - Issue 工作流
|
||||
|
||||
**发布和搜索**:
|
||||
- [gitlink-release/SKILL.md](gitlink-release/SKILL.md) - Release 命令
|
||||
- [gitlink-pipeline/SKILL.md](gitlink-pipeline/SKILL.md) - Pipeline 命令
|
||||
- [gitlink-search/SKILL.md](gitlink-search/SKILL.md) - 搜索命令
|
||||
|
||||
**组织和用户**:
|
||||
- [gitlink-org/SKILL.md](gitlink-org/SKILL.md) - 组织命令
|
||||
- [gitlink-user/SKILL.md](gitlink-user/SKILL.md) - 用户命令
|
||||
|
||||
**维护者驾驶舱**:
|
||||
- [gitlink-maintainer-copilot/SKILL.md](gitlink-maintainer-copilot/SKILL.md) - 维护者诊断
|
||||
- [gitlink-maintainer-copilot/examples/maintainer-copilot-workflow.md](gitlink-maintainer-copilot/examples/maintainer-copilot-workflow.md) - 演示流程
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
|
@ -304,7 +312,6 @@ AI 代理可以:
|
|||
- ✅ 自动分类 Issue
|
||||
- ✅ 自动生成 Release Notes
|
||||
- ✅ 自动执行代码审查
|
||||
- ✅ 自动生成维护者驾驶舱和治理 Issue 草稿
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -316,7 +323,7 @@ AI 代理可以:
|
|||
- 所有边界情况处理正确
|
||||
- 完整的文档和示例
|
||||
|
||||
可通过 `cd npm && npm test` 验证 Skill 结构和安装脚本。
|
||||
详见: [../doc/design.md](../doc/design.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -324,6 +331,7 @@ AI 代理可以:
|
|||
|
||||
- [主项目 README](../README.md) - gitlink-cli 项目说明
|
||||
- [设计文档](../doc/design.md) - 架构设计和开发计划
|
||||
- [API 参考文档](../doc/gitlink_api_reference.md) - GitLink API 参考文档
|
||||
- [gitlink-bisync](https://www.gitlink.org.cn/wbtiger/gitlink-bisync) - 代码双向同步系统
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ metadata:
|
|||
| `repo +list` | 仓库列表 | 否(公开项目) |
|
||||
| `repo +info` | 仓库详情 | 否(公开项目) |
|
||||
| `repo +readme` | README 内容 | 否(公开项目) |
|
||||
| `repo +file` | 任意仓库文件内容 | 否(公开项目) |
|
||||
| `repo +tree` | 仓库文件树 | 否(公开项目) |
|
||||
| `repo +languages` | 仓库语言统计 | 否(公开项目) |
|
||||
| `repo +contributors` | 仓库贡献者列表 | 否(公开项目) |
|
||||
|
|
@ -37,10 +36,10 @@ metadata:
|
|||
| `repo +unlike` | 取消点赞仓库 | 是 |
|
||||
| `repo +create` | 创建仓库 | 是 |
|
||||
| `repo +fork` | Fork 仓库 | 是 |
|
||||
| `repo +delete` | 删除仓库 | 是 |
|
||||
| `repo +transfer-orgs` | 列出可接收仓库转移的组织 | 是 |
|
||||
| `repo +transfer` | 发起仓库转移 | 是 |
|
||||
| `repo +transfer-orgs` | 查看可接收仓库转移的组织 | 是 |
|
||||
| `repo +transfer` | 发起仓库转移请求 | 是 |
|
||||
| `repo +transfer-cancel` | 取消待处理的仓库转移 | 是 |
|
||||
| `repo +delete` | 删除仓库 | 是 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
|
|
@ -58,8 +57,6 @@ gitlink-cli repo +list --user zhangsan
|
|||
# 查看文件树、语言占比和贡献者
|
||||
gitlink-cli repo +tree --owner Gitlink --repo forgeplus --ref master
|
||||
gitlink-cli repo +tree --owner Gitlink --repo forgeplus --path src --ref main
|
||||
gitlink-cli repo +file --owner Gitlink --repo forgeplus --path go.mod --ref master
|
||||
gitlink-cli repo +file --owner Gitlink --repo forgeplus --path .gitignore --content-only
|
||||
gitlink-cli repo +languages --owner Gitlink --repo forgeplus
|
||||
gitlink-cli repo +contributors --owner Gitlink --repo forgeplus
|
||||
|
||||
|
|
@ -95,8 +92,6 @@ gitlink-cli repo +transfer --owner Gitlink --repo forgeplus --target-owner my-or
|
|||
|
||||
# 取消待处理的仓库转移
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --dry-run
|
||||
|
||||
# 确认后取消待处理的仓库转移
|
||||
gitlink-cli repo +transfer-cancel --owner Gitlink --repo forgeplus --yes
|
||||
|
||||
# 删除仓库(⚠️ 危险操作)
|
||||
|
|
@ -125,11 +120,8 @@ gitlink-cli api GET /:owner/:repo/commits --query 'page=1&limit=20'
|
|||
# 获取标签列表
|
||||
gitlink-cli api GET /:owner/:repo/tags
|
||||
|
||||
# 获取文件内容(Shortcut 优先)
|
||||
gitlink-cli repo +file --owner Gitlink --repo forgeplus --path README.md --ref master
|
||||
|
||||
# Raw API 仍可用于未封装场景
|
||||
gitlink-cli api GET /:owner/:repo/sub_entries --query 'filepath=README.md&ref=master'
|
||||
# 获取文件内容
|
||||
gitlink-cli api GET /:owner/:repo/raw/main/README.md
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
|
@ -138,4 +130,3 @@ gitlink-cli api GET /:owner/:repo/sub_entries --query 'filepath=README.md&ref=ma
|
|||
- `repo +transfer` 会改变仓库所有者,只有显式传入 `--yes` 才会真正发起;执行前先使用 `repo +transfer-orgs` 确认可转移目标,并用 `--dry-run` 预览请求
|
||||
- `repo +transfer-cancel` 只用于取消已发起且未处理的转移申请,只有显式传入 `--yes` 才会真正执行,建议先用 `--dry-run` 预览
|
||||
- 创建仓库默认为公开,使用 `--private true` 创建私有仓库
|
||||
- `repo +file` 只接受文件路径;如果目标是目录,请改用 `repo +tree`
|
||||
|
|
|
|||
Loading…
Reference in New Issue