From 753b7104af7435733eaca608af65899d78f3228f Mon Sep 17 00:00:00 2001 From: yystopf Date: Wed, 14 Dec 2022 09:40:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8Bcommit=E5=88=97=E8=A1=A8=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/git/repo.go | 26 +++++++++++ modules/git/repo_commit.go | 48 +++++++++++++++++++++ routers/hat/hat.go | 3 ++ routers/hat/repo/commits.go | 86 +++++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 modules/git/repo.go diff --git a/modules/git/repo.go b/modules/git/repo.go new file mode 100644 index 0000000..b4fdbdc --- /dev/null +++ b/modules/git/repo.go @@ -0,0 +1,26 @@ +package git + +import ( + "bytes" + + gitea_git "code.gitea.io/gitea/modules/git" +) + +func parsePrettyFormatLogToList(repo *gitea_git.Repository, logs []byte) ([]*gitea_git.Commit, error) { + var commits []*gitea_git.Commit + if len(logs) == 0 { + return commits, nil + } + + parts := bytes.Split(logs, []byte{'\n'}) + + for _, commitID := range parts { + commit, err := repo.GetCommit(string(commitID)) + if err != nil { + return nil, err + } + commits = append(commits, commit) + } + + return commits, nil +} diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index ee328b5..0f625f8 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -2,6 +2,9 @@ package git import ( "bytes" + "io" + "strconv" + "strings" gitea_git "code.gitea.io/gitea/modules/git" ) @@ -31,3 +34,48 @@ func GetFirstAndLastCommitByPath(repo *gitea_git.Repository, revision, relpath s return commits[0], commits[len(commits)-1], nil } + +// CommitsByFileAndRange return the commits according revision file and the page +func CommitsByFileAndRange(repo *gitea_git.Repository, revision, file string, page, pageSize int) ([]*gitea_git.Commit, error) { + skip := (page - 1) * pageSize + + stdoutReader, stdoutWriter := io.Pipe() + defer func() { + _ = stdoutReader.Close() + _ = stdoutWriter.Close() + }() + go func() { + stderr := strings.Builder{} + gitCmd := gitea_git.NewCommand(repo.Ctx, "log", prettyLogFormat, "--follow", + "--max-count="+strconv.Itoa(pageSize)) + gitCmd.AddDynamicArguments(revision) + gitCmd.AddArguments("--", file) + err := gitCmd.Run(&gitea_git.RunOpts{ + Dir: repo.Path, + Stdout: stdoutWriter, + Stderr: &stderr, + }) + if err != nil { + _ = stdoutWriter.CloseWithError(gitea_git.ConcatenateError(err, (&stderr).String())) + } else { + _ = stdoutWriter.Close() + } + }() + + if skip > 0 { + _, err := io.CopyN(io.Discard, stdoutReader, int64(skip*41)) + if err != nil { + if err == io.EOF { + return []*gitea_git.Commit{}, nil + } + _ = stdoutReader.CloseWithError(err) + return nil, err + } + } + + stdout, err := io.ReadAll(stdoutReader) + if err != nil { + return nil, err + } + return parsePrettyFormatLogToList(repo, stdout) +} diff --git a/routers/hat/hat.go b/routers/hat/hat.go index ddfa08b..820aefb 100644 --- a/routers/hat/hat.go +++ b/routers/hat/hat.go @@ -100,6 +100,9 @@ func Routers() *web.Route { m.Group("/count", func() { m.Get("", context.ReferencesGitRepo(), repo.GetCommitCount) }) + m.Group("/file_commits", func() { + m.Get("/*", context.ReferencesGitRepo(), repo.GetFileAllCommits) + }) }, repoAssignment()) }) m.Post("/orgs", reqToken(), bind(gitea_api.CreateOrgOption{}), org.Create) diff --git a/routers/hat/repo/commits.go b/routers/hat/repo/commits.go index 8444e64..7559cb8 100644 --- a/routers/hat/repo/commits.go +++ b/routers/hat/repo/commits.go @@ -8,12 +8,14 @@ import ( "code.gitea.io/gitea/models/repo" user "code.gitea.io/gitea/models/user" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + hat_git "code.gitlink.org.cn/Gitlink/gitea_hat.git/modules/git" ) func GetAllCommitsSliceByTime(ctx *context.APIContext) { @@ -121,3 +123,87 @@ func toResponseCommit(repo *repo.Repository, gitRepo *git.Repository, commit *gi CommitDate: apiCommit.Created.Format("2006-01-02"), }, nil } + +func GetFileAllCommits(ctx *context.APIContext) { + if ctx.Repo.Repository.IsEmpty { + ctx.JSON(http.StatusConflict, api.APIError{ + Message: "Git Repository is empty", + URL: setting.API.SwaggerURL, + }) + return + } + listOptions := utils.GetListOptions(ctx) + if listOptions.Page <= 0 { + listOptions.Page = 1 + } + if listOptions.PageSize > setting.Git.CommitsRangeSize { + listOptions.PageSize = setting.Git.CommitsRangeSize + } + + ref := ctx.FormString("sha") + if ref == "" { + ref = ctx.FormString("ref") + } + + treePath := ctx.Params("*") + var baseCommit *git.Commit + var commitsCountTotal int64 + var err error + if len(ref) == 0 { + head, err := ctx.Repo.GitRepo.GetHEADBranch() + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetHeadBranch", err) + return + } + baseCommit, err = ctx.Repo.GitRepo.GetBranchCommit(head.Name) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetBranchCommit", err) + return + } + commitsCountTotal, err = git.CommitsCountFiles(ctx, ctx.Repo.Repository.RepoPath(), []string{head.Name}, []string{treePath}) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CommitsCountFiles", err) + return + } + + } else { + baseCommit, err = ctx.Repo.GitRepo.GetCommit(ref) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetCommit", err) + return + } + commitsCountTotal, err = git.CommitsCountFiles(ctx, ctx.Repo.GitRepo.Path, []string{ref}, []string{treePath}) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CommitsCountFiles", err) + return + } + + } + pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize))) + commits, err := hat_git.CommitsByFileAndRange(ctx.Repo.GitRepo, baseCommit.ID.String(), treePath, listOptions.Page, listOptions.PageSize) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CommitsByRange", err) + return + } + + userCache := make(map[string]*user_model.User) + apiCommits := make([]*api.Commit, len(commits)) + for i, commit := range commits { + apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ToCommit", err) + return + } + } + + ctx.Resp.Header().Set("X-Page", strconv.Itoa(listOptions.Page)) + ctx.Resp.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize)) + ctx.Resp.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) + ctx.Resp.Header().Set("X-PageCount", strconv.Itoa(pageCount)) + ctx.Resp.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount)) + + ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize) + ctx.Resp.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal)) + + ctx.JSON(http.StatusOK, apiCommits) +}