mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-30 19:15:23 +01:00 
			
		
		
		
	Fix various bugs (#35177)
* Fix #35144 * Fix #35117 * Fix https://github.com/go-gitea/gitea/issues/35054#issuecomment-3131793977 * Fix #35136
This commit is contained in:
		
							parent
							
								
									b7d8fade72
								
							
						
					
					
						commit
						85b5877bb0
					
				| @ -42,7 +42,7 @@ func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreL | ||||
| 		return nil | ||||
| 	} | ||||
| 	if strings.HasPrefix(sf.refURL, "../") { | ||||
| 		targetLink := path.Join(sf.repoLink, path.Dir(sf.fullPath), sf.refURL) | ||||
| 		targetLink := path.Join(sf.repoLink, sf.refURL) | ||||
| 		return &SubmoduleWebLink{RepoWebLink: targetLink, CommitWebLink: targetLink + moreLinkPath} | ||||
| 	} | ||||
| 	if !sf.parsed { | ||||
|  | ||||
| @ -32,7 +32,7 @@ func TestCommitSubmoduleLink(t *testing.T) { | ||||
| 		assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink) | ||||
| 		assert.Equal(t, "/subpath/user/repo/tree/aaaa", wl.CommitWebLink) | ||||
| 
 | ||||
| 		sf = NewCommitSubmoduleFile("/subpath/any/repo-home-link", "dir/submodule", "../../../user/repo", "aaaa") | ||||
| 		sf = NewCommitSubmoduleFile("/subpath/any/repo-home-link", "dir/submodule", "../../user/repo", "aaaa") | ||||
| 		wl = sf.SubmoduleWebLinkCompare(t.Context(), "1111", "2222") | ||||
| 		assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink) | ||||
| 		assert.Equal(t, "/subpath/user/repo/compare/1111...2222", wl.CommitWebLink) | ||||
|  | ||||
| @ -11,6 +11,8 @@ import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
| ) | ||||
| 
 | ||||
| // ObjectCache provides thread-safe cache operations. | ||||
| @ -106,3 +108,16 @@ func HashFilePathForWebUI(s string) string { | ||||
| 	_, _ = h.Write([]byte(s)) | ||||
| 	return hex.EncodeToString(h.Sum(nil)) | ||||
| } | ||||
| 
 | ||||
