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.7KB

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