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:
parent
56d79301b9
commit
bb39359668
14 changed files with 357 additions and 1 deletions
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 a new issue