From 30ee9c70e81d186d2c98005e153cbc10c0f1d33d Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Fri, 14 May 2021 17:58:51 +0800 Subject: [PATCH 1/8] Add a simple way to rename branch like gh - 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> --- models/branches.go | 97 ++++++++++++++++++++++++ models/branches_test.go | 49 ++++++++++++ models/fixtures/renamed_branch.yml | 5 ++ models/migrations/migrations.go | 2 + models/migrations/v180.go | 19 +++++ models/models.go | 1 + modules/context/repo.go | 73 +++++++++++++----- modules/git/repo_branch.go | 6 ++ options/locale/locale_en-US.ini | 7 ++ routers/repo/setting_protected_branch.go | 58 ++++++++++++++ routers/routes/web.go | 1 + services/forms/repo_branch_form.go | 12 +++ templates/repo/settings/branches.tmpl | 22 ++++++ 13 files changed, 333 insertions(+), 19 deletions(-) create mode 100644 models/fixtures/renamed_branch.yml create mode 100644 models/migrations/v180.go diff --git a/models/branches.go b/models/branches.go index 1ac1fa49e580..cf15266d45c3 100644 --- a/models/branches.go +++ b/models/branches.go @@ -596,3 +596,100 @@ func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) { log.Error("DeletedBranchesCleanup: %v", err) } } + +// RenamedBranch proivde renamed branch log +// will check it when an branch can't be found +type RenamedBranch struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX NOT NULL"` + From string + To string +} + +// 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 = x.Get(branch) + + return +} + +// RenameBranch rename a branch +func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault bool) error) (err error) { + sess := x.NewSession() + 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.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 { + if err2 := sess.Rollback(); err2 != nil { + log.Error("RenameBranch: sess.Rollback: %v", err2) + } + return err + } + + if protectedBranch != nil { + protectedBranch.BranchName = to + _, err = sess.Cols("branch_name").Update(protectedBranch) + if err != nil { + if err2 := sess.Rollback(); err2 != nil { + log.Error("RenameBranch: sess.Rollback: %v", err2) + } + 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 { + if err2 := sess.Rollback(); err2 != nil { + log.Error("RenameBranch: sess.Rollback: %v", err2) + } + return err + } + + // 4. do git action + if err = gitAction(isDefault); err != nil { + if err2 := sess.Rollback(); err2 != nil { + log.Error("RenameBranch: sess.Rollback: %v", err2) + } + + return err + } + + // 5. insert renamed branch record + renamedBranch := &RenamedBranch{ + RepoID: repo.ID, + From: from, + To: to, + } + + _, err = sess.Insert(renamedBranch) + if err != nil { + if err2 := sess.Rollback(); err2 != nil { + log.Error("RenameBranch: sess.Rollback: %v", err2) + } + + return err + } + + return sess.Commit() +} diff --git a/models/branches_test.go b/models/branches_test.go index b7984331edb1..acbc99639cc4 100644 --- a/models/branches_test.go +++ b/models/branches_test.go @@ -78,3 +78,52 @@ func getDeletedBranch(t *testing.T, branch *DeletedBranch) *DeletedBranch { return deletedBranch } + +func TestFindRenamedBranch(t *testing.T) { + assert.NoError(t, 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, PrepareTestDatabase()) + repo1 := 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 = AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) + assert.Equal(t, "main", repo1.DefaultBranch) + + pull := AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) // merged + assert.Equal(t, "master", pull.BaseBranch) + + pull = AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) // open + assert.Equal(t, "main", pull.BaseBranch) + + renamedBranch := 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) + + AssertExistsAndLoadBean(t, &ProtectedBranch{ + RepoID: repo1.ID, + BranchName: "main", + }) +} diff --git a/models/fixtures/renamed_branch.yml b/models/fixtures/renamed_branch.yml new file mode 100644 index 000000000000..efa5130a2b9e --- /dev/null +++ b/models/fixtures/renamed_branch.yml @@ -0,0 +1,5 @@ +- + id: 1 + repo_id: 1 + from: dev + to: master diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index c54c383fb810..0dc4f597d55e 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -309,6 +309,8 @@ var migrations = []Migration{ NewMigration("Add LFS columns to Mirror", addLFSMirrorColumns), // v179 -> v180 NewMigration("Convert avatar url to text", convertAvatarURLToText), + // v180 -> v181 + NewMigration("Add renamed_branch table", addRenamedBranchTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v180.go b/models/migrations/v180.go new file mode 100644 index 000000000000..7fbd091f9c12 --- /dev/null +++ b/models/migrations/v180.go @@ -0,0 +1,19 @@ +// 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 + } + return x.Sync2(new(RenamedBranch)) +} diff --git a/models/models.go b/models/models.go index b0a9062566ee..921af19da8d6 100644 --- a/models/models.go +++ b/models/models.go @@ -134,6 +134,7 @@ func init() { new(ProjectIssue), new(Session), new(RepoTransfer), + new(RenamedBranch), ) gonicNames := []string{"SSL", "UID"} diff --git a/modules/context/repo.go b/modules/context/repo.go index 3e48b34b3d16..e1e8deab0aa2 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -49,23 +49,25 @@ type PullRequest struct { // Repository contains information to operate a repository type Repository struct { models.Permission - IsWatching bool - IsViewBranch bool - IsViewTag bool - IsViewCommit bool - Repository *models.Repository - Owner *models.User - Commit *git.Commit - Tag *git.Tag - GitRepo *git.Repository - BranchName string - TagName string - TreePath string - CommitID string - RepoLink string - CloneLink models.CloneLink - CommitsCount int64 - Mirror *models.Mirror + IsWatching bool + IsViewBranch bool + IsViewTag bool + IsViewCommit bool + Repository *models.Repository + Owner *models.User + Commit *git.Commit + Tag *git.Tag + GitRepo *git.Repository + BranchName string + IsRenamedBranch bool + RenamedBranchName string + TagName string + TreePath string + CommitID string + RepoLink string + CloneLink models.CloneLink + CommitsCount int64 + Mirror *models.Mirror PullRequest *PullRequest } @@ -701,7 +703,28 @@ func getRefName(ctx *Context, pathType RepoRefType) string { ctx.Repo.TreePath = path return ctx.Repo.Repository.DefaultBranch 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.Repo.IsRenamedBranch = true + ctx.Repo.RenamedBranchName = b.To + + return true + }) + } + + return ref case RepoRefTag: return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist) case RepoRefCommit: @@ -780,9 +803,21 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } else { refName = getRefName(ctx, refType) ctx.Repo.BranchName = refName - if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) { + if refType.RefTypeIncludesBranches() { ctx.Repo.IsViewBranch = true + if ctx.Repo.IsRenamedBranch { + ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, ctx.Repo.RenamedBranchName)) + link := strings.Replace(ctx.Req.RequestURI, refName, ctx.Repo.RenamedBranchName, 1) + ctx.Redirect(link) + return + } + + if !ctx.Repo.GitRepo.IsBranchExist(refName) { + ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) + return + } + ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) if err != nil { ctx.ServerError("GetBranchCommit", err) diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 58781eb1c71a..44c81e8aeb47 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -156,3 +156,9 @@ func (repo *Repository) RemoveRemote(name string) error { func (branch *Branch) GetCommit() (*Commit, error) { 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 +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 936677e31d33..422c8041b504 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1862,6 +1862,12 @@ settings.lfs_pointers.inRepo=In Repo settings.lfs_pointers.exists=Exists in store settings.lfs_pointers.accessible=Accessible to User settings.lfs_pointers.associateAccessible=Associate accessible %d OIDs +settings.rename_branch_failed_exist=can not rename branch because target branch %s is exist +settings.rename_branch_failed_not_exist= can not rename branch because target branch %s is not exist +settings.rename_branch_success = branch %s has been succed 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.parent = parent @@ -1976,6 +1982,7 @@ branch.restore = Restore Branch '%s' branch.download = Download Branch '%s' branch.included_desc = This branch is part of the default branch branch.included = Included +branch.renamed = Branch %s was renamed to %s. tag.create_tag = Create tag %s tag.create_success = Tag '%s' has been created. diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index fba2c095cf93..865f696d930c 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" @@ -284,3 +285,60 @@ func SettingsProtectedBranchPost(ctx *context.Context) { 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 + } + + if form.From == form.To { + 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 ctx.Repo.GitRepo.IsBranchExist(form.To) { + ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_exist", form.To)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) + } + + if !ctx.Repo.GitRepo.IsBranchExist(form.From) { + ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_not_exist", form.From)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) + } + + if err := ctx.Repo.Repository.RenameBranch(form.From, form.To, func(isDefault bool) error { + err2 := ctx.Repo.GitRepo.RenameBranch(form.From, form.To) + if err2 != nil { + return err2 + } + + if isDefault { + err2 = ctx.Repo.GitRepo.SetDefaultBranch(form.To) + if err2 != nil { + return err2 + } + } + + return nil + }); err != nil { + ctx.ServerError("RenameBranch", err) + return + } + + notification.NotifyDeleteRef(ctx.User, ctx.Repo.Repository, "branch", "refs/heads/"+form.From) + notification.NotifyCreateRef(ctx.User, ctx.Repo.Repository, "branch", "refs/heads/"+form.To) + + ctx.Flash.Success(ctx.Tr("repo.settings.rename_branch_success", form.From, form.To)) + ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) +} diff --git a/routers/routes/web.go b/routers/routes/web.go index 008c745d6e09..c8c0336efee7 100644 --- a/routers/routes/web.go +++ b/routers/routes/web.go @@ -664,6 +664,7 @@ func RegisterRoutes(m *web.Route) { m.Combo("/*").Get(repo.SettingsProtectedBranch). Post(bindIgnErr(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) }, repo.MustBeNotEmpty) + m.Post("/rename_branch", bindIgnErr(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost) m.Group("/hooks/git", func() { m.Get("", repo.GitHooks) diff --git a/services/forms/repo_branch_form.go b/services/forms/repo_branch_form.go index 88a069b8310c..f9262aaede77 100644 --- a/services/forms/repo_branch_form.go +++ b/services/forms/repo_branch_form.go @@ -24,3 +24,15 @@ func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding ctx := context.GetContext(req) 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) +} diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl index fbe9a7463e5e..fe936667fc48 100644 --- a/templates/repo/settings/branches.tmpl +++ b/templates/repo/settings/branches.tmpl @@ -77,6 +77,28 @@ + + {{if $.Repository.CanCreateBranch}} +