| func SplitCommitTitleBody(commitMessage string, titleRuneLimit int) (title, body string) { | ||||
| 	title, body, _ = strings.Cut(commitMessage, "\n") | ||||
| 	title, title2 := util.EllipsisTruncateRunes(title, titleRuneLimit) | ||||
| 	if title2 != "" { | ||||
| 		if body == "" { | ||||
| 			body = title2 | ||||
| 		} else { | ||||
| 			body = title2 + "\n" + body | ||||
| 		} | ||||
| 	} | ||||
| 	return title, body | ||||
| } | ||||
|  | ||||
| @ -15,3 +15,17 @@ func TestHashFilePathForWebUI(t *testing.T) { | ||||
| 		HashFilePathForWebUI("foobar"), | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| func TestSplitCommitTitleBody(t *testing.T) { | ||||
| 	title, body := SplitCommitTitleBody("啊bcdefg", 4) | ||||
| 	assert.Equal(t, "啊…", title) | ||||
| 	assert.Equal(t, "…bcdefg", body) | ||||
| 
 | ||||
| 	title, body = SplitCommitTitleBody("abcdefg\n1234567", 4) | ||||
| 	assert.Equal(t, "a…", title) | ||||
| 	assert.Equal(t, "…bcdefg\n1234567", body) | ||||
| 
 | ||||
| 	title, body = SplitCommitTitleBody("abcdefg\n1234567", 100) | ||||
| 	assert.Equal(t, "abcdefg", title) | ||||
| 	assert.Equal(t, "1234567", body) | ||||
| } | ||||
|  | ||||
| @ -19,7 +19,7 @@ func IsLikelyEllipsisLeftPart(s string) bool { | ||||
| 	return strings.HasSuffix(s, utf8Ellipsis) || strings.HasSuffix(s, asciiEllipsis) | ||||
| } | ||||
| 
 | ||||
| func ellipsisGuessDisplayWidth(r rune) int { | ||||
| func ellipsisDisplayGuessWidth(r rune) int { | ||||
| 	// To make the truncated string as long as possible, | ||||
| 	// CJK/emoji chars are considered as 2-ASCII width but not 3-4 bytes width. | ||||
| 	// Here we only make the best guess (better than counting them in bytes), | ||||
| @ -48,13 +48,17 @@ func ellipsisGuessDisplayWidth(r rune) int { | ||||
| // It appends "…" or "..." at the end of truncated string. | ||||
| // It guarantees the length of the returned runes doesn't exceed the limit. | ||||
| func EllipsisDisplayString(str string, limit int) string { | ||||
| 	s, _, _, _ := ellipsisDisplayString(str, limit) | ||||
| 	s, _, _, _ := ellipsisDisplayString(str, limit, ellipsisDisplayGuessWidth) | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| // EllipsisDisplayStringX works like EllipsisDisplayString while it also returns the right part | ||||
| func EllipsisDisplayStringX(str string, limit int) (left, right string) { | ||||
| 	left, offset, truncated, encounterInvalid := ellipsisDisplayString(str, limit) | ||||
| 	return ellipsisDisplayStringX(str, limit, ellipsisDisplayGuessWidth) | ||||
| } | ||||
| 
 | ||||
| func ellipsisDisplayStringX(str string, limit int, widthGuess func(rune) int) (left, right string) { | ||||
| 	left, offset, truncated, encounterInvalid := ellipsisDisplayString(str, limit, widthGuess) | ||||
| 	if truncated { | ||||
| 		right = str[offset:] | ||||
| 		r, _ := utf8.DecodeRune(UnsafeStringToBytes(right)) | ||||
| @ -68,7 +72,7 @@ func EllipsisDisplayStringX(str string, limit int) (left, right string) { | ||||
| 	return left, right | ||||
| } | ||||
| 
 | ||||
| func ellipsisDisplayString(str string, limit int) (res string, offset int, truncated, encounterInvalid bool) { | ||||
| func ellipsisDisplayString(str string, limit int, widthGuess func(rune) int) (res string, offset int, truncated, encounterInvalid bool) { | ||||
| 	if len(str) <= limit { | ||||
| 		return str, len(str), false, false | ||||
| 	} | ||||
| @ -81,7 +85,7 @@ func ellipsisDisplayString(str string, limit int) (res string, offset int, trunc | ||||
| 	for i, r := range str { | ||||
| 		encounterInvalid = encounterInvalid || r == utf8.RuneError | ||||
| 		pos = i | ||||
| 		runeWidth := ellipsisGuessDisplayWidth(r) | ||||
| 		runeWidth := widthGuess(r) | ||||
| 		if used+runeWidth+3 > limit { | ||||
| 			break | ||||
| 		} | ||||
| @ -96,7 +100,7 @@ func ellipsisDisplayString(str string, limit int) (res string, offset int, trunc | ||||
| 			if nextCnt >= 4 { | ||||
| 				break | ||||
| 			} | ||||
| 			nextWidth += ellipsisGuessDisplayWidth(r) | ||||
| 			nextWidth += widthGuess(r) | ||||
| 			nextCnt++ | ||||
| 		} | ||||
| 		if nextCnt <= 3 && used+nextWidth <= limit { | ||||
| @ -114,6 +118,10 @@ func ellipsisDisplayString(str string, limit int) (res string, offset int, trunc | ||||
| 	return str[:offset] + ellipsis, offset, true, encounterInvalid | ||||
| } | ||||
| 
 | ||||
| func EllipsisTruncateRunes(str string, limit int) (left, right string) { | ||||
| 	return ellipsisDisplayStringX(str, limit, func(r rune) int { return 1 }) | ||||
| } | ||||
| 
 | ||||
| // TruncateRunes returns a truncated string with given rune limit, | ||||
| // it returns input string if its rune length doesn't exceed the limit. | ||||
| func TruncateRunes(str string, limit int) string { | ||||
|  | ||||
| @ -29,7 +29,7 @@ func TestEllipsisGuessDisplayWidth(t *testing.T) { | ||||
| 		t.Run(c.r, func(t *testing.T) { | ||||
| 			w := 0 | ||||
| 			for _, r := range c.r { | ||||
| 				w += ellipsisGuessDisplayWidth(r) | ||||
| 				w += ellipsisDisplayGuessWidth(r) | ||||
| 			} | ||||
| 			assert.Equal(t, c.want, w, "hex=% x", []byte(c.r)) | ||||
| 		}) | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"code.gitea.io/gitea/models/repo" | ||||
| 	"code.gitea.io/gitea/modules/git" | ||||
| 	"code.gitea.io/gitea/services/context" | ||||
| 
 | ||||
| 	"github.com/gorilla/feeds" | ||||
| @ -15,10 +16,14 @@ import ( | ||||
| 
 | ||||
| // ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed | ||||
| func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) { | ||||
| 	commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "", "", "") | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("ShowBranchFeed", err) | ||||
| 		return | ||||
| 	var commits []*git.Commit | ||||
| 	var err error | ||||
| 	if ctx.Repo.Commit != nil { | ||||
| 		commits, err = ctx.Repo.Commit.CommitsByRange(0, 10, "", "", "") | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("ShowBranchFeed", err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	title := "Latest commits for branch " + ctx.Repo.BranchName | ||||
|  | ||||
| @ -90,11 +90,8 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git | ||||
| 	if rangeStart >= len(entries) { | ||||
| 		return tree, nil | ||||
| 	} | ||||
| 	var rangeEnd int | ||||
| 	if len(entries) > perPage { | ||||
| 		tree.Truncated = true | ||||
| 	} | ||||
| 	rangeEnd = min(rangeStart+perPage, len(entries)) | ||||
| 	rangeEnd := min(rangeStart+perPage, len(entries)) | ||||
| 	tree.Truncated = rangeEnd < len(entries) | ||||
| 	tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart) | ||||
| 	for e := rangeStart; e < rangeEnd; e++ { | ||||
| 		i := e - rangeStart | ||||
|  | ||||
| @ -402,16 +402,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo | ||||
| 		} | ||||
| 
 | ||||
| 		rel, has := relMap[lowerTag] | ||||
| 
 | ||||
| 		parts := strings.SplitN(tag.Message, "\n", 2) | ||||
| 		note := "" | ||||
| 		if len(parts) > 1 { | ||||
| 			note = parts[1] | ||||
| 		} | ||||
| 		title, note := git.SplitCommitTitleBody(tag.Message, 255) | ||||
| 		if !has { | ||||
| 			rel = &repo_model.Release{ | ||||
| 				RepoID:       repo.ID, | ||||
| 				Title:        parts[0], | ||||
| 				Title:        title, | ||||
| 				TagName:      tags[i], | ||||
| 				LowerTagName: lowerTag, | ||||
| 				Target:       "", | ||||
| @ -430,7 +425,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo | ||||
| 			rel.Sha1 = commit.ID.String() | ||||
| 			rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) | ||||
| 			if rel.IsTag { | ||||
| 				rel.Title = parts[0] | ||||
| 				rel.Title = title | ||||
| 				rel.Note = note | ||||
| 			} else { | ||||
| 				rel.IsDraft = false | ||||
|  | ||||
| @ -11,7 +11,11 @@ import ( | ||||
| 	repo_model "code.gitea.io/gitea/models/repo" | ||||
| 	"code.gitea.io/gitea/models/unittest" | ||||
| 	user_model "code.gitea.io/gitea/models/user" | ||||
| 	api "code.gitea.io/gitea/modules/structs" | ||||
| 	"code.gitea.io/gitea/tests" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
| 
 | ||||
| func TestAPIReposGitTrees(t *testing.T) { | ||||
| @ -32,13 +36,21 @@ func TestAPIReposGitTrees(t *testing.T) { | ||||
| 	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) | ||||
| 
 | ||||
| 	// Test a public repo that anyone can GET the tree of | ||||
| 	for _, ref := range [...]string{ | ||||
| 		"master",     // Branch | ||||
| 		repo1TreeSHA, // Tree SHA | ||||
| 	} { | ||||
| 		req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/trees/%s", user2.Name, repo1.Name, ref) | ||||
| 		MakeRequest(t, req, http.StatusOK) | ||||
| 	} | ||||
| 	_ = MakeRequest(t, NewRequest(t, "GET", "/api/v1/repos/user2/repo1/git/trees/master"), http.StatusOK) | ||||
| 
 | ||||
| 	resp := MakeRequest(t, NewRequest(t, "GET", "/api/v1/repos/user2/repo1/git/trees/62fb502a7172d4453f0322a2cc85bddffa57f07a?per_page=1"), http.StatusOK) | ||||
| 	var respGitTree api.GitTreeResponse | ||||
| 	DecodeJSON(t, resp, &respGitTree) | ||||
| 	assert.True(t, respGitTree.Truncated) | ||||
| 	require.Len(t, respGitTree.Entries, 1) | ||||
| 	assert.Equal(t, "File-WoW", respGitTree.Entries[0].Path) | ||||
| 
 | ||||
| 	resp = MakeRequest(t, NewRequest(t, "GET", "/api/v1/repos/user2/repo1/git/trees/62fb502a7172d4453f0322a2cc85bddffa57f07a?page=2&per_page=1"), http.StatusOK) | ||||
| 	respGitTree = api.GitTreeResponse{} | ||||
| 	DecodeJSON(t, resp, &respGitTree) | ||||
| 	assert.False(t, respGitTree.Truncated) | ||||
| 	require.Len(t, respGitTree.Entries, 1) | ||||
| 	assert.Equal(t, "README.md", respGitTree.Entries[0].Path) | ||||
| 
 | ||||
| 	// Tests a private repo with no token so will fail | ||||
| 	for _, ref := range [...]string{ | ||||
|  | ||||
| @ -75,6 +75,11 @@ func TestEmptyRepoAddFile(t *testing.T) { | ||||
| 	req = NewRequest(t, "GET", "/api/v1/repos/user30/empty/raw/main/README.md").AddTokenAuth(token) | ||||
| 	session.MakeRequest(t, req, http.StatusNotFound) | ||||
| 
 | ||||
| 	// test feed | ||||
| 	req = NewRequest(t, "GET", "/user30/empty/rss/branch/main/README.md").AddTokenAuth(token).SetHeader("Accept", "application/rss+xml") | ||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||
| 	assert.Contains(t, resp.Body.String(), "</rss>") | ||||
| 
 | ||||
| 	// create a new file | ||||
| 	req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch) | ||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user