Add HEAD fix to gitea doctor (#21352) (#21751)

Backport #21352

Due to a bug in presumably an older version of Gitea, multiple of my
repositories still have their HEADs pointing to a `master` branch while
the default branch on the UI is listed as `main`. This adds a `gitea
doctor` command that will fix all of the HEAD references for repos when
they're not synchronized with the default branch in the DB.

This will help with cloning to ensure that git automatically checks out
the right branch, instead of a nonexistent one.

Note: I'm not sure if I actually need to do more other than add a file
here. Will try testing this out on my server soon.

Co-authored-by: Clar Fon <15850505+clarfonthey@users.noreply.github.com>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
Xinyu Zhou 2022-11-11 14:38:52 +08:00 committed by GitHub
parent f241201484
commit f321cdced7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

91
modules/doctor/heads.go Normal file
View file

@ -0,0 +1,91 @@
// Copyright 2022 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 doctor
import (
"context"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
)
func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error {
numRepos := 0
numHeadsBroken := 0
numDefaultBranchesBroken := 0
numReposUpdated := 0
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
numRepos++
runOpts := &git.RunOpts{Dir: repo.RepoPath()}
_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts)
head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts)
// what we expect: default branch is valid, and HEAD points to it
if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
return nil
}
if headErr != nil {
numHeadsBroken++
}
if defaultBranchErr != nil {
numDefaultBranchesBroken++
}
// if default branch is broken, let the user fix that in the UI
if defaultBranchErr != nil {
logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name)
return nil
}
// if we're not autofixing, that's all we can do
if !autofix {
return nil
}
// otherwise, let's try fixing HEAD
err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts)
if err != nil {
logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
return nil
}
numReposUpdated++
return nil
})
if err != nil {
logger.Critical("Error when fixing repo HEADs: %v", err)
}
if autofix {
logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated)
} else {
if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 {
logger.Info("All %d repos have their HEADs in the correct state")
} else {
if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 {
logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos)
} else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 {
logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos)
} else {
logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken)
}
}
}
return err
}
func init() {
Register(&Check{
Title: "Synchronize repo HEADs",
Name: "synchronize-repo-heads",
IsDefault: true,
Run: synchronizeRepoHeads,
Priority: 7,
})
}