From 692707f14519b677de4601e5f40469989f82eed6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sat, 30 Jul 2022 10:09:04 +0200 Subject: [PATCH] Add latest commit's SHA to content response (#20398) * Add latest commit's SHA to content response - When requesting the contents of a filepath, add the latest commit's SHA to the requested file. - Resolves #12840 * Add swagger * Fix NPE * Fix tests * Hook into LastCommitCache * Move AddLastCommitCache to a common nogogit and gogit file Signed-off-by: Andrew Thornton * Prevent NPE Co-authored-by: Andrew Thornton Co-authored-by: wxiaoguang --- integrations/api_repo_file_create_test.go | 31 +++++---- integrations/api_repo_file_update_test.go | 28 ++++---- .../api_repo_get_contents_list_test.go | 44 ++++++++----- integrations/api_repo_get_contents_test.go | 40 ++++++----- integrations/repofiles_update_test.go | 66 ++++++++++--------- modules/git/repo_commit.go | 18 +++++ modules/structs/repo_file.go | 7 +- routers/api/v1/utils/git.go | 13 +--- services/repository/files/content.go | 21 ++++-- services/repository/files/content_test.go | 23 +++---- services/repository/files/file_test.go | 23 +++---- templates/swagger/v1_json.tmpl | 4 ++ 12 files changed, 187 insertions(+), 131 deletions(-) diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go index eb550f1d28..fd460ce564 100644 --- a/integrations/api_repo_file_create_test.go +++ b/integrations/api_repo_file_create_test.go @@ -50,7 +50,7 @@ func getCreateFileOptions() api.CreateFileOptions { } } -func getExpectedFileResponseForCreate(repoFullName, commitID, treePath string) *api.FileResponse { +func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCommitSHA string) *api.FileResponse { sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" encoding := "base64" content := "VGhpcyBpcyBuZXcgdGV4dA==" @@ -60,17 +60,18 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath string) * downloadURL := setting.AppURL + repoFullName + "/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Size: 16, - Type: "file", - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: latestCommitSHA, + Size: 16, + Type: "file", + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -170,7 +171,8 @@ func TestAPICreateFile(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusCreated) gitRepo, _ := git.OpenRepository(stdCtx.Background(), repo1.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath) + latestCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) @@ -289,7 +291,8 @@ func TestAPICreateFile(t *testing.T) { emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "empty-repo"}).(*repo_model.Repository) // public repo gitRepo, _ := git.OpenRepository(stdCtx.Background(), emptyRepo.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath) + latestCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath, latestCommit.ID.String()) DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go index 0c9c0763f4..e39d83e49e 100644 --- a/integrations/api_repo_file_update_test.go +++ b/integrations/api_repo_file_update_test.go @@ -48,7 +48,7 @@ func getUpdateFileOptions() *api.UpdateFileOptions { } } -func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileResponse { +func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string) *api.FileResponse { sha := "08bd14b2e2852529157324de9c226b3364e76136" encoding := "base64" content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ=" @@ -58,17 +58,18 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Type: "file", - Size: 20, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 20, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -137,7 +138,8 @@ func TestAPIUpdateFile(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) gitRepo, _ := git.OpenRepository(stdCtx.Background(), repo1.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) + lasCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) diff --git a/integrations/api_repo_get_contents_list_test.go b/integrations/api_repo_get_contents_list_test.go index 42227a9c4b..97b152bf1b 100644 --- a/integrations/api_repo_get_contents_list_test.go +++ b/integrations/api_repo_get_contents_list_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" ) -func getExpectedContentsListResponseForContents(ref, refType string) []*api.ContentsResponse { +func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA string) []*api.ContentsResponse { treePath := "README.md" sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref @@ -30,15 +30,16 @@ func getExpectedContentsListResponseForContents(ref, refType string) []*api.Cont downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath return []*api.ContentsResponse{ { - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 30, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -94,7 +95,9 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { var contentsListResponse []*api.ContentsResponse DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType) + lastCommit, err := gitRepo.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // No ref @@ -103,17 +106,22 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType) + + expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) - // ref is the branch we created above in setup + // ref is the branch we created above in setup ref = newBranch refType = "branch" req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + branchCommit, err := gitRepo.GetBranchCommit(ref) + assert.NoError(t, err) + lastCommit, err = branchCommit.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // ref is the new tag we created above in setup @@ -123,7 +131,11 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + tagCommit, err := gitRepo.GetTagCommit(ref) + assert.NoError(t, err) + lastCommit, err = tagCommit.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // ref is a commit @@ -133,7 +145,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, commitID) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // Test file contents a file with a bad ref diff --git a/integrations/api_repo_get_contents_test.go b/integrations/api_repo_get_contents_test.go index 67f2cb8362..56f5336b7b 100644 --- a/integrations/api_repo_get_contents_test.go +++ b/integrations/api_repo_get_contents_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" ) -func getExpectedContentsResponseForContents(ref, refType string) *api.ContentsResponse { +func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) *api.ContentsResponse { treePath := "README.md" sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" encoding := "base64" @@ -30,17 +30,18 @@ func getExpectedContentsResponseForContents(ref, refType string) *api.ContentsRe gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath return &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -96,7 +97,8 @@ func testAPIGetContents(t *testing.T, u *url.URL) { var contentsResponse api.ContentsResponse DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType) + lastCommit, _ := gitRepo.GetCommitByPath("README.md") + expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // No ref @@ -105,7 +107,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType) + expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is the branch we created above in setup @@ -115,7 +117,9 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + branchCommit, _ := gitRepo.GetBranchCommit(ref) + lastCommit, _ = branchCommit.GetCommitByPath("README.md") + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is the new tag we created above in setup @@ -125,7 +129,9 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + tagCommit, _ := gitRepo.GetTagCommit(ref) + lastCommit, _ = tagCommit.GetCommitByPath("README.md") + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is a commit @@ -135,7 +141,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, commitID) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // Test file contents a file with a bad ref diff --git a/integrations/repofiles_update_test.go b/integrations/repofiles_update_test.go index 2add99cc86..ac9b0509ea 100644 --- a/integrations/repofiles_update_test.go +++ b/integrations/repofiles_update_test.go @@ -47,7 +47,7 @@ func getUpdateRepoFileOptions(repo *repo_model.Repository) *files_service.Update } } -func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse { +func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *api.FileResponse { treePath := "new/file.txt" encoding := "base64" content := "VGhpcyBpcyBhIE5FVyBmaWxl" @@ -57,17 +57,18 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", - Type: "file", - Size: 18, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 18, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -115,7 +116,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons } } -func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.FileResponse { +func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA string) *api.FileResponse { encoding := "base64" content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ==" selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master" @@ -124,17 +125,18 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.F downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filename, - Path: filename, - SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", - Type: "file", - Size: 43, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filename, + Path: filename, + SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 43, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -206,7 +208,8 @@ func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID) + lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt") + expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID, lastCommit.ID.String()) assert.NotNil(t, expectedFileResponse) if expectedFileResponse != nil { assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) @@ -241,8 +244,9 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) defer gitRepo.Close() - commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) + commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) @@ -277,7 +281,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) // assert that the old file no longer exists in the last commit of the branch fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) switch err.(type) { @@ -326,8 +331,9 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) defer gitRepo.Close() - commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) + commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) }) } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index e6fec4d1a3..7ff23af42e 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/setting" ) @@ -434,3 +435,20 @@ func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err e } return len(stdout) > 0, err } + +func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error { + if repo.LastCommitCache == nil { + commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) { + commit, err := repo.GetCommit(sha) + if err != nil { + return 0, err + } + return commit.CommitsCount() + }) + if err != nil { + return err + } + repo.LastCommitCache = NewLastCommitCache(commitsCount, fullName, repo, cache.GetCache()) + } + return nil +} diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 135e6484cd..ce1a9fe4be 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -87,9 +87,10 @@ type FileLinksResponse struct { // ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content type ContentsResponse struct { - Name string `json:"name"` - Path string `json:"path"` - SHA string `json:"sha"` + Name string `json:"name"` + Path string `json:"path"` + SHA string `json:"sha"` + LastCommitSHA string `json:"last_commit_sha"` // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index f18442d046..816f8b3595 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -8,7 +8,6 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -35,19 +34,11 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string { } } - if ctx.Repo.GitRepo != nil && ctx.Repo.GitRepo.LastCommitCache == nil { - commitsCount, err := cache.GetInt64(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, true), func() (int64, error) { - commit, err := ctx.Repo.GitRepo.GetCommit(sha) - if err != nil { - return 0, err - } - return commit.CommitsCount() - }) + if ctx.Repo.GitRepo != nil { + err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha) if err != nil { log.Error("Unable to get commits count for %s in %s. Error: %v", sha, ctx.Repo.Repository.FullName(), err) - return sha } - ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(commitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache()) } return sha diff --git a/services/repository/files/content.go b/services/repository/files/content.go index c206909289..34c8aeec25 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -165,13 +165,24 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref } selfURLString := selfURL.String() + err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(ref, refType != git.ObjectCommit), repo.FullName(), commitID) + if err != nil { + return nil, err + } + + lastCommit, err := commit.GetCommitByPath(treePath) + if err != nil { + return nil, err + } + // All content types have these fields in populated contentsResponse := &api.ContentsResponse{ - Name: entry.Name(), - Path: treePath, - SHA: entry.ID.String(), - Size: entry.Size(), - URL: &selfURLString, + Name: entry.Name(), + Path: treePath, + SHA: entry.ID.String(), + LastCommitSHA: lastCommit.ID.String(), + Size: entry.Size(), + URL: &selfURLString, Links: &api.FileLinksResponse{ Self: &selfURLString, }, diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 342ebae329..24fcd6c4c5 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -33,17 +33,18 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse { gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath return &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", + LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go index ee0582dfc2..e158c63de2 100644 --- a/services/repository/files/file_test.go +++ b/services/repository/files/file_test.go @@ -43,17 +43,18 @@ func getExpectedFileResponse() *api.FileResponse { downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: sha, + LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ff0b4c6468..c0dd50a7b9 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -13779,6 +13779,10 @@ "type": "string", "x-go-name": "HTMLURL" }, + "last_commit_sha": { + "type": "string", + "x-go-name": "LastCommitSHA" + }, "name": { "type": "string", "x-go-name": "Name"