package main import ( "fmt" "path/filepath" "slices" "strings" "time" "go.wit.com/lib/gui/shell" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) func doExamine() error { me.found = new(gitpb.Repos) all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if tag := repo.ExamineBranches(); tag != nil { me.found.AppendByGoPath(repo) // ctime := tag.Creatordate.AsTime() // dur := time.Since(ctime) // log.Printf("UNKNOWN BRANCH %-50s %s %4s %s\n", repo.GetFullPath(), tag.Hash, shell.FormatDuration(dur), tag.Refname) repo.CurrentTag = tag } else { repo.CurrentTag = nil } } if len(me.found.Repos) == 0 { return nil } // slices.Reverse(me.found.Repos) slices.SortFunc(me.found.Repos, func(a, b *gitpb.Repo) int { atime := a.CurrentTag.Creatordate.AsTime() btime := b.CurrentTag.Creatordate.AsTime() if atime.Before(btime) { // log.Info("atime vs btime is 0", atime, btime) return 1 } // log.Info("atime vs btime is 1", atime, btime) return -1 }) all = me.found.All() me.found = new(gitpb.Repos) for all.Scan() { repo := all.Next() if isNormal(repo) { continue } tag := repo.CurrentTag ctime := tag.Creatordate.AsTime() dur := time.Since(ctime) log.Printf("UNKNOWN BRANCH %-50s %s %4s %s\n", repo.GetFullPath(), tag.Hash, shell.FormatDuration(dur), tag.Refname) err := examineBranch(repo) if argv.Clean.Examine.Fix != nil { if err != nil { badExit(err) } } me.found.AppendByGoPath(repo) } me.forge.PrintHumanTableDirty(me.found) return nil } func isNormal(repo *gitpb.Repo) bool { if repo.IsDirty() { return true } if repo.GetUserVersion() != repo.GetDevelVersion() { return true } return false } func examineBranch(repo *gitpb.Repo) error { if !isLocal(repo) { base := filepath.Base(repo.CurrentTag.Refname) if base == repo.GetUserBranchName() { log.Info("The user branch also has a remote branch", repo.CurrentTag.Refname) log.Info("TODO: verify the remote branch is out of date", repo.CurrentTag.Refname) log.Info("TODO: delete the remote branch", repo.CurrentTag.Refname) return nil } if repo.Exists(filepath.Join(".git/refs/heads", base)) { repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"}) repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/remotes/origin", base)}) repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/heads", base)}) log.Info("why is this local branch a problem?", repo.CurrentTag.Refname) } else { repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"}) log.Info("why is this non-local branch a problem?", repo.CurrentTag.Refname) r, err := repo.RunVerbose([]string{"cat", filepath.Join(".git/refs/remotes/origin", base)}) if err == nil { cmd := []string{"git", "show", "-s", "--format=\"%H %ae %as %s\"", r.Stdout[0]} repo.RunVerbose(cmd) log.Info(cmd) return nil } } return fmt.Errorf("%s repo.CurrentTag is not local: %s. Don't proceed yet", repo.GetGoPath(), repo.CurrentTag.Refname) } dcount, err := showNotMaster(repo) if err != nil { return err } if repo.CurrentTag == nil { return fmt.Errorf("repo.CurrentTag == nil") } userbranch := repo.GetUserBranchName() if repo.CurrentTag.Refname == userbranch { return requiresGitPush(repo, userbranch) } if repo.CurrentTag.Refname == "origin/"+userbranch { return requiresGitPush(repo, userbranch) } if len(dcount) == 0 { if repo.CurrentTag.Refname == repo.GetMasterBranchName() { err = fmt.Errorf("examineBranch() SPECIAL CASE. %s is the master branch", repo.CurrentTag.Refname) log.Info(err) return nil } err = fmt.Errorf("examineBranch() branch differs. patch diff len == 0. PROBABLY DELETE BRANCH %s", repo.CurrentTag.Refname) log.Info(err) cmd := repo.ConstructGitDiffLog(repo.CurrentTag.Refname, repo.GetMasterBranchName()) if argv.Clean.Examine.Fix == nil { log.Info(repo.GetGoPath(), cmd) } else { if _, err := repo.RunVerbose(cmd); err != nil { return err } } cmd = repo.ConstructGitDiffLog(repo.GetMasterBranchName(), repo.CurrentTag.Refname) if argv.Clean.Examine.Fix == nil { log.Info(repo.GetGoPath(), cmd) } else { if _, err := repo.RunVerbose(cmd); err != nil { return err } } cmd = []string{"git", "branch", "-D", repo.CurrentTag.Refname} log.Info(repo.GetGoPath(), "TRY THIS:", cmd) if argv.Clean.Examine.Fix == nil { log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname) repo.RunVerbose([]string{"ls", "-l", ".git/refs/remotes/origin"}) } else { log.Info(repo.GetGoPath(), "TODO: CHECK REMOTE BRANCH DOES NOT EXIST", repo.CurrentTag.Refname) if _, err := repo.RunVerbose(cmd); err != nil { return err } else { return nil } } return err } err = fmt.Errorf("examineBranch() branch differs, but not sure how to fix it yet == %d", len(dcount)) log.Info(err) return nil } func checkJcarr(repo *gitpb.Repo) int { b1 := countDiffObjects(repo, "jcarr", "origin/jcarr") b2 := countDiffObjects(repo, "origin/jcarr", "jcarr") log.Info("jcarr vs origin count", b1, b2) if b1 == 0 && b2 == 0 { return 0 } if b1 != 0 { log.Info("jcarr vs origin count b1 != 0, b2 ==", b1, b2) log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1, b2) return b1 } if b2 != 0 { log.Info("jcarr vs origin count b2 != 0, b1 ==", b2, b1) return b2 } return -1 } func isLocal(repo *gitpb.Repo) bool { base, name := filepath.Split(repo.CurrentTag.Refname) if name == "" { return false } if base == "" { return true } return false } func showNotMaster(repo *gitpb.Repo) ([]string, error) { var cmd []string cmd = append(cmd, "git") cmd = append(cmd, "log") cmd = append(cmd, "--format=\"%H\"") cmd = append(cmd, repo.CurrentTag.Hash) cmd = append(cmd, "--not") cmd = append(cmd, repo.GetMasterBranchName()) r, err := repo.RunVerboseOnError(cmd) return r.Stdout, err } func showNotDevel(repo *gitpb.Repo) ([]string, error) { var cmd []string cmd = append(cmd, "git") cmd = append(cmd, "log") cmd = append(cmd, "--format=\"%H\"") cmd = append(cmd, repo.CurrentTag.Hash) cmd = append(cmd, "--not") cmd = append(cmd, "devel") r, err := repo.RunVerboseOnError(cmd) return r.Stdout, err } // count all objects only in branch1 func countDiffObjects(repo *gitpb.Repo, branch1, branch2 string) int { cmd := repo.ConstructGitDiffLog(branch1, branch2) r, err := repo.RunVerboseOnError(cmd) if err != nil { return -1 } log.Info("countDiffObjects()", cmd, len(r.Stdout), strings.Join(r.Stdout, " ")) return len(r.Stdout) } /* func constructGitDiffLog(repo *gitpb.Repo, branch1, branch2 string) []string { var cmd []string cmd = append(cmd, "git") cmd = append(cmd, "log") cmd = append(cmd, "--format=\"%H\"") cmd = append(cmd, branch1) cmd = append(cmd, "--not") cmd = append(cmd, branch2) return cmd } */ // count all objects only in branch1 func gitPushStrict(repo *gitpb.Repo, branchName string) error { var cmd []string cmd = append(cmd, "git") cmd = append(cmd, "push") _, err := repo.RunVerbose(cmd) if err != nil { cmd = []string{"git", "whatchanged", repo.CurrentTag.Hash, "-1"} repo.RunVerbose(cmd) } return err } func requiresGitPush(repo *gitpb.Repo, branchName string) error { b1 := countDiffObjects(repo, branchName, "origin/"+branchName) b2 := countDiffObjects(repo, "origin/"+branchName, branchName) log.Info(branchName, "vs origin count", b1, b2) if b1 == 0 && b2 == 0 { return nil } if b1 != 0 { log.Info(branchName, "vs origin count b1 != 0, b2 ==", b1, b2) log.Info("THIS MEANS THE LOCAL BRANCH NEEDS GIT PUSH TO ORIGIN BRANCH ==", b1) if argv.Clean.Examine.Fix != nil { return gitPushStrict(repo, branchName) } return nil } return nil }