code.gitea.io/gitea@v1.19.3/modules/doctor/heads.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package doctor 5 6 import ( 7 "context" 8 9 repo_model "code.gitea.io/gitea/models/repo" 10 "code.gitea.io/gitea/modules/git" 11 "code.gitea.io/gitea/modules/log" 12 ) 13 14 func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error { 15 numRepos := 0 16 numHeadsBroken := 0 17 numDefaultBranchesBroken := 0 18 numReposUpdated := 0 19 err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { 20 numRepos++ 21 _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) 22 23 head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) 24 25 // what we expect: default branch is valid, and HEAD points to it 26 if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { 27 return nil 28 } 29 30 if headErr != nil { 31 numHeadsBroken++ 32 } 33 if defaultBranchErr != nil { 34 numDefaultBranchesBroken++ 35 } 36 37 // if default branch is broken, let the user fix that in the UI 38 if defaultBranchErr != nil { 39 logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name) 40 return nil 41 } 42 43 // if we're not autofixing, that's all we can do 44 if !autofix { 45 return nil 46 } 47 48 // otherwise, let's try fixing HEAD 49 err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()}) 50 if err != nil { 51 logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) 52 return nil 53 } 54 numReposUpdated++ 55 return nil 56 }) 57 if err != nil { 58 logger.Critical("Error when fixing repo HEADs: %v", err) 59 } 60 61 if autofix { 62 logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated) 63 } else { 64 if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 { 65 logger.Info("All %d repos have their HEADs in the correct state", numRepos) 66 } else { 67 if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 { 68 logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos) 69 } else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 { 70 logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos) 71 } else { 72 logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken) 73 } 74 } 75 } 76 77 return err 78 } 79 80 func init() { 81 Register(&Check{ 82 Title: "Synchronize repo HEADs", 83 Name: "synchronize-repo-heads", 84 IsDefault: true, 85 Run: synchronizeRepoHeads, 86 Priority: 7, 87 }) 88 }