diff --git a/cmd/pulls/checkout.go b/cmd/pulls/checkout.go index d93d429..b072f00 100644 --- a/cmd/pulls/checkout.go +++ b/cmd/pulls/checkout.go @@ -5,16 +5,13 @@ package pulls import ( - "fmt" "log" "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/config" - local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/utils" - "code.gitea.io/sdk/gitea" - "github.com/go-git/go-git/v5" "github.com/urfave/cli/v2" ) @@ -38,78 +35,5 @@ func runPullsCheckout(ctx *cli.Context) error { return err } - localRepo, err := local_git.RepoForWorkdir() - if err != nil { - return err - } - - localBranchName, remoteBranchName, newRemoteName, remoteURL, err := - gitConfigForPR(localRepo, login, owner, repo, idx) - if err != nil { - return err - } - - // verify related remote is in local repo, otherwise add it - localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName) - if err != nil { - return err - } - localRemoteName := localRemote.Config().Name - - // get auth & fetch remote - fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n", - idx, remoteURL, remoteBranchName, localRemoteName) - url, err := local_git.ParseURL(remoteURL) - if err != nil { - return err - } - auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) - if err != nil { - return err - } - err = localRemote.Fetch(&git.FetchOptions{Auth: auth}) - if err == git.NoErrAlreadyUpToDate { - fmt.Println(err) - } else if err != nil { - return err - } - - // checkout local branch - fmt.Printf("Creating branch '%s'\n", localBranchName) - err = localRepo.TeaCreateBranch(localBranchName, remoteBranchName, localRemoteName) - if err == git.ErrBranchExists { - fmt.Println("There may be changes since you last checked out, run `git pull` to get them.") - } else if err != nil { - return err - } - - return localRepo.TeaCheckout(localBranchName) -} - -func gitConfigForPR(repo *local_git.TeaRepo, login *config.Login, owner, repoName string, idx int64) (localBranch, remoteBranch, remoteName, remoteURL string, err error) { - // fetch PR source-repo & -branch from gitea - pr, _, err := login.Client().GetPullRequest(owner, repoName, idx) - if err != nil { - return - } - - // test if we can pull via SSH, and configure git remote accordingly - remoteURL = pr.Head.Repository.CloneURL - keys, _, err := login.Client().ListMyPublicKeys(gitea.ListPublicKeysOptions{}) - if err != nil { - return - } - if len(keys) != 0 { - remoteURL = pr.Head.Repository.SSHURL - } - - // try to find a matching existing branch, otherwise return branch in pulls/ namespace - localBranch = fmt.Sprintf("pulls/%v-%v", idx, pr.Head.Ref) - if b, _ := repo.TeaFindBranchBySha(pr.Head.Sha, remoteURL); b != nil { - localBranch = b.Name - } - - remoteBranch = pr.Head.Ref - remoteName = fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName) - return + return task.PullCheckout(login, owner, repo, idx) } diff --git a/cmd/pulls/clean.go b/cmd/pulls/clean.go index 98c9f4b..a750061 100644 --- a/cmd/pulls/clean.go +++ b/cmd/pulls/clean.go @@ -9,11 +9,9 @@ import ( "code.gitea.io/tea/cmd/flags" "code.gitea.io/tea/modules/config" - local_git "code.gitea.io/tea/modules/git" + "code.gitea.io/tea/modules/task" "code.gitea.io/tea/modules/utils" - "code.gitea.io/sdk/gitea" - git_config "github.com/go-git/go-git/v5/config" "github.com/urfave/cli/v2" ) @@ -38,68 +36,10 @@ func runPullsClean(ctx *cli.Context) error { return fmt.Errorf("Must specify a PR index") } - // fetch PR source-repo & -branch from gitea idx, err := utils.ArgToIndex(ctx.Args().First()) if err != nil { return err } - pr, _, err := login.Client().GetPullRequest(owner, repo, idx) - if err != nil { - return err - } - if pr.State == gitea.StateOpen { - return fmt.Errorf("PR is still open, won't delete branches") - } - // IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL? - - r, err := local_git.RepoForWorkdir() - if err != nil { - return err - } - - // find a branch with matching sha or name, that has a remote matching the repo url - var branch *git_config.Branch - if ctx.Bool("ignore-sha") { - branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL) - } else { - branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL) - } - if err != nil { - return err - } - if branch == nil { - if ctx.Bool("ignore-sha") { - return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref) - } - return fmt.Errorf(`Remote branch %s not found in local repo. -Either you don't track this PR, or the local branch has diverged from the remote. -If you still want to continue & are sure you don't loose any important commits, -call me again with the --ignore-sha flag`, pr.Head.Ref) - } - - // prepare deletion of local branch: - headRef, err := r.Head() - if err != nil { - return err - } - if headRef.Name().Short() == branch.Name { - fmt.Printf("Checking out 'master' to delete local branch '%s'\n", branch.Name) - err = r.TeaCheckout("master") - if err != nil { - return err - } - } - - // remove local & remote branch - fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref) - url, err := r.TeaRemoteURL(branch.Remote) - if err != nil { - return err - } - auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) - if err != nil { - return err - } - return r.TeaDeleteBranch(branch, pr.Head.Ref, auth) + return task.PullClean(login, owner, repo, idx, ctx.Bool("ignore-sha")) } diff --git a/modules/task/pull_checkout.go b/modules/task/pull_checkout.go new file mode 100644 index 0000000..eb16f3d --- /dev/null +++ b/modules/task/pull_checkout.go @@ -0,0 +1,84 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package task + +import ( + "fmt" + + "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/config" + local_git "code.gitea.io/tea/modules/git" + + "github.com/go-git/go-git/v5" +) + +// PullCheckout checkout current workdir to the head branch of specified pull request +func PullCheckout(login *config.Login, repoOwner, repoName string, index int64) error { + client := login.Client() + + localRepo, err := local_git.RepoForWorkdir() + if err != nil { + return err + } + + // fetch PR source-localRepo & -branch from gitea + pr, _, err := client.GetPullRequest(repoOwner, repoName, index) + if err != nil { + return err + } + + // test if we can pull via SSH, and configure git remote accordingly + remoteURL := pr.Head.Repository.CloneURL + keys, _, err := client.ListMyPublicKeys(gitea.ListPublicKeysOptions{}) + if err != nil { + return err + } + if len(keys) != 0 { + remoteURL = pr.Head.Repository.SSHURL + } + + // try to find a matching existing branch, otherwise return branch in pulls/ namespace + localBranchName := fmt.Sprintf("pulls/%v-%v", index, pr.Head.Ref) + if b, _ := localRepo.TeaFindBranchBySha(pr.Head.Sha, remoteURL); b != nil { + localBranchName = b.Name + } + + newRemoteName := fmt.Sprintf("pulls/%v", pr.Head.Repository.Owner.UserName) + + // verify related remote is in local repo, otherwise add it + localRemote, err := localRepo.GetOrCreateRemote(remoteURL, newRemoteName) + if err != nil { + return err + } + localRemoteName := localRemote.Config().Name + + // get auth & fetch remote + fmt.Printf("Fetching PR %v (head %s:%s) from remote '%s'\n", index, remoteURL, pr.Head.Ref, localRemoteName) + url, err := local_git.ParseURL(remoteURL) + if err != nil { + return err + } + auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) + if err != nil { + return err + } + err = localRemote.Fetch(&git.FetchOptions{Auth: auth}) + if err == git.NoErrAlreadyUpToDate { + fmt.Println(err) + } else if err != nil { + return err + } + + // checkout local branch + fmt.Printf("Creating branch '%s'\n", localBranchName) + err = localRepo.TeaCreateBranch(localBranchName, pr.Head.Ref, localRemoteName) + if err == git.ErrBranchExists { + fmt.Println("There may be changes since you last checked out, run `git pull` to get them.") + } else if err != nil { + return err + } + + return localRepo.TeaCheckout(localBranchName) +} diff --git a/modules/task/pull_clean.go b/modules/task/pull_clean.go new file mode 100644 index 0000000..28094df --- /dev/null +++ b/modules/task/pull_clean.go @@ -0,0 +1,86 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package task + +import ( + "fmt" + + "code.gitea.io/sdk/gitea" + "code.gitea.io/tea/modules/config" + local_git "code.gitea.io/tea/modules/git" + + git_config "github.com/go-git/go-git/v5/config" +) + +// PullClean deletes local & remote feature-branches for a closed pull +func PullClean(login *config.Login, repoOwner, repoName string, index int64, ignoreSHA bool) error { + client := login.Client() + + repo, _, err := client.GetRepo(repoOwner, repoName) + defaultBranch := repo.DefaultBranch + if len(defaultBranch) == 0 { + defaultBranch = "master" + } + + // fetch PR source-repo & -branch from gitea + pr, _, err := client.GetPullRequest(repoOwner, repoName, index) + if err != nil { + return err + } + if pr.State == gitea.StateOpen { + return fmt.Errorf("PR is still open, won't delete branches") + } + + // IDEA: abort if PR.Head.Repository.CloneURL does not match login.URL? + + r, err := local_git.RepoForWorkdir() + if err != nil { + return err + } + + // find a branch with matching sha or name, that has a remote matching the repo url + var branch *git_config.Branch + if ignoreSHA { + branch, err = r.TeaFindBranchByName(pr.Head.Ref, pr.Head.Repository.CloneURL) + } else { + branch, err = r.TeaFindBranchBySha(pr.Head.Sha, pr.Head.Repository.CloneURL) + } + if err != nil { + return err + } + if branch == nil { + if ignoreSHA { + return fmt.Errorf("Remote branch %s not found in local repo", pr.Head.Ref) + } + return fmt.Errorf(`Remote branch %s not found in local repo. +Either you don't track this PR, or the local branch has diverged from the remote. +If you still want to continue & are sure you don't loose any important commits, +call me again with the --ignore-sha flag`, pr.Head.Ref) + } + + // prepare deletion of local branch: + headRef, err := r.Head() + if err != nil { + return err + } + if headRef.Name().Short() == branch.Name { + fmt.Printf("Checking out '%s' to delete local branch '%s'\n", defaultBranch, branch.Name) + if err = r.TeaCheckout(defaultBranch); err != nil { + return err + } + } + + // remove local & remote branch + fmt.Printf("Deleting local branch %s and remote branch %s\n", branch.Name, pr.Head.Ref) + url, err := r.TeaRemoteURL(branch.Remote) + if err != nil { + return err + } + auth, err := local_git.GetAuthForURL(url, login.User, login.SSHKey) + if err != nil { + return err + } + return r.TeaDeleteBranch(branch, pr.Head.Ref, auth) +}