From 5cddab4f74bbb307ddf13e458c7ac22f93b9283a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 6 Mar 2024 14:26:32 +0800 Subject: [PATCH] Make wiki default branch name changable (#29603) Fix #29000 Fix #28685 Fix #18568 Related: #27497 And by the way fix #24036, add a Cancel button there (one line) --- models/migrations/migrations.go | 2 + models/migrations/v1_22/v289.go | 18 ++++++ models/repo/repo.go | 4 ++ modules/git/repo_base_gogit.go | 4 +- modules/git/repo_base_nogogit.go | 4 +- options/locale/locale_en-US.ini | 2 + routers/web/repo/setting/setting.go | 7 +++ routers/web/repo/wiki.go | 60 +++++++++++-------- routers/web/repo/wiki_test.go | 30 ++++++++++ services/forms/repo_form.go | 1 + services/repository/create.go | 2 + services/repository/migrate.go | 88 ++++++++++++++++++---------- services/wiki/wiki.go | 79 ++++++++++++++++++------- services/wiki/wiki_test.go | 10 ++-- templates/repo/settings/options.tmpl | 4 ++ templates/repo/wiki/new.tmpl | 5 +- templates/repo/wiki/pages.tmpl | 1 + 17 files changed, 232 insertions(+), 89 deletions(-) create mode 100644 models/migrations/v1_22/v289.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 9d288ec2bdf..d40866f3e93 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -562,6 +562,8 @@ var migrations = []Migration{ NewMigration("Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges), // v288 -> v289 NewMigration("Add user_blocking table", v1_22.AddUserBlockingTable), + // v289 -> v290 + NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go new file mode 100644 index 00000000000..e2dfc48715a --- /dev/null +++ b/models/migrations/v1_22/v289.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddDefaultWikiBranch(x *xorm.Engine) error { + type Repository struct { + ID int64 + DefaultWikiBranch string + } + if err := x.Sync(&Repository{}); err != nil { + return err + } + _, err := x.Exec("UPDATE `repository` SET default_wiki_branch = 'master' WHERE (default_wiki_branch IS NULL) OR (default_wiki_branch = '')") + return err +} diff --git a/models/repo/repo.go b/models/repo/repo.go index f6758f1591a..1d17e565aef 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -136,6 +136,7 @@ type Repository struct { OriginalServiceType api.GitServiceType `xorm:"index"` OriginalURL string `xorm:"VARCHAR(2048)"` DefaultBranch string + DefaultWikiBranch string NumWatches int NumStars int @@ -285,6 +286,9 @@ func (repo *Repository) AfterLoad() { repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns + if repo.DefaultWikiBranch == "" { + repo.DefaultWikiBranch = setting.Repository.DefaultBranch + } } // LoadAttributes loads attributes of the repository. diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go index 3ca5eb36c6e..0cd07dcdc89 100644 --- a/modules/git/repo_base_gogit.go +++ b/modules/git/repo_base_gogit.go @@ -8,11 +8,11 @@ package git import ( "context" - "errors" "path/filepath" gitealog "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/go-git/go-billy/v5" "github.com/go-git/go-billy/v5/osfs" @@ -52,7 +52,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) { if err != nil { return nil, err } else if !isDir(repoPath) { - return nil, errors.New("no such file or directory") + return nil, util.NewNotExistErrorf("no such file or directory") } fs := osfs.New(repoPath) diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index 86b6a935677..7f6512200ba 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -9,10 +9,10 @@ package git import ( "bufio" "context" - "errors" "path/filepath" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" ) func init() { @@ -54,7 +54,7 @@ func OpenRepository(ctx context.Context, repoPath string) (*Repository, error) { if err != nil { return nil, err } else if !isDir(repoPath) { - return nil, errors.New("no such file or directory") + return nil, util.NewNotExistErrorf("no such file or directory") } // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3fc74959cad..59aae68cae6 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2092,6 +2092,8 @@ settings.branches.add_new_rule = Add New Rule settings.advanced_settings = Advanced Settings settings.wiki_desc = Enable Repository Wiki settings.use_internal_wiki = Use Built-In Wiki +settings.default_wiki_branch_name = Default Wiki Branch Name +settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch. settings.use_external_wiki = Use External Wiki settings.external_wiki_url = External Wiki URL settings.external_wiki_url_error = The external wiki URL is not a valid URL. diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 992a980d9e6..e045e3b8dcc 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -488,6 +488,13 @@ func SettingsPost(ctx *context.Context) { } } + if form.DefaultWikiBranch != "" { + if err := wiki_service.ChangeDefaultWikiBranch(ctx, repo, form.DefaultWikiBranch); err != nil { + log.Error("ChangeDefaultWikiBranch failed, err: %v", err) + ctx.Flash.Warning(ctx.Tr("repo.settings.failed_to_change_default_wiki_branch")) + } + } + if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { if !validation.IsValidExternalURL(form.ExternalTrackerURL) { ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 91cf727e2c5..88b63da88d9 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -93,17 +93,32 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error) } func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) { - wikiRepo, err := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository) - if err != nil { - ctx.ServerError("OpenRepository", err) - return nil, nil, err + wikiGitRepo, errGitRepo := gitrepo.OpenWikiRepository(ctx, ctx.Repo.Repository) + if errGitRepo != nil { + ctx.ServerError("OpenRepository", errGitRepo) + return nil, nil, errGitRepo } - commit, err := wikiRepo.GetBranchCommit(wiki_service.DefaultBranch) - if err != nil { - return wikiRepo, nil, err + commit, errCommit := wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch) + if git.IsErrNotExist(errCommit) { + // if the default branch recorded in database is out of sync, then re-sync it + gitRepoDefaultBranch, errBranch := wikiGitRepo.GetDefaultBranch() + if errBranch != nil { + return wikiGitRepo, nil, errBranch + } + // update the default branch in the database + errDb := repo_model.UpdateRepositoryCols(ctx, &repo_model.Repository{ID: ctx.Repo.Repository.ID, DefaultWikiBranch: gitRepoDefaultBranch}, "default_wiki_branch") + if errDb != nil { + return wikiGitRepo, nil, errDb + } + ctx.Repo.Repository.DefaultWikiBranch = gitRepoDefaultBranch + // retry to get the commit from the correct default branch + commit, errCommit = wikiGitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultWikiBranch) } - return wikiRepo, commit, nil + if errCommit != nil { + return wikiGitRepo, nil, errCommit + } + return wikiGitRepo, commit, nil } // wikiContentsByEntry returns the contents of the wiki page referenced by the @@ -316,7 +331,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename) ctx.Data["CommitCount"] = commitsCount return wikiRepo, entry @@ -368,7 +383,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) ctx.Data["footerContent"] = "" // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.DefaultWikiBranch, pageFilename) ctx.Data["CommitCount"] = commitsCount // get page @@ -380,7 +395,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) // get Commit Count commitsHistory, err := wikiRepo.CommitsByFileAndRange( git.CommitsByFileAndRangeOptions{ - Revision: wiki_service.DefaultBranch, + Revision: ctx.Repo.Repository.DefaultWikiBranch, File: pageFilename, Page: page, }) @@ -402,20 +417,17 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) func renderEditPage(ctx *context.Context) { wikiRepo, commit, err := findWikiRepoCommit(ctx) - if err != nil { + defer func() { if wikiRepo != nil { - wikiRepo.Close() + _ = wikiRepo.Close() } + }() + if err != nil { if !git.IsErrNotExist(err) { ctx.ServerError("GetBranchCommit", err) } return } - defer func() { - if wikiRepo != nil { - wikiRepo.Close() - } - }() // get requested pagename pageName := wiki_service.WebPathFromRequest(ctx.PathParamRaw("*")) @@ -584,17 +596,15 @@ func WikiPages(ctx *context.Context) { ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(unit.TypeWiki) && !ctx.Repo.Repository.IsArchived wikiRepo, commit, err := findWikiRepoCommit(ctx) - if err != nil { - if wikiRepo != nil { - wikiRepo.Close() - } - return - } defer func() { if wikiRepo != nil { - wikiRepo.Close() + _ = wikiRepo.Close() } }() + if err != nil { + ctx.Redirect(ctx.Repo.RepoLink + "/wiki") + return + } entries, err := commit.ListEntries() if err != nil { diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index 719cca30492..52e216e6a0a 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -9,6 +9,7 @@ import ( "net/url" "testing" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" @@ -221,3 +222,32 @@ func TestWikiRaw(t *testing.T) { } } } + +func TestDefaultWikiBranch(t *testing.T) { + unittest.PrepareTestEnv(t) + + assert.NoError(t, repo_model.UpdateRepositoryCols(db.DefaultContext, &repo_model.Repository{ID: 1, DefaultWikiBranch: "wrong-branch"})) + + ctx, _ := contexttest.MockContext(t, "user2/repo1/wiki") + ctx.SetParams("*", "Home") + contexttest.LoadRepo(t, ctx, 1) + assert.Equal(t, "wrong-branch", ctx.Repo.Repository.DefaultWikiBranch) + Wiki(ctx) // after the visiting, the out-of-sync database record will update the branch name to "master" + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.Equal(t, "master", ctx.Repo.Repository.DefaultWikiBranch) + + // invalid branch name should fail + assert.Error(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repo, "the bad name")) + repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.Equal(t, "master", repo.DefaultWikiBranch) + + // the same branch name, should succeed (actually a no-op) + assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repo, "master")) + repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.Equal(t, "master", repo.DefaultWikiBranch) + + // change to another name + assert.NoError(t, wiki_service.ChangeDefaultWikiBranch(db.DefaultContext, repo, "main")) + repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.Equal(t, "main", repo.DefaultWikiBranch) +} diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 8c3e458d2ff..e45a2a16955 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -133,6 +133,7 @@ type RepoSettingForm struct { EnableCode bool EnableWiki bool EnableExternalWiki bool + DefaultWikiBranch string ExternalWikiURL string EnableIssues bool EnableExternalTracker bool diff --git a/services/repository/create.go b/services/repository/create.go index c47ce9c4136..8d8c39197de 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -173,6 +173,7 @@ func initRepository(ctx context.Context, repoPath string, u *user_model.User, re } repo.DefaultBranch = setting.Repository.DefaultBranch + repo.DefaultWikiBranch = setting.Repository.DefaultBranch if len(opts.DefaultBranch) > 0 { repo.DefaultBranch = opts.DefaultBranch @@ -240,6 +241,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, u *user_model.User, opt TrustModel: opts.TrustModel, IsMirror: opts.IsMirror, DefaultBranch: opts.DefaultBranch, + DefaultWikiBranch: setting.Repository.DefaultBranch, ObjectFormatName: opts.ObjectFormatName, } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 51fdd90f540..aae2ddc1200 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -25,6 +25,54 @@ import ( "code.gitea.io/gitea/modules/util" ) +func cloneWiki(ctx context.Context, u *user_model.User, opts migration.MigrateOptions, migrateTimeout time.Duration) (string, error) { + wikiPath := repo_model.WikiPath(u.Name, opts.RepoName) + wikiRemotePath := repo_module.WikiRemoteURL(ctx, opts.CloneAddr) + if wikiRemotePath == "" { + return "", nil + } + + if err := util.RemoveAll(wikiPath); err != nil { + return "", fmt.Errorf("failed to remove existing wiki dir %q, err: %w", wikiPath, err) + } + + cleanIncompleteWikiPath := func() { + if err := util.RemoveAll(wikiPath); err != nil { + log.Error("Failed to remove incomplete wiki dir %q, err: %v", wikiPath, err) + } + } + if err := git.Clone(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + SkipTLSVerify: setting.Migrations.SkipTLSVerify, + }); err != nil { + log.Error("Clone wiki failed, err: %v", err) + cleanIncompleteWikiPath() + return "", err + } + + if err := git.WriteCommitGraph(ctx, wikiPath); err != nil { + cleanIncompleteWikiPath() + return "", err + } + + wikiRepo, err := git.OpenRepository(ctx, wikiPath) + if err != nil { + cleanIncompleteWikiPath() + return "", fmt.Errorf("failed to open wiki repo %q, err: %w", wikiPath, err) + } + defer wikiRepo.Close() + + defaultBranch, err := wikiRepo.GetDefaultBranch() + if err != nil { + cleanIncompleteWikiPath() + return "", fmt.Errorf("failed to get wiki repo default branch for %q, err: %w", wikiPath, err) + } + + return defaultBranch, nil +} + // MigrateRepositoryGitData starts migrating git related data after created migrating repository func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, repo *repo_model.Repository, opts migration.MigrateOptions, @@ -44,21 +92,20 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second - var err error - if err = util.RemoveAll(repoPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", repoPath, err) + if err := util.RemoveAll(repoPath); err != nil { + return repo, fmt.Errorf("failed to remove existing repo dir %q, err: %w", repoPath, err) } - if err = git.Clone(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ + if err := git.Clone(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ Mirror: true, Quiet: true, Timeout: migrateTimeout, SkipTLSVerify: setting.Migrations.SkipTLSVerify, }); err != nil { if errors.Is(err, context.DeadlineExceeded) { - return repo, fmt.Errorf("Clone timed out. Consider increasing [git.timeout] MIGRATE in app.ini. Underlying Error: %w", err) + return repo, fmt.Errorf("clone timed out, consider increasing [git.timeout] MIGRATE in app.ini, underlying err: %w", err) } - return repo, fmt.Errorf("Clone: %w", err) + return repo, fmt.Errorf("clone error: %w", err) } if err := git.WriteCommitGraph(ctx, repoPath); err != nil { @@ -66,37 +113,18 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } if opts.Wiki { - wikiPath := repo_model.WikiPath(u.Name, opts.RepoName) - wikiRemotePath := repo_module.WikiRemoteURL(ctx, opts.CloneAddr) - if len(wikiRemotePath) > 0 { - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - - if err := git.Clone(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ - Mirror: true, - Quiet: true, - Timeout: migrateTimeout, - Branch: "master", - SkipTLSVerify: setting.Migrations.SkipTLSVerify, - }); err != nil { - log.Warn("Clone wiki: %v", err) - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - } else { - if err := git.WriteCommitGraph(ctx, wikiPath); err != nil { - return repo, err - } - } + defaultWikiBranch, err := cloneWiki(ctx, u, opts, migrateTimeout) + if err != nil { + return repo, fmt.Errorf("clone wiki error: %w", err) } + repo.DefaultWikiBranch = defaultWikiBranch } if repo.OwnerID == u.ID { repo.Owner = u } - if err = repo_module.CheckDaemonExportOK(ctx, repo); err != nil { + if err := repo_module.CheckDaemonExportOK(ctx, repo); err != nil { return repo, fmt.Errorf("checkDaemonExportOK: %w", err) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index 50d52d3140f..6f1ca120b0a 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -6,18 +6,22 @@ package wiki import ( "context" + "errors" "fmt" "os" "strings" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/sync" + "code.gitea.io/gitea/modules/util" asymkey_service "code.gitea.io/gitea/services/asymkey" repo_service "code.gitea.io/gitea/services/repository" ) @@ -25,10 +29,7 @@ import ( // TODO: use clustered lock (unique queue? or *abuse* cache) var wikiWorkingPool = sync.NewExclusivePool() -const ( - DefaultRemote = "origin" - DefaultBranch = "master" -) +const DefaultRemote = "origin" // InitWiki initializes a wiki for repository, // it does nothing when repository already has wiki. @@ -41,25 +42,25 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error { return fmt.Errorf("InitRepository: %w", err) } else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) - } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil { - return fmt.Errorf("unable to set default wiki branch to master: %w", err) + } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD").AddDynamicArguments(git.BranchPrefix + repo.DefaultWikiBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil { + return fmt.Errorf("unable to set default wiki branch to %q: %w", repo.DefaultWikiBranch, err) } return nil } // prepareGitPath try to find a suitable file path with file name by the given raw wiki name. // return: existence, prepared file path with name, error -func prepareGitPath(gitRepo *git.Repository, wikiPath WebPath) (bool, string, error) { +func prepareGitPath(gitRepo *git.Repository, defaultWikiBranch string, wikiPath WebPath) (bool, string, error) { unescaped := string(wikiPath) + ".md" gitPath := WebPathToGitPath(wikiPath) // Look for both files - filesInIndex, err := gitRepo.LsTree(DefaultBranch, unescaped, gitPath) + filesInIndex, err := gitRepo.LsTree(defaultWikiBranch, unescaped, gitPath) if err != nil { - if strings.Contains(err.Error(), "Not a valid object name master") { - return false, gitPath, nil + if strings.Contains(err.Error(), "Not a valid object name") { + return false, gitPath, nil // branch doesn't exist } - log.Error("%v", err) + log.Error("Wiki LsTree failed, err: %v", err) return false, gitPath, err } @@ -95,7 +96,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return fmt.Errorf("InitWiki: %w", err) } - hasMasterBranch := git.IsBranchExist(ctx, repo.WikiPath(), DefaultBranch) + hasDefaultBranch := git.IsBranchExist(ctx, repo.WikiPath(), repo.DefaultWikiBranch) basePath, err := repo_module.CreateTemporaryPath("update-wiki") if err != nil { @@ -112,8 +113,8 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model Shared: true, } - if hasMasterBranch { - cloneOpts.Branch = DefaultBranch + if hasDefaultBranch { + cloneOpts.Branch = repo.DefaultWikiBranch } if err := git.Clone(ctx, repo.WikiPath(), basePath, cloneOpts); err != nil { @@ -128,14 +129,14 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model } defer gitRepo.Close() - if hasMasterBranch { + if hasDefaultBranch { if err := gitRepo.ReadTreeToIndex("HEAD"); err != nil { log.Error("Unable to read HEAD tree to index in: %s %v", basePath, err) return fmt.Errorf("fnable to read HEAD tree to index in: %s %w", basePath, err) } } - isWikiExist, newWikiPath, err := prepareGitPath(gitRepo, newWikiName) + isWikiExist, newWikiPath, err := prepareGitPath(gitRepo, repo.DefaultWikiBranch, newWikiName) if err != nil { return err } @@ -151,7 +152,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model isOldWikiExist := true oldWikiPath := newWikiPath if oldWikiName != newWikiName { - isOldWikiExist, oldWikiPath, err = prepareGitPath(gitRepo, oldWikiName) + isOldWikiExist, oldWikiPath, err = prepareGitPath(gitRepo, repo.DefaultWikiBranch, oldWikiName) if err != nil { return err } @@ -200,7 +201,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model } else { commitTreeOpts.NoGPGSign = true } - if hasMasterBranch { + if hasDefaultBranch { commitTreeOpts.Parents = []string{"HEAD"} } @@ -212,7 +213,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{ Remote: DefaultRemote, - Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, DefaultBranch), + Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, repo.DefaultWikiBranch), Env: repo_module.FullPushingEnvironment( doer, doer, @@ -269,7 +270,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model if err := git.Clone(ctx, repo.WikiPath(), basePath, git.CloneRepoOptions{ Bare: true, Shared: true, - Branch: DefaultBranch, + Branch: repo.DefaultWikiBranch, }); err != nil { log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err) return fmt.Errorf("failed to clone repository: %s (%w)", repo.FullName(), err) @@ -287,7 +288,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model return fmt.Errorf("unable to read HEAD tree to index in: %s %w", basePath, err) } - found, wikiPath, err := prepareGitPath(gitRepo, wikiName) + found, wikiPath, err := prepareGitPath(gitRepo, repo.DefaultWikiBranch, wikiName) if err != nil { return err } @@ -331,7 +332,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{ Remote: DefaultRemote, - Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, DefaultBranch), + Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, repo.DefaultWikiBranch), Env: repo_module.FullPushingEnvironment( doer, doer, @@ -358,3 +359,37 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error { system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath()) return nil } + +func ChangeDefaultWikiBranch(ctx context.Context, repo *repo_model.Repository, newBranch string) error { + if !git.IsValidRefPattern(newBranch) { + return fmt.Errorf("invalid branch name: %s", newBranch) + } + return db.WithTx(ctx, func(ctx context.Context) error { + repo.DefaultWikiBranch = newBranch + if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_wiki_branch"); err != nil { + return fmt.Errorf("unable to update database: %w", err) + } + + gitRepo, err := gitrepo.OpenWikiRepository(ctx, repo) + if errors.Is(err, util.ErrNotExist) { + return nil // no git repo on storage, no need to do anything else + } else if err != nil { + return fmt.Errorf("unable to open repository: %w", err) + } + defer gitRepo.Close() + + oldDefBranch, err := gitRepo.GetDefaultBranch() + if err != nil { + return fmt.Errorf("unable to get default branch: %w", err) + } + if oldDefBranch == newBranch { + return nil + } + + err = gitRepo.RenameBranch(oldDefBranch, newBranch) + if err != nil { + return fmt.Errorf("unable to rename default branch: %w", err) + } + return nil + }) +} diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index 59c77060f2c..0a18cffa253 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -170,7 +170,7 @@ func TestRepository_AddWikiPage(t *testing.T) { return } defer gitRepo.Close() - masterTree, err := gitRepo.GetTree(DefaultBranch) + masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch) assert.NoError(t, err) gitPath := WebPathToGitPath(webPath) entry, err := masterTree.GetTreeEntryByPath(gitPath) @@ -215,7 +215,7 @@ func TestRepository_EditWikiPage(t *testing.T) { // Now need to show that the page has been added: gitRepo, err := gitrepo.OpenWikiRepository(git.DefaultContext, repo) assert.NoError(t, err) - masterTree, err := gitRepo.GetTree(DefaultBranch) + masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch) assert.NoError(t, err) gitPath := WebPathToGitPath(webPath) entry, err := masterTree.GetTreeEntryByPath(gitPath) @@ -242,7 +242,7 @@ func TestRepository_DeleteWikiPage(t *testing.T) { return } defer gitRepo.Close() - masterTree, err := gitRepo.GetTree(DefaultBranch) + masterTree, err := gitRepo.GetTree(repo.DefaultWikiBranch) assert.NoError(t, err) gitPath := WebPathToGitPath("Home") _, err = masterTree.GetTreeEntryByPath(gitPath) @@ -280,7 +280,7 @@ func TestPrepareWikiFileName(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { webPath := UserTitleToWebPath("", tt.arg) - existence, newWikiPath, err := prepareGitPath(gitRepo, webPath) + existence, newWikiPath, err := prepareGitPath(gitRepo, repo.DefaultWikiBranch, webPath) if (err != nil) != tt.wantErr { assert.NoError(t, err) return @@ -312,7 +312,7 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) { } defer gitRepo.Close() - existence, newWikiPath, err := prepareGitPath(gitRepo, "Home") + existence, newWikiPath, err := prepareGitPath(gitRepo, "master", "Home") assert.False(t, existence) assert.NoError(t, err) assert.EqualValues(t, "Home.md", newWikiPath) diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 0de42b34ea8..a699396a84b 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -335,6 +335,10 @@ +
+ + +
diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index ff31df0c32d..640f8ca9cd6 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -36,9 +36,8 @@
- + {{ctx.Locale.Tr "cancel"}} +
diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl index 22eb2619f91..cace0d48e00 100644 --- a/templates/repo/wiki/pages.tmpl +++ b/templates/repo/wiki/pages.tmpl @@ -10,6 +10,7 @@ {{end}} + {{if .IsRepositoryAdmin}}
{{ctx.Locale.Tr "repo.default_branch"}}: {{.Repository.DefaultWikiBranch}}
{{end}} {{range .Pages}}