diff --git a/cmd/admin.go b/cmd/admin.go index 64106f5060..858498ed37 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/storage" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" + repo_service "code.gitea.io/gitea/services/repository" "github.com/urfave/cli" ) @@ -612,7 +613,7 @@ func runRegenerateHooks(_ *cli.Context) error { if err := initDB(ctx); err != nil { return err } - return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) + return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) } func runRegenerateKeys(_ *cli.Context) error { diff --git a/integrations/api_repo_get_contents_list_test.go b/integrations/api_repo_get_contents_list_test.go index c35a345e81..823a72c726 100644 --- a/integrations/api_repo_get_contents_list_test.go +++ b/integrations/api_repo_get_contents_list_test.go @@ -13,9 +13,9 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" ) @@ -72,7 +72,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { // Make a new branch in repo1 newBranch := "test_branch" - err := repo_module.CreateNewBranch(user2, repo1, repo1.DefaultBranch, newBranch) + err := repo_service.CreateNewBranch(user2, repo1, repo1.DefaultBranch, newBranch) assert.NoError(t, err) // Get the commit ID of the default branch gitRepo, err := git.OpenRepository(repo1.RepoPath()) diff --git a/integrations/api_repo_get_contents_test.go b/integrations/api_repo_get_contents_test.go index 243ee229dc..67ec02b7b0 100644 --- a/integrations/api_repo_get_contents_test.go +++ b/integrations/api_repo_get_contents_test.go @@ -12,9 +12,9 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" ) @@ -73,7 +73,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { // Make a new branch in repo1 newBranch := "test_branch" - err := repo_module.CreateNewBranch(user2, repo1, repo1.DefaultBranch, newBranch) + err := repo_service.CreateNewBranch(user2, repo1, repo1.DefaultBranch, newBranch) assert.NoError(t, err) // Get the commit ID of the default branch gitRepo, err := git.OpenRepository(repo1.RepoPath()) diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 275bae91e3..dcd82554d5 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -24,92 +24,3 @@ func GetBranch(repo *models.Repository, branch string) (*git.Branch, error) { return gitRepo.GetBranch(branch) } - -// GetBranches returns branches from the repository, skipping skip initial branches and -// returning at most limit branches, or all branches if limit is 0. -func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, error) { - return git.GetBranchesByPath(repo.RepoPath(), skip, limit) -} - -// checkBranchName validates branch name with existing repository branches -func checkBranchName(repo *models.Repository, name string) error { - gitRepo, err := git.OpenRepository(repo.RepoPath()) - if err != nil { - return err - } - defer gitRepo.Close() - - branches, _, err := GetBranches(repo, 0, 0) - if err != nil { - return err - } - - for _, branch := range branches { - if branch.Name == name { - return models.ErrBranchAlreadyExists{ - BranchName: branch.Name, - } - } else if (len(branch.Name) < len(name) && branch.Name+"/" == name[0:len(branch.Name)+1]) || - (len(branch.Name) > len(name) && name+"/" == branch.Name[0:len(name)+1]) { - return models.ErrBranchNameConflict{ - BranchName: branch.Name, - } - } - } - - if _, err := gitRepo.GetTag(name); err == nil { - return models.ErrTagAlreadyExists{ - TagName: name, - } - } - - return nil -} - -// CreateNewBranch creates a new repository branch -func CreateNewBranch(doer *models.User, repo *models.Repository, oldBranchName, branchName string) (err error) { - // Check if branch name can be used - if err := checkBranchName(repo, branchName); err != nil { - return err - } - - if !git.IsBranchExist(repo.RepoPath(), oldBranchName) { - return models.ErrBranchDoesNotExist{ - BranchName: oldBranchName, - } - } - - if err := git.Push(repo.RepoPath(), git.PushOptions{ - Remote: repo.RepoPath(), - Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName), - Env: models.PushingEnvironment(doer, repo), - }); err != nil { - if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { - return err - } - return fmt.Errorf("Push: %v", err) - } - - return nil -} - -// CreateNewBranchFromCommit creates a new repository branch -func CreateNewBranchFromCommit(doer *models.User, repo *models.Repository, commit, branchName string) (err error) { - // Check if branch name can be used - if err := checkBranchName(repo, branchName); err != nil { - return err - } - - if err := git.Push(repo.RepoPath(), git.PushOptions{ - Remote: repo.RepoPath(), - Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName), - Env: models.PushingEnvironment(doer, repo), - }); err != nil { - if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { - return err - } - return fmt.Errorf("Push: %v", err) - } - - return nil -} diff --git a/modules/repository/hooks.go b/modules/repository/hooks.go index 23eee8897d..bd77423afc 100644 --- a/modules/repository/hooks.go +++ b/modules/repository/hooks.go @@ -5,19 +5,13 @@ package repository import ( - "context" "fmt" "os" "path/filepath" - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - - "xorm.io/builder" ) func getHookTemplates() (hookNames, hookTpls, giteaHookTpls []string) { @@ -240,38 +234,3 @@ func CheckDelegateHooks(repoPath string) ([]string, error) { } return results, nil } - -// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks -// to make sure the binary and custom conf path are up-to-date. -func SyncRepositoryHooks(ctx context.Context) error { - log.Trace("Doing: SyncRepositoryHooks") - - if err := db.Iterate( - db.DefaultContext, - new(models.Repository), - builder.Gt{"id": 0}, - func(idx int, bean interface{}) error { - repo := bean.(*models.Repository) - select { - case <-ctx.Done(): - return db.ErrCancelledf("before sync repository hooks for %s", repo.FullName()) - default: - } - - if err := createDelegateHooks(repo.RepoPath()); err != nil { - return fmt.Errorf("SyncRepositoryHook: %v", err) - } - if repo.HasWiki() { - if err := createDelegateHooks(repo.WikiPath()); err != nil { - return fmt.Errorf("SyncRepositoryHook: %v", err) - } - } - return nil - }, - ); err != nil { - return err - } - - log.Trace("Finished: SyncRepositoryHooks") - return nil -} diff --git a/modules/repository/update.go b/modules/repository/update.go deleted file mode 100644 index b9a5db2a6a..0000000000 --- a/modules/repository/update.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. - -package repository - -import ( - "context" - "fmt" - "strings" - "time" - - "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/timeutil" -) - -// PushUpdateAddDeleteTags updates a number of added and delete tags -func PushUpdateAddDeleteTags(repo *models.Repository, gitRepo *git.Repository, addTags, delTags []string) error { - return db.WithTx(func(ctx context.Context) error { - if err := models.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil { - return err - } - return pushUpdateAddTags(ctx, repo, gitRepo, addTags) - }) -} - -// pushUpdateAddTags updates a number of add tags -func pushUpdateAddTags(ctx context.Context, repo *models.Repository, gitRepo *git.Repository, tags []string) error { - if len(tags) == 0 { - return nil - } - - lowerTags := make([]string, 0, len(tags)) - for _, tag := range tags { - lowerTags = append(lowerTags, strings.ToLower(tag)) - } - - releases, err := models.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags) - if err != nil { - return fmt.Errorf("GetReleasesByRepoIDAndNames: %v", err) - } - relMap := make(map[string]*models.Release) - for _, rel := range releases { - relMap[rel.LowerTagName] = rel - } - - newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap)) - - emailToUser := make(map[string]*models.User) - - for i, lowerTag := range lowerTags { - tag, err := gitRepo.GetTag(tags[i]) - if err != nil { - return fmt.Errorf("GetTag: %v", err) - } - commit, err := tag.Commit() - if err != nil { - return fmt.Errorf("Commit: %v", err) - } - - sig := tag.Tagger - if sig == nil { - sig = commit.Author - } - if sig == nil { - sig = commit.Committer - } - var author *models.User - var createdAt = time.Unix(1, 0) - - if sig != nil { - var ok bool - author, ok = emailToUser[sig.Email] - if !ok { - author, err = models.GetUserByEmailContext(ctx, sig.Email) - if err != nil && !models.IsErrUserNotExist(err) { - return fmt.Errorf("GetUserByEmail: %v", err) - } - if author != nil { - emailToUser[sig.Email] = author - } - } - createdAt = sig.When - } - - commitsCount, err := commit.CommitsCount() - if err != nil { - return fmt.Errorf("CommitsCount: %v", err) - } - - rel, has := relMap[lowerTag] - - if !has { - rel = &models.Release{ - RepoID: repo.ID, - Title: "", - TagName: tags[i], - LowerTagName: lowerTag, - Target: "", - Sha1: commit.ID.String(), - NumCommits: commitsCount, - Note: "", - IsDraft: false, - IsPrerelease: false, - IsTag: true, - CreatedUnix: timeutil.TimeStamp(createdAt.Unix()), - } - if author != nil { - rel.PublisherID = author.ID - } - - newReleases = append(newReleases, rel) - } else { - rel.Sha1 = commit.ID.String() - rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) - rel.NumCommits = commitsCount - rel.IsDraft = false - if rel.IsTag && author != nil { - rel.PublisherID = author.ID - } - if err = models.UpdateRelease(ctx, rel); err != nil { - return fmt.Errorf("Update: %v", err) - } - } - } - - if len(newReleases) > 0 { - if err = models.InsertReleasesContext(ctx, newReleases); err != nil { - return fmt.Errorf("Insert: %v", err) - } - } - - return nil -} diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index c57075e3b8..2e08b56826 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -176,7 +176,7 @@ func CreateBranch(ctx *context.APIContext) { opt.OldBranchName = ctx.Repo.Repository.DefaultBranch } - err := repo_module.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName) + err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName) if err != nil { if models.IsErrBranchDoesNotExist(err) { @@ -257,7 +257,7 @@ func ListBranches(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) skip, _ := listOptions.GetStartEnd() - branches, totalNumOfBranches, err := repo_module.GetBranches(ctx.Repo.Repository, skip, listOptions.PageSize) + branches, totalNumOfBranches, err := repo_service.GetBranches(ctx.Repo.Repository, skip, listOptions.PageSize) if err != nil { ctx.Error(http.StatusInternalServerError, "GetBranches", err) return diff --git a/routers/init.go b/routers/init.go index 0f19e7f732..762508cee5 100644 --- a/routers/init.go +++ b/routers/init.go @@ -25,7 +25,6 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/external" "code.gitea.io/gitea/modules/notification" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/ssh" "code.gitea.io/gitea/modules/storage" @@ -45,7 +44,7 @@ import ( repo_migrations "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" pull_service "code.gitea.io/gitea/services/pull" - "code.gitea.io/gitea/services/repository" + repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/services/webhook" "gitea.com/go-chi/session" @@ -73,7 +72,7 @@ func mustInitCtx(ctx context.Context, fn func(ctx context.Context) error) { func InitGitServices() { setting.NewServices() mustInit(storage.Init) - mustInit(repository.NewContext) + mustInit(repo_service.NewContext) } func syncAppPathForGit(ctx context.Context) error { @@ -85,7 +84,7 @@ func syncAppPathForGit(ctx context.Context) error { log.Info("AppPath changed from '%s' to '%s'", runtimeState.LastAppPath, setting.AppPath) log.Info("re-sync repository hooks ...") - mustInitCtx(ctx, repo_module.SyncRepositoryHooks) + mustInitCtx(ctx, repo_service.SyncRepositoryHooks) log.Info("re-write ssh public keys ...") mustInit(models.RewriteAllPublicKeys) diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 991beeace2..10557ff3db 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -171,7 +171,7 @@ func loadBranches(ctx *context.Context, skip, limit int) ([]*Branch, int) { return nil, 0 } - rawBranches, totalNumOfBranches, err := repo_module.GetBranches(ctx.Repo.Repository, skip, limit) + rawBranches, totalNumOfBranches, err := repo_service.GetBranches(ctx.Repo.Repository, skip, limit) if err != nil { log.Error("GetBranches: %v", err) ctx.ServerError("GetBranches", err) @@ -350,11 +350,11 @@ func CreateBranch(ctx *context.Context) { err = release_service.CreateNewTag(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName, "") } } else if ctx.Repo.IsViewBranch { - err = repo_module.CreateNewBranch(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName) + err = repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName) } else if ctx.Repo.IsViewTag { - err = repo_module.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName) + err = repo_service.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName) } else { - err = repo_module.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName) + err = repo_service.CreateNewBranchFromCommit(ctx.User, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName) } if err != nil { if models.IsErrTagAlreadyExists(err) { diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 4ddcd44537..1f115de43a 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -85,7 +85,7 @@ func registerRepositoryUpdateHook() { RunAtStart: false, Schedule: "@every 72h", }, func(ctx context.Context, _ *models.User, _ Config) error { - return repo_module.SyncRepositoryHooks(ctx) + return repo_service.SyncRepositoryHooks(ctx) }) } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 2dee49f710..84644145f7 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -333,7 +333,7 @@ func runSync(ctx context.Context, m *models.Mirror) ([]*mirrorSyncResult, bool) } log.Trace("SyncMirrors [repo: %-v]: invalidating mirror branch caches...", m.Repo) - branches, _, err := repo_module.GetBranches(m.Repo, 0, 0) + branches, _, err := git.GetBranchesByPath(m.Repo.RepoPath(), 0, 0) if err != nil { log.Error("GetBranches: %v", err) return nil, false diff --git a/services/repository/branch.go b/services/repository/branch.go index 5e246cbec6..83980c5bd9 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -6,6 +6,7 @@ package repository import ( "errors" + "fmt" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/git" @@ -15,6 +16,95 @@ import ( pull_service "code.gitea.io/gitea/services/pull" ) +// CreateNewBranch creates a new repository branch +func CreateNewBranch(doer *models.User, repo *models.Repository, oldBranchName, branchName string) (err error) { + // Check if branch name can be used + if err := checkBranchName(repo, branchName); err != nil { + return err + } + + if !git.IsBranchExist(repo.RepoPath(), oldBranchName) { + return models.ErrBranchDoesNotExist{ + BranchName: oldBranchName, + } + } + + if err := git.Push(repo.RepoPath(), git.PushOptions{ + Remote: repo.RepoPath(), + Branch: fmt.Sprintf("%s:%s%s", oldBranchName, git.BranchPrefix, branchName), + Env: models.PushingEnvironment(doer, repo), + }); err != nil { + if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { + return err + } + return fmt.Errorf("Push: %v", err) + } + + return nil +} + +// GetBranches returns branches from the repository, skipping skip initial branches and +// returning at most limit branches, or all branches if limit is 0. +func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, error) { + return git.GetBranchesByPath(repo.RepoPath(), skip, limit) +} + +// checkBranchName validates branch name with existing repository branches +func checkBranchName(repo *models.Repository, name string) error { + gitRepo, err := git.OpenRepository(repo.RepoPath()) + if err != nil { + return err + } + defer gitRepo.Close() + + branches, _, err := GetBranches(repo, 0, 0) + if err != nil { + return err + } + + for _, branch := range branches { + if branch.Name == name { + return models.ErrBranchAlreadyExists{ + BranchName: branch.Name, + } + } else if (len(branch.Name) < len(name) && branch.Name+"/" == name[0:len(branch.Name)+1]) || + (len(branch.Name) > len(name) && name+"/" == branch.Name[0:len(name)+1]) { + return models.ErrBranchNameConflict{ + BranchName: branch.Name, + } + } + } + + if _, err := gitRepo.GetTag(name); err == nil { + return models.ErrTagAlreadyExists{ + TagName: name, + } + } + + return nil +} + +// CreateNewBranchFromCommit creates a new repository branch +func CreateNewBranchFromCommit(doer *models.User, repo *models.Repository, commit, branchName string) (err error) { + // Check if branch name can be used + if err := checkBranchName(repo, branchName); err != nil { + return err + } + + if err := git.Push(repo.RepoPath(), git.PushOptions{ + Remote: repo.RepoPath(), + Branch: fmt.Sprintf("%s:%s%s", commit, git.BranchPrefix, branchName), + Env: models.PushingEnvironment(doer, repo), + }); err != nil { + if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { + return err + } + return fmt.Errorf("Push: %v", err) + } + + return nil +} + // RenameBranch rename a branch func RenameBranch(repo *models.Repository, doer *models.User, gitRepo *git.Repository, from, to string) (string, error) { if from == to { diff --git a/modules/repository/cache.go b/services/repository/cache.go similarity index 100% rename from modules/repository/cache.go rename to services/repository/cache.go diff --git a/services/repository/hooks.go b/services/repository/hooks.go new file mode 100644 index 0000000000..a50853f6af --- /dev/null +++ b/services/repository/hooks.go @@ -0,0 +1,52 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repository + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + + "xorm.io/builder" +) + +// SyncRepositoryHooks rewrites all repositories' pre-receive, update and post-receive hooks +// to make sure the binary and custom conf path are up-to-date. +func SyncRepositoryHooks(ctx context.Context) error { + log.Trace("Doing: SyncRepositoryHooks") + + if err := db.Iterate( + db.DefaultContext, + new(models.Repository), + builder.Gt{"id": 0}, + func(idx int, bean interface{}) error { + repo := bean.(*models.Repository) + select { + case <-ctx.Done(): + return db.ErrCancelledf("before sync repository hooks for %s", repo.FullName()) + default: + } + + if err := repo_module.CreateDelegateHooks(repo.RepoPath()); err != nil { + return fmt.Errorf("SyncRepositoryHook: %v", err) + } + if repo.HasWiki() { + if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { + return fmt.Errorf("SyncRepositoryHook: %v", err) + } + } + return nil + }, + ); err != nil { + return err + } + + log.Trace("Finished: SyncRepositoryHooks") + return nil +} diff --git a/services/repository/push.go b/services/repository/push.go index ab1b404609..a98cfb1758 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -5,8 +5,10 @@ package repository import ( + "context" "errors" "fmt" + "strings" "time" "code.gitea.io/gitea/models" @@ -20,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/repofiles" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" pull_service "code.gitea.io/gitea/services/pull" ) @@ -210,7 +213,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // Cache for big repository - if err := repo_module.CacheRef(graceful.GetManager().HammerContext(), repo, gitRepo, opts.RefFullName); err != nil { + if err := CacheRef(graceful.GetManager().HammerContext(), repo, gitRepo, opts.RefFullName); err != nil { log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err) } } else { @@ -229,7 +232,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { log.Trace("Non-tag and non-branch commits pushed.") } } - if err := repo_module.PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil { + if err := PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil { return fmt.Errorf("PushUpdateAddDeleteTags: %v", err) } @@ -240,3 +243,122 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { return nil } + +// PushUpdateAddDeleteTags updates a number of added and delete tags +func PushUpdateAddDeleteTags(repo *models.Repository, gitRepo *git.Repository, addTags, delTags []string) error { + return db.WithTx(func(ctx context.Context) error { + if err := models.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil { + return err + } + return pushUpdateAddTags(ctx, repo, gitRepo, addTags) + }) +} + +// pushUpdateAddTags updates a number of add tags +func pushUpdateAddTags(ctx context.Context, repo *models.Repository, gitRepo *git.Repository, tags []string) error { + if len(tags) == 0 { + return nil + } + + lowerTags := make([]string, 0, len(tags)) + for _, tag := range tags { + lowerTags = append(lowerTags, strings.ToLower(tag)) + } + + releases, err := models.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags) + if err != nil { + return fmt.Errorf("GetReleasesByRepoIDAndNames: %v", err) + } + relMap := make(map[string]*models.Release) + for _, rel := range releases { + relMap[rel.LowerTagName] = rel + } + + newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap)) + + emailToUser := make(map[string]*models.User) + + for i, lowerTag := range lowerTags { + tag, err := gitRepo.GetTag(tags[i]) + if err != nil { + return fmt.Errorf("GetTag: %v", err) + } + commit, err := tag.Commit() + if err != nil { + return fmt.Errorf("Commit: %v", err) + } + + sig := tag.Tagger + if sig == nil { + sig = commit.Author + } + if sig == nil { + sig = commit.Committer + } + var author *models.User + var createdAt = time.Unix(1, 0) + + if sig != nil { + var ok bool + author, ok = emailToUser[sig.Email] + if !ok { + author, err = models.GetUserByEmailContext(ctx, sig.Email) + if err != nil && !models.IsErrUserNotExist(err) { + return fmt.Errorf("GetUserByEmail: %v", err) + } + if author != nil { + emailToUser[sig.Email] = author + } + } + createdAt = sig.When + } + + commitsCount, err := commit.CommitsCount() + if err != nil { + return fmt.Errorf("CommitsCount: %v", err) + } + + rel, has := relMap[lowerTag] + + if !has { + rel = &models.Release{ + RepoID: repo.ID, + Title: "", + TagName: tags[i], + LowerTagName: lowerTag, + Target: "", + Sha1: commit.ID.String(), + NumCommits: commitsCount, + Note: "", + IsDraft: false, + IsPrerelease: false, + IsTag: true, + CreatedUnix: timeutil.TimeStamp(createdAt.Unix()), + } + if author != nil { + rel.PublisherID = author.ID + } + + newReleases = append(newReleases, rel) + } else { + rel.Sha1 = commit.ID.String() + rel.CreatedUnix = timeutil.TimeStamp(createdAt.Unix()) + rel.NumCommits = commitsCount + rel.IsDraft = false + if rel.IsTag && author != nil { + rel.PublisherID = author.ID + } + if err = models.UpdateRelease(ctx, rel); err != nil { + return fmt.Errorf("Update: %v", err) + } + } + } + + if len(newReleases) > 0 { + if err = models.InsertReleasesContext(ctx, newReleases); err != nil { + return fmt.Errorf("Insert: %v", err) + } + } + + return nil +}