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
This commit is contained in:
qwerty287 2021-12-22 07:17:33 +01:00 committed by GitHub
parent d155ffc610
commit a9ed1c5c7c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 26 deletions

View file

@ -132,3 +132,21 @@ func TestDownloadCommitDiffOrPatch(t *testing.T) {
resp.Body.String()) 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)
}

View file

@ -108,13 +108,17 @@ func GetAllCommits(ctx *context.APIContext) {
// in: query // in: query
// description: SHA or branch to start listing commits from (usually 'master') // description: SHA or branch to start listing commits from (usually 'master')
// type: string // type: string
// - name: path
// in: query
// description: filepath of a file/dir
// type: string
// - name: page // - name: page
// in: query // in: query
// description: page number of results to return (1-based) // description: page number of results to return (1-based)
// type: integer // type: integer
// - name: limit // - name: limit
// in: query // in: query
// description: page size of results // description: page size of results (ignored if used with 'path')
// type: integer // type: integer
// responses: // responses:
// "200": // "200":
@ -149,46 +153,73 @@ func GetAllCommits(ctx *context.APIContext) {
} }
sha := ctx.FormString("sha") sha := ctx.FormString("sha")
path := ctx.FormString("path")
var baseCommit *git.Commit var (
if len(sha) == 0 { commitsCountTotal int64
// no sha supplied - use default branch commits []*git.Commit
head, err := gitRepo.GetHEADBranch() )
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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err) ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err)
return return
} }
baseCommit, err = gitRepo.GetBranchCommit(head.Name) // Query commits
commits, err = baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommit", err) ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
return return
} }
} else { } else {
// get commit specified by sha if len(sha) == 0 {
baseCommit, err = gitRepo.GetCommit(sha) sha = ctx.Repo.Repository.DefaultBranch
}
commitsCountTotal, err = gitRepo.FileCommitsCount(sha, path)
if err != nil { 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 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))) 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) userCache := make(map[string]*user_model.User)
apiCommits := make([]*api.Commit, len(commits)) apiCommits := make([]*api.Commit, len(commits))

View file

@ -2950,6 +2950,12 @@
"name": "sha", "name": "sha",
"in": "query" "in": "query"
}, },
{
"type": "string",
"description": "filepath of a file/dir",
"name": "path",
"in": "query"
},
{ {
"type": "integer", "type": "integer",
"description": "page number of results to return (1-based)", "description": "page number of results to return (1-based)",
@ -2958,7 +2964,7 @@
}, },
{ {
"type": "integer", "type": "integer",
"description": "page size of results", "description": "page size of results (ignored if used with 'path')",
"name": "limit", "name": "limit",
"in": "query" "in": "query"
} }