aboutsummaryrefslogtreecommitdiffstats
path: root/services/repository
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-11-17 23:17:31 +0800
committerGitHub <noreply@github.com>2021-11-17 23:17:31 +0800
commit5233051e64e90238bb7b6ddf9ecd1513e57bf8e9 (patch)
tree6e21565fd47954877bc14fd66f57d798c86e3300 /services/repository
parent750a8465f547e9f08a87612c75898d56b8ec1f88 (diff)
downloadgitea-5233051e64e90238bb7b6ddf9ecd1513e57bf8e9.tar.gz
gitea-5233051e64e90238bb7b6ddf9ecd1513e57bf8e9.zip
Move some functions into services/repository (#17677)
Diffstat (limited to 'services/repository')
-rw-r--r--services/repository/branch.go90
-rw-r--r--services/repository/cache.go48
-rw-r--r--services/repository/hooks.go52
-rw-r--r--services/repository/push.go126
4 files changed, 314 insertions, 2 deletions
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/services/repository/cache.go b/services/repository/cache.go
new file mode 100644
index 0000000000..e574f1adb7
--- /dev/null
+++ b/services/repository/cache.go
@@ -0,0 +1,48 @@
+// 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"
+ "strings"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
+)
+
+func getRefName(fullRefName string) string {
+ if strings.HasPrefix(fullRefName, git.TagPrefix) {
+ return fullRefName[len(git.TagPrefix):]
+ } else if strings.HasPrefix(fullRefName, git.BranchPrefix) {
+ return fullRefName[len(git.BranchPrefix):]
+ }
+ return ""
+}
+
+// CacheRef cachhe last commit information of the branch or the tag
+func CacheRef(ctx context.Context, repo *models.Repository, gitRepo *git.Repository, fullRefName string) error {
+ if !setting.CacheService.LastCommit.Enabled {
+ return nil
+ }
+
+ commit, err := gitRepo.GetCommit(fullRefName)
+ if err != nil {
+ return err
+ }
+
+ commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(getRefName(fullRefName), true), commit.CommitsCount)
+ if err != nil {
+ return err
+ }
+ if commitsCount < setting.CacheService.LastCommit.CommitsCount {
+ return nil
+ }
+
+ commitCache := git.NewLastCommitCache(repo.FullName(), gitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
+
+ return commitCache.CacheCommit(ctx, commit)
+}
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
+}