From a9ed1c5c7c6c85ae280e6a13ca0d4e665945efb4 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Wed, 22 Dec 2021 07:17:33 +0100 Subject: [PATCH] Add API to get file commit history (#17652) Adds an API endpoint `api/v1/repos/{owner}/{repo}/git/history/{filepath}` to get the commits affecting the given file or directory. Closes https://github.com/go-gitea/gitea/issues/16206 and closes https://github.com/go-gitea/gitea/issues/16703 --- integrations/api_repo_git_commits_test.go | 18 +++++ routers/api/v1/repo/commits.go | 81 ++++++++++++++++------- templates/swagger/v1_json.tmpl | 8 ++- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go index 1e138eb915..2099d568f7 100644 --- a/integrations/api_repo_git_commits_test.go +++ b/integrations/api_repo_git_commits_test.go @@ -132,3 +132,21 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) { resp.Body.String()) } + +func TestGetFileHistory(t *testing.T) { + defer prepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + // Login as User2. + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo16/commits?path=readme.md&token="+token+"&sha=good-sign", user.Name) + resp := session.MakeRequest(t, req, http.StatusOK) + + var apiData []api.Commit + DecodeJSON(t, resp, &apiData) + + assert.Len(t, apiData, 1) + assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData[0].CommitMeta.SHA) + compareCommitFiles(t, []string{"readme.md"}, apiData[0].Files) +} diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 6845ccb363..a2b4298602 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -108,13 +108,17 @@ func GetAllCommits(ctx *context.APIContext) { // in: query // description: SHA or branch to start listing commits from (usually 'master') // type: string + // - name: path + // in: query + // description: filepath of a file/dir + // type: string // - name: page // in: query // description: page number of results to return (1-based) // type: integer // - name: limit // in: query - // description: page size of results + // description: page size of results (ignored if used with 'path') // type: integer // responses: // "200": @@ -149,46 +153,73 @@ func GetAllCommits(ctx *context.APIContext) { } sha := ctx.FormString("sha") + path := ctx.FormString("path") - var baseCommit *git.Commit - if len(sha) == 0 { - // no sha supplied - use default branch - head, err := gitRepo.GetHEADBranch() + var ( + commitsCountTotal int64 + commits []*git.Commit + ) + + if len(path) == 0 { + var baseCommit *git.Commit + if len(sha) == 0 { + // no sha supplied - use default branch + head, err := gitRepo.GetHEADBranch() + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err) + return + } + + baseCommit, err = gitRepo.GetBranchCommit(head.Name) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetCommit", err) + return + } + } else { + // get commit specified by sha + baseCommit, err = gitRepo.GetCommit(sha) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetCommit", err) + return + } + } + + // Total commit count + commitsCountTotal, err = baseCommit.CommitsCount() if err != nil { - ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err) + ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err) return } - baseCommit, err = gitRepo.GetBranchCommit(head.Name) + // Query commits + commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommit", err) + ctx.Error(http.StatusInternalServerError, "CommitsByRange", err) return } } else { - // get commit specified by sha - baseCommit, err = gitRepo.GetCommit(sha) + if len(sha) == 0 { + sha = ctx.Repo.Repository.DefaultBranch + } + + commitsCountTotal, err = gitRepo.FileCommitsCount(sha, path) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommit", err) + ctx.Error(http.StatusInternalServerError, "FileCommitsCount", err) + return + } else if commitsCountTotal == 0 { + ctx.NotFound("FileCommitsCount", nil) + return + } + + commits, err = gitRepo.CommitsByFileAndRange(sha, path, listOptions.Page) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err) return } } - // Total commit count - commitsCountTotal, err := baseCommit.CommitsCount() - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err) - return - } - pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize))) - // Query commits - commits, err := baseCommit.CommitsByRange(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)) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 751d77e6ff..b016ad22a2 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2950,6 +2950,12 @@ "name": "sha", "in": "query" }, + { + "type": "string", + "description": "filepath of a file/dir", + "name": "path", + "in": "query" + }, { "type": "integer", "description": "page number of results to return (1-based)", @@ -2958,7 +2964,7 @@ }, { "type": "integer", - "description": "page size of results", + "description": "page size of results (ignored if used with 'path')", "name": "limit", "in": "query" }