// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main // checks that repos are in a "normal" state import ( "fmt" "path/filepath" "strings" "time" "go.wit.com/lib/config" "go.wit.com/lib/fhelp" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/forgepb" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" "google.golang.org/protobuf/proto" ) // This does lots of "sanity" checking. It digs around and tries to resolve as much as possible // It'll dig into the actual git patchId to match patches since the normal hashes don't match // when you are applying with 'git am'. It tries to be thourogh (I can't spell) func doNormal() (string, error) { if doNormalOld() { if me.forge.Config.Mode != forgepb.ForgeMode_NORMAL { setForgeMode(forgepb.ForgeMode_NORMAL) me.forge.Config.Save() } s := fmt.Sprintf("all your %d repos are in a normal stete for development", me.forge.Repos.Len()) return s, nil } start := time.Now() err := me.forge.DoAllCheckoutUser(argv.Force) me.forge.ScanRepoDir() // looks for new dirs, checks existing repos for changes me.forge.SaveRepos() dur := time.Since(start) log.Printf("Checked out %d user braches in %s\n", me.forge.Repos.Len(), shell.FormatDuration(dur)) if err != nil { return "not everything is 'normal' yet", err } setForgeMode(forgepb.ForgeMode_NORMAL) me.forge.Config.Save() log.Info("normal mode on") return "normal mode on", nil } func doNormalOld() bool { me.forge.CheckDirtyQuiet() var count int stats := me.forge.RillRepos(checkNormalRepoState) for path, stat := range stats { dur := stat.End.Sub(stat.Start) if dur > 10*time.Second { log.Infof("%s checkNormalRepoState() took a long time (%s)\n", path, shell.FormatDuration(dur)) } if stat.Err == nil { continue } repo := me.forge.Repos.FindByFullPath(path) if repo == nil { log.Info("path deleted while running?", path) continue } if stat.Err == ErrorLocalDevelBranch { if argv.Fix { bname := repo.GetDevelBranchName() s := fmt.Sprintf("repair the %s branch on %s", bname, repo.FullPath) if fhelp.QuestionUser(s) { // todo, check to make sure we aren't on this branch repo.RunVerbose([]string{"git", "checkout", repo.GetMasterBranchName()}) repo.RunVerbose([]string{"git", "branch", "-D", bname}) repo.RunVerbose([]string{"git", "checkout", bname}) } } } if stat.Err == ErrorLocalMasterBranch { if argv.Fix { bname := repo.GetMasterBranchName() s := fmt.Sprintf("repair the %s branch on %s", bname, repo.FullPath) if fhelp.QuestionUser(s) { // todo, check to make sure we aren't on this branch repo.RunVerbose([]string{"git", "branch", "-D", bname}) repo.RunVerbose([]string{"git", "checkout", bname}) } } } if stat.Err == ErrorLocalBehindDevel { log.Info(path, "local branch is behind devel?") } // log.Infof("%-60s, %-60s %v %s\n", stat.Start, stat.End.String(), dur, path) // log.Infof("%-30v %s %v\n", dur, path, stat.Err) // log.Info("got path", path, stat.Err) count += 1 } if count > 0 { log.Info("Some repos are not in a 'normal' state. error count =", count) log.Info("TODO: list the repos here. forge patch repos?") found := findWorkRepos() found.SortNamespace() if found.Len() == 0 { log.Info("you currently have no repos with patches") // return "you currently have no repos with patches", nil } footer := me.forge.PrintDefaultTB(found) log.Info("repos with patches or unsaved changes: " + footer) // return "repos with patches or unsaved changes: " + footer, nil config.SetChanged("repos", true) return false } return true } // 99% of the time, the repos during development should be on your user branch. // error out if it's not // this checks to see if a devel and user branch exist // this needs to run each time in case repos were added manually by the user // this also verifies that func checkNormalRepoState(repo *gitpb.Repo) error { var err error tmp := filepath.Join(me.forge.Config.ReposDir, repo.GetNamespace()) if tmp != repo.FullPath { log.Infof("%s != %s\n", repo.FullPath, tmp) if strings.HasPrefix(repo.FullPath, me.forge.Config.ReposDir) { tmp = strings.TrimPrefix(repo.FullPath, me.forge.Config.ReposDir) tmp = strings.Trim(tmp, "/") repo.Namespace = tmp err = log.Errorf("namespace set to filepath") } } else { // log.Infof("%s == %s\n", repo.FullPath, tmp) } tmp = strings.Trim(repo.Namespace, "/") if tmp != repo.Namespace { err = log.Errorf("junk in ns %s", repo.Namespace) repo.Namespace = tmp } if repo.GetMasterBranchName() == "" { me.forge.VerifyBranchNames(repo) log.Info("ABNORMAL: master branch name was blank in", repo.GetFullPath()) } if repo.GetMasterBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("master branch name blank") } if repo.GetDevelBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("devel branch name blank") } if repo.GetUserBranchName() == "" { me.forge.VerifyBranchNames(repo) err = log.Errorf("user branch name blank") } if repo.GetGoPath() == repo.GetNamespace() { // log.Info(repo.FullPath, "gopath == namespace", repo.GetGoPath(), repo.GetNamespace()) } else { log.Info(repo.FullPath, "gopath != namespace", repo.GetGoPath(), repo.GetNamespace()) } repo.MakeLocalDevelBranch() if !repo.VerifyRemoteAndLocalBranches(repo.GetDevelBranchName()) { return ErrorLocalDevelBranch } if !repo.VerifyRemoteAndLocalBranches(repo.GetMasterBranchName()) { return ErrorLocalMasterBranch } if repo.Tags.Master == nil { if found := repo.GetRemoteTag(repo.GetMasterBranchName()); found != nil { // log.Info("found master tag ", repo.FullPath, found) repo.Tags.Master = proto.Clone(found).(*gitpb.GitTag) config.SetChanged("repos", true) } else { log.Info("not found master tag (Reload() ?)", repo.FullPath) } } if repo.IsBranchRemote(repo.GetUserBranchName()) { log.Info(repo.FullPath) } // check to see if the user branch is behind the devel branch if repo.GetUserVersion() != repo.GetDevelVersion() { uver := repo.NewCompareTag(repo.GetUserBranchName()) dver := repo.NewCompareTag(repo.GetDevelBranchName()) if uver == nil { log.Info(repo.FullPath, "uver == nil") // make user here (should have already happened) return ErrorNoUserBranch } if dver == nil { log.Info(repo.FullPath, "dver == nil") // make dev here (should have already happened) return ErrorNoDevelBranch } /* // THIS IS WRONG LOGIC if len(dver.GreaterThan(uver)) == 0 { log.Info(repo.FullPath, "usr < dev") repo.State = "usr < dev" // check if nothing new exists in user, then delete return ErrorLocalBehindDevel } else { repo.State = "normal" } */ // everything is fine } if repo.GetCurrentBranchName() != repo.GetUserBranchName() { log.Infof("changing to user(%s) branch: %s\n", repo.GetUserBranchName(), repo.FullPath) repo.CheckoutUser() repo.ReloadCheck() err = log.Errorf("now on user branch") } // hopefully this can be deprecated soon if repo.Namespace != repo.GetGoPath() { log.Info(repo.Namespace, repo.GetGoPath()) } // verify read-only if me.forge.Config.IsReadOnly(repo.GetNamespace()) != repo.GetReadOnly() { repo.ReadOnly = me.forge.Config.IsReadOnly(repo.GetNamespace()) log.Info("damnit", repo.FullPath) err = log.Errorf("readonly bit wrong") } return err }