Skip to content

Commit 1a16902

Browse files
authored
Merge branch 'main' into fix-string-equal
2 parents aefac78 + 8cbec63 commit 1a16902

File tree

16 files changed

+174
-157
lines changed

16 files changed

+174
-157
lines changed

models/issues/comment.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,8 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository
715715
return nil
716716
}
717717

718-
func (c *Comment) loadReview(ctx context.Context) (err error) {
718+
// LoadReview loads the associated review
719+
func (c *Comment) LoadReview(ctx context.Context) (err error) {
719720
if c.ReviewID == 0 {
720721
return nil
721722
}
@@ -732,11 +733,6 @@ func (c *Comment) loadReview(ctx context.Context) (err error) {
732733
return nil
733734
}
734735

735-
// LoadReview loads the associated review
736-
func (c *Comment) LoadReview(ctx context.Context) error {
737-
return c.loadReview(ctx)
738-
}
739-
740736
// DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes.
741737
func (c *Comment) DiffSide() string {
742738
if c.Line < 0 {
@@ -856,7 +852,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
856852
}
857853
if comment.ReviewID != 0 {
858854
if comment.Review == nil {
859-
if err := comment.loadReview(ctx); err != nil {
855+
if err := comment.LoadReview(ctx); err != nil {
860856
return err
861857
}
862858
}

modules/structs/repo_file.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,17 @@ type ContentsExtResponse struct {
116116

117117
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
118118
type ContentsResponse struct {
119-
Name string `json:"name"`
120-
Path string `json:"path"`
121-
SHA string `json:"sha"`
122-
LastCommitSHA string `json:"last_commit_sha"`
119+
Name string `json:"name"`
120+
Path string `json:"path"`
121+
SHA string `json:"sha"`
122+
123+
LastCommitSHA *string `json:"last_commit_sha,omitempty"`
123124
// swagger:strfmt date-time
124-
LastCommitterDate time.Time `json:"last_committer_date"`
125+
LastCommitterDate *time.Time `json:"last_committer_date,omitempty"`
125126
// swagger:strfmt date-time
126-
LastAuthorDate time.Time `json:"last_author_date"`
127+
LastAuthorDate *time.Time `json:"last_author_date,omitempty"`
128+
LastCommitMessage *string `json:"last_commit_message,omitempty"`
129+
127130
// `type` will be `file`, `dir`, `symlink`, or `submodule`
128131
Type string `json:"type"`
129132
Size int64 `json:"size"`
@@ -141,8 +144,8 @@ type ContentsResponse struct {
141144
SubmoduleGitURL *string `json:"submodule_git_url"`
142145
Links *FileLinksResponse `json:"_links"`
143146

144-
LfsOid *string `json:"lfs_oid"`
145-
LfsSize *int64 `json:"lfs_size"`
147+
LfsOid *string `json:"lfs_oid,omitempty"`
148+
LfsSize *int64 `json:"lfs_size,omitempty"`
146149
}
147150

148151
// FileCommitResponse contains information generated from a Git commit for a repo's file.

options/locale/locale_fr-FR.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,7 @@ pulls.cmd_instruction_checkout_title=Basculer
19691969
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
19701970
pulls.cmd_instruction_merge_title=Fusionner
19711971
pulls.cmd_instruction_merge_desc=Fusionner les modifications et mettre à jour sur Gitea.
1972+
pulls.cmd_instruction_merge_warning=Attention : cette opération ne peut pas fusionner la demande d’ajout car la « détection automatique de fusion manuelle » n’a pas été activée
19721973
pulls.clear_merge_message=Effacer le message de fusion
19731974
pulls.clear_merge_message_hint=Effacer le message de fusion ne supprimera que le message de la révision, mais pas les pieds de révision générés tels que "Co-Authored-By:".
19741975

@@ -2768,6 +2769,8 @@ branch.new_branch_from=`Créer une nouvelle branche à partir de "%s"`
27682769
branch.renamed=La branche %s à été renommée en %s.
27692770
branch.rename_default_or_protected_branch_error=Seuls les administrateurs peuvent renommer les branches par défaut ou protégées.
27702771
branch.rename_protected_branch_failed=Cette branche est protégée par des règles de protection basées sur des globs.
2772+
branch.commits_divergence_from=Divergence de révisions : %[1]d en retard et %[2]d en avance sur %[3]s
2773+
branch.commits_no_divergence=Identique à la branche %[1]s
27712774

27722775
tag.create_tag=Créer l'étiquette %s
27732776
tag.create_tag_operation=Créer une étiquette

options/locale/locale_ga-IE.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,6 +2769,8 @@ branch.new_branch_from=`Cruthaigh brainse nua ó "%s"`
27692769
branch.renamed=Ainmníodh brainse %s go %s.
27702770
branch.rename_default_or_protected_branch_error=Ní féidir ach le riarthóirí brainsí réamhshocraithe nó cosanta a athainmniú.
27712771
branch.rename_protected_branch_failed=Tá an brainse seo faoi chosaint ag rialacha cosanta domhanda.
2772+
branch.commits_divergence_from=Déanann sé dialltacht a thiomnú: %[1]d taobh thiar agus %[2]d chun tosaigh ar %[3]s
2773+
branch.commits_no_divergence=Mar an gcéanna le brainse %[1]s
27722774
27732775
tag.create_tag=Cruthaigh clib %s
27742776
tag.create_tag_operation=Cruthaigh clib

options/locale/locale_pt-PT.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,6 +2769,8 @@ branch.new_branch_from=`Criar um novo ramo a partir do ramo "%s"`
27692769
branch.renamed=O ramo %s foi renomeado para %s.
27702770
branch.rename_default_or_protected_branch_error=Só os administradores é que podem renomear o ramo principal ou ramos protegidos.
27712771
branch.rename_protected_branch_failed=Este ramo está protegido por regras de salvaguarda baseadas em padrões glob.
2772+
branch.commits_divergence_from=Divergência nos cometimentos: %[1]d atrás e %[2]d à frente de %[3]s
2773+
branch.commits_no_divergence=Idêntico ao ramo %[1]s
27722774

27732775
tag.create_tag=Criar etiqueta %s
27742776
tag.create_tag_operation=Criar etiqueta

routers/api/v1/repo/file.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,8 @@ func GetContentsExt(ctx *context.APIContext) {
812812
// required: true
813813
// - name: filepath
814814
// in: path
815-
// description: path of the dir, file, symlink or submodule in the repo
815+
// description: path of the dir, file, symlink or submodule in the repo. Swagger requires path parameter to be "required",
816+
// you can leave it empty or pass a single dot (".") to get the root directory.
816817
// type: string
817818
// required: true
818819
// - name: ref
@@ -823,7 +824,8 @@ func GetContentsExt(ctx *context.APIContext) {
823824
// - name: includes
824825
// in: query
825826
// description: By default this API's response only contains file's metadata. Use comma-separated "includes" options to retrieve more fields.
826-
// Option "file_content" will try to retrieve the file content, option "lfs_metadata" will try to retrieve LFS metadata.
827+
// Option "file_content" will try to retrieve the file content, "lfs_metadata" will try to retrieve LFS metadata,
828+
// "commit_metadata" will try to retrieve commit metadata, and "commit_message" will try to retrieve commit message.
827829
// type: string
828830
// required: false
829831
// responses:
@@ -832,6 +834,9 @@ func GetContentsExt(ctx *context.APIContext) {
832834
// "404":
833835
// "$ref": "#/responses/notFound"
834836

837+
if treePath := ctx.PathParam("*"); treePath == "." || treePath == "/" {
838+
ctx.SetPathParam("*", "") // workaround for swagger, it requires path parameter to be "required", but we need to list root directory
839+
}
835840
opts := files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*")}
836841
for includeOpt := range strings.SplitSeq(ctx.FormString("includes"), ",") {
837842
if includeOpt == "" {
@@ -842,6 +847,10 @@ func GetContentsExt(ctx *context.APIContext) {
842847
opts.IncludeSingleFileContent = true
843848
case "lfs_metadata":
844849
opts.IncludeLfsMetadata = true
850+
case "commit_metadata":
851+
opts.IncludeCommitMetadata = true
852+
case "commit_message":
853+
opts.IncludeCommitMessage = true
845854
default:
846855
ctx.APIError(http.StatusBadRequest, fmt.Sprintf("unknown include option %q", includeOpt))
847856
return
@@ -883,7 +892,11 @@ func GetContents(ctx *context.APIContext) {
883892
// "$ref": "#/responses/ContentsResponse"
884893
// "404":
885894
// "$ref": "#/responses/notFound"
886-
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{TreePath: ctx.PathParam("*"), IncludeSingleFileContent: true})
895+
ret := getRepoContents(ctx, files_service.GetContentsOrListOptions{
896+
TreePath: ctx.PathParam("*"),
897+
IncludeSingleFileContent: true,
898+
IncludeCommitMetadata: true,
899+
})
887900
if ctx.Written() {
888901
return
889902
}

services/notify/notify.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,25 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
4646
}
4747
}
4848

49+
func shouldSendCommentChangeNotification(ctx context.Context, comment *issues_model.Comment) bool {
50+
if err := comment.LoadReview(ctx); err != nil {
51+
log.Error("LoadReview: %v", err)
52+
return false
53+
} else if comment.Review != nil && comment.Review.Type == issues_model.ReviewTypePending {
54+
// Pending review comments updating should not triggered
55+
return false
56+
}
57+
return true
58+
}
59+
4960
// CreateIssueComment notifies issue comment related message to notifiers
5061
func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository,
5162
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
5263
) {
64+
if !shouldSendCommentChangeNotification(ctx, comment) {
65+
return
66+
}
67+
5368
for _, notifier := range notifiers {
5469
notifier.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
5570
}
@@ -156,13 +171,21 @@ func PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issue
156171

157172
// UpdateComment notifies update comment to notifiers
158173
func UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
174+
if !shouldSendCommentChangeNotification(ctx, c) {
175+
return
176+
}
177+
159178
for _, notifier := range notifiers {
160179
notifier.UpdateComment(ctx, doer, c, oldContent)
161180
}
162181
}
163182

164183
// DeleteComment notifies delete comment to notifiers
165184
func DeleteComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment) {
185+
if !shouldSendCommentChangeNotification(ctx, c) {
186+
return
187+
}
188+
166189
for _, notifier := range notifiers {
167190
notifier.DeleteComment(ctx, doer, c)
168191
}

services/repository/files/content.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ type GetContentsOrListOptions struct {
3939
TreePath string
4040
IncludeSingleFileContent bool // include the file's content when the tree path is a file
4141
IncludeLfsMetadata bool
42+
IncludeCommitMetadata bool
43+
IncludeCommitMessage bool
4244
}
4345

4446
// GetContentsOrList gets the metadata of a file's contents (*ContentsResponse) if treePath not a tree
@@ -132,39 +134,46 @@ func getFileContentsByEntryInternal(_ context.Context, repo *repo_model.Reposito
132134
}
133135
selfURLString := selfURL.String()
134136

135-
err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
136-
if err != nil {
137-
return nil, err
138-
}
139-
140-
lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
141-
if err != nil {
142-
return nil, err
143-
}
144-
145137
// All content types have these fields in populated
146138
contentsResponse := &api.ContentsResponse{
147-
Name: entry.Name(),
148-
Path: opts.TreePath,
149-
SHA: entry.ID.String(),
150-
LastCommitSHA: lastCommit.ID.String(),
151-
Size: entry.Size(),
152-
URL: &selfURLString,
139+
Name: entry.Name(),
140+
Path: opts.TreePath,
141+
SHA: entry.ID.String(),
142+
Size: entry.Size(),
143+
URL: &selfURLString,
153144
Links: &api.FileLinksResponse{
154145
Self: &selfURLString,
155146
},
156147
}
157148

158-
// GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
159-
// https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
160-
if lastCommit.Committer != nil {
161-
contentsResponse.LastCommitterDate = lastCommit.Committer.When
162-
}
163-
if lastCommit.Author != nil {
164-
contentsResponse.LastAuthorDate = lastCommit.Author.When
149+
if opts.IncludeCommitMetadata || opts.IncludeCommitMessage {
150+
err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(refCommit.InputRef, refType != git.RefTypeCommit), repo.FullName(), refCommit.CommitID)
151+
if err != nil {
152+
return nil, err
153+
}
154+
155+
lastCommit, err := refCommit.Commit.GetCommitByPath(opts.TreePath)
156+
if err != nil {
157+
return nil, err
158+
}
159+
160+
if opts.IncludeCommitMetadata {
161+
contentsResponse.LastCommitSHA = util.ToPointer(lastCommit.ID.String())
162+
// GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them
163+
// https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits
164+
if lastCommit.Committer != nil {
165+
contentsResponse.LastCommitterDate = util.ToPointer(lastCommit.Committer.When)
166+
}
167+
if lastCommit.Author != nil {
168+
contentsResponse.LastAuthorDate = util.ToPointer(lastCommit.Author.When)
169+
}
170+
}
171+
if opts.IncludeCommitMessage {
172+
contentsResponse.LastCommitMessage = util.ToPointer(lastCommit.Message())
173+
}
165174
}
166175

167-
// Now populate the rest of the ContentsResponse based on entry type
176+
// Now populate the rest of the ContentsResponse based on the entry type
168177
if entry.IsRegular() || entry.IsExecutable() {
169178
contentsResponse.Type = string(ContentTypeRegular)
170179
// if it is listing the repo root dir, don't waste system resources on reading content

services/repository/files/content_test.go

Lines changed: 1 addition & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,21 @@ package files
55

66
import (
77
"testing"
8-
"time"
98

109
"code.gitea.io/gitea/models/unittest"
1110
api "code.gitea.io/gitea/modules/structs"
1211
"code.gitea.io/gitea/modules/util"
13-
"code.gitea.io/gitea/routers/api/v1/utils"
1412
"code.gitea.io/gitea/services/contexttest"
1513

1614
_ "code.gitea.io/gitea/models/actions"
1715

1816
"github.com/stretchr/testify/assert"
19-
"github.com/stretchr/testify/require"
2017
)
2118

2219
func TestMain(m *testing.M) {
2320
unittest.MainTest(m)
2421
}
2522

26-
func getExpectedReadmeContentsResponse() *api.ContentsResponse {
27-
treePath := "README.md"
28-
sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f"
29-
encoding := "base64"
30-
content := "IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"
31-
selfURL := "https://try.gitea.io/api/v1/repos/user2/repo1/contents/" + treePath + "?ref=master"
32-
htmlURL := "https://try.gitea.io/user2/repo1/src/branch/master/" + treePath
33-
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
34-
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
35-
return &api.ContentsResponse{
36-
Name: treePath,
37-
Path: treePath,
38-
SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
39-
LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
40-
LastCommitterDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
41-
LastAuthorDate: time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400)),
42-
Type: "file",
43-
Size: 30,
44-
Encoding: &encoding,
45-
Content: &content,
46-
URL: &selfURL,
47-
HTMLURL: &htmlURL,
48-
GitURL: &gitURL,
49-
DownloadURL: &downloadURL,
50-
Links: &api.FileLinksResponse{
51-
Self: &selfURL,
52-
GitURL: &gitURL,
53-
HTMLURL: &htmlURL,
54-
},
55-
}
56-
}
57-
5823
func TestGetContents(t *testing.T) {
5924
unittest.PrepareTestEnv(t)
6025
ctx, _ := contexttest.MockContext(t, "user2/repo1")
@@ -63,45 +28,8 @@ func TestGetContents(t *testing.T) {
6328
contexttest.LoadRepoCommit(t, ctx)
6429
contexttest.LoadUser(t, ctx, 2)
6530
contexttest.LoadGitRepo(t, ctx)
66-
defer ctx.Repo.GitRepo.Close()
67-
repo, gitRepo := ctx.Repo.Repository, ctx.Repo.GitRepo
68-
refCommit, err := utils.ResolveRefCommit(ctx, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch)
69-
require.NoError(t, err)
70-
71-
t.Run("GetContentsOrList(README.md)-MetaOnly", func(t *testing.T) {
72-
expectedContentsResponse := getExpectedReadmeContentsResponse()
73-
expectedContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
74-
expectedContentsResponse.Content = nil
75-
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: false})
76-
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
77-
assert.NoError(t, err)
78-
})
79-
80-
t.Run("GetContentsOrList(README.md)", func(t *testing.T) {
81-
expectedContentsResponse := getExpectedReadmeContentsResponse()
82-
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "README.md", IncludeSingleFileContent: true})
83-
assert.Equal(t, expectedContentsResponse, extResp.FileContents)
84-
assert.NoError(t, err)
85-
})
86-
87-
t.Run("GetContentsOrList(RootDir)", func(t *testing.T) {
88-
readmeContentsResponse := getExpectedReadmeContentsResponse()
89-
readmeContentsResponse.Encoding = nil // because will be in a list, doesn't have encoding and content
90-
readmeContentsResponse.Content = nil
91-
expectedContentsListResponse := []*api.ContentsResponse{readmeContentsResponse}
92-
// even if IncludeFileContent is true, it has no effect for directory listing
93-
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "", IncludeSingleFileContent: true})
94-
assert.Equal(t, expectedContentsListResponse, extResp.DirContents)
95-
assert.NoError(t, err)
96-
})
9731

98-
t.Run("GetContentsOrList(NoSuchTreePath)", func(t *testing.T) {
99-
extResp, err := GetContentsOrList(ctx, repo, gitRepo, refCommit, GetContentsOrListOptions{TreePath: "no-such/file.md"})
100-
assert.Error(t, err)
101-
assert.EqualError(t, err, "object does not exist [id: , rel_path: no-such]")
102-
assert.Nil(t, extResp.DirContents)
103-
assert.Nil(t, extResp.FileContents)
104-
})
32+
// GetContentsOrList's behavior is fully tested in integration tests, so we don't need to test it here.
10533

10634
t.Run("GetBlobBySHA", func(t *testing.T) {
10735
sha := "65f1bf27bc3bf70f64657658635e66094edbcb4d"

0 commit comments

Comments
 (0)