Refactor git attributes (#29356)
This commit is contained in:
parent
98ab9445d1
commit
4197e28100
35
modules/git/attribute.go
Normal file
35
modules/git/attribute.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AttributeLinguistVendored = "linguist-vendored"
|
||||||
|
AttributeLinguistGenerated = "linguist-generated"
|
||||||
|
AttributeLinguistDocumentation = "linguist-documentation"
|
||||||
|
AttributeLinguistDetectable = "linguist-detectable"
|
||||||
|
AttributeLinguistLanguage = "linguist-language"
|
||||||
|
AttributeGitlabLanguage = "gitlab-language"
|
||||||
|
)
|
||||||
|
|
||||||
|
// true if "set"/"true", false if "unset"/"false", none otherwise
|
||||||
|
func AttributeToBool(attr map[string]string, name string) optional.Option[bool] {
|
||||||
|
switch attr[name] {
|
||||||
|
case "set", "true":
|
||||||
|
return optional.Some(true)
|
||||||
|
case "unset", "false":
|
||||||
|
return optional.Some(false)
|
||||||
|
}
|
||||||
|
return optional.None[bool]()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AttributeToString(attr map[string]string, name string) optional.Option[string] {
|
||||||
|
if value, has := attr[name]; has && value != "unspecified" {
|
||||||
|
return optional.Some(value)
|
||||||
|
}
|
||||||
|
return optional.None[string]()
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckAttributeOpts represents the possible options to CheckAttribute
|
// CheckAttributeOpts represents the possible options to CheckAttribute
|
||||||
|
@ -292,10 +291,17 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
|
||||||
}
|
}
|
||||||
|
|
||||||
checker := &CheckAttributeReader{
|
checker := &CheckAttributeReader{
|
||||||
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language", "linguist-documentation", "linguist-detectable"},
|
Attributes: []string{
|
||||||
Repo: repo,
|
AttributeLinguistVendored,
|
||||||
IndexFile: indexFilename,
|
AttributeLinguistGenerated,
|
||||||
WorkTree: worktree,
|
AttributeLinguistDocumentation,
|
||||||
|
AttributeLinguistDetectable,
|
||||||
|
AttributeLinguistLanguage,
|
||||||
|
AttributeGitlabLanguage,
|
||||||
|
},
|
||||||
|
Repo: repo,
|
||||||
|
IndexFile: indexFilename,
|
||||||
|
WorkTree: worktree,
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(repo.Ctx)
|
ctx, cancel := context.WithCancel(repo.Ctx)
|
||||||
if err := checker.Init(ctx); err != nil {
|
if err := checker.Init(ctx); err != nil {
|
||||||
|
@ -317,23 +323,3 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
|
||||||
|
|
||||||
return checker, deferable
|
return checker, deferable
|
||||||
}
|
}
|
||||||
|
|
||||||
// true if "set"/"true", false if "unset"/"false", none otherwise
|
|
||||||
func attributeToBool(attr map[string]string, name string) optional.Option[bool] {
|
|
||||||
if value, has := attr[name]; has && value != "unspecified" {
|
|
||||||
switch value {
|
|
||||||
case "set", "true":
|
|
||||||
return optional.Some(true)
|
|
||||||
case "unset", "false":
|
|
||||||
return optional.Some(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return optional.None[bool]()
|
|
||||||
}
|
|
||||||
|
|
||||||
func attributeToString(attr map[string]string, name string) optional.Option[string] {
|
|
||||||
if value, has := attr[name]; has && value != "unspecified" {
|
|
||||||
return optional.Some(value)
|
|
||||||
}
|
|
||||||
return optional.None[string]()
|
|
||||||
}
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||||
select {
|
select {
|
||||||
case attr := <-wr.ReadAttribute():
|
case attr := <-wr.ReadAttribute():
|
||||||
assert.Equal(t, ".gitignore\"\n", attr.Filename)
|
assert.Equal(t, ".gitignore\"\n", attr.Filename)
|
||||||
assert.Equal(t, "linguist-vendored", attr.Attribute)
|
assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
|
||||||
assert.Equal(t, "unspecified", attr.Value)
|
assert.Equal(t, "unspecified", attr.Value)
|
||||||
case <-time.After(100 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
assert.FailNow(t, "took too long to read an attribute from the list")
|
assert.FailNow(t, "took too long to read an attribute from the list")
|
||||||
|
@ -38,7 +38,7 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||||
select {
|
select {
|
||||||
case attr := <-wr.ReadAttribute():
|
case attr := <-wr.ReadAttribute():
|
||||||
assert.Equal(t, ".gitignore\"\n", attr.Filename)
|
assert.Equal(t, ".gitignore\"\n", attr.Filename)
|
||||||
assert.Equal(t, "linguist-vendored", attr.Attribute)
|
assert.Equal(t, AttributeLinguistVendored, attr.Attribute)
|
||||||
assert.Equal(t, "unspecified", attr.Value)
|
assert.Equal(t, "unspecified", attr.Value)
|
||||||
case <-time.After(100 * time.Millisecond):
|
case <-time.After(100 * time.Millisecond):
|
||||||
assert.FailNow(t, "took too long to read an attribute from the list")
|
assert.FailNow(t, "took too long to read an attribute from the list")
|
||||||
|
@ -77,21 +77,21 @@ func Test_nulSeparatedAttributeWriter_ReadAttribute(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, attributeTriple{
|
assert.EqualValues(t, attributeTriple{
|
||||||
Filename: "shouldbe.vendor",
|
Filename: "shouldbe.vendor",
|
||||||
Attribute: "linguist-vendored",
|
Attribute: AttributeLinguistVendored,
|
||||||
Value: "set",
|
Value: "set",
|
||||||
}, attr)
|
}, attr)
|
||||||
attr = <-wr.ReadAttribute()
|
attr = <-wr.ReadAttribute()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, attributeTriple{
|
assert.EqualValues(t, attributeTriple{
|
||||||
Filename: "shouldbe.vendor",
|
Filename: "shouldbe.vendor",
|
||||||
Attribute: "linguist-generated",
|
Attribute: AttributeLinguistGenerated,
|
||||||
Value: "unspecified",
|
Value: "unspecified",
|
||||||
}, attr)
|
}, attr)
|
||||||
attr = <-wr.ReadAttribute()
|
attr = <-wr.ReadAttribute()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, attributeTriple{
|
assert.EqualValues(t, attributeTriple{
|
||||||
Filename: "shouldbe.vendor",
|
Filename: "shouldbe.vendor",
|
||||||
Attribute: "linguist-language",
|
Attribute: AttributeLinguistLanguage,
|
||||||
Value: "unspecified",
|
Value: "unspecified",
|
||||||
}, attr)
|
}, attr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ package git
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -46,3 +48,20 @@ func mergeLanguageStats(stats map[string]int64) map[string]int64 {
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TryReadLanguageAttribute(attrs map[string]string) optional.Option[string] {
|
||||||
|
language := AttributeToString(attrs, AttributeLinguistLanguage)
|
||||||
|
if language.Value() == "" {
|
||||||
|
language = AttributeToString(attrs, AttributeGitlabLanguage)
|
||||||
|
if language.Has() {
|
||||||
|
raw := language.Value()
|
||||||
|
// gitlab-language may have additional parameters after the language
|
||||||
|
// ignore them and just use the main language
|
||||||
|
// https://docs.gitlab.com/ee/user/project/highlighting.html#override-syntax-highlighting-for-a-file-type
|
||||||
|
if idx := strings.IndexByte(raw, '?'); idx >= 0 {
|
||||||
|
language = optional.Some(raw[:idx])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return language
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ package git
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/analyze"
|
"code.gitea.io/gitea/modules/analyze"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
|
@ -66,36 +65,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
|
||||||
if checker != nil {
|
if checker != nil {
|
||||||
attrs, err := checker.CheckPath(f.Name)
|
attrs, err := checker.CheckPath(f.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isVendored = attributeToBool(attrs, "linguist-vendored")
|
isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
|
||||||
if isVendored.ValueOrDefault(false) {
|
if isVendored.ValueOrDefault(false) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
isGenerated = attributeToBool(attrs, "linguist-generated")
|
isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
|
||||||
if isGenerated.ValueOrDefault(false) {
|
if isGenerated.ValueOrDefault(false) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
isDocumentation = attributeToBool(attrs, "linguist-documentation")
|
isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
|
||||||
if isDocumentation.ValueOrDefault(false) {
|
if isDocumentation.ValueOrDefault(false) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
isDetectable = attributeToBool(attrs, "linguist-detectable")
|
isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
|
||||||
if !isDetectable.ValueOrDefault(true) {
|
if !isDetectable.ValueOrDefault(true) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLanguage := attributeToString(attrs, "linguist-language")
|
hasLanguage := TryReadLanguageAttribute(attrs)
|
||||||
if hasLanguage.Value() == "" {
|
|
||||||
hasLanguage = attributeToString(attrs, "gitlab-language")
|
|
||||||
if hasLanguage.Has() {
|
|
||||||
language := hasLanguage.Value()
|
|
||||||
if idx := strings.IndexByte(language, '?'); idx >= 0 {
|
|
||||||
hasLanguage = optional.Some(language[:idx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasLanguage.Value() != "" {
|
if hasLanguage.Value() != "" {
|
||||||
language := hasLanguage.Value()
|
language := hasLanguage.Value()
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ package git
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/analyze"
|
"code.gitea.io/gitea/modules/analyze"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -97,36 +96,27 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
|
||||||
if checker != nil {
|
if checker != nil {
|
||||||
attrs, err := checker.CheckPath(f.Name())
|
attrs, err := checker.CheckPath(f.Name())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
isVendored = attributeToBool(attrs, "linguist-vendored")
|
isVendored = AttributeToBool(attrs, AttributeLinguistVendored)
|
||||||
if isVendored.ValueOrDefault(false) {
|
if isVendored.ValueOrDefault(false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isGenerated = attributeToBool(attrs, "linguist-generated")
|
isGenerated = AttributeToBool(attrs, AttributeLinguistGenerated)
|
||||||
if isGenerated.ValueOrDefault(false) {
|
if isGenerated.ValueOrDefault(false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isDocumentation = attributeToBool(attrs, "linguist-documentation")
|
isDocumentation = AttributeToBool(attrs, AttributeLinguistDocumentation)
|
||||||
if isDocumentation.ValueOrDefault(false) {
|
if isDocumentation.ValueOrDefault(false) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
isDetectable = attributeToBool(attrs, "linguist-detectable")
|
isDetectable = AttributeToBool(attrs, AttributeLinguistDetectable)
|
||||||
if !isDetectable.ValueOrDefault(true) {
|
if !isDetectable.ValueOrDefault(true) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
hasLanguage := attributeToString(attrs, "linguist-language")
|
hasLanguage := TryReadLanguageAttribute(attrs)
|
||||||
if hasLanguage.Value() == "" {
|
|
||||||
hasLanguage = attributeToString(attrs, "gitlab-language")
|
|
||||||
if hasLanguage.Has() {
|
|
||||||
language := hasLanguage.Value()
|
|
||||||
if idx := strings.IndexByte(language, '?'); idx >= 0 {
|
|
||||||
hasLanguage = optional.Some(language[:idx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasLanguage.Value() != "" {
|
if hasLanguage.Value() != "" {
|
||||||
language := hasLanguage.Value()
|
language := hasLanguage.Value()
|
||||||
|
|
||||||
|
|
|
@ -635,11 +635,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||||
defer deferable()
|
defer deferable()
|
||||||
attrs, err := checker.CheckPath(ctx.Repo.TreePath)
|
attrs, err := checker.CheckPath(ctx.Repo.TreePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
vendored, has := attrs["linguist-vendored"]
|
ctx.Data["IsVendored"] = git.AttributeToBool(attrs, git.AttributeLinguistVendored).Value()
|
||||||
ctx.Data["IsVendored"] = has && (vendored == "set" || vendored == "true")
|
ctx.Data["IsGenerated"] = git.AttributeToBool(attrs, git.AttributeLinguistGenerated).Value()
|
||||||
|
|
||||||
generated, has := attrs["linguist-generated"]
|
|
||||||
ctx.Data["IsGenerated"] = has && (generated == "set" || generated == "true")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/highlight"
|
"code.gitea.io/gitea/modules/highlight"
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
|
||||||
|
@ -1181,41 +1182,30 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi
|
||||||
|
|
||||||
for _, diffFile := range diff.Files {
|
for _, diffFile := range diff.Files {
|
||||||
|
|
||||||
gotVendor := false
|
isVendored := optional.None[bool]()
|
||||||
gotGenerated := false
|
isGenerated := optional.None[bool]()
|
||||||
if checker != nil {
|
if checker != nil {
|
||||||
attrs, err := checker.CheckPath(diffFile.Name)
|
attrs, err := checker.CheckPath(diffFile.Name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if vendored, has := attrs["linguist-vendored"]; has {
|
isVendored = git.AttributeToBool(attrs, git.AttributeLinguistVendored)
|
||||||
if vendored == "set" || vendored == "true" {
|
isGenerated = git.AttributeToBool(attrs, git.AttributeLinguistGenerated)
|
||||||
diffFile.IsVendored = true
|
|
||||||
gotVendor = true
|
language := git.TryReadLanguageAttribute(attrs)
|
||||||
} else {
|
if language.Has() {
|
||||||
gotVendor = vendored == "false"
|
diffFile.Language = language.Value()
|
||||||
}
|
|
||||||
}
|
|
||||||
if generated, has := attrs["linguist-generated"]; has {
|
|
||||||
if generated == "set" || generated == "true" {
|
|
||||||
diffFile.IsGenerated = true
|
|
||||||
gotGenerated = true
|
|
||||||
} else {
|
|
||||||
gotGenerated = generated == "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if language, has := attrs["linguist-language"]; has && language != "unspecified" && language != "" {
|
|
||||||
diffFile.Language = language
|
|
||||||
} else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" {
|
|
||||||
diffFile.Language = language
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gotVendor {
|
if !isVendored.Has() {
|
||||||
diffFile.IsVendored = analyze.IsVendor(diffFile.Name)
|
isVendored = optional.Some(analyze.IsVendor(diffFile.Name))
|
||||||
}
|
}
|
||||||
if !gotGenerated {
|
diffFile.IsVendored = isVendored.Value()
|
||||||
diffFile.IsGenerated = analyze.IsGenerated(diffFile.Name)
|
|
||||||
|
if !isGenerated.Has() {
|
||||||
|
isGenerated = optional.Some(analyze.IsGenerated(diffFile.Name))
|
||||||
}
|
}
|
||||||
|
diffFile.IsGenerated = isGenerated.Value()
|
||||||
|
|
||||||
tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID)
|
tailSection := diffFile.GetTailSection(gitRepo, opts.BeforeCommitID, opts.AfterCommitID)
|
||||||
if tailSection != nil {
|
if tailSection != nil {
|
||||||
|
|
|
@ -282,7 +282,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) (
|
||||||
|
|
||||||
filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{
|
filename2attribute2info, err := gitRepo.CheckAttribute(git.CheckAttributeOpts{
|
||||||
CachedOnly: true,
|
CachedOnly: true,
|
||||||
Attributes: []string{"linguist-language", "gitlab-language"},
|
Attributes: []string{git.AttributeLinguistLanguage, git.AttributeGitlabLanguage},
|
||||||
Filenames: []string{treePath},
|
Filenames: []string{treePath},
|
||||||
IndexFile: indexFilename,
|
IndexFile: indexFilename,
|
||||||
WorkTree: worktree,
|
WorkTree: worktree,
|
||||||
|
@ -291,13 +291,7 @@ func TryGetContentLanguage(gitRepo *git.Repository, commitID, treePath string) (
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
language := filename2attribute2info[treePath]["linguist-language"]
|
language := git.TryReadLanguageAttribute(filename2attribute2info[treePath])
|
||||||
if language == "" || language == "unspecified" {
|
|
||||||
language = filename2attribute2info[treePath]["gitlab-language"]
|
|
||||||
}
|
|
||||||
if language == "unspecified" {
|
|
||||||
language = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return language, nil
|
return language.Value(), nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user