gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright 2025 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package attribute
  4. import (
  5. "bytes"
  6. "context"
  7. "errors"
  8. "fmt"
  9. "os"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/git/gitcmd"
  12. )
  13. func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*gitcmd.Command, []string, func(), error) {
  14. cancel := func() {}
  15. envs := []string{"GIT_FLUSH=1"}
  16. cmd := gitcmd.NewCommand("check-attr", "-z")
  17. if len(attributes) == 0 {
  18. cmd.AddArguments("--all")
  19. }
  20. // there is treeish, read from bare repo or temp index created by "read-tree"
  21. if treeish != "" {
  22. if git.DefaultFeatures().SupportCheckAttrOnBare {
  23. cmd.AddArguments("--source")
  24. cmd.AddDynamicArguments(treeish)
  25. } else {
  26. indexFilename, worktree, deleteTemporaryFile, err := gitRepo.ReadTreeToTemporaryIndex(treeish)
  27. if err != nil {
  28. return nil, nil, nil, err
  29. }
  30. cmd.AddArguments("--cached")
  31. envs = append(envs,
  32. "GIT_INDEX_FILE="+indexFilename,
  33. "GIT_WORK_TREE="+worktree,
  34. )
  35. cancel = deleteTemporaryFile
  36. }
  37. } else {
  38. // Read from existing index, in cases where the repo is bare and has an index,
  39. // or the work tree contains unstaged changes that shouldn't affect the attribute check.
  40. // It is caller's responsibility to add changed ".gitattributes" into the index if they want to respect the new changes.
  41. cmd.AddArguments("--cached")
  42. }
  43. cmd.AddDynamicArguments(attributes...)
  44. if len(filenames) > 0 {
  45. cmd.AddDashesAndList(filenames...)
  46. }
  47. return cmd, envs, cancel, nil
  48. }
  49. type CheckAttributeOpts struct {
  50. Filenames []string
  51. Attributes []string
  52. }
  53. // CheckAttributes return the attributes of the given filenames and attributes in the given treeish.
  54. // If treeish is empty, then it will use current working directory, otherwise it will use the provided treeish on the bare repo
  55. func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish string, opts CheckAttributeOpts) (map[string]*Attributes, error) {
  56. cmd, envs, cancel, err := checkAttrCommand(gitRepo, treeish, opts.Filenames, opts.Attributes)
  57. if err != nil {
  58. return nil, err
  59. }
  60. defer cancel()
  61. stdOut := new(bytes.Buffer)
  62. stdErr := new(bytes.Buffer)
  63. if err := cmd.Run(ctx, &gitcmd.RunOpts{
  64. Env: append(os.Environ(), envs...),
  65. Dir: gitRepo.Path,
  66. Stdout: stdOut,
  67. Stderr: stdErr,
  68. }); err != nil {
  69. return nil, fmt.Errorf("failed to run check-attr: %w\n%s\n%s", err, stdOut.String(), stdErr.String())
  70. }
  71. fields := bytes.Split(stdOut.Bytes(), []byte{'\000'})
  72. if len(fields)%3 != 1 {
  73. return nil, errors.New("wrong number of fields in return from check-attr")
  74. }
  75. attributesMap := make(map[string]*Attributes)
  76. for i := 0; i < (len(fields) / 3); i++ {
  77. filename := string(fields[3*i])
  78. attribute := string(fields[3*i+1])
  79. info := string(fields[3*i+2])
  80. attribute2info, ok := attributesMap[filename]
  81. if !ok {
  82. attribute2info = NewAttributes()
  83. attributesMap[filename] = attribute2info
  84. }
  85. attribute2info.m[attribute] = Attribute(info)
  86. }
  87. return attributesMap, nil
  88. }