You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

git.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2017 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package git
  6. import (
  7. "context"
  8. "fmt"
  9. "os/exec"
  10. "runtime"
  11. "strings"
  12. "time"
  13. "code.gitea.io/gitea/modules/process"
  14. "github.com/mcuadros/go-version"
  15. )
  16. // Version return this package's current version
  17. func Version() string {
  18. return "0.4.2"
  19. }
  20. var (
  21. // Debug enables verbose logging on everything.
  22. // This should be false in case Gogs starts in SSH mode.
  23. Debug = false
  24. // Prefix the log prefix
  25. Prefix = "[git-module] "
  26. // GitVersionRequired is the minimum Git version required
  27. GitVersionRequired = "1.7.2"
  28. // GitExecutable is the command name of git
  29. // Could be updated to an absolute path while initialization
  30. GitExecutable = "git"
  31. // DefaultContext is the default context to run git commands in
  32. DefaultContext = context.Background()
  33. gitVersion string
  34. )
  35. func log(format string, args ...interface{}) {
  36. if !Debug {
  37. return
  38. }
  39. fmt.Print(Prefix)
  40. if len(args) == 0 {
  41. fmt.Println(format)
  42. } else {
  43. fmt.Printf(format+"\n", args...)
  44. }
  45. }
  46. // BinVersion returns current Git version from shell.
  47. func BinVersion() (string, error) {
  48. if len(gitVersion) > 0 {
  49. return gitVersion, nil
  50. }
  51. stdout, err := NewCommand("version").Run()
  52. if err != nil {
  53. return "", err
  54. }
  55. fields := strings.Fields(stdout)
  56. if len(fields) < 3 {
  57. return "", fmt.Errorf("not enough output: %s", stdout)
  58. }
  59. // Handle special case on Windows.
  60. i := strings.Index(fields[2], "windows")
  61. if i >= 1 {
  62. gitVersion = fields[2][:i-1]
  63. return gitVersion, nil
  64. }
  65. gitVersion = fields[2]
  66. return gitVersion, nil
  67. }
  68. // SetExecutablePath changes the path of git executable and checks the file permission and version.
  69. func SetExecutablePath(path string) error {
  70. // If path is empty, we use the default value of GitExecutable "git" to search for the location of git.
  71. if path != "" {
  72. GitExecutable = path
  73. }
  74. absPath, err := exec.LookPath(GitExecutable)
  75. if err != nil {
  76. return fmt.Errorf("Git not found: %v", err)
  77. }
  78. GitExecutable = absPath
  79. gitVersion, err := BinVersion()
  80. if err != nil {
  81. return fmt.Errorf("Git version missing: %v", err)
  82. }
  83. if version.Compare(gitVersion, GitVersionRequired, "<") {
  84. return fmt.Errorf("Git version not supported. Requires version > %v", GitVersionRequired)
  85. }
  86. return nil
  87. }
  88. // Init initializes git module
  89. func Init(ctx context.Context) error {
  90. DefaultContext = ctx
  91. // Git requires setting user.name and user.email in order to commit changes - if they're not set just add some defaults
  92. for configKey, defaultValue := range map[string]string{"user.name": "Gitea", "user.email": "gitea@fake.local"} {
  93. if err := checkAndSetConfig(configKey, defaultValue, false); err != nil {
  94. return err
  95. }
  96. }
  97. // Set git some configurations - these must be set to these values for gitea to work correctly
  98. if err := checkAndSetConfig("core.quotePath", "false", true); err != nil {
  99. return err
  100. }
  101. if version.Compare(gitVersion, "2.18", ">=") {
  102. if err := checkAndSetConfig("core.commitGraph", "true", true); err != nil {
  103. return err
  104. }
  105. if err := checkAndSetConfig("gc.writeCommitGraph", "true", true); err != nil {
  106. return err
  107. }
  108. }
  109. if runtime.GOOS == "windows" {
  110. if err := checkAndSetConfig("core.longpaths", "true", true); err != nil {
  111. return err
  112. }
  113. }
  114. return nil
  115. }
  116. func checkAndSetConfig(key, defaultValue string, forceToDefault bool) error {
  117. stdout, stderr, err := process.GetManager().Exec("git.Init(get setting)", GitExecutable, "config", "--get", key)
  118. if err != nil {
  119. perr, ok := err.(*process.Error)
  120. if !ok {
  121. return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
  122. }
  123. eerr, ok := perr.Err.(*exec.ExitError)
  124. if !ok || eerr.ExitCode() != 1 {
  125. return fmt.Errorf("Failed to get git %s(%v) errType %T: %s", key, err, err, stderr)
  126. }
  127. }
  128. currValue := strings.TrimSpace(stdout)
  129. if currValue == defaultValue || (!forceToDefault && len(currValue) > 0) {
  130. return nil
  131. }
  132. if _, stderr, err = process.GetManager().Exec(fmt.Sprintf("git.Init(set %s)", key), "git", "config", "--global", key, defaultValue); err != nil {
  133. return fmt.Errorf("Failed to set git %s(%s): %s", key, err, stderr)
  134. }
  135. return nil
  136. }
  137. // Fsck verifies the connectivity and validity of the objects in the database
  138. func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error {
  139. // Make sure timeout makes sense.
  140. if timeout <= 0 {
  141. timeout = -1
  142. }
  143. _, err := NewCommandContext(ctx, "fsck").AddArguments(args...).RunInDirTimeout(timeout, repoPath)
  144. return err
  145. }