mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-08 05:02:38 +09:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d49feab428 | ||
|
|
9162f4403a | ||
|
|
d05cf08fad | ||
|
|
f4b4b0bf98 | ||
|
|
99596044d7 | ||
|
|
693d26914f | ||
|
|
315f197790 | ||
|
|
76b8f0c3a7 | ||
|
|
f99bbd7f3f | ||
|
|
f7ef657b5a |
12
CHANGELOG.md
12
CHANGELOG.md
@@ -4,6 +4,18 @@ This changelog goes through the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||||
|
|
||||||
|
## [1.24.4](https://github.com/go-gitea/gitea/releases/tag/1.24.4) - 2025-08-03
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix various bugs (1.24) (#35186)
|
||||||
|
* Fix migrate input box bug (#35166) (#35171)
|
||||||
|
* Only hide dropzone when no files have been uploaded (#35156) (#35167)
|
||||||
|
* Fix review comment/dimiss comment x reference can be refereced back (#35094) (#35099)
|
||||||
|
* Fix submodule nil check (#35096) (#35098)
|
||||||
|
* MISC
|
||||||
|
* Don't use full-file highlight when there is a git diff textconv (#35114) (#35119)
|
||||||
|
* Increase gap on latest commit (#35104) (#35113)
|
||||||
|
|
||||||
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/1.24.3) - 2025-07-15
|
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/1.24.3) - 2025-07-15
|
||||||
|
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
|||||||
6
flake.lock
generated
6
flake.lock
generated
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747179050,
|
"lastModified": 1752480373,
|
||||||
"narHash": "sha256-qhFMmDkeJX9KJwr5H32f1r7Prs7XbQWtO0h3V0a0rFY=",
|
"narHash": "sha256-JHQbm+OcGp32wAsXTE/FLYGNpb+4GLi5oTvCxwSoBOA=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "adaa24fbf46737f3f1b5497bf64bae750f82942e",
|
"rev": "62e0f05ede1da0d54515d4ea8ce9c733f12d9f08",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe
|
|||||||
|
|
||||||
// AddCrossReferences add cross references
|
// AddCrossReferences add cross references
|
||||||
func (c *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
|
func (c *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
|
||||||
if c.Type != CommentTypeCode && c.Type != CommentTypeComment {
|
if !c.Type.HasContentSupport() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := c.LoadIssue(stdCtx); err != nil {
|
if err := c.LoadIssue(stdCtx); err != nil {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const (
|
|||||||
GitlabLanguage = "gitlab-language"
|
GitlabLanguage = "gitlab-language"
|
||||||
Lockable = "lockable"
|
Lockable = "lockable"
|
||||||
Filter = "filter"
|
Filter = "filter"
|
||||||
|
Diff = "diff"
|
||||||
)
|
)
|
||||||
|
|
||||||
var LinguistAttributes = []string{
|
var LinguistAttributes = []string{
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
package git
|
package git
|
||||||
|
|
||||||
import "path"
|
|
||||||
|
|
||||||
// CommitInfo describes the first commit with the provided entry
|
// CommitInfo describes the first commit with the provided entry
|
||||||
type CommitInfo struct {
|
type CommitInfo struct {
|
||||||
Entry *TreeEntry
|
Entry *TreeEntry
|
||||||
@@ -12,11 +10,14 @@ type CommitInfo struct {
|
|||||||
SubmoduleFile *CommitSubmoduleFile
|
SubmoduleFile *CommitSubmoduleFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCommitInfoSubmoduleFile(repoLink string, entry *TreeEntry, commit *Commit, treePathDir string) (*CommitSubmoduleFile, error) {
|
func GetCommitInfoSubmoduleFile(repoLink, fullPath string, commit *Commit, refCommitID ObjectID) (*CommitSubmoduleFile, error) {
|
||||||
fullPath := path.Join(treePathDir, entry.Name())
|
|
||||||
submodule, err := commit.GetSubModule(fullPath)
|
submodule, err := commit.GetSubModule(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, entry.ID.String()), nil
|
if submodule == nil {
|
||||||
|
// unable to find submodule from ".gitmodules" file
|
||||||
|
return NewCommitSubmoduleFile(repoLink, fullPath, "", refCommitID.String()), nil
|
||||||
|
}
|
||||||
|
return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, refCommitID.String()), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *
|
|||||||
|
|
||||||
// If the entry is a submodule, add a submodule file for this
|
// If the entry is a submodule, add a submodule file for this
|
||||||
if entry.IsSubModule() {
|
if entry.IsSubModule() {
|
||||||
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath)
|
commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *
|
|||||||
|
|
||||||
// If the entry is a submodule, add a submodule file for this
|
// If the entry is a submodule, add a submodule file for this
|
||||||
if entry.IsSubModule() {
|
if entry.IsSubModule() {
|
||||||
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath)
|
commitsInfo[i].SubmoduleFile, err = GetCommitInfoSubmoduleFile(repoLink, path.Join(treePath, entry.Name()), commit, entry.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -120,6 +121,23 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
|
|||||||
defer clonedRepo1.Close()
|
defer clonedRepo1.Close()
|
||||||
|
|
||||||
testGetCommitsInfo(t, clonedRepo1)
|
testGetCommitsInfo(t, clonedRepo1)
|
||||||
|
|
||||||
|
t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) {
|
||||||
|
commit, err := bareRepo1.GetCommit("HEAD")
|
||||||
|
require.NoError(t, err)
|
||||||
|
treeEntry, err := commit.GetTreeEntryByPath("file1.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
cisf, err := GetCommitInfoSubmoduleFile("/any/repo-link", "file1.txt", commit, treeEntry.ID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, &CommitSubmoduleFile{
|
||||||
|
repoLink: "/any/repo-link",
|
||||||
|
fullPath: "file1.txt",
|
||||||
|
refURL: "",
|
||||||
|
refID: "e2129701f1a4d54dc44f03c93bca0a2aec7c5449",
|
||||||
|
}, cisf)
|
||||||
|
// since there is no refURL, it means that the submodule info doesn't exist, so it won't have a web link
|
||||||
|
assert.Nil(t, cisf.SubmoduleWebLinkTree(t.Context()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
|
func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) {
|
|||||||
return c.submoduleCache, nil
|
return c.submoduleCache, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubModule get the submodule according entry name
|
// GetSubModule gets the submodule by the entry name.
|
||||||
|
// It returns "nil, nil" if the submodule does not exist, caller should always remember to check the "nil"
|
||||||
func (c *Commit) GetSubModule(entryName string) (*SubModule, error) {
|
func (c *Commit) GetSubModule(entryName string) (*SubModule, error) {
|
||||||
modules, err := c.GetSubModules()
|
modules, err := c.GetSubModules()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -29,16 +29,20 @@ func NewCommitSubmoduleFile(repoLink, fullPath, refURL, refID string) *CommitSub
|
|||||||
return &CommitSubmoduleFile{repoLink: repoLink, fullPath: fullPath, refURL: refURL, refID: refID}
|
return &CommitSubmoduleFile{repoLink: repoLink, fullPath: fullPath, refURL: refURL, refID: refID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefID returns the commit ID of the submodule, it returns empty string for nil receiver
|
||||||
func (sf *CommitSubmoduleFile) RefID() string {
|
func (sf *CommitSubmoduleFile) RefID() string {
|
||||||
|
if sf == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return sf.refID
|
return sf.refID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreLinkPath string) *SubmoduleWebLink {
|
func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreLinkPath string) *SubmoduleWebLink {
|
||||||
if sf == nil {
|
if sf == nil || sf.refURL == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(sf.refURL, "../") {
|
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}
|
return &SubmoduleWebLink{RepoWebLink: targetLink, CommitWebLink: targetLink + moreLinkPath}
|
||||||
}
|
}
|
||||||
if !sf.parsed {
|
if !sf.parsed {
|
||||||
@@ -53,14 +57,13 @@ func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreL
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SubmoduleWebLinkTree tries to make the submodule's tree link in its own repo, it also works on "nil" receiver
|
// SubmoduleWebLinkTree tries to make the submodule's tree link in its own repo, it also works on "nil" receiver
|
||||||
|
// It returns nil if the submodule does not have a valid URL or is nil
|
||||||
func (sf *CommitSubmoduleFile) SubmoduleWebLinkTree(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
|
func (sf *CommitSubmoduleFile) SubmoduleWebLinkTree(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
|
||||||
if sf == nil {
|
return sf.getWebLinkInTargetRepo(ctx, "/tree/"+util.OptionalArg(optCommitID, sf.RefID()))
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return sf.getWebLinkInTargetRepo(ctx, "/tree/"+util.OptionalArg(optCommitID, sf.refID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmoduleWebLinkCompare tries to make the submodule's compare link in its own repo, it also works on "nil" receiver
|
// SubmoduleWebLinkCompare tries to make the submodule's compare link in its own repo, it also works on "nil" receiver
|
||||||
|
// It returns nil if the submodule does not have a valid URL or is nil
|
||||||
func (sf *CommitSubmoduleFile) SubmoduleWebLinkCompare(ctx context.Context, commitID1, commitID2 string) *SubmoduleWebLink {
|
func (sf *CommitSubmoduleFile) SubmoduleWebLinkCompare(ctx context.Context, commitID1, commitID2 string) *SubmoduleWebLink {
|
||||||
return sf.getWebLinkInTargetRepo(ctx, "/compare/"+commitID1+"..."+commitID2)
|
return sf.getWebLinkInTargetRepo(ctx, "/compare/"+commitID1+"..."+commitID2)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
func TestCommitSubmoduleLink(t *testing.T) {
|
func TestCommitSubmoduleLink(t *testing.T) {
|
||||||
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkTree(t.Context()))
|
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkTree(t.Context()))
|
||||||
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkCompare(t.Context(), "", ""))
|
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkCompare(t.Context(), "", ""))
|
||||||
|
assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkTree(t.Context()))
|
||||||
|
assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkCompare(t.Context(), "", ""))
|
||||||
|
|
||||||
t.Run("GitHubRepo", func(t *testing.T) {
|
t.Run("GitHubRepo", func(t *testing.T) {
|
||||||
sf := NewCommitSubmoduleFile("/any/repo-link", "full-path", "git@github.com:user/repo.git", "aaaa")
|
sf := NewCommitSubmoduleFile("/any/repo-link", "full-path", "git@github.com:user/repo.git", "aaaa")
|
||||||
@@ -30,7 +32,7 @@ func TestCommitSubmoduleLink(t *testing.T) {
|
|||||||
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
|
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
|
||||||
assert.Equal(t, "/subpath/user/repo/tree/aaaa", wl.CommitWebLink)
|
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")
|
wl = sf.SubmoduleWebLinkCompare(t.Context(), "1111", "2222")
|
||||||
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
|
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
|
||||||
assert.Equal(t, "/subpath/user/repo/compare/1111...2222", wl.CommitWebLink)
|
assert.Equal(t, "/subpath/user/repo/compare/1111...2222", wl.CommitWebLink)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObjectCache provides thread-safe cache operations.
|
// ObjectCache provides thread-safe cache operations.
|
||||||
@@ -106,3 +108,16 @@ func HashFilePathForWebUI(s string) string {
|
|||||||
_, _ = h.Write([]byte(s))
|
_, _ = h.Write([]byte(s))
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
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"),
|
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)
|
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,
|
// To make the truncated string as long as possible,
|
||||||
// CJK/emoji chars are considered as 2-ASCII width but not 3-4 bytes width.
|
// 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),
|
// 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 appends "…" or "..." at the end of truncated string.
|
||||||
// It guarantees the length of the returned runes doesn't exceed the limit.
|
// It guarantees the length of the returned runes doesn't exceed the limit.
|
||||||
func EllipsisDisplayString(str string, limit int) string {
|
func EllipsisDisplayString(str string, limit int) string {
|
||||||
s, _, _, _ := ellipsisDisplayString(str, limit)
|
s, _, _, _ := ellipsisDisplayString(str, limit, ellipsisDisplayGuessWidth)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// EllipsisDisplayStringX works like EllipsisDisplayString while it also returns the right part
|
// EllipsisDisplayStringX works like EllipsisDisplayString while it also returns the right part
|
||||||
func EllipsisDisplayStringX(str string, limit int) (left, right string) {
|
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 {
|
if truncated {
|
||||||
right = str[offset:]
|
right = str[offset:]
|
||||||
r, _ := utf8.DecodeRune(UnsafeStringToBytes(right))
|
r, _ := utf8.DecodeRune(UnsafeStringToBytes(right))
|
||||||
@@ -68,7 +72,7 @@ func EllipsisDisplayStringX(str string, limit int) (left, right string) {
|
|||||||
return left, right
|
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 {
|
if len(str) <= limit {
|
||||||
return str, len(str), false, false
|
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 {
|
for i, r := range str {
|
||||||
encounterInvalid = encounterInvalid || r == utf8.RuneError
|
encounterInvalid = encounterInvalid || r == utf8.RuneError
|
||||||
pos = i
|
pos = i
|
||||||
runeWidth := ellipsisGuessDisplayWidth(r)
|
runeWidth := widthGuess(r)
|
||||||
if used+runeWidth+3 > limit {
|
if used+runeWidth+3 > limit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -96,7 +100,7 @@ func ellipsisDisplayString(str string, limit int) (res string, offset int, trunc
|
|||||||
if nextCnt >= 4 {
|
if nextCnt >= 4 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
nextWidth += ellipsisGuessDisplayWidth(r)
|
nextWidth += widthGuess(r)
|
||||||
nextCnt++
|
nextCnt++
|
||||||
}
|
}
|
||||||
if nextCnt <= 3 && used+nextWidth <= limit {
|
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
|
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,
|
// TruncateRunes returns a truncated string with given rune limit,
|
||||||
// it returns input string if its rune length doesn't exceed the limit.
|
// it returns input string if its rune length doesn't exceed the limit.
|
||||||
func TruncateRunes(str string, limit int) string {
|
func TruncateRunes(str string, limit int) string {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestEllipsisGuessDisplayWidth(t *testing.T) {
|
|||||||
t.Run(c.r, func(t *testing.T) {
|
t.Run(c.r, func(t *testing.T) {
|
||||||
w := 0
|
w := 0
|
||||||
for _, r := range c.r {
|
for _, r := range c.r {
|
||||||
w += ellipsisGuessDisplayWidth(r)
|
w += ellipsisDisplayGuessWidth(r)
|
||||||
}
|
}
|
||||||
assert.Equal(t, c.want, w, "hex=% x", []byte(c.r))
|
assert.Equal(t, c.want, w, "hex=% x", []byte(c.r))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/repo"
|
"code.gitea.io/gitea/models/repo"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
|
|
||||||
"github.com/gorilla/feeds"
|
"github.com/gorilla/feeds"
|
||||||
@@ -15,10 +16,14 @@ import (
|
|||||||
|
|
||||||
// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed
|
// ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed
|
||||||
func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
|
func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) {
|
||||||
commits, err := ctx.Repo.Commit.CommitsByRange(0, 10, "")
|
var commits []*git.Commit
|
||||||
if err != nil {
|
var err error
|
||||||
ctx.ServerError("ShowBranchFeed", err)
|
if ctx.Repo.Commit != nil {
|
||||||
return
|
commits, err = ctx.Repo.Commit.CommitsByRange(0, 10, "")
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("ShowBranchFeed", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
title := "Latest commits for branch " + ctx.Repo.BranchName
|
title := "Latest commits for branch " + ctx.Repo.BranchName
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import (
|
|||||||
unit_model "code.gitea.io/gitea/models/unit"
|
unit_model "code.gitea.io/gitea/models/unit"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
giturl "code.gitea.io/gitea/modules/git/url"
|
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
|
"code.gitea.io/gitea/modules/htmlutil"
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
@@ -309,34 +309,41 @@ func handleRepoEmptyOrBroken(ctx *context.Context) {
|
|||||||
ctx.Redirect(link)
|
ctx.Redirect(link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRepoViewSubmodule(ctx *context.Context, submodule *git.SubModule) {
|
func isViewHomeOnlyContent(ctx *context.Context) bool {
|
||||||
submoduleRepoURL, err := giturl.ParseRepositoryURL(ctx, submodule.URL)
|
return ctx.FormBool("only_content")
|
||||||
if err != nil {
|
}
|
||||||
HandleGitError(ctx, "prepareToRenderDirOrFile: ParseRepositoryURL", err)
|
|
||||||
|
func handleRepoViewSubmodule(ctx *context.Context, commitSubmoduleFile *git.CommitSubmoduleFile) {
|
||||||
|
submoduleWebLink := commitSubmoduleFile.SubmoduleWebLinkTree(ctx)
|
||||||
|
if submoduleWebLink == nil {
|
||||||
|
ctx.Data["NotFoundPrompt"] = ctx.Repo.TreePath
|
||||||
|
ctx.NotFound(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
submoduleURL := giturl.MakeRepositoryWebLink(submoduleRepoURL)
|
|
||||||
if httplib.IsCurrentGiteaSiteURL(ctx, submoduleURL) {
|
redirectLink := submoduleWebLink.CommitWebLink
|
||||||
ctx.RedirectToCurrentSite(submoduleURL)
|
if isViewHomeOnlyContent(ctx) {
|
||||||
} else {
|
ctx.Resp.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
_, _ = ctx.Resp.Write([]byte(htmlutil.HTMLFormat(`<a href="%s">%s</a>`, redirectLink, redirectLink)))
|
||||||
|
} else if !httplib.IsCurrentGiteaSiteURL(ctx, redirectLink) {
|
||||||
// don't auto-redirect to external URL, to avoid open redirect or phishing
|
// don't auto-redirect to external URL, to avoid open redirect or phishing
|
||||||
ctx.Data["NotFoundPrompt"] = submoduleURL
|
ctx.Data["NotFoundPrompt"] = redirectLink
|
||||||
ctx.NotFound(nil)
|
ctx.NotFound(nil)
|
||||||
|
} else {
|
||||||
|
ctx.Redirect(submoduleWebLink.CommitWebLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
|
func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
|
||||||
return func(ctx *context.Context) {
|
return func(ctx *context.Context) {
|
||||||
if entry.IsSubModule() {
|
if entry.IsSubModule() {
|
||||||
submodule, err := ctx.Repo.Commit.GetSubModule(entry.Name())
|
commitSubmoduleFile, err := git.GetCommitInfoSubmoduleFile(ctx.Repo.RepoLink, ctx.Repo.TreePath, ctx.Repo.Commit, entry.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleGitError(ctx, "prepareToRenderDirOrFile: GetSubModule", err)
|
HandleGitError(ctx, "prepareToRenderDirOrFile: GetCommitInfoSubmoduleFile", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
handleRepoViewSubmodule(ctx, submodule)
|
handleRepoViewSubmodule(ctx, commitSubmoduleFile)
|
||||||
return
|
} else if entry.IsDir() {
|
||||||
}
|
|
||||||
if entry.IsDir() {
|
|
||||||
prepareToRenderDirectory(ctx)
|
prepareToRenderDirectory(ctx)
|
||||||
} else {
|
} else {
|
||||||
prepareToRenderFile(ctx, entry)
|
prepareToRenderFile(ctx, entry)
|
||||||
@@ -472,7 +479,7 @@ func Home(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.FormBool("only_content") {
|
if isViewHomeOnlyContent(ctx) {
|
||||||
ctx.HTML(http.StatusOK, tplRepoViewContent)
|
ctx.HTML(http.StatusOK, tplRepoViewContent)
|
||||||
} else if len(treeNames) != 0 {
|
} else if len(treeNames) != 0 {
|
||||||
ctx.HTML(http.StatusOK, tplRepoView)
|
ctx.HTML(http.StatusOK, tplRepoView)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
git_module "code.gitea.io/gitea/modules/git"
|
git_module "code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/services/contexttest"
|
"code.gitea.io/gitea/services/contexttest"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -19,14 +18,20 @@ func TestViewHomeSubmoduleRedirect(t *testing.T) {
|
|||||||
unittest.PrepareTestEnv(t)
|
unittest.PrepareTestEnv(t)
|
||||||
|
|
||||||
ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
|
ctx, _ := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
|
||||||
submodule := &git_module.SubModule{Path: "test-submodule", URL: setting.AppURL + "user2/repo-other.git"}
|
submodule := git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id")
|
||||||
handleRepoViewSubmodule(ctx, submodule)
|
handleRepoViewSubmodule(ctx, submodule)
|
||||||
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
|
assert.Equal(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
|
||||||
assert.Equal(t, "/user2/repo-other", ctx.Resp.Header().Get("Location"))
|
assert.Equal(t, "/user2/repo-other/tree/any-ref-id", ctx.Resp.Header().Get("Location"))
|
||||||
|
|
||||||
ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
|
ctx, _ = contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule")
|
||||||
submodule = &git_module.SubModule{Path: "test-submodule", URL: "https://other/user2/repo-other.git"}
|
submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "https://other/user2/repo-other.git", "any-ref-id")
|
||||||
handleRepoViewSubmodule(ctx, submodule)
|
handleRepoViewSubmodule(ctx, submodule)
|
||||||
// do not auto-redirect for external URLs, to avoid open redirect or phishing
|
// do not auto-redirect for external URLs, to avoid open redirect or phishing
|
||||||
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
|
assert.Equal(t, http.StatusNotFound, ctx.Resp.WrittenStatus())
|
||||||
|
|
||||||
|
ctx, respWriter := contexttest.MockContext(t, "/user2/repo1/src/branch/master/test-submodule?only_content=true")
|
||||||
|
submodule = git_module.NewCommitSubmoduleFile("/user2/repo1", "test-submodule", "../repo-other", "any-ref-id")
|
||||||
|
handleRepoViewSubmodule(ctx, submodule)
|
||||||
|
assert.Equal(t, http.StatusOK, ctx.Resp.WrittenStatus())
|
||||||
|
assert.Equal(t, `<a href="/user2/repo-other/tree/any-ref-id">/user2/repo-other/tree/any-ref-id</a>`, respWriter.Body.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1239,7 +1239,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
checker, err := attribute.NewBatchChecker(gitRepo, opts.AfterCommitID, []string{attribute.LinguistVendored, attribute.LinguistGenerated, attribute.LinguistLanguage, attribute.GitlabLanguage})
|
checker, err := attribute.NewBatchChecker(gitRepo, opts.AfterCommitID, []string{attribute.LinguistVendored, attribute.LinguistGenerated, attribute.LinguistLanguage, attribute.GitlabLanguage, attribute.Diff})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1248,6 +1248,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit
|
|||||||
for _, diffFile := range diff.Files {
|
for _, diffFile := range diff.Files {
|
||||||
isVendored := optional.None[bool]()
|
isVendored := optional.None[bool]()
|
||||||
isGenerated := optional.None[bool]()
|
isGenerated := optional.None[bool]()
|
||||||
|
attrDiff := optional.None[string]()
|
||||||
attrs, err := checker.CheckPath(diffFile.Name)
|
attrs, err := checker.CheckPath(diffFile.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isVendored, isGenerated = attrs.GetVendored(), attrs.GetGenerated()
|
isVendored, isGenerated = attrs.GetVendored(), attrs.GetGenerated()
|
||||||
@@ -1255,6 +1256,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit
|
|||||||
if language.Has() {
|
if language.Has() {
|
||||||
diffFile.Language = language.Value()
|
diffFile.Language = language.Value()
|
||||||
}
|
}
|
||||||
|
attrDiff = attrs.Get(attribute.Diff).ToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate Submodule URLs
|
// Populate Submodule URLs
|
||||||
@@ -1276,7 +1278,8 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit
|
|||||||
diffFile.Sections = append(diffFile.Sections, tailSection)
|
diffFile.Sections = append(diffFile.Sections, tailSection)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setting.Git.DisableDiffHighlight {
|
shouldFullFileHighlight := !setting.Git.DisableDiffHighlight && attrDiff.Value() == ""
|
||||||
|
if shouldFullFileHighlight {
|
||||||
if limitedContent.LeftContent != nil && limitedContent.LeftContent.buf.Len() < MaxDiffHighlightEntireFileSize {
|
if limitedContent.LeftContent != nil && limitedContent.LeftContent.buf.Len() < MaxDiffHighlightEntireFileSize {
|
||||||
diffFile.highlightedLeftLines = highlightCodeLines(diffFile, true /* left */, limitedContent.LeftContent.buf.String())
|
diffFile.highlightedLeftLines = highlightCodeLines(diffFile, true /* left */, limitedContent.LeftContent.buf.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,15 +90,8 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
|
|||||||
if rangeStart >= len(entries) {
|
if rangeStart >= len(entries) {
|
||||||
return tree, nil
|
return tree, nil
|
||||||
}
|
}
|
||||||
var rangeEnd int
|
rangeEnd := min(rangeStart+perPage, len(entries))
|
||||||
if len(entries) > perPage {
|
tree.Truncated = rangeEnd < len(entries)
|
||||||
tree.Truncated = true
|
|
||||||
}
|
|
||||||
if rangeStart+perPage < len(entries) {
|
|
||||||
rangeEnd = rangeStart + perPage
|
|
||||||
} else {
|
|
||||||
rangeEnd = len(entries)
|
|
||||||
}
|
|
||||||
tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart)
|
tree.Entries = make([]api.GitEntry, rangeEnd-rangeStart)
|
||||||
for e := rangeStart; e < rangeEnd; e++ {
|
for e := rangeStart; e < rangeEnd; e++ {
|
||||||
i := e - rangeStart
|
i := e - rangeStart
|
||||||
@@ -178,7 +171,9 @@ func newTreeViewNodeFromEntry(ctx context.Context, repoLink string, renderedIcon
|
|||||||
} else if subModule != nil {
|
} else if subModule != nil {
|
||||||
submoduleFile := git.NewCommitSubmoduleFile(repoLink, node.FullPath, subModule.URL, entry.ID.String())
|
submoduleFile := git.NewCommitSubmoduleFile(repoLink, node.FullPath, subModule.URL, entry.ID.String())
|
||||||
webLink := submoduleFile.SubmoduleWebLinkTree(ctx)
|
webLink := submoduleFile.SubmoduleWebLinkTree(ctx)
|
||||||
node.SubmoduleURL = webLink.CommitWebLink
|
if webLink != nil {
|
||||||
|
node.SubmoduleURL = webLink.CommitWebLink
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -402,16 +402,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
rel, has := relMap[lowerTag]
|
rel, has := relMap[lowerTag]
|
||||||
|
title, note := git.SplitCommitTitleBody(tag.Message, 255)
|
||||||
parts := strings.SplitN(tag.Message, "\n", 2)
|
|
||||||
note := ""
|
|
||||||
if len(parts) > 1 {
|
|
||||||
note = parts[1]
|
|
||||||
}
|
|
||||||
if !has {
|
if !has {
|
||||||
rel = &repo_model.Release{
|
rel = &repo_model.Release{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
Title: parts[0],
|
Title: title,
|
||||||
TagName: tags[i],
|
TagName: tags[i],
|
||||||
LowerTagName: lowerTag,
|
LowerTagName: lowerTag,
|
||||||
Target: "",
|
Target: "",
|
||||||
@@ -430,7 +425,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
|
|||||||
rel.Sha1 = commit.ID.String()
|
rel.Sha1 = commit.ID.String()
|
||||||
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
|
rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix())
|
||||||
if rel.IsTag {
|
if rel.IsTag {
|
||||||
rel.Title = parts[0]
|
rel.Title = title
|
||||||
rel.Note = note
|
rel.Note = note
|
||||||
} else {
|
} else {
|
||||||
rel.IsDraft = false
|
rel.IsDraft = false
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import (
|
|||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAPIReposGitTrees(t *testing.T) {
|
func TestAPIReposGitTrees(t *testing.T) {
|
||||||
@@ -32,13 +36,21 @@ func TestAPIReposGitTrees(t *testing.T) {
|
|||||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||||
|
|
||||||
// Test a public repo that anyone can GET the tree of
|
// Test a public repo that anyone can GET the tree of
|
||||||
for _, ref := range [...]string{
|
_ = MakeRequest(t, NewRequest(t, "GET", "/api/v1/repos/user2/repo1/git/trees/master"), http.StatusOK)
|
||||||
"master", // Branch
|
|
||||||
repo1TreeSHA, // Tree SHA
|
resp := MakeRequest(t, NewRequest(t, "GET", "/api/v1/repos/user2/repo1/git/trees/62fb502a7172d4453f0322a2cc85bddffa57f07a?per_page=1"), http.StatusOK)
|
||||||
} {
|
var respGitTree api.GitTreeResponse
|
||||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/trees/%s", user2.Name, repo1.Name, ref)
|
DecodeJSON(t, resp, &respGitTree)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
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
|
// Tests a private repo with no token so will fail
|
||||||
for _, ref := range [...]string{
|
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)
|
req = NewRequest(t, "GET", "/api/v1/repos/user30/empty/raw/main/README.md").AddTokenAuth(token)
|
||||||
session.MakeRequest(t, req, http.StatusNotFound)
|
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
|
// create a new file
|
||||||
req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
|
req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ td .commit-summary {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
gap: 0.25em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
|
|||||||
@@ -547,7 +547,12 @@ function initIssueTemplateCommentEditors(commentForm: HTMLFormElement) {
|
|||||||
// deactivate all markdown editors
|
// deactivate all markdown editors
|
||||||
showElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-real'));
|
showElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-real'));
|
||||||
hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .combo-markdown-editor'));
|
hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .combo-markdown-editor'));
|
||||||
hideElem(commentForm.querySelectorAll('.combo-editor-dropzone .form-field-dropzone'));
|
queryElems(commentForm, '.combo-editor-dropzone .form-field-dropzone', (dropzoneContainer) => {
|
||||||
|
// if "form-field-dropzone" exists, then "dropzone" must also exist
|
||||||
|
const dropzone = dropzoneContainer.querySelector<HTMLElement>('.dropzone').dropzone;
|
||||||
|
const hasUploadedFiles = dropzone.files.length !== 0;
|
||||||
|
toggleElem(dropzoneContainer, hasUploadedFiles);
|
||||||
|
});
|
||||||
|
|
||||||
// activate this markdown editor
|
// activate this markdown editor
|
||||||
hideElem(fieldTextarea);
|
hideElem(fieldTextarea);
|
||||||
|
|||||||
@@ -34,8 +34,12 @@ export function initRepoMigration() {
|
|||||||
elCloneAddr.addEventListener('input', () => {
|
elCloneAddr.addEventListener('input', () => {
|
||||||
if (repoNameChanged) return;
|
if (repoNameChanged) return;
|
||||||
let repoNameFromUrl = elCloneAddr.value.split(/[?#]/)[0];
|
let repoNameFromUrl = elCloneAddr.value.split(/[?#]/)[0];
|
||||||
repoNameFromUrl = /^(.*\/)?((.+?)\/?)$/.exec(repoNameFromUrl)[3];
|
const parts = /^(.*\/)?((.+?)\/?)$/.exec(repoNameFromUrl);
|
||||||
repoNameFromUrl = repoNameFromUrl.split(/[?#]/)[0];
|
if (!parts || parts.length < 4) {
|
||||||
|
elRepoName.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
repoNameFromUrl = parts[3].split(/[?#]/)[0];
|
||||||
elRepoName.value = sanitizeRepoName(repoNameFromUrl);
|
elRepoName.value = sanitizeRepoName(repoNameFromUrl);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user