aboutsummaryrefslogtreecommitdiffstats
path: root/models/git
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2023-06-29 18:03:20 +0800
committerGitHub <noreply@github.com>2023-06-29 10:03:20 +0000
commit6e19484f4d3bf372212f2da462110a1a8c10cbf2 (patch)
treee8f1b4920b286241e4ad59151b4f00d9941a27aa /models/git
parent5a871932f0efc19a731ee5c1202679653d3cefff (diff)
downloadgitea-6e19484f4d3bf372212f2da462110a1a8c10cbf2.tar.gz
gitea-6e19484f4d3bf372212f2da462110a1a8c10cbf2.zip
Sync branches into databases (#22743)
Related #14180 Related #25233 Related #22639 Close #19786 Related #12763 This PR will change all the branches retrieve method from reading git data to read database to reduce git read operations. - [x] Sync git branches information into database when push git data - [x] Create a new table `Branch`, merge some columns of `DeletedBranch` into `Branch` table and drop the table `DeletedBranch`. - [x] Read `Branch` table when visit `code` -> `branch` page - [x] Read `Branch` table when list branch names in `code` page dropdown - [x] Read `Branch` table when list git ref compare page - [x] Provide a button in admin page to manually sync all branches. - [x] Sync branches if repository is not empty but database branches are empty when visiting pages with branches list - [x] Use `commit_time desc` as the default FindBranch order by to keep consistent as before and deleted branches will be always at the end. --------- Co-authored-by: Jason Song <i@wolfogre.com>
Diffstat (limited to 'models/git')
-rw-r--r--models/git/branch.go379
-rw-r--r--models/git/branch_list.go132
-rw-r--r--models/git/branch_test.go (renamed from models/git/branches_test.go)44
-rw-r--r--models/git/branches.go197
-rw-r--r--models/git/protected_branch_list.go37
5 files changed, 565 insertions, 224 deletions
diff --git a/models/git/branch.go b/models/git/branch.go
new file mode 100644
index 0000000000..adf8b0a78d
--- /dev/null
+++ b/models/git/branch.go
@@ -0,0 +1,379 @@
+// Copyright 2016 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
+)
+
+// ErrBranchNotExist represents an error that branch with such name does not exist.
+type ErrBranchNotExist struct {
+ RepoID int64
+ BranchName string
+}
+
+// IsErrBranchNotExist checks if an error is an ErrBranchDoesNotExist.
+func IsErrBranchNotExist(err error) bool {
+ _, ok := err.(ErrBranchNotExist)
+ return ok
+}
+
+func (err ErrBranchNotExist) Error() string {
+ return fmt.Sprintf("branch does not exist [repo_id: %d name: %s]", err.RepoID, err.BranchName)
+}
+
+func (err ErrBranchNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
+// ErrBranchAlreadyExists represents an error that branch with such name already exists.
+type ErrBranchAlreadyExists struct {
+ BranchName string
+}
+
+// IsErrBranchAlreadyExists checks if an error is an ErrBranchAlreadyExists.
+func IsErrBranchAlreadyExists(err error) bool {
+ _, ok := err.(ErrBranchAlreadyExists)
+ return ok
+}
+
+func (err ErrBranchAlreadyExists) Error() string {
+ return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
+}
+
+func (err ErrBranchAlreadyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
+// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
+type ErrBranchNameConflict struct {
+ BranchName string
+}
+
+// IsErrBranchNameConflict checks if an error is an ErrBranchNameConflict.
+func IsErrBranchNameConflict(err error) bool {
+ _, ok := err.(ErrBranchNameConflict)
+ return ok
+}
+
+func (err ErrBranchNameConflict) Error() string {
+ return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
+}
+
+func (err ErrBranchNameConflict) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
+// ErrBranchesEqual represents an error that base branch is equal to the head branch.
+type ErrBranchesEqual struct {
+ BaseBranchName string
+ HeadBranchName string
+}
+
+// IsErrBranchesEqual checks if an error is an ErrBranchesEqual.
+func IsErrBranchesEqual(err error) bool {
+ _, ok := err.(ErrBranchesEqual)
+ return ok
+}
+
+func (err ErrBranchesEqual) Error() string {
+ return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
+}
+
+func (err ErrBranchesEqual) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
+// Branch represents a branch of a repository
+// For those repository who have many branches, stored into database is a good choice
+// for pagination, keyword search and filtering
+type Branch struct {
+ ID int64
+ RepoID int64 `xorm:"UNIQUE(s)"`
+ Name string `xorm:"UNIQUE(s) NOT NULL"`
+ CommitID string
+ CommitMessage string `xorm:"TEXT"`
+ PusherID int64
+ Pusher *user_model.User `xorm:"-"`
+ IsDeleted bool `xorm:"index"`
+ DeletedByID int64
+ DeletedBy *user_model.User `xorm:"-"`
+ DeletedUnix timeutil.TimeStamp `xorm:"index"`
+ CommitTime timeutil.TimeStamp // The commit
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
+}
+
+func (b *Branch) LoadDeletedBy(ctx context.Context) (err error) {
+ if b.DeletedBy == nil {
+ b.DeletedBy, err = user_model.GetUserByID(ctx, b.DeletedByID)
+ if user_model.IsErrUserNotExist(err) {
+ b.DeletedBy = user_model.NewGhostUser()
+ err = nil
+ }
+ }
+ return err
+}
+
+func (b *Branch) LoadPusher(ctx context.Context) (err error) {
+ if b.Pusher == nil && b.PusherID > 0 {
+ b.Pusher, err = user_model.GetUserByID(ctx, b.PusherID)
+ if user_model.IsErrUserNotExist(err) {
+ b.Pusher = user_model.NewGhostUser()
+ err = nil
+ }
+ }
+ return err
+}
+
+func init() {
+ db.RegisterModel(new(Branch))
+ db.RegisterModel(new(RenamedBranch))
+}
+
+func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, error) {
+ var branch Branch
+ has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("name=?", branchName).Get(&branch)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrBranchNotExist{
+ RepoID: repoID,
+ BranchName: branchName,
+ }
+ }
+ return &branch, nil
+}
+
+func AddBranches(ctx context.Context, branches []*Branch) error {
+ for _, branch := range branches {
+ if _, err := db.GetEngine(ctx).Insert(branch); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func GetDeletedBranchByID(ctx context.Context, repoID, branchID int64) (*Branch, error) {
+ var branch Branch
+ has, err := db.GetEngine(ctx).ID(branchID).Get(&branch)
+ if err != nil {
+ return nil, err
+ } else if !has {
+ return nil, ErrBranchNotExist{
+ RepoID: repoID,
+ }
+ }
+ if branch.RepoID != repoID {
+ return nil, ErrBranchNotExist{
+ RepoID: repoID,
+ }
+ }
+ if !branch.IsDeleted {
+ return nil, ErrBranchNotExist{
+ RepoID: repoID,
+ }
+ }
+ return &branch, nil
+}
+
+func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ branches := make([]*Branch, 0, len(branchIDs))
+ if err := db.GetEngine(ctx).In("id", branchIDs).Find(&branches); err != nil {
+ return err
+ }
+ for _, branch := range branches {
+ if err := AddDeletedBranch(ctx, repoID, branch.Name, doerID); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}
+
+// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
+// If it doest not exist, insert a new record into database
+func UpdateBranch(ctx context.Context, repoID int64, branchName, commitID, commitMessage string, pusherID int64, commitTime time.Time) error {
+ cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
+ Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
+ Update(&Branch{
+ CommitID: commitID,
+ CommitMessage: commitMessage,
+ PusherID: pusherID,
+ CommitTime: timeutil.TimeStamp(commitTime.Unix()),
+ IsDeleted: false,
+ })
+ if err != nil {
+ return err
+ }
+ if cnt > 0 {
+ return nil
+ }
+
+ return db.Insert(ctx, &Branch{
+ RepoID: repoID,
+ Name: branchName,
+ CommitID: commitID,
+ CommitMessage: commitMessage,
+ PusherID: pusherID,
+ CommitTime: timeutil.TimeStamp(commitTime.Unix()),
+ })
+}
+
+// AddDeletedBranch adds a deleted branch to the database
+func AddDeletedBranch(ctx context.Context, repoID int64, branchName string, deletedByID int64) error {
+ branch, err := GetBranch(ctx, repoID, branchName)
+ if err != nil {
+ return err
+ }
+ if branch.IsDeleted {
+ return nil
+ }
+
+ cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=? AND is_deleted=?", repoID, branchName, false).
+ Cols("is_deleted, deleted_by_id, deleted_unix").
+ Update(&Branch{
+ IsDeleted: true,
+ DeletedByID: deletedByID,
+ DeletedUnix: timeutil.TimeStampNow(),
+ })
+ if err != nil {
+ return err
+ }
+ if cnt == 0 {
+ return fmt.Errorf("branch %s not found or has been deleted", branchName)
+ }
+ return err
+}
+
+func RemoveDeletedBranchByID(ctx context.Context, repoID, branchID int64) error {
+ _, err := db.GetEngine(ctx).Where("repo_id=? AND id=? AND is_deleted = ?", repoID, branchID, true).Delete(new(Branch))
+ return err
+}
+
+// RemoveOldDeletedBranches removes old deleted branches
+func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
+ // Nothing to do for shutdown or terminate
+ log.Trace("Doing: DeletedBranchesCleanup")
+
+ deleteBefore := time.Now().Add(-olderThan)
+ _, err := db.GetEngine(ctx).Where("is_deleted=? AND deleted_unix < ?", true, deleteBefore.Unix()).Delete(new(Branch))
+ if err != nil {
+ log.Error("DeletedBranchesCleanup: %v", err)
+ }
+}
+
+// RenamedBranch provide renamed branch log
+// will check it when a branch can't be found
+type RenamedBranch struct {
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX NOT NULL"`
+ From string
+ To string
+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
+}
+
+// FindRenamedBranch check if a branch was renamed
+func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
+ branch = &RenamedBranch{
+ RepoID: repoID,
+ From: from,
+ }
+ exist, err = db.GetEngine(ctx).Get(branch)
+
+ return branch, exist, err
+}
+
+// RenameBranch rename a branch
+func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
+ return err
+ }
+ defer committer.Close()
+
+ sess := db.GetEngine(ctx)
+
+ // 1. update branch in database
+ if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
+ Name: to,
+ }); err != nil {
+ return err
+ } else if n <= 0 {
+ return ErrBranchNotExist{
+ RepoID: repo.ID,
+ BranchName: from,
+ }
+ }
+
+ // 2. update default branch if needed
+ isDefault := repo.DefaultBranch == from
+ if isDefault {
+ repo.DefaultBranch = to
+ _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
+ if err != nil {
+ return err
+ }
+ }
+
+ // 3. Update protected branch if needed
+ protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
+ if err != nil {
+ return err
+ }
+
+ if protectedBranch != nil {
+ // there is a protect rule for this branch
+ protectedBranch.RuleName = to
+ _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
+ if err != nil {
+ return err
+ }
+ } else {
+ // some glob protect rules may match this branch
+ protected, err := IsBranchProtected(ctx, repo.ID, from)
+ if err != nil {
+ return err
+ }
+ if protected {
+ return ErrBranchIsProtected
+ }
+ }
+
+ // 4. Update all not merged pull request base branch name
+ _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
+ repo.ID, from, false).
+ Update(map[string]interface{}{"base_branch": to})
+ if err != nil {
+ return err
+ }
+
+ // 5. do git action
+ if err = gitAction(isDefault); err != nil {
+ return err
+ }
+
+ // 6. insert renamed branch record
+ renamedBranch := &RenamedBranch{
+ RepoID: repo.ID,
+ From: from,
+ To: to,
+ }
+ err = db.Insert(ctx, renamedBranch)
+ if err != nil {
+ return err
+ }
+
+ return committer.Commit()
+}
diff --git a/models/git/branch_list.go b/models/git/branch_list.go
new file mode 100644
index 0000000000..da78248c0b
--- /dev/null
+++ b/models/git/branch_list.go
@@ -0,0 +1,132 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
+ "code.gitea.io/gitea/modules/util"
+
+ "xorm.io/builder"
+ "xorm.io/xorm"
+)
+
+type BranchList []*Branch
+
+func (branches BranchList) LoadDeletedBy(ctx context.Context) error {
+ ids := container.Set[int64]{}
+ for _, branch := range branches {
+ if !branch.IsDeleted {
+ continue
+ }
+ ids.Add(branch.DeletedByID)
+ }
+ usersMap := make(map[int64]*user_model.User, len(ids))
+ if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
+ return err
+ }
+ for _, branch := range branches {
+ if !branch.IsDeleted {
+ continue
+ }
+ branch.DeletedBy = usersMap[branch.DeletedByID]
+ if branch.DeletedBy == nil {
+ branch.DeletedBy = user_model.NewGhostUser()
+ }
+ }
+ return nil
+}
+
+func (branches BranchList) LoadPusher(ctx context.Context) error {
+ ids := container.Set[int64]{}
+ for _, branch := range branches {
+ if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher
+ ids.Add(branch.PusherID)
+ }
+ }
+ usersMap := make(map[int64]*user_model.User, len(ids))
+ if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil {
+ return err
+ }
+ for _, branch := range branches {
+ if branch.PusherID <= 0 {
+ continue
+ }
+ branch.Pusher = usersMap[branch.PusherID]
+ if branch.Pusher == nil {
+ branch.Pusher = user_model.NewGhostUser()
+ }
+ }
+ return nil
+}
+
+const (
+ BranchOrderByNameAsc = "name ASC"
+ BranchOrderByCommitTimeDesc = "commit_time DESC"
+)
+
+type FindBranchOptions struct {
+ db.ListOptions
+ RepoID int64
+ ExcludeBranchNames []string
+ IsDeletedBranch util.OptionalBool
+ OrderBy string
+}
+
+func (opts *FindBranchOptions) Cond() builder.Cond {
+ cond := builder.NewCond()
+ if opts.RepoID > 0 {
+ cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
+ }
+
+ if len(opts.ExcludeBranchNames) > 0 {
+ cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames))
+ }
+ if !opts.IsDeletedBranch.IsNone() {
+ cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.IsTrue()})
+ }
+ return cond
+}
+
+func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
+ return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
+}
+
+func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
+ if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end
+ sess = sess.OrderBy("is_deleted ASC")
+ }
+
+ if opts.OrderBy == "" {
+ opts.OrderBy = BranchOrderByCommitTimeDesc
+ }
+ return sess.OrderBy(opts.OrderBy)
+}
+
+func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
+ sess := db.GetEngine(ctx).Where(opts.Cond())
+ if opts.PageSize > 0 && !opts.IsListAll() {
+ sess = db.SetSessionPagination(sess, &opts.ListOptions)
+ }
+ sess = orderByBranches(sess, opts)
+
+ var branches []*Branch
+ return branches, sess.Find(&branches)
+}
+
+func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
+ sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
+ if opts.PageSize > 0 && !opts.IsListAll() {
+ sess = db.SetSessionPagination(sess, &opts.ListOptions)
+ }
+ sess = orderByBranches(sess, opts)
+ var branches []string
+ if err := sess.Table("branch").Find(&branches); err != nil {
+ return nil, err
+ }
+ return branches, nil
+}
diff --git a/models/git/branches_test.go b/models/git/branch_test.go
index 5d18d9525e..bb63660d07 100644
--- a/models/git/branches_test.go
+++ b/models/git/branch_test.go
@@ -11,6 +11,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -18,24 +19,37 @@ import (
func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
- assert.Error(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
- assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "test", "5655464564554545466464656", int64(1)))
+ assert.True(t, firstBranch.IsDeleted)
+ assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, firstBranch.Name, firstBranch.DeletedByID))
+ assert.NoError(t, git_model.AddDeletedBranch(db.DefaultContext, repo.ID, "branch2", int64(1)))
+
+ secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: "branch2"})
+ assert.True(t, secondBranch.IsDeleted)
+
+ err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.Name, secondBranch.CommitID, secondBranch.CommitMessage, secondBranch.PusherID, secondBranch.CommitTime.AsLocalTime())
+ assert.NoError(t, err)
}
func TestGetDeletedBranches(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- branches, err := git_model.GetDeletedBranches(db.DefaultContext, repo.ID)
+ branches, err := git_model.FindBranches(db.DefaultContext, git_model.FindBranchOptions{
+ ListOptions: db.ListOptions{
+ ListAll: true,
+ },
+ RepoID: repo.ID,
+ IsDeletedBranch: util.OptionalBoolTrue,
+ })
assert.NoError(t, err)
assert.Len(t, branches, 2)
}
func TestGetDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
assert.NotNil(t, getDeletedBranch(t, firstBranch))
}
@@ -43,18 +57,18 @@ func TestGetDeletedBranch(t *testing.T) {
func TestDeletedBranchLoadUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
- secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
+ secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
branch := getDeletedBranch(t, firstBranch)
assert.Nil(t, branch.DeletedBy)
- branch.LoadUser(db.DefaultContext)
+ branch.LoadDeletedBy(db.DefaultContext)
assert.NotNil(t, branch.DeletedBy)
assert.Equal(t, "user1", branch.DeletedBy.Name)
branch = getDeletedBranch(t, secondBranch)
assert.Nil(t, branch.DeletedBy)
- branch.LoadUser(db.DefaultContext)
+ branch.LoadDeletedBy(db.DefaultContext)
assert.NotNil(t, branch.DeletedBy)
assert.Equal(t, "Ghost", branch.DeletedBy.Name)
}
@@ -63,22 +77,22 @@ func TestRemoveDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 1})
err := git_model.RemoveDeletedBranchByID(db.DefaultContext, repo.ID, 1)
assert.NoError(t, err)
unittest.AssertNotExistsBean(t, firstBranch)
- unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &git_model.Branch{ID: 2})
}
-func getDeletedBranch(t *testing.T, branch *git_model.DeletedBranch) *git_model.DeletedBranch {
+func getDeletedBranch(t *testing.T, branch *git_model.Branch) *git_model.Branch {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo.ID, branch.ID)
assert.NoError(t, err)
assert.Equal(t, branch.ID, deletedBranch.ID)
assert.Equal(t, branch.Name, deletedBranch.Name)
- assert.Equal(t, branch.Commit, deletedBranch.Commit)
+ assert.Equal(t, branch.CommitID, deletedBranch.CommitID)
assert.Equal(t, branch.DeletedByID, deletedBranch.DeletedByID)
return deletedBranch
@@ -146,8 +160,8 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) {
deletedBranch, err := git_model.GetDeletedBranchByID(db.DefaultContext, repo2.ID, 1)
- // Expect no error, and the returned branch is nil.
- assert.NoError(t, err)
+ // Expect error, and the returned branch is nil.
+ assert.Error(t, err)
assert.Nil(t, deletedBranch)
// Now get the deletedBranch with ID of 1 on repo with ID 1.
diff --git a/models/git/branches.go b/models/git/branches.go
deleted file mode 100644
index b94ea32959..0000000000
--- a/models/git/branches.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package git
-
-import (
- "context"
- "fmt"
- "time"
-
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/timeutil"
-)
-
-// DeletedBranch struct
-type DeletedBranch struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"`
- Name string `xorm:"UNIQUE(s) NOT NULL"`
- Commit string `xorm:"UNIQUE(s) NOT NULL"`
- DeletedByID int64 `xorm:"INDEX"`
- DeletedBy *user_model.User `xorm:"-"`
- DeletedUnix timeutil.TimeStamp `xorm:"INDEX created"`
-}
-
-func init() {
- db.RegisterModel(new(DeletedBranch))
- db.RegisterModel(new(RenamedBranch))
-}
-
-// AddDeletedBranch adds a deleted branch to the database
-func AddDeletedBranch(ctx context.Context, repoID int64, branchName, commit string, deletedByID int64) error {
- deletedBranch := &DeletedBranch{
- RepoID: repoID,
- Name: branchName,
- Commit: commit,
- DeletedByID: deletedByID,
- }
-
- _, err := db.GetEngine(ctx).Insert(deletedBranch)
- return err
-}
-
-// GetDeletedBranches returns all the deleted branches
-func GetDeletedBranches(ctx context.Context, repoID int64) ([]*DeletedBranch, error) {
- deletedBranches := make([]*DeletedBranch, 0)
- return deletedBranches, db.GetEngine(ctx).Where("repo_id = ?", repoID).Desc("deleted_unix").Find(&deletedBranches)
-}
-
-// GetDeletedBranchByID get a deleted branch by its ID
-func GetDeletedBranchByID(ctx context.Context, repoID, id int64) (*DeletedBranch, error) {
- deletedBranch := &DeletedBranch{}
- has, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).And("id = ?", id).Get(deletedBranch)
- if err != nil {
- return nil, err
- }
- if !has {
- return nil, nil
- }
- return deletedBranch, nil
-}
-
-// RemoveDeletedBranchByID removes a deleted branch from the database
-func RemoveDeletedBranchByID(ctx context.Context, repoID, id int64) (err error) {
- deletedBranch := &DeletedBranch{
- RepoID: repoID,
- ID: id,
- }
-
- if affected, err := db.GetEngine(ctx).Delete(deletedBranch); err != nil {
- return err
- } else if affected != 1 {
- return fmt.Errorf("remove deleted branch ID(%v) failed", id)
- }
-
- return nil
-}
-
-// LoadUser loads the user that deleted the branch
-// When there's no user found it returns a user_model.NewGhostUser
-func (deletedBranch *DeletedBranch) LoadUser(ctx context.Context) {
- user, err := user_model.GetUserByID(ctx, deletedBranch.DeletedByID)
- if err != nil {
- user = user_model.NewGhostUser()
- }
- deletedBranch.DeletedBy = user
-}
-
-// RemoveDeletedBranchByName removes all deleted branches
-func RemoveDeletedBranchByName(ctx context.Context, repoID int64, branch string) error {
- _, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branch).Delete(new(DeletedBranch))
- return err
-}
-
-// RemoveOldDeletedBranches removes old deleted branches
-func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
- // Nothing to do for shutdown or terminate
- log.Trace("Doing: DeletedBranchesCleanup")
-
- deleteBefore := time.Now().Add(-olderThan)
- _, err := db.GetEngine(ctx).Where("deleted_unix < ?", deleteBefore.Unix()).Delete(new(DeletedBranch))
- if err != nil {
- log.Error("DeletedBranchesCleanup: %v", err)
- }
-}
-
-// RenamedBranch provide renamed branch log
-// will check it when a branch can't be found
-type RenamedBranch struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX NOT NULL"`
- From string
- To string
- CreatedUnix timeutil.TimeStamp `xorm:"created"`
-}
-
-// FindRenamedBranch check if a branch was renamed
-func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
- branch = &RenamedBranch{
- RepoID: repoID,
- From: from,
- }
- exist, err = db.GetEngine(ctx).Get(branch)
-
- return branch, exist, err
-}
-
-// RenameBranch rename a branch
-func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(isDefault bool) error) (err error) {
- ctx, committer, err := db.TxContext(ctx)
- if err != nil {
- return err
- }
- defer committer.Close()
-
- sess := db.GetEngine(ctx)
- // 1. update default branch if needed
- isDefault := repo.DefaultBranch == from
- if isDefault {
- repo.DefaultBranch = to
- _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo)
- if err != nil {
- return err
- }
- }
-
- // 2. Update protected branch if needed
- protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from)
- if err != nil {
- return err
- }
-
- if protectedBranch != nil {
- protectedBranch.RuleName = to
- _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
- if err != nil {
- return err
- }
- } else {
- protected, err := IsBranchProtected(ctx, repo.ID, from)
- if err != nil {
- return err
- }
- if protected {
- return ErrBranchIsProtected
- }
- }
-
- // 3. Update all not merged pull request base branch name
- _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?",
- repo.ID, from, false).
- Update(map[string]interface{}{"base_branch": to})
- if err != nil {
- return err
- }
-
- // 4. do git action
- if err = gitAction(isDefault); err != nil {
- return err
- }
-
- // 5. insert renamed branch record
- renamedBranch := &RenamedBranch{
- RepoID: repo.ID,
- From: from,
- To: to,
- }
- err = db.Insert(ctx, renamedBranch)
- if err != nil {
- return err
- }
-
- return committer.Commit()
-}
diff --git a/models/git/protected_branch_list.go b/models/git/protected_branch_list.go
index 17fe6d701f..eeb307e245 100644
--- a/models/git/protected_branch_list.go
+++ b/models/git/protected_branch_list.go
@@ -8,7 +8,7 @@ import (
"sort"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
)
@@ -47,19 +47,32 @@ func FindRepoProtectedBranchRules(ctx context.Context, repoID int64) (ProtectedB
}
// FindAllMatchedBranches find all matched branches
-func FindAllMatchedBranches(ctx context.Context, gitRepo *git.Repository, ruleName string) ([]string, error) {
- // FIXME: how many should we get?
- branches, _, err := gitRepo.GetBranchNames(0, 9999999)
- if err != nil {
- return nil, err
- }
- rule := glob.MustCompile(ruleName)
- results := make([]string, 0, len(branches))
- for _, branch := range branches {
- if rule.Match(branch) {
- results = append(results, branch)
+func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) ([]string, error) {
+ results := make([]string, 0, 10)
+ for page := 1; ; page++ {
+ brancheNames, err := FindBranchNames(ctx, FindBranchOptions{
+ ListOptions: db.ListOptions{
+ PageSize: 100,
+ Page: page,
+ },
+ RepoID: repoID,
+ IsDeletedBranch: util.OptionalBoolFalse,
+ })
+ if err != nil {
+ return nil, err
+ }
+ rule := glob.MustCompile(ruleName)
+
+ for _, branch := range brancheNames {
+ if rule.Match(branch) {
+ results = append(results, branch)
+ }
+ }
+ if len(brancheNames) < 100 {
+ break
}
}
+
return results, nil
}