269 lines
8.4 KiB
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)
|
|
}
|