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.

init.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repository
  5. import (
  6. "bytes"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "code.gitea.io/gitea/models"
  14. "code.gitea.io/gitea/modules/git"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/util"
  18. "github.com/unknwon/com"
  19. )
  20. func prepareRepoCommit(ctx models.DBContext, repo *models.Repository, tmpDir, repoPath string, opts models.CreateRepoOptions) error {
  21. commitTimeStr := time.Now().Format(time.RFC3339)
  22. authorSig := repo.Owner.NewGitSig()
  23. // Because this may call hooks we should pass in the environment
  24. env := append(os.Environ(),
  25. "GIT_AUTHOR_NAME="+authorSig.Name,
  26. "GIT_AUTHOR_EMAIL="+authorSig.Email,
  27. "GIT_AUTHOR_DATE="+commitTimeStr,
  28. "GIT_COMMITTER_NAME="+authorSig.Name,
  29. "GIT_COMMITTER_EMAIL="+authorSig.Email,
  30. "GIT_COMMITTER_DATE="+commitTimeStr,
  31. )
  32. // Clone to temporary path and do the init commit.
  33. if stdout, err := git.NewCommand("clone", repoPath, tmpDir).
  34. SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)).
  35. RunInDirWithEnv("", env); err != nil {
  36. log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
  37. return fmt.Errorf("git clone: %v", err)
  38. }
  39. // README
  40. data, err := models.GetRepoInitFile("readme", opts.Readme)
  41. if err != nil {
  42. return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.Readme, err)
  43. }
  44. cloneLink := repo.CloneLink()
  45. match := map[string]string{
  46. "Name": repo.Name,
  47. "Description": repo.Description,
  48. "CloneURL.SSH": cloneLink.SSH,
  49. "CloneURL.HTTPS": cloneLink.HTTPS,
  50. "OwnerName": repo.OwnerName,
  51. }
  52. if err = ioutil.WriteFile(filepath.Join(tmpDir, "README.md"),
  53. []byte(com.Expand(string(data), match)), 0644); err != nil {
  54. return fmt.Errorf("write README.md: %v", err)
  55. }
  56. // .gitignore
  57. if len(opts.Gitignores) > 0 {
  58. var buf bytes.Buffer
  59. names := strings.Split(opts.Gitignores, ",")
  60. for _, name := range names {
  61. data, err = models.GetRepoInitFile("gitignore", name)
  62. if err != nil {
  63. return fmt.Errorf("GetRepoInitFile[%s]: %v", name, err)
  64. }
  65. buf.WriteString("# ---> " + name + "\n")
  66. buf.Write(data)
  67. buf.WriteString("\n")
  68. }
  69. if buf.Len() > 0 {
  70. if err = ioutil.WriteFile(filepath.Join(tmpDir, ".gitignore"), buf.Bytes(), 0644); err != nil {
  71. return fmt.Errorf("write .gitignore: %v", err)
  72. }
  73. }
  74. }
  75. // LICENSE
  76. if len(opts.License) > 0 {
  77. data, err = models.GetRepoInitFile("license", opts.License)
  78. if err != nil {
  79. return fmt.Errorf("GetRepoInitFile[%s]: %v", opts.License, err)
  80. }
  81. if err = ioutil.WriteFile(filepath.Join(tmpDir, "LICENSE"), data, 0644); err != nil {
  82. return fmt.Errorf("write LICENSE: %v", err)
  83. }
  84. }
  85. return nil
  86. }
  87. // initRepoCommit temporarily changes with work directory.
  88. func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, defaultBranch string) (err error) {
  89. commitTimeStr := time.Now().Format(time.RFC3339)
  90. sig := u.NewGitSig()
  91. // Because this may call hooks we should pass in the environment
  92. env := append(os.Environ(),
  93. "GIT_AUTHOR_NAME="+sig.Name,
  94. "GIT_AUTHOR_EMAIL="+sig.Email,
  95. "GIT_AUTHOR_DATE="+commitTimeStr,
  96. "GIT_COMMITTER_DATE="+commitTimeStr,
  97. )
  98. committerName := sig.Name
  99. committerEmail := sig.Email
  100. if stdout, err := git.NewCommand("add", "--all").
  101. SetDescription(fmt.Sprintf("initRepoCommit (git add): %s", tmpPath)).
  102. RunInDir(tmpPath); err != nil {
  103. log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err)
  104. return fmt.Errorf("git add --all: %v", err)
  105. }
  106. err = git.LoadGitVersion()
  107. if err != nil {
  108. return fmt.Errorf("Unable to get git version: %v", err)
  109. }
  110. args := []string{
  111. "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
  112. "-m", "Initial commit",
  113. }
  114. if git.CheckGitVersionAtLeast("1.7.9") == nil {
  115. sign, keyID, signer, _ := models.SignInitialCommit(tmpPath, u)
  116. if sign {
  117. args = append(args, "-S"+keyID)
  118. if repo.GetTrustModel() == models.CommitterTrustModel || repo.GetTrustModel() == models.CollaboratorCommitterTrustModel {
  119. // need to set the committer to the KeyID owner
  120. committerName = signer.Name
  121. committerEmail = signer.Email
  122. }
  123. } else if git.CheckGitVersionAtLeast("2.0.0") == nil {
  124. args = append(args, "--no-gpg-sign")
  125. }
  126. }
  127. env = append(env,
  128. "GIT_COMMITTER_NAME="+committerName,
  129. "GIT_COMMITTER_EMAIL="+committerEmail,
  130. )
  131. if stdout, err := git.NewCommand(args...).
  132. SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
  133. RunInDirWithEnv(tmpPath, env); err != nil {
  134. log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err)
  135. return fmt.Errorf("git commit: %v", err)
  136. }
  137. if len(defaultBranch) == 0 {
  138. defaultBranch = setting.Repository.DefaultBranch
  139. }
  140. if stdout, err := git.NewCommand("push", "origin", "HEAD:"+defaultBranch).
  141. SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
  142. RunInDirWithEnv(tmpPath, models.InternalPushingEnvironment(u, repo)); err != nil {
  143. log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)
  144. return fmt.Errorf("git push: %v", err)
  145. }
  146. return nil
  147. }
  148. func checkInitRepository(owner, name string) (err error) {
  149. // Somehow the directory could exist.
  150. repoPath := models.RepoPath(owner, name)
  151. isExist, err := util.IsExist(repoPath)
  152. if err != nil {
  153. log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
  154. return err
  155. }
  156. if isExist {
  157. return models.ErrRepoFilesAlreadyExist{
  158. Uname: owner,
  159. Name: name,
  160. }
  161. }
  162. // Init git bare new repository.
  163. if err = git.InitRepository(repoPath, true); err != nil {
  164. return fmt.Errorf("git.InitRepository: %v", err)
  165. } else if err = createDelegateHooks(repoPath); err != nil {
  166. return fmt.Errorf("createDelegateHooks: %v", err)
  167. }
  168. return nil
  169. }
  170. func adoptRepository(ctx models.DBContext, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
  171. isExist, err := util.IsExist(repoPath)
  172. if err != nil {
  173. log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
  174. return err
  175. }
  176. if !isExist {
  177. return fmt.Errorf("adoptRepository: path does not already exist: %s", repoPath)
  178. }
  179. if err := createDelegateHooks(repoPath); err != nil {
  180. return fmt.Errorf("createDelegateHooks: %v", err)
  181. }
  182. // Re-fetch the repository from database before updating it (else it would
  183. // override changes that were done earlier with sql)
  184. if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil {
  185. return fmt.Errorf("getRepositoryByID: %v", err)
  186. }
  187. repo.IsEmpty = false
  188. gitRepo, err := git.OpenRepository(repo.RepoPath())
  189. if err != nil {
  190. return fmt.Errorf("openRepository: %v", err)
  191. }
  192. defer gitRepo.Close()
  193. if len(opts.DefaultBranch) > 0 {
  194. repo.DefaultBranch = opts.DefaultBranch
  195. if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  196. return fmt.Errorf("setDefaultBranch: %v", err)
  197. }
  198. } else {
  199. repo.DefaultBranch, err = gitRepo.GetDefaultBranch()
  200. if err != nil {
  201. repo.DefaultBranch = setting.Repository.DefaultBranch
  202. if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  203. return fmt.Errorf("setDefaultBranch: %v", err)
  204. }
  205. }
  206. repo.DefaultBranch = strings.TrimPrefix(repo.DefaultBranch, git.BranchPrefix)
  207. }
  208. branches, _, _ := gitRepo.GetBranches(0, 0)
  209. found := false
  210. hasDefault := false
  211. hasMaster := false
  212. hasMain := false
  213. for _, branch := range branches {
  214. if branch == repo.DefaultBranch {
  215. found = true
  216. break
  217. } else if branch == setting.Repository.DefaultBranch {
  218. hasDefault = true
  219. } else if branch == "master" {
  220. hasMaster = true
  221. } else if branch == "main" {
  222. hasMain = true
  223. }
  224. }
  225. if !found {
  226. if hasDefault {
  227. repo.DefaultBranch = setting.Repository.DefaultBranch
  228. } else if hasMaster {
  229. repo.DefaultBranch = "master"
  230. } else if hasMain {
  231. repo.DefaultBranch = "main"
  232. } else if len(branches) > 0 {
  233. repo.DefaultBranch = branches[0]
  234. } else {
  235. repo.IsEmpty = true
  236. repo.DefaultBranch = setting.Repository.DefaultBranch
  237. }
  238. if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  239. return fmt.Errorf("setDefaultBranch: %v", err)
  240. }
  241. }
  242. if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
  243. return fmt.Errorf("updateRepository: %v", err)
  244. }
  245. return nil
  246. }
  247. // InitRepository initializes README and .gitignore if needed.
  248. func initRepository(ctx models.DBContext, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
  249. if err = checkInitRepository(repo.OwnerName, repo.Name); err != nil {
  250. return err
  251. }
  252. // Initialize repository according to user's choice.
  253. if opts.AutoInit {
  254. tmpDir, err := ioutil.TempDir(os.TempDir(), "gitea-"+repo.Name)
  255. if err != nil {
  256. return fmt.Errorf("Failed to create temp dir for repository %s: %v", repo.RepoPath(), err)
  257. }
  258. defer func() {
  259. if err := util.RemoveAll(tmpDir); err != nil {
  260. log.Warn("Unable to remove temporary directory: %s: Error: %v", tmpDir, err)
  261. }
  262. }()
  263. if err = prepareRepoCommit(ctx, repo, tmpDir, repoPath, opts); err != nil {
  264. return fmt.Errorf("prepareRepoCommit: %v", err)
  265. }
  266. // Apply changes and commit.
  267. if err = initRepoCommit(tmpDir, repo, u, opts.DefaultBranch); err != nil {
  268. return fmt.Errorf("initRepoCommit: %v", err)
  269. }
  270. }
  271. // Re-fetch the repository from database before updating it (else it would
  272. // override changes that were done earlier with sql)
  273. if repo, err = models.GetRepositoryByIDCtx(ctx, repo.ID); err != nil {
  274. return fmt.Errorf("getRepositoryByID: %v", err)
  275. }
  276. if !opts.AutoInit {
  277. repo.IsEmpty = true
  278. }
  279. repo.DefaultBranch = setting.Repository.DefaultBranch
  280. if len(opts.DefaultBranch) > 0 {
  281. repo.DefaultBranch = opts.DefaultBranch
  282. gitRepo, err := git.OpenRepository(repo.RepoPath())
  283. if err != nil {
  284. return fmt.Errorf("openRepository: %v", err)
  285. }
  286. defer gitRepo.Close()
  287. if err = gitRepo.SetDefaultBranch(repo.DefaultBranch); err != nil {
  288. return fmt.Errorf("setDefaultBranch: %v", err)
  289. }
  290. }
  291. if err = models.UpdateRepositoryCtx(ctx, repo, false); err != nil {
  292. return fmt.Errorf("updateRepository: %v", err)
  293. }
  294. return nil
  295. }