// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Use of this source code is governed by the GPL 3.0 package main import ( "fmt" "path/filepath" "go.wit.com/lib/protobuf/gitpb" "go.wit.com/log" ) var ErrorReposHasLocalBranches error = fmt.Errorf("repo still has local branches") var ErrorMergeBranch error = fmt.Errorf("trunk has things not in the branch") var ErrorMergeTrunk error = fmt.Errorf("branch has things not in trunk") func doClean() error { if argv.Clean.Pub != nil { if err := doCleanPub(); err != nil { badExit(err) } log.Info("finished attempt at cleaning devel branches") return nil } if argv.Clean.Devel != nil { if err := doCleanDevel(); err != nil { badExit(err) } log.Info("finished attempt at cleaning devel branches") return nil } if argv.Clean.User != nil { if err := doCleanUser(); err != nil { log.Info(err) okExit("") } return nil } return nil } func doCleanUser() error { if _, count, _, err := IsEverythingOnMaster(); err != nil { if count == 0 { log.Info("No repos are on the master branch") return nil } log.Info("Not all repos are on the master branch") // return err } var anyerr error all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if err := doCleanUserRepo(repo); err != nil { log.Info(repo.GetGoPath(), err) anyerr = err } } return anyerr } /* func doesLocalBranchExist(repo *gitpb.Repo, branch string) bool { return repo.Exists(filepath.Join(".git/refs/heads", branch)) } */ func doCleanDevel() error { var total int var count int all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() total += 1 // devel := repo.GetDevelBranchName() if repo.GetDevelVersion() == "derr" { // already deleted return nil } /* if !doesLocalBranchExist(repo, devel) { if argv.Verbose { log.Info("local branch was already deleted:", repo.GetGoPath()) } continue } */ if repo.GetCurrentBranchName() != repo.GetMasterBranchName() { log.Info("Repo not on master branch:", repo.GetGoPath()) continue } if repo.IsDirty() { log.Info("Repo is dirty:", repo.GetGoPath()) continue } count += 1 if err := justDeleteTheDevelBranchAlready(repo); err != nil { log.Info("justDeleteTheDevel() err", repo.GetGoPath(), err) } } log.Info("") log.Printf("attempted cleaning %d devel branches of %d total branches\n", count, total) return nil } /* func checkhashes(repo *gitpb.Repo, hashes []string, refpath string) ([]string, error) { if !repo.Exists(refpath) { return hashes, nil } r, err := repo.RunStrict([]string{"cat", refpath}) if err != nil { return hashes, err } newhash := r.Stdout[0] for _, hash := range hashes { if newhash != hash { return hashes, fmt.Errorf("%s hash broke %s %s", repo.GetGoPath(), newhash, hash) } } hashes = append(hashes, newhash) return hashes, nil } */ // removes all local branches func doCleanUserRepo(repo *gitpb.Repo) error { if repo.IsDirty() { return nil } bruser := repo.GetUserBranchName() brdevel := repo.GetDevelBranchName() if repo.GetUserVersion() == "uerr" { // already deleted return nil } log.Info("trying", bruser, repo.GetUserVersion()) if repo.IsBranchRemote(bruser) { log.Info("forge is designed to always have local only user branches", bruser) return fmt.Errorf("forge is designed to always have local only user branches") } b1 := repo.CountDiffObjects(bruser, brdevel) // should be zero if b1 == 0 { cmd := []string{"git", "branch", "-D", bruser} log.Info("USER IS IN DEVEL", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } return fmt.Errorf("%s branch has things not in %s count=%d", bruser, brdevel, b1) } func doCleanPub() error { total := 0 all := me.forge.Repos.SortByFullPath() for all.Scan() { repo := all.Next() if repo.GetTargetVersion() != "" { repo.SetTargetVersion("") configSave = true total += 1 } } log.Printf("clearing %d total repos\n", total) return nil } // if you call this, there is no going back. no checks anymore. nothing // it deletes the 'devel' branch. git branch -D "devel". END OF STORY func justDeleteTheDevelBranchAlready(repo *gitpb.Repo) error { branch := repo.GetDevelBranchName() remote := filepath.Join("origin", branch) if me.forge.Config.IsReadOnly(repo.GetGoPath()) { if repo.IsDevelRemote() { // just make sure the remote & local branches are the same return repo.DeleteLocalDevelBranch() } } // check against remote if it exists if repo.IsDevelRemote() { b1 := repo.CountDiffObjects(branch, remote) // should be zero if b1 == 0 { cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()} log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } cmd := []string{"git", "push"} log.Info("DEVEL LOCAL NEEDS GIT PUSH TO REMOTE", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } // remote doesn't exist, check against master master := repo.GetMasterBranchName() b1 := repo.CountDiffObjects(branch, master) // should be zero if b1 == 0 { cmd := []string{"git", "branch", "-D", repo.GetDevelBranchName()} log.Info("DEVEL IS IN REMOTE", repo.GetGoPath(), cmd) err := repo.RunVerbose(cmd) return err } cmd := []string{"git", "merge something somehow"} log.Info("DEVEL LOCAL NEEDS GIT MERGE TO MASTER", repo.GetGoPath(), cmd, b1) // _, err := repo.RunVerbose(cmd) return nil }