From 5be1baac4b5a54ab2fee7f9a6397b35290ca1d22 Mon Sep 17 00:00:00 2001 From: yystopf Date: Thu, 1 Dec 2022 16:59:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E8=AF=B7=E6=B1=82commit=E5=88=97=E8=A1=A8=E5=92=8C=E5=8F=98?= =?UTF-8?q?=E6=9B=B4=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/git/repo_compare.go | 8 ++ routers/hat/hat.go | 2 + routers/hat/repo/pull.go | 230 ++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 modules/git/repo_compare.go diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go new file mode 100644 index 0000000..3fd83f3 --- /dev/null +++ b/modules/git/repo_compare.go @@ -0,0 +1,8 @@ +package git + +import gitea_git "code.gitea.io/gitea/modules/git" + +func GetDiffFileOnlyName(repo *gitea_git.Repository, base, head string) (string, error) { + stdout, _, err := gitea_git.NewCommand(repo.Ctx, "diff", "--name-only", base, head).RunStdString(&gitea_git.RunOpts{Dir: repo.Path}) + return stdout, err +} diff --git a/routers/hat/hat.go b/routers/hat/hat.go index 1b0895c..6f68da9 100644 --- a/routers/hat/hat.go +++ b/routers/hat/hat.go @@ -84,6 +84,8 @@ func Routers() *web.Route { m.Group("/pulls", func() { m.Group("/{index}", func() { m.Combo("").Get(repo.GetPullRequest) + m.Get("/commits", context.RepoRef(), repo.GetPullCommits) + m.Get("/files", context.RepoRef(), repo.GetPullFiles) }) }, mustAllowPulls, reqRepoReader(unit_model.TypeCode), context.ReferencesGitRepo()) m.Group("/releases", func() { diff --git a/routers/hat/repo/pull.go b/routers/hat/repo/pull.go index 00fd9ae..55a7a77 100644 --- a/routers/hat/repo/pull.go +++ b/routers/hat/repo/pull.go @@ -2,10 +2,21 @@ 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) { @@ -34,3 +45,222 @@ func GetPullRequest(ctx *context.APIContext) { } 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", 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", 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} + if len(startCommitID) == 0 || startCommitID == git.EmptySHA { + shortstatArgs = []string{git.EmptyTreeSHA, 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 = []string{startCommitID, 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: 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) +}