+ {{.i18n.Tr "repo.settings.rename_branch"}} +

+
+
+ {{.CsrfTokenHtml}} +
+ + +
+
+ + +
+
+ +
+
+
+ {{end}} {{end}} From b51c6a7b6fc56b735bff6b4b0e950622e2ba44f0 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Fri, 14 May 2021 19:11:23 +0800 Subject: [PATCH 2/8] fix bug --- modules/context/repo.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/modules/context/repo.go b/modules/context/repo.go index e1e8deab0aa2..81a94d562c73 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -803,20 +803,15 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } else { refName = getRefName(ctx, refType) ctx.Repo.BranchName = refName - if refType.RefTypeIncludesBranches() { - ctx.Repo.IsViewBranch = true - - if ctx.Repo.IsRenamedBranch { - ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, ctx.Repo.RenamedBranchName)) - link := strings.Replace(ctx.Req.RequestURI, refName, ctx.Repo.RenamedBranchName, 1) - ctx.Redirect(link) - return - } + if ctx.Repo.IsRenamedBranch { + ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, ctx.Repo.RenamedBranchName)) + link := strings.Replace(ctx.Req.RequestURI, refName, ctx.Repo.RenamedBranchName, 1) + ctx.Redirect(link) + return + } - if !ctx.Repo.GitRepo.IsBranchExist(refName) { - ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) - return - } + if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) { + ctx.Repo.IsViewBranch = true ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) if err != nil { From 7059da87d35576e497e61638817c0c379e5a7e62 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Fri, 14 May 2021 19:45:15 +0800 Subject: [PATCH 3/8] add test --- integrations/rename_branch_test.go | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 integrations/rename_branch_test.go diff --git a/integrations/rename_branch_test.go b/integrations/rename_branch_test.go new file mode 100644 index 000000000000..76fb91a08f3d --- /dev/null +++ b/integrations/rename_branch_test.go @@ -0,0 +1,43 @@ +// 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" + "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 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + assert.Equal(t, "main", repo1.DefaultBranch) +} From 83ab12d48935a408ec1e748280cc75bca64539fb Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Sat, 15 May 2021 10:51:41 +0800 Subject: [PATCH 4/8] add id condition --- models/branches.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/branches.go b/models/branches.go index cf15266d45c3..2d904c302462 100644 --- a/models/branches.go +++ b/models/branches.go @@ -629,7 +629,7 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b isDefault := repo.DefaultBranch == from if isDefault { repo.DefaultBranch = to - _, err = sess.Cols("default_branch").Update(repo) + _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo) if err != nil { return err } @@ -646,7 +646,7 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b if protectedBranch != nil { protectedBranch.BranchName = to - _, err = sess.Cols("branch_name").Update(protectedBranch) + _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch) if err != nil { if err2 := sess.Rollback(); err2 != nil { log.Error("RenameBranch: sess.Rollback: %v", err2) From 9e5d9aa6e026dfd108963a3737b707482da61979 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Sun, 16 May 2021 22:59:51 +0800 Subject: [PATCH 5/8] apply suggestions from code review --- models/branches.go | 27 +++---------- models/migrations/v180.go | 9 +++-- modules/context/repo.go | 48 +++++++++++------------ routers/repo/setting_protected_branch.go | 34 ++++------------ services/repository/branch.go | 49 ++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 77 deletions(-) create mode 100644 services/repository/branch.go diff --git a/models/branches.go b/models/branches.go index 2d904c302462..4bce14a8e031 100644 --- a/models/branches.go +++ b/models/branches.go @@ -600,10 +600,11 @@ func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) { // RenamedBranch proivde renamed branch log // will check it when an branch can't be found type RenamedBranch struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX NOT NULL"` - From string - To string + 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 @@ -638,9 +639,6 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b // 2. Update protected branch if needed protectedBranch, err := getProtectedBranchBy(sess, repo.ID, from) if err != nil { - if err2 := sess.Rollback(); err2 != nil { - log.Error("RenameBranch: sess.Rollback: %v", err2) - } return err } @@ -648,9 +646,6 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b protectedBranch.BranchName = to _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch) if err != nil { - if err2 := sess.Rollback(); err2 != nil { - log.Error("RenameBranch: sess.Rollback: %v", err2) - } return err } } @@ -660,18 +655,11 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b repo.ID, from, false). Update(map[string]interface{}{"base_branch": to}) if err != nil { - if err2 := sess.Rollback(); err2 != nil { - log.Error("RenameBranch: sess.Rollback: %v", err2) - } return err } // 4. do git action if err = gitAction(isDefault); err != nil { - if err2 := sess.Rollback(); err2 != nil { - log.Error("RenameBranch: sess.Rollback: %v", err2) - } - return err } @@ -681,13 +669,8 @@ func (repo *Repository) RenameBranch(from, to string, gitAction func(isDefault b From: from, To: to, } - _, err = sess.Insert(renamedBranch) if err != nil { - if err2 := sess.Rollback(); err2 != nil { - log.Error("RenameBranch: sess.Rollback: %v", err2) - } - return err } diff --git a/models/migrations/v180.go b/models/migrations/v180.go index 7fbd091f9c12..3517896a23dd 100644 --- a/models/migrations/v180.go +++ b/models/migrations/v180.go @@ -10,10 +10,11 @@ import ( func addRenamedBranchTable(x *xorm.Engine) error { type RenamedBranch struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX NOT NULL"` - From string - To string + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX NOT NULL"` + From string + To string + CreatedUnix int64 `xorm:"created"` } return x.Sync2(new(RenamedBranch)) } diff --git a/modules/context/repo.go b/modules/context/repo.go index 81a94d562c73..9090ae940ff8 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -49,25 +49,23 @@ type PullRequest struct { // Repository contains information to operate a repository type Repository struct { models.Permission - IsWatching bool - IsViewBranch bool - IsViewTag bool - IsViewCommit bool - Repository *models.Repository - Owner *models.User - Commit *git.Commit - Tag *git.Tag - GitRepo *git.Repository - BranchName string - IsRenamedBranch bool - RenamedBranchName string - TagName string - TreePath string - CommitID string - RepoLink string - CloneLink models.CloneLink - CommitsCount int64 - Mirror *models.Mirror + IsWatching bool + IsViewBranch bool + IsViewTag bool + IsViewCommit bool + Repository *models.Repository + Owner *models.User + Commit *git.Commit + Tag *git.Tag + GitRepo *git.Repository + BranchName string + TagName string + TreePath string + CommitID string + RepoLink string + CloneLink models.CloneLink + CommitsCount int64 + Mirror *models.Mirror PullRequest *PullRequest } @@ -717,8 +715,8 @@ func getRefName(ctx *Context, pathType RepoRefType) string { return false } - ctx.Repo.IsRenamedBranch = true - ctx.Repo.RenamedBranchName = b.To + ctx.Data["IsRenamedBranch"] = true + ctx.Data["RenamedBranchName"] = b.To return true }) @@ -803,9 +801,11 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } else { refName = getRefName(ctx, refType) ctx.Repo.BranchName = refName - if ctx.Repo.IsRenamedBranch { - ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, ctx.Repo.RenamedBranchName)) - link := strings.Replace(ctx.Req.RequestURI, refName, ctx.Repo.RenamedBranchName, 1) + 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 } diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go index 865f696d930c..19606bd1b8c0 100644 --- a/routers/repo/setting_protected_branch.go +++ b/routers/repo/setting_protected_branch.go @@ -15,11 +15,11 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" + "code.gitea.io/gitea/services/repository" ) // ProtectedBranch render the page to protect the repository @@ -301,44 +301,24 @@ func RenameBranchPost(ctx *context.Context) { return } - if form.From == form.To { - ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_exist", form.To)) - ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) + 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 ctx.Repo.GitRepo.IsBranchExist(form.To) { + 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 !ctx.Repo.GitRepo.IsBranchExist(form.From) { + 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)) - } - - if err := ctx.Repo.Repository.RenameBranch(form.From, form.To, func(isDefault bool) error { - err2 := ctx.Repo.GitRepo.RenameBranch(form.From, form.To) - if err2 != nil { - return err2 - } - - if isDefault { - err2 = ctx.Repo.GitRepo.SetDefaultBranch(form.To) - if err2 != nil { - return err2 - } - } - - return nil - }); err != nil { - ctx.ServerError("RenameBranch", err) return } - notification.NotifyDeleteRef(ctx.User, ctx.Repo.Repository, "branch", "refs/heads/"+form.From) - notification.NotifyCreateRef(ctx.User, ctx.Repo.Repository, "branch", "refs/heads/"+form.To) - ctx.Flash.Success(ctx.Tr("repo.settings.rename_branch_success", form.From, form.To)) ctx.Redirect(fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink)) } diff --git a/services/repository/branch.go b/services/repository/branch.go new file mode 100644 index 000000000000..194249e63aa7 --- /dev/null +++ b/services/repository/branch.go @@ -0,0 +1,49 @@ +// 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 ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/notification" +) + +// 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 +} From c01d4d27ef07ef4d8184a2950e770c7a318ac081 Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Sun, 3 Oct 2021 12:11:58 +0800 Subject: [PATCH 6/8] Update models/branches.go Co-authored-by: delvh --- models/branches.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/branches.go b/models/branches.go index 7349e29daf2c..aeb5079eae5e 100644 --- a/models/branches.go +++ b/models/branches.go @@ -572,8 +572,8 @@ func RemoveOldDeletedBranches(ctx context.Context, olderThan time.Duration) { } } -// RenamedBranch proivde renamed branch log -// will check it when an branch can't be found +// 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 7216645d73265ccb3b9c169c72f535b36a17be4f Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Sun, 3 Oct 2021 12:17:42 +0800 Subject: [PATCH 7/8] Update options/locale/locale_en-US.ini Co-authored-by: delvh --- options/locale/locale_en-US.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7f21a6e58d3f..69a303de8a3a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1974,12 +1974,12 @@ settings.lfs_pointers.inRepo=In Repo settings.lfs_pointers.exists=Exists in store settings.lfs_pointers.accessible=Accessible to User settings.lfs_pointers.associateAccessible=Associate accessible %d OIDs -settings.rename_branch_failed_exist=can not rename branch because target branch %s is exist -settings.rename_branch_failed_not_exist= can not rename branch because target branch %s is not exist -settings.rename_branch_success = branch %s has been succed renamed to %s +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 +settings.rename_branch=Rename branch diff.browse_source = Browse Source diff.parent = parent From 57fc73892a669d7da8834708e92c8e1181bfd37e Mon Sep 17 00:00:00 2001 From: a1012112796 <1012112796@qq.com> Date: Thu, 7 Oct 2021 15:42:32 +0800 Subject: [PATCH 8/8] fix db --- models/branches.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/branches.go b/models/branches.go index 124b82871400..3c62c7a87bd8 100644 --- a/models/branches.go +++ b/models/branches.go @@ -53,6 +53,7 @@ type ProtectedBranch struct { func init() { db.RegisterModel(new(ProtectedBranch)) db.RegisterModel(new(DeletedBranch)) + db.RegisterModel(new(RenamedBranch)) } // IsProtected returns if the branch is protected