mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-25 19:08:48 +03:00

This PR moved git attributes related code to `modules/git/attribute` sub package and moved language stats related code to `modules/git/languagestats` sub package to make it easier to maintain. And it also introduced a performance improvement which use the `git check-attr --source` which can be run in a bare git repository so that we don't need to create a git index file. The new parameter need a git version >= 2.40 . If git version less than 2.40, it will fall back to previous implementation. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: yp05327 <576951401@qq.com>
97 lines
2.7 KiB
Go
97 lines
2.7 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package attribute
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"code.gitea.io/gitea/modules/git"
|
|
)
|
|
|
|
func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*git.Command, []string, func(), error) {
|
|
cancel := func() {}
|
|
envs := []string{"GIT_FLUSH=1"}
|
|
cmd := git.NewCommand("check-attr", "-z")
|
|
if len(attributes) == 0 {
|
|
cmd.AddArguments("--all")
|
|
}
|
|
|
|
// there is treeish, read from bare repo or temp index created by "read-tree"
|
|
if treeish != "" {
|
|
if git.DefaultFeatures().SupportCheckAttrOnBare {
|
|
cmd.AddArguments("--source")
|
|
cmd.AddDynamicArguments(treeish)
|
|
} else {
|
|
indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(treeish)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
cmd.AddArguments("--cached")
|
|
envs = append(envs,
|
|
"GIT_INDEX_FILE="+indexFilename,
|
|
"GIT_WORK_TREE="+worktree,
|
|
)
|
|
cancel = deleteTemporaryFile
|
|
}
|
|
} // else: no treeish, assume it is a not a bare repo, read from working directory
|
|
|
|
cmd.AddDynamicArguments(attributes...)
|
|
if len(filenames) > 0 {
|
|
cmd.AddDashesAndList(filenames...)
|
|
}
|
|
return cmd, envs, cancel, nil
|
|
}
|
|
|
|
type CheckAttributeOpts struct {
|
|
Filenames []string
|
|
Attributes []string
|
|
}
|
|
|
|
// CheckAttributes return the attributes of the given filenames and attributes in the given treeish.
|
|
// If treeish is empty, then it will use current working directory, otherwise it will use the provided treeish on the bare repo
|
|
func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish string, opts CheckAttributeOpts) (map[string]*Attributes, error) {
|
|
cmd, envs, cancel, err := checkAttrCommand(gitRepo, treeish, opts.Filenames, opts.Attributes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cancel()
|
|
|
|
stdOut := new(bytes.Buffer)
|
|
stdErr := new(bytes.Buffer)
|
|
|
|
if err := cmd.Run(ctx, &git.RunOpts{
|
|
Env: append(os.Environ(), envs...),
|
|
Dir: gitRepo.Path,
|
|
Stdout: stdOut,
|
|
Stderr: stdErr,
|
|
}); err != nil {
|
|
return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
|
|
}
|
|
|
|
fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
|
|
if len(fields)%3 != 1 {
|
|
return nil, errors.New("wrong number of fields in return from check-attr")
|
|
}
|
|
|
|
attributesMap := make(map[string]*Attributes)
|
|
for i := 0; i < (len(fields) / 3); i++ {
|
|
filename := string(fields[3*i])
|
|
attribute := string(fields[3*i+1])
|
|
info := string(fields[3*i+2])
|
|
attribute2info, ok := attributesMap[filename]
|
|
if !ok {
|
|
attribute2info = NewAttributes()
|
|
attributesMap[filename] = attribute2info
|
|
}
|
|
attribute2info.m[attribute] = Attribute(info)
|
|
}
|
|
|
|
return attributesMap, nil
|
|
}
|