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.

fork.go 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  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. "fmt"
  7. "strings"
  8. "time"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/git"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/structs"
  13. )
  14. // ForkRepository forks a repository
  15. func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc string) (_ *models.Repository, err error) {
  16. forkedRepo, err := oldRepo.GetUserFork(owner.ID)
  17. if err != nil {
  18. return nil, err
  19. }
  20. if forkedRepo != nil {
  21. return nil, models.ErrForkAlreadyExist{
  22. Uname: owner.Name,
  23. RepoName: oldRepo.FullName(),
  24. ForkName: forkedRepo.FullName(),
  25. }
  26. }
  27. repo := &models.Repository{
  28. OwnerID: owner.ID,
  29. Owner: owner,
  30. OwnerName: owner.Name,
  31. Name: name,
  32. LowerName: strings.ToLower(name),
  33. Description: desc,
  34. DefaultBranch: oldRepo.DefaultBranch,
  35. IsPrivate: oldRepo.IsPrivate || oldRepo.Owner.Visibility == structs.VisibleTypePrivate,
  36. IsEmpty: oldRepo.IsEmpty,
  37. IsFork: true,
  38. ForkID: oldRepo.ID,
  39. }
  40. oldRepoPath := oldRepo.RepoPath()
  41. err = models.WithTx(func(ctx models.DBContext) error {
  42. if err = models.CreateRepository(ctx, doer, owner, repo); err != nil {
  43. return err
  44. }
  45. if err = models.IncrementRepoForkNum(ctx, oldRepo.ID); err != nil {
  46. return err
  47. }
  48. repoPath := models.RepoPath(owner.Name, repo.Name)
  49. if stdout, err := git.NewCommand(
  50. "clone", "--bare", oldRepoPath, repoPath).
  51. SetDescription(fmt.Sprintf("ForkRepository(git clone): %s to %s", oldRepo.FullName(), repo.FullName())).
  52. RunInDirTimeout(10*time.Minute, ""); err != nil {
  53. log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, oldRepo, stdout, err)
  54. return fmt.Errorf("git clone: %v", err)
  55. }
  56. if stdout, err := git.NewCommand("update-server-info").
  57. SetDescription(fmt.Sprintf("ForkRepository(git update-server-info): %s", repo.FullName())).
  58. RunInDir(repoPath); err != nil {
  59. log.Error("Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v", repo, stdout, err)
  60. return fmt.Errorf("git update-server-info: %v", err)
  61. }
  62. if err = createDelegateHooks(repoPath); err != nil {
  63. return fmt.Errorf("createDelegateHooks: %v", err)
  64. }
  65. return nil
  66. })
  67. if err != nil {
  68. return nil, err
  69. }
  70. ctx := models.DefaultDBContext()
  71. if err = repo.UpdateSize(ctx); err != nil {
  72. log.Error("Failed to update size for repository: %v", err)
  73. }
  74. if err := models.CopyLanguageStat(oldRepo, repo); err != nil {
  75. log.Error("Copy language stat from oldRepo failed")
  76. }
  77. return repo, models.CopyLFS(ctx, repo, oldRepo)
  78. }