mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Add a simple way to rename branch like gh (#15870)
- Update default branch if needed - Update protected branch if needed - Update all not merged pull request base branch name - Rename git branch - Record this rename work and auto redirect for old branch on ui Signed-off-by: a1012112796 <1012112796@qq.com> Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
		
							
								
								
									
										44
									
								
								integrations/rename_branch_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								integrations/rename_branch_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					// 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 integrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRenameBranch(t *testing.T) {
 | 
				
			||||||
 | 
						// get branch setting page
 | 
				
			||||||
 | 
						session := loginUser(t, "user2")
 | 
				
			||||||
 | 
						req := NewRequest(t, "GET", "/user2/repo1/settings/branches")
 | 
				
			||||||
 | 
						resp := session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
						htmlDoc := NewHTMLParser(t, resp.Body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						postData := map[string]string{
 | 
				
			||||||
 | 
							"_csrf": htmlDoc.GetCSRF(),
 | 
				
			||||||
 | 
							"from":  "master",
 | 
				
			||||||
 | 
							"to":    "main",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", postData)
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check new branch link
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", postData)
 | 
				
			||||||
 | 
						session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check old branch link
 | 
				
			||||||
 | 
						req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", postData)
 | 
				
			||||||
 | 
						resp = session.MakeRequest(t, req, http.StatusFound)
 | 
				
			||||||
 | 
						location := resp.HeaderMap.Get("Location")
 | 
				
			||||||
 | 
						assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check db
 | 
				
			||||||
 | 
						repo1 := db.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 | 
				
			||||||
 | 
						assert.Equal(t, "main", repo1.DefaultBranch)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -53,6 +53,7 @@ type ProtectedBranch struct {
 | 
				
			|||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	db.RegisterModel(new(ProtectedBranch))
 | 
						db.RegisterModel(new(ProtectedBranch))
 | 
				
			||||||
	db.RegisterModel(new(DeletedBranch))
 | 
						db.RegisterModel(new(DeletedBranch))
 | 
				
			||||||
 | 
						db.RegisterModel(new(RenamedBranch))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsProtected returns if the branch is protected
 | 
					// IsProtected returns if the branch is protected
 | 
				
			||||||
@@ -588,3 +589,83 @@ func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) {
 | 
				
			|||||||
		log.Error("DeletedBranchesCleanup: %v", err)
 | 
							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(repoID int64, from string) (branch *RenamedBranch, exist bool, err error) {
 | 
				
			||||||
 | 
						branch = &RenamedBranch{
 | 
				
			||||||
 | 
							RepoID: repoID,
 | 
				
			||||||
 | 
							From:   from,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						exist, err = db.GetEngine(db.DefaultContext).Get(branch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenameBranch rename a branch
 | 
				
			||||||
 | 
					func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault bool) error) (err error) {
 | 
				
			||||||
 | 
						sess := db.NewSession(db.DefaultContext)
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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 := getProtectedBranchBy(sess, repo.ID, from)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if protectedBranch != nil {
 | 
				
			||||||
 | 
							protectedBranch.BranchName = to
 | 
				
			||||||
 | 
							_, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 3. Update all not merged pull request base branch name
 | 
				
			||||||
 | 
						_, err = sess.Table(new(PullRequest)).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 = sess.Insert(renamedBranch)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,3 +79,52 @@ func getDeletedBranch(t *testing.T, branch *DeletedBranch) *DeletedBranch {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return deletedBranch
 | 
						return deletedBranch
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestFindRenamedBranch(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, db.PrepareTestDatabase())
 | 
				
			||||||
 | 
						branch, exist, err := FindRenamedBranch(1, "dev")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, true, exist)
 | 
				
			||||||
 | 
						assert.Equal(t, "master", branch.To)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, exist, err = FindRenamedBranch(1, "unknow")
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						assert.Equal(t, false, exist)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRenameBranch(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, db.PrepareTestDatabase())
 | 
				
			||||||
 | 
						repo1 := db.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 | 
				
			||||||
 | 
						_isDefault := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := UpdateProtectBranch(repo1, &ProtectedBranch{
 | 
				
			||||||
 | 
							RepoID:     repo1.ID,
 | 
				
			||||||
 | 
							BranchName: "master",
 | 
				
			||||||
 | 
						}, WhitelistOptions{})
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, repo1.RenameBranch("master", "main", func(isDefault bool) error {
 | 
				
			||||||
 | 
							_isDefault = isDefault
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(t, true, _isDefault)
 | 
				
			||||||
 | 
						repo1 = db.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
 | 
				
			||||||
 | 
						assert.Equal(t, "main", repo1.DefaultBranch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pull := db.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) // merged
 | 
				
			||||||
 | 
						assert.Equal(t, "master", pull.BaseBranch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pull = db.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) // open
 | 
				
			||||||
 | 
						assert.Equal(t, "main", pull.BaseBranch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						renamedBranch := db.AssertExistsAndLoadBean(t, &RenamedBranch{ID: 2}).(*RenamedBranch)
 | 
				
			||||||
 | 
						assert.Equal(t, "master", renamedBranch.From)
 | 
				
			||||||
 | 
						assert.Equal(t, "main", renamedBranch.To)
 | 
				
			||||||
 | 
						assert.Equal(t, int64(1), renamedBranch.RepoID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						db.AssertExistsAndLoadBean(t, &ProtectedBranch{
 | 
				
			||||||
 | 
							RepoID:     repo1.ID,
 | 
				
			||||||
 | 
							BranchName: "main",
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								models/fixtures/renamed_branch.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								models/fixtures/renamed_branch.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 1
 | 
				
			||||||
 | 
					  repo_id: 1
 | 
				
			||||||
 | 
					  from: dev
 | 
				
			||||||
 | 
					  to: master
 | 
				
			||||||
@@ -346,6 +346,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("Add table commit_status_index", addTableCommitStatusIndex),
 | 
						NewMigration("Add table commit_status_index", addTableCommitStatusIndex),
 | 
				
			||||||
	// v196 -> v197
 | 
						// v196 -> v197
 | 
				
			||||||
	NewMigration("Add Color to ProjectBoard table", addColorColToProjectBoard),
 | 
						NewMigration("Add Color to ProjectBoard table", addColorColToProjectBoard),
 | 
				
			||||||
 | 
						// v197 -> v198
 | 
				
			||||||
 | 
						NewMigration("Add renamed_branch table", addRenamedBranchTable),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetCurrentDBVersion returns the current db version
 | 
					// GetCurrentDBVersion returns the current db version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								models/migrations/v197.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								models/migrations/v197.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					// 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 migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addRenamedBranchTable(x *xorm.Engine) error {
 | 
				
			||||||
 | 
						type RenamedBranch struct {
 | 
				
			||||||
 | 
							ID          int64 `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
							RepoID      int64 `xorm:"INDEX NOT NULL"`
 | 
				
			||||||
 | 
							From        string
 | 
				
			||||||
 | 
							To          string
 | 
				
			||||||
 | 
							CreatedUnix int64 `xorm:"created"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return x.Sync2(new(RenamedBranch))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -705,7 +705,28 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
 | 
				
			|||||||
		ctx.Repo.TreePath = path
 | 
							ctx.Repo.TreePath = path
 | 
				
			||||||
		return ctx.Repo.Repository.DefaultBranch
 | 
							return ctx.Repo.Repository.DefaultBranch
 | 
				
			||||||
	case RepoRefBranch:
 | 
						case RepoRefBranch:
 | 
				
			||||||
		return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
 | 
							ref := getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
 | 
				
			||||||
 | 
							if len(ref) == 0 {
 | 
				
			||||||
 | 
								// maybe it's a renamed branch
 | 
				
			||||||
 | 
								return getRefNameFromPath(ctx, path, func(s string) bool {
 | 
				
			||||||
 | 
									b, exist, err := models.FindRenamedBranch(ctx.Repo.Repository.ID, s)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										log.Error("FindRenamedBranch", err)
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if !exist {
 | 
				
			||||||
 | 
										return false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									ctx.Data["IsRenamedBranch"] = true
 | 
				
			||||||
 | 
									ctx.Data["RenamedBranchName"] = b.To
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return ref
 | 
				
			||||||
	case RepoRefTag:
 | 
						case RepoRefTag:
 | 
				
			||||||
		return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
 | 
							return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
 | 
				
			||||||
	case RepoRefCommit:
 | 
						case RepoRefCommit:
 | 
				
			||||||
@@ -784,6 +805,15 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			|||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			refName = getRefName(ctx, refType)
 | 
								refName = getRefName(ctx, refType)
 | 
				
			||||||
			ctx.Repo.BranchName = refName
 | 
								ctx.Repo.BranchName = refName
 | 
				
			||||||
 | 
								isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
 | 
				
			||||||
 | 
								if isRenamedBranch && has {
 | 
				
			||||||
 | 
									renamedBranchName := ctx.Data["RenamedBranchName"].(string)
 | 
				
			||||||
 | 
									ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
 | 
				
			||||||
 | 
									link := strings.Replace(ctx.Req.RequestURI, refName, renamedBranchName, 1)
 | 
				
			||||||
 | 
									ctx.Redirect(link)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
								if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
				
			||||||
				ctx.Repo.IsViewBranch = true
 | 
									ctx.Repo.IsViewBranch = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -164,3 +164,9 @@ func (repo *Repository) RemoveRemote(name string) error {
 | 
				
			|||||||
func (branch *Branch) GetCommit() (*Commit, error) {
 | 
					func (branch *Branch) GetCommit() (*Commit, error) {
 | 
				
			||||||
	return branch.gitRepo.GetBranchCommit(branch.Name)
 | 
						return branch.gitRepo.GetBranchCommit(branch.Name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenameBranch rename a branch
 | 
				
			||||||
 | 
					func (repo *Repository) RenameBranch(from, to string) error {
 | 
				
			||||||
 | 
						_, err := NewCommand("branch", "-m", from, to).RunInDir(repo.Path)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1985,6 +1985,12 @@ settings.lfs_pointers.inRepo=In Repo
 | 
				
			|||||||
settings.lfs_pointers.exists=Exists in store
 | 
					settings.lfs_pointers.exists=Exists in store
 | 
				
			||||||
settings.lfs_pointers.accessible=Accessible to User
 | 
					settings.lfs_pointers.accessible=Accessible to User
 | 
				
			||||||
settings.lfs_pointers.associateAccessible=Associate accessible %d OIDs
 | 
					settings.lfs_pointers.associateAccessible=Associate accessible %d OIDs
 | 
				
			||||||
 | 
					settings.rename_branch_failed_exist=Cannot rename branch because target branch %s exists.
 | 
				
			||||||
 | 
					settings.rename_branch_failed_not_exist=Cannot rename branch %s because it does not exist.
 | 
				
			||||||
 | 
					settings.rename_branch_success =Branch %s was successfully renamed to %s.
 | 
				
			||||||
 | 
					settings.rename_branch_from=old branch name
 | 
				
			||||||
 | 
					settings.rename_branch_to=new branch name
 | 
				
			||||||
 | 
					settings.rename_branch=Rename branch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
diff.browse_source = Browse Source
 | 
					diff.browse_source = Browse Source
 | 
				
			||||||
diff.parent = parent
 | 
					diff.parent = parent
 | 
				
			||||||
@@ -2106,6 +2112,7 @@ branch.create_new_branch = Create branch from branch:
 | 
				
			|||||||
branch.confirm_create_branch = Create branch
 | 
					branch.confirm_create_branch = Create branch
 | 
				
			||||||
branch.new_branch = Create new branch
 | 
					branch.new_branch = Create new branch
 | 
				
			||||||
branch.new_branch_from = Create new branch from '%s'
 | 
					branch.new_branch_from = Create new branch from '%s'
 | 
				
			||||||
 | 
					branch.renamed = Branch %s was renamed to %s.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tag.create_tag = Create tag <strong>%s</strong>
 | 
					tag.create_tag = Create tag <strong>%s</strong>
 | 
				
			||||||
tag.create_success = Tag '%s' has been created.
 | 
					tag.create_success = Tag '%s' has been created.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/services/forms"
 | 
						"code.gitea.io/gitea/services/forms"
 | 
				
			||||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						pull_service "code.gitea.io/gitea/services/pull"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/services/repository"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ProtectedBranch render the page to protect the repository
 | 
					// ProtectedBranch render the page to protect the repository
 | 
				
			||||||
@@ -285,3 +286,40 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
 | 
				
			|||||||
		ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
							ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenameBranchPost responses for rename a branch
 | 
				
			||||||
 | 
					func RenameBranchPost(ctx *context.Context) {
 | 
				
			||||||
 | 
						form := web.GetForm(ctx).(*forms.RenameBranchForm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !ctx.Repo.CanCreateBranch() {
 | 
				
			||||||
 | 
							ctx.NotFound("RenameBranch", nil)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ctx.HasError() {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.GetErrMsg())
 | 
				
			||||||
 | 
							ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg, err := repository.RenameBranch(ctx.Repo.Repository, ctx.User, ctx.Repo.GitRepo, form.From, form.To)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.ServerError("RenameBranch", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if msg == "target_exist" {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_exist", form.To))
 | 
				
			||||||
 | 
							ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if msg == "from_not_exist" {
 | 
				
			||||||
 | 
							ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_not_exist", form.From))
 | 
				
			||||||
 | 
							ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.rename_branch_success", form.From, form.To))
 | 
				
			||||||
 | 
						ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -612,6 +612,7 @@ func RegisterRoutes(m *web.Route) {
 | 
				
			|||||||
				m.Combo("/*").Get(repo.SettingsProtectedBranch).
 | 
									m.Combo("/*").Get(repo.SettingsProtectedBranch).
 | 
				
			||||||
					Post(bindIgnErr(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
 | 
										Post(bindIgnErr(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
 | 
				
			||||||
			}, repo.MustBeNotEmpty)
 | 
								}, repo.MustBeNotEmpty)
 | 
				
			||||||
 | 
								m.Post("/rename_branch", bindIgnErr(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			m.Group("/tags", func() {
 | 
								m.Group("/tags", func() {
 | 
				
			||||||
				m.Get("", repo.Tags)
 | 
									m.Get("", repo.Tags)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,3 +24,15 @@ func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding
 | 
				
			|||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenameBranchForm form for rename a branch
 | 
				
			||||||
 | 
					type RenameBranchForm struct {
 | 
				
			||||||
 | 
						From string `binding:"Required;MaxSize(100);GitRefName"`
 | 
				
			||||||
 | 
						To   string `binding:"Required;MaxSize(100);GitRefName"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Validate validates the fields
 | 
				
			||||||
 | 
					func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						ctx := context.GetContext(req)
 | 
				
			||||||
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,10 +10,49 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/models"
 | 
						"code.gitea.io/gitea/models"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/notification"
 | 
				
			||||||
	repo_module "code.gitea.io/gitea/modules/repository"
 | 
						repo_module "code.gitea.io/gitea/modules/repository"
 | 
				
			||||||
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						pull_service "code.gitea.io/gitea/services/pull"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenameBranch rename a branch
 | 
				
			||||||
 | 
					func RenameBranch(repo *models.Repository, doer *models.User, gitRepo *git.Repository, from, to string) (string, error) {
 | 
				
			||||||
 | 
						if from == to {
 | 
				
			||||||
 | 
							return "target_exist", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if gitRepo.IsBranchExist(to) {
 | 
				
			||||||
 | 
							return "target_exist", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !gitRepo.IsBranchExist(from) {
 | 
				
			||||||
 | 
							return "from_not_exist", nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := repo.RenameBranch(from, to, func(isDefault bool) error {
 | 
				
			||||||
 | 
							err2 := gitRepo.RenameBranch(from, to)
 | 
				
			||||||
 | 
							if err2 != nil {
 | 
				
			||||||
 | 
								return err2
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if isDefault {
 | 
				
			||||||
 | 
								err2 = gitRepo.SetDefaultBranch(to)
 | 
				
			||||||
 | 
								if err2 != nil {
 | 
				
			||||||
 | 
									return err2
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						notification.NotifyDeleteRef(doer, repo, "branch", "refs/heads/"+from)
 | 
				
			||||||
 | 
						notification.NotifyCreateRef(doer, repo, "branch", "refs/heads/"+to)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return "", nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// enmuerates all branch related errors
 | 
					// enmuerates all branch related errors
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	ErrBranchIsDefault   = errors.New("branch is default")
 | 
						ErrBranchIsDefault   = errors.New("branch is default")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,6 +77,28 @@
 | 
				
			|||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								{{if $.Repository.CanCreateBranch}}
 | 
				
			||||||
 | 
									<h4 class="ui top attached header">
 | 
				
			||||||
 | 
										{{.i18n.Tr "repo.settings.rename_branch"}}
 | 
				
			||||||
 | 
									</h4>
 | 
				
			||||||
 | 
									<div class="ui attached segment">
 | 
				
			||||||
 | 
										<form class="ui form" action="{{$.Repository.Link}}/settings/rename_branch" method="post">
 | 
				
			||||||
 | 
											{{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
											<div class="required field">
 | 
				
			||||||
 | 
												<label for="from">{{.i18n.Tr "repo.settings.rename_branch_from"}}</label>
 | 
				
			||||||
 | 
												<input id="from" name="from" required>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="required field {{if .Err_BranchName}}error{{end}}">
 | 
				
			||||||
 | 
												<label for="to">{{.i18n.Tr "repo.settings.rename_branch_to"}}</label>
 | 
				
			||||||
 | 
												<input id="to" name="to" required>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
											<div class="field">
 | 
				
			||||||
 | 
												<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										</form>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
		{{end}}
 | 
							{{end}}
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user