Documentation ¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var Attach = app.Register(&app.Command[app.Empty, core.RepositoryStorage]{ Use: "attach {remote} {repo}", Desc: "Attach current path to a repo", Init: initData[app.Empty], PrepareNoFlag: func(cmd *cobra.Command) { cmd.Args = cobra.ExactArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompGroup) }, Run: func(ctx *app.Context[app.Empty, core.RepositoryStorage]) error { dir, err := git.EnsureCurrent() if err != nil { return err } remote, err := core.GetRemote(ctx.Arg(0)) if err != nil { return err } apiRepo, err := api.SearchRepo(remote, ctx.Arg(1)) if err != nil { return err } repo, err := core.AttachRepository(remote, apiRepo.Name, dir) if err != nil { return err } err = ctx.Data.Add(repo) if err != nil { return err } if term.Confirm("overwrite git url") { url, err := remote.GetCloneURL(repo) if err != nil { return err } err = git.SetRemoteURL("origin", url, git.Default) if err != nil { return err } } if term.Confirm("overwrite user and email") { user, email := remote.GetUserEmail(repo) err = git.Config("user.name", user, git.Default) if err != nil { return err } err = git.Config("user.email", email, git.Default) if err != nil { return err } } return nil }, })
View Source
var Clean = app.Register(&app.Command[CleanFlags, CleanData]{ Use: "clean [--days days] [-e] [-r remote]...", Desc: "Clean repos", Prepare: func(cmd *cobra.Command, flags *CleanFlags) { cmd.Flags().IntVarP(&flags.Days, "days", "d", 30, "days threshold") cmd.Flags().BoolVarP(&flags.Never, "never", "n", false, "only delete repo that never access") cmd.Flags().BoolVarP(&flags.Edit, "edit", "e", false, "edit items") cmd.Flags().StringSliceVarP(&flags.Remote, "remote", "r", nil, "scan remotes") cmd.RegisterFlagCompletionFunc("remote", app.Comp(app.CompRemote)) }, Init: func(ctx *app.Context[CleanFlags, CleanData]) error { if ctx.Flags.Days <= 1 { return fmt.Errorf("invalid flag days %d: should be bigger than 1", ctx.Flags.Days) } remotes := ctx.Flags.Remote var err error if len(remotes) == 0 { remotes, err = core.ListRemoteNames() if err != nil { return errors.Trace(err, "list remote") } } store, err := core.NewRepositoryStorage() if err != nil { return errors.Trace(err, "init repo storage") } var items []*CleanItem for _, remoteName := range remotes { remote, err := core.GetRemote(remoteName) if err != nil { return errors.Trace(err, "get remote %q", remoteName) } repos := store.List(remoteName) for _, repo := range repos { var deltaDays int = -1 if repo.LastAccess > 0 { if ctx.Flags.Never { continue } deltaSeconds := config.Now() - repo.LastAccess deltaDaysFlt := float64(deltaSeconds) / float64(config.DaySeconds) deltaDays = int(deltaDaysFlt) if deltaDays < ctx.Flags.Days { continue } } item := &CleanItem{ Repo: repo, Remote: remote, Days: deltaDays, } if deltaDays < 0 { item.Never = true } items = append(items, item) } } ctx.OnClose(func() error { return store.Close() }) ctx.Data = &CleanData{ Items: items, Store: store, } return nil }, Run: func(ctx *app.Context[CleanFlags, CleanData]) error { items := ctx.Data.Items if len(items) == 0 { term.Println("nothing to do") return nil } var err error if ctx.Flags.Edit { items, err = term.EditItems(config.Get().Editor, items, func(item *CleanItem) string { return item.Repo.FullName() }) if err != nil { return err } } showCleanItems(items) term.ConfirmExit("continue") for _, item := range items { err := ctx.Data.Store.DeleteAll(item.Repo) if err != nil { return err } } return nil }, })
View Source
var Delete = app.Register(&app.Command[app.Empty, core.RepositoryStorage]{ Use: "repo {remote} {repo}", Desc: "delete a repo", Action: "Delete", Init: initData[app.Empty], PrepareNoFlag: func(cmd *cobra.Command) { cmd.Args = cobra.ExactArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) }, Run: func(ctx *app.Context[app.Empty, core.RepositoryStorage]) error { remote, err := core.GetRemote(ctx.Arg(0)) if err != nil { return err } repo, err := ctx.Data.GetLocal(remote, ctx.Arg(1)) if err != nil { return err } if !term.Confirm("delete %s", repo.Path) { return nil } return ctx.Data.DeleteAll(repo) }, })
View Source
var Describe = app.Register(&app.Command[app.Empty, core.RepositoryStorage]{ Use: "repo [remote] [repo]", Desc: "Describe repo", Action: "Describe", Init: initData[app.Empty], PrepareNoFlag: func(cmd *cobra.Command) { cmd.Args = cobra.MaximumNArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) }, Run: func(ctx *app.Context[app.Empty, core.RepositoryStorage]) error { ctx.Data.ReadOnly() var repo *core.Repository var err error var remote *core.Remote switch ctx.ArgLen() { case 0: repo, err = ctx.Data.GetCurrent() if err != nil { return err } default: remote, err = core.GetRemote(ctx.Arg(0)) if err != nil { return err } repo, err = ctx.Data.GetLocal(remote, ctx.Arg(1)) if err != nil { return err } } timeStr := time.Unix(repo.LastAccess, 0).Format("2006-01-02 15:04:05") term.Printf("Name: %s", term.Style(repo.Name, "green")) term.Printf("Group: %s", term.Style(repo.Group(), "green")) term.Printf("Base: %s", term.Style(repo.Base(), "green")) term.Printf("Remote: %s", term.Style(repo.Remote, "green")) term.Println() term.Println("Access:") term.Printf("* Count: %s", term.Style(repo.Access, "green")) term.Printf("* Last: %s", term.Style(timeStr, "green")) term.Printf("* Score: %s", term.Style(repo.Score(), "green")) return nil }, })
View Source
var Detach = app.Register(&app.Command[app.Empty, core.RepositoryStorage]{ Use: "detach {remote} {repo}", Desc: "Detach current path", Init: initData[app.Empty], Run: func(ctx *app.Context[app.Empty, core.RepositoryStorage]) error { dir, err := git.EnsureCurrent() if err != nil { return err } repo, err := ctx.Data.GetByPath(dir) if err != nil { return err } term.ConfirmExit("Do you want to detach this path from %s", repo.FullName()) ctx.Data.Delete(repo) return nil }, })
View Source
var Download = app.Register(&app.Command[DownloadFlags, core.RepositoryStorage]{ Use: "download {remote} {repo} [-t tag] [-s] [-p pattern] [-o out] [--no-bar]", Desc: "Download files from release", Init: initData[DownloadFlags], Prepare: func(cmd *cobra.Command, flags *DownloadFlags) { cmd.Args = cobra.MaximumNArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) cmd.Flags().BoolVarP(&flags.Search, "search", "s", false, "search repo") cmd.Flags().StringVarP(&flags.Pattern, "pattern", "p", "", "file pattern") cmd.Flags().StringVarP(&flags.Output, "output", "o", "", "output dir") cmd.Flags().StringVarP(&flags.Tag, "tag", "t", "", "tag name") cmd.RegisterFlagCompletionFunc("tag", app.Comp(app.CompGitTag)) cmd.Flags().BoolVarP(&flags.NoBar, "no-bar", "n", false, "donot show download bar") cmd.Flags().StringVarP(&flags.LogPath, "log-path", "", "", "error log path") cmd.Flags().BoolVarP(&flags.Latest, "latest", "l", false, "use latest release") }, Run: func(ctx *app.Context[DownloadFlags, core.RepositoryStorage]) error { var repo *core.Repository var remote *core.Remote var err error switch ctx.ArgLen() { case 0: repo, err = ctx.Data.GetCurrent() if err != nil { return err } remote, err = core.GetRemote(repo.Remote) if err != nil { return err } default: remote, err = core.GetRemote(ctx.Arg(0)) if ctx.Flags.Search { apiRepo, err := api.SearchRepo(remote, ctx.Arg(1)) if err != nil { return err } repo, err = core.WorkspaceRepository(remote, apiRepo.Name) if err != nil { return err } } else { repo, err = ctx.Data.GetLocal(remote, ctx.Arg(1)) } } if err != nil { return err } releases, err := downloadGetRelease(ctx, remote, repo) if err != nil { return err } if len(releases) == 0 { return errors.New("no release found") } var release *api.Release if len(releases) == 1 { release = releases[0] } else { release, err = downloadSelectRelease(releases) if err != nil { return err } } files, err := downloadSelectFiles(release, ctx.Flags.Pattern) if err != nil { return err } fileWord := english.Plural(len(files), "release file", "") term.ConfirmExit("Do you want to download %s", fileWord) tasks := make([]*worker.Task[worker.BytesTask], len(files)) err = api.Exec("open release download stream", remote, func(p api.Provider) error { for i, file := range files { reader, err := p.DownloadReleaseFile(repo, file) if err != nil { return errors.Trace(err, "open file %q", file.Name) } tasks[i] = worker.DownloadTask(file.Name, reader, uint64(file.Size)) } return nil }) if err != nil { return err } var tracker worker.Tracker[worker.BytesTask] if ctx.Flags.NoBar { tracker = worker.NewBytesTracker(tasks) } else { tracker = worker.NewBytesBarTracker(tasks) } w := worker.Bytes{ Tracker: tracker, Tasks: tasks, LogPath: ctx.Flags.LogPath, } term.PrintOperation("begin to download %s", fileWord) return w.Download(ctx.Flags.Output) }, })
View Source
var Home = app.Register(&app.Command[HomeFlags, core.RepositoryStorage]{ Use: "home {remote} {repo}", Desc: "Enter or clone a repo", Init: initData[HomeFlags], Prepare: func(cmd *cobra.Command, flags *HomeFlags) { cmd.Args = cobra.RangeArgs(1, 2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) cmd.Flags().BoolVarP(&flags.Search, "search", "s", false, "search from remote") }, Run: func(ctx *app.Context[HomeFlags, core.RepositoryStorage]) error { var repo *core.Repository var err error remote, err := core.GetRemote(ctx.Arg(0)) if err != nil { return err } if ctx.Flags.Search { repo, err = homeSearchRepo(ctx, remote) } else { repo, err = ctx.Data.GetLocal(remote, ctx.Arg(1)) } if err != nil { return err } err = homeEnsureRepo(ctx, remote, repo) if err != nil { return err } repo.MarkAccess() fmt.Println(repo.Path) return nil }, })
View Source
var Import = app.Register(&app.Command[ImportFlags, core.RepositoryStorage]{ Use: "import [-i ignore-repo]... {remote} {group}", Desc: "Import repos to workspace", Init: initData[ImportFlags], Prepare: func(cmd *cobra.Command, flags *ImportFlags) { cmd.Flags().StringSliceVarP(&flags.Ignore, "ignore", "i", nil, "ignore repo pattern") cmd.Flags().StringVarP(&flags.LogPath, "log-path", "", "", "log path") cmd.Args = cobra.ExactArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompGroup) }, Run: func(ctx *app.Context[ImportFlags, core.RepositoryStorage]) error { group := ctx.Arg(1) group = strings.Trim(group, "/") remote, err := core.GetRemote(ctx.Arg(0)) if err != nil { return err } var apiRepos []*api.Repository err = api.Exec("list repos", remote, func(p api.Provider) error { apiRepos, err = p.ListRepositories(group) return err }) if err != nil { return err } if len(ctx.Flags.Ignore) > 0 { apiRepos, err = importFilterIgnore(apiRepos, ctx.Flags.Ignore) if err != nil { return err } } if len(apiRepos) == 0 { term.Println("no repo to import") return nil } apiRepos, err = term.EditItems(config.Get().Editor, apiRepos, func(repo *api.Repository) string { return repo.Name }) if err != nil { return err } tasks, err := importGetTasks(ctx, remote, apiRepos) if err != nil { return err } if len(tasks) == 0 { if len(apiRepos) <= 1 { term.Println("nothing to do, repo is already exists") } else { term.Println("nothing to do, all repos are already exists") } return nil } repoWord := english.Plural(len(tasks), "repo", "repos") term.ConfirmExit("Do you want to clone %s", repoWord) w := worker.Worker[CloneTask]{ Name: "import", Tasks: tasks, Tracker: worker.NewJobTracker[CloneTask]("cloning"), LogPath: ctx.Flags.LogPath, } return w.Run(func(task *worker.Task[CloneTask]) error { return task.Value.Execute() }) }, })
View Source
var Jump = app.Register(&app.Command[JumpFlags, JumpData]{ Use: "jump [-r remote] [keyword]", Desc: "Auto jump to a repo", Prepare: func(cmd *cobra.Command, flags *JumpFlags) { cmd.Args = cobra.MaximumNArgs(1) cmd.ValidArgsFunction = app.Comp(compJump) cmd.Flags().StringVarP(&flags.Remote, "remote", "r", "", "remote name") cmd.RegisterFlagCompletionFunc("remote", app.Comp(app.CompRemote)) }, Init: func(ctx *app.Context[JumpFlags, JumpData]) error { repoStore, err := core.NewRepositoryStorage() if err != nil { return err } ctx.OnClose(func() error { err = repoStore.Close() return errors.Trace(err, "close repo storage") }) jumpStore, err := core.NewJumpKeywordStorage() if err != nil { return err } ctx.OnClose(func() error { err = jumpStore.Close() return errors.Trace(err, "close jump keyword storage") }) var remotes []string if ctx.Flags.Remote != "" { remotes = []string{ctx.Flags.Remote} } else { remotes, err = core.ListRemoteNames() if err != nil { return errors.Trace(err, "list remotes") } } ctx.Data = &JumpData{ RepoStore: repoStore, KeywordStore: jumpStore, Remotes: remotes, } return nil }, Run: func(ctx *app.Context[JumpFlags, JumpData]) error { var repos []*core.Repository for _, remote := range ctx.Data.Remotes { remoteRepos := ctx.Data.RepoStore.List(remote) repos = append(repos, remoteRepos...) } if len(repos) == 0 { return errors.New("no repo") } core.SortRepositories(repos) repo := jumpSelectRepo(repos, ctx) if repo == nil { return errors.New("cannot find match repo") } repo.MarkAccess() fmt.Println(repo.Path) return nil }, })
View Source
var List = app.Register(&app.Command[ListFlags, core.RepositoryStorage]{ Use: "repo {remote} {repo}", Desc: "list remotes or repos", Action: "List", Init: initData[ListFlags], Prepare: func(cmd *cobra.Command, flags *ListFlags) { cmd.Args = cobra.MaximumNArgs(2) cmd.Flags().BoolVarP(&flags.Group, "group", "", false, "list group") cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) }, Run: func(ctx *app.Context[ListFlags, core.RepositoryStorage]) error { ctx.Data.ReadOnly() remoteName := ctx.Arg(0) if remoteName == "" { remoteNames, err := core.ListRemoteNames() if err != nil { return err } for _, name := range remoteNames { fmt.Println(name) } return nil } repos := ctx.Data.List(remoteName) if ctx.Flags.Group { groups := core.ConvertToGroups(repos) for _, group := range groups { fmt.Println(group) } return nil } for _, repo := range repos { fmt.Println(repo.Name) } return nil }, })
View Source
var Merge = app.Register(&app.Command[MergeFlags, core.RepositoryStorage]{ Use: "merge [-u] [-s source-branch] [-t target-branch]", Desc: "Open or create PullRequest or MergeRequest", Init: initData[MergeFlags], Prepare: func(cmd *cobra.Command, flags *MergeFlags) { cmd.Flags().BoolVarP(&flags.Upstream, "upstream", "u", false, "merge to upstream repo") cmd.Flags().StringVarP(&flags.SourceBranch, "source", "s", "", "source branch") cmd.Flags().StringVarP(&flags.TargetBranch, "target", "t", "", "target branch") cmd.RegisterFlagCompletionFunc("source", app.Comp(app.CompGitLocalBranch(false))) cmd.RegisterFlagCompletionFunc("target", app.Comp(app.CompGitLocalBranch(false))) cmd.Args = cobra.ExactArgs(0) }, Run: func(ctx *app.Context[MergeFlags, core.RepositoryStorage]) error { ctx.Data.ReadOnly() err := git.EnsureNoUncommitted(git.Default) if err != nil { return err } var repo *core.Repository repo, err = ctx.Data.GetCurrent() if err != nil { return err } remote, err := core.GetRemote(repo.Remote) if err != nil { return err } apiRepo, err := api.GetRepo(remote, repo) if err != nil { return err } opts, err := mergeBuildOptions(ctx, apiRepo) if err != nil { return err } var url string err = api.Exec("get merge", remote, func(p api.Provider) error { url, err = p.GetMerge(repo, *opts) return err }) if err != nil { return err } if url != "" { return term.Open(url) } term.ConfirmExit("cannot find merge, do you want to create one") title, body, err := mergeEdit() if err != nil { return err } opts.Title = title opts.Body = body term.Println() term.Println("About to create merge:") mergeShowInfo(repo, opts) term.ConfirmExit("continue") err = api.Exec("create merge", remote, func(p api.Provider) error { url, err = p.CreateMerge(repo, *opts) return err }) if err != nil { return err } return term.Open(url) }, })
View Source
var Open = app.Register(&app.Command[app.Empty, core.RepositoryStorage]{ Use: "repo {remote} {repo}", Desc: "Open repo in default browser", Action: "Open", Init: initData[app.Empty], PrepareNoFlag: func(cmd *cobra.Command) { cmd.Args = cobra.MaximumNArgs(2) cmd.ValidArgsFunction = app.Comp(app.CompRemote, app.CompRepo) }, Run: func(ctx *app.Context[app.Empty, core.RepositoryStorage]) error { ctx.Data.ReadOnly() var apiRepo *api.Repository var err error switch ctx.ArgLen() { case 0: var repo *core.Repository repo, err = ctx.Data.GetCurrent() if err != nil { return err } remote, err := core.GetRemote(repo.Remote) if err != nil { return err } apiRepo, err = api.GetRepo(remote, repo) if err != nil { return err } default: remote, err := core.GetRemote(ctx.Arg(0)) if err != nil { return err } apiRepo, err = api.SearchRepo(remote, ctx.Arg(1)) if err != nil { return err } } return term.Open(apiRepo.WebURL) }, })
View Source
var Sync = app.Register(&app.Command[SyncFlags, SyncData]{ Use: "repo", Desc: "Sync workspace repo", Action: "Sync", Init: func(ctx *app.Context[SyncFlags, SyncData]) error { store, err := core.NewRepositoryStorage() if err != nil { return errors.Trace(err, "init repo storage") } remoteNames, err := core.ListRemoteNames() if err != nil { return errors.Trace(err, "list remotes") } remotes := make([]*core.Remote, len(remoteNames)) remoteMap := make(map[string]*core.Remote, len(remoteNames)) for i, name := range remoteNames { remote, err := core.GetRemote(name) if err != nil { return errors.Trace(err, "get remote %q", name) } remotes[i] = remote remoteMap[name] = remote } var repos []*core.Repository for _, remote := range remotes { remoteRepos := store.List(remote.Name) repos = append(repos, remoteRepos...) } var wpRepos []*core.Repository for _, remote := range remotes { rootDir := filepath.Join(config.Get().Workspace, remote.Name) exists, err := osutil.DirExists(rootDir) if err != nil { return errors.Trace(err, "check remote dir exists") } if !exists { continue } remoteWpRepos, err := core.DiscoverLocalRepositories(rootDir) if err != nil { return errors.Trace(err, "discover remote repos") } for _, wpRepo := range remoteWpRepos { wpRepo.Remote = remote.Name } wpRepos = append(wpRepos, remoteWpRepos...) } ctx.Data = &SyncData{ Store: store, Remotes: remotes, remoteMap: remoteMap, Repos: repos, WorkspaceRepos: wpRepos, } ctx.OnClose(func() error { return store.Close() }) return nil }, Run: func(ctx *app.Context[SyncFlags, SyncData]) error { err := syncStorage(ctx) if err != nil { return err } return syncWorkspace(ctx) }, })
Functions ¶
This section is empty.
Types ¶
type CleanData ¶ added in v0.4.0
type CleanData struct { Items []*CleanItem Store *core.RepositoryStorage }
type CleanFlags ¶ added in v0.4.0
type DownloadFlags ¶ added in v0.3.0
type ImportFlags ¶ added in v0.2.0
type JumpData ¶ added in v0.3.0
type JumpData struct { RepoStore *core.RepositoryStorage KeywordStore *core.JumpKeywordStorage Remotes []string }
type MergeFlags ¶ added in v0.1.0
type SyncData ¶ added in v0.2.0
type SyncData struct { Store *core.RepositoryStorage Remotes []*core.Remote Repos []*core.Repository WorkspaceRepos []*core.Repository // contains filtered or unexported fields }
Click to show internal directories.
Click to hide internal directories.