gitea_hat/routers/hat/repo/pull.go

269 lines
8.4 KiB
Go

package repo
import (
"net/http"
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/services/gitdiff"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/convert"
hat_git "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git"
)
func GetPullRequest(ctx *context.APIContext) {
pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
}
return
}
if err = pr.LoadBaseRepoCtx(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err)
return
}
if err = pr.LoadHeadRepoCtx(ctx); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return
}
apiResult, err := convert.ToAPIPullRequest(ctx, ctx.Repo.GitRepo, pr, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ToAPIPullRequest", err)
return
}
ctx.JSON(http.StatusOK, apiResult)
}
type PullRequestCommit struct {
git_model.SignCommitWithStatuses
Sha string
}
func GetPullCommits(ctx *context.APIContext) {
pull, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
}
return
}
issue := pull.Issue
if issue == nil {
ctx.NotFound("issue is not present.")
return
}
gitRepo := ctx.Repo.GitRepo
var commits []*git.Commit
var prInfo *git.CompareInfo
if pull.HasMerged {
var baseCommit string
if pull.MergeBase == "" {
var commitSHA, parentCommit string
commitSHA, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
commitSHA, err := gitRepo.ReadPatchCommit(pull.Index)
if err == nil {
if err := gitRepo.SetReference(pull.GetGitRefName(), commitSHA); err != nil {
log.Error("Could not write head file", err)
}
} else {
log.Trace("No history file available for PR %d", pull.Index)
}
}
if commitSHA != "" {
parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: gitRepo.Path})
if err == nil {
parentCommit = strings.TrimSpace(parentCommit)
}
if err != nil || parentCommit == "" {
log.Info("No known parent commit for PR %d, error: %v", pull.Index, err)
parentCommit = commitSHA
}
}
baseCommit = parentCommit
} else {
baseCommit = pull.MergeBase
}
prInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, baseCommit, pull.GetGitRefName(), false, false)
} else {
prInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, pull.MergeBase, pull.GetGitRefName(), false, false)
}
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
return
}
ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
commits = prInfo.Commits
userCommits := user_model.ValidateCommitsWithEmails(commits)
signCommits := asymkey_model.ParseCommitsWithSignature(userCommits, ctx.Repo.Repository.GetTrustModel(),
func(user *user_model.User) (bool, error) {
return repo_model.IsOwnerMemberCollaborator(ctx.Repo.Repository, user.ID)
})
signStatusCommits := git_model.ParseCommitsWithStatus(signCommits, ctx.Repo.Repository)
result := make([]PullRequestCommit, 0)
for _, commit := range signStatusCommits {
result = append(result, PullRequestCommit{
SignCommitWithStatuses: *commit,
Sha: commit.ID.String(),
})
}
ctx.JSON(http.StatusOK, result)
}
func GetPullFiles(ctx *context.APIContext) {
pull, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if issues_model.IsErrPullRequestNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
}
return
}
issue := pull.Issue
if issue == nil {
ctx.NotFound("issue is not present.")
return
}
gitRepo := ctx.Repo.GitRepo
var prInfo *git.CompareInfo
if pull.HasMerged {
var baseCommit string
if pull.MergeBase == "" {
var commitSHA, parentCommit string
commitSHA, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
commitSHA, err := gitRepo.ReadPatchCommit(pull.Index)
if err == nil {
if err := gitRepo.SetReference(pull.GetGitRefName(), commitSHA); err != nil {
log.Error("Could not write head file", err)
}
} else {
log.Trace("No history file available for PR %d", pull.Index)
}
}
if commitSHA != "" {
parentCommit, _, err = git.NewCommand(ctx, "rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(&git.RunOpts{Dir: gitRepo.Path})
if err == nil {
parentCommit = strings.TrimSpace(parentCommit)
}
if err != nil || parentCommit == "" {
log.Info("No known parent commit for PR %d, error: %v", pull.Index, err)
parentCommit = commitSHA
}
}
baseCommit = parentCommit
} else {
baseCommit = pull.MergeBase
}
prInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, baseCommit, pull.GetGitRefName(), false, false)
} else {
prInfo, err = gitRepo.GetCompareInfo(gitRepo.Path, pull.MergeBase, pull.GetGitRefName(), false, false)
}
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCompareInfo", err)
return
}
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetRefCommitID", err)
return
}
startCommitID := prInfo.MergeBase
endCommitID := headCommitID
ctx.Data["WhitespaceBehavior"] = ""
diff := &gitdiff.Diff{Files: make([]*gitdiff.DiffFile, 0)}
if ctx.Params("not-need-files") == "true" || ctx.Params("only-file-name") == "true" {
// shortstatArgs := []string{startCommitID + "..." + endCommitID}
shortStatArgs := []git.CmdArg{git.CmdArgCheck(startCommitID + "..." + endCommitID)}
if len(startCommitID) == 0 || startCommitID == git.EmptySHA {
shortStatArgs = []git.CmdArg{git.EmptyTreeSHA, git.CmdArgCheck(endCommitID)}
}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(ctx, gitRepo.Path, shortStatArgs...)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if base and head have become unrelated.
// previously it would return the results of git diff --shortstat base head so let's try that...
shortStatArgs = []git.CmdArg{git.CmdArgCheck(startCommitID), git.CmdArgCheck(endCommitID)}
diff.NumFiles, diff.TotalAddition, diff.TotalDeletion, _ = git.GetDiffShortStat(ctx, gitRepo.Path, shortStatArgs...)
}
if ctx.Params("only-file-name") == "true" {
var diffString string
var err error
if len(startCommitID) == 0 || startCommitID == git.EmptySHA {
diffString, err = hat_git.GetDiffFileOnlyName(ctx.Repo.GitRepo, git.EmptyTreeSHA, headCommitID)
} else {
diffString, err = hat_git.GetDiffFileOnlyName(ctx.Repo.GitRepo, startCommitID, headCommitID)
}
if err == nil {
for _, fileName := range strings.Split(diffString, "\n") {
if fileName != "" {
diff.Files = append(diff.Files, &gitdiff.DiffFile{
Name: fileName,
})
}
}
}
}
} else {
diff, err = gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
BeforeCommitID: startCommitID,
AfterCommitID: endCommitID,
SkipTo: ctx.FormString("skip-to"),
MaxLines: setting.Git.MaxGitDiffLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
MaxFiles: setting.Git.MaxGitDiffFiles,
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
})
if err != nil {
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
return
}
if err = diff.LoadComments(ctx, issue, ctx.Doer); err != nil {
ctx.ServerError("LoadComments", err)
return
}
}
fileDiff := struct {
*gitdiff.Diff
LatestSha string
}{
Diff: diff,
LatestSha: endCommitID,
}
ctx.JSON(http.StatusOK, fileDiff)
